docs(user-docs): cockpit rechrome, two-path reposition, CLI reframe, sphere gaze fix
- Rechrome VitePress theme to the RelayRefresh cockpit palette: navy-black
base, warm-white ink + alpha hairlines, electric-indigo accent, grid/dot
home texture, warm-paper light mode; swept hardcoded old-palette colors
from HermesFlow/HermesFlowNode/HeroDemo/ExperimentalBadge/FeatureMatrix
- Reposition marketing: hero "Runs on your machine. Lives on your devices.",
quick-path-first funnel ("Just connect" no-server-install card above the
"Give it hands" relay-plugin power path), SurfaceCards + HowItWorks
components slotted into the home layout, benefit-led feature cards
- Rename "Desktop CLI" -> "CLI" across copy (binary is host-agnostic; path/
track rename deferred to code refactor); Windows-today / macOS-Linux-soon
status on every availability claim incl. hero subtext; drop "self-hosted"
qualifier in favor of plain "Hermes agent"
- desktop/index.md re-led with the remote-hands story; tray/chat copy
rescoped (chat & management belong to hermes-desktop); modes table
reordered Tools/Daemon first
- Sidebar: add voice, voice-intents, phone-control-tools, relay-server,
flavor-differences (existing pages previously unreachable); bump stale
version pins (app 0.8.1, desktop alpha.18)
- SphereMark: fix gaze drift/snap by pinning lightAngleBlend to exactly 1
(partial blends leak the unbounded natural light angle), ambient life
moved into proximity-eased fbm wander, mouse-only pointer tracking,
occlusion halo over the home dot grid, larger + tighter mobile sizing
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
ab92229feb
commit
176fc7f6bc
@@ -26,6 +26,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/), and this
|
||||
|
||||
### Changed
|
||||
|
||||
- **Docs site rechromed to the relay cockpit theme and repositioned around the two-path story.** `user-docs/` now mirrors the app's `RelayRefresh` palette (navy-black base, warm-white ink, electric-indigo accent, grid/dot texture) instead of the old neutral-gray/purple chrome. The homepage leads with "Runs on your machine. Lives on your devices.", a quick-path-first funnel ("Just connect" — no server install, just a running Hermes — above the "Give it hands" relay-plugin power path), role-framed surface cards (companion app / remote-hands CLI), and a How-it-works strip. "Desktop CLI" is now plain "CLI" with a Windows-today / macOS-Linux-coming-soon status on every availability claim; "self-hosted" dropped as a qualifier. Sidebar gained five previously unreachable pages (voice, voice intents, phone control tools, relay server, flavor differences); stale version pins refreshed. Sphere gaze tracking on the homepage no longer drifts over time or snaps between scroll/cursor modes (`lightAngleBlend` partial-mix leak — blend is now exactly 1 with ambient life moved into the wander term).
|
||||
|
||||
- **Standard (no-plugin) voice now rides the Hermes dashboard surface.** STT/TTS for the standard route uses the dashboard's `/api/audio/transcribe` + `/api/audio/speak` (the hermes-desktop voice contract) with the same cookie session Manage signs in with — a vanilla hermes-agent install needs no Relay plugin for voice. Previously the client targeted the API server, which has no audio routes, so standard-only voice always failed.
|
||||
|
||||
- **Auto STT/TTS route prefers Relay when paired.** Paired Relay voice is profile-aware and needs no dashboard sign-in; the standard dashboard route is the zero-plugin fallback. Voice Settings now shows live per-route status (ready / sign-in required / unreachable / unsupported build) with a "Sign in via Manage" shortcut, and the Realtime Agent engine is clearly marked as requiring a paired Relay.
|
||||
@@ -66,6 +68,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/), and this
|
||||
|
||||
- **Cold start no longer waits 15 seconds to learn there's no API key.** On devices with StrongBox secure hardware (recent Samsungs), every keystore operation takes ~half a second and they all run one at a time — a measured cold start spent 15 seconds decrypting the credential store before the app could even build its HTTP client, only to find the connection had no API key (the normal local setup). A plain non-sensitive "has API key?" hint now lets key-less connections build the client immediately — chat, health, and the conversation restore start within a couple of seconds — while keyed connections still wait for the real decrypt (a stale hint can only ever make startup slower, never strip auth). The startup checks also now count the route prober's successful health probe as "hermes online" instead of waiting for the client-based probe to repeat the same check.
|
||||
|
||||
- **Startup checks visibly check, and the OS splash blends into the sphere.** The sphere's check lines now resolve strictly top-to-bottom, each holding a brief spinner beat before its ✓ lands — with the fast cold-start path everything could already be true before the sphere faded in, and an all-✓-at-once reveal read as "nothing was actually verified". The gate waits for the ticking to finish (~1.5 s) before showing chat. The system splash (which Android always draws first and can't be replaced) now uses the app's exact background color — the old splash was a visibly different navy — and its icon is properly transparent, so launch reads as one continuous dark screen that the sphere fades into.
|
||||
|
||||
- **The startup sphere is now the actual loading screen.** Cold starts used to flash a slideshow of half-ready states — the disconnected "connect" prompt, then the connected state, then the conversation, each revealing separately — because the splash gate released on the first health verdict (often a probe against the old route, moments before the resolver switched) and force-hid itself after 5.5 s no matter what. The sphere now holds until the app is presentable — server answering AND the last conversation restored — or until an unreachable verdict survives a settle window (then the normal UI takes over with its offline status), with a 12 s backstop. While it holds, terminal-style check lines narrate progress at the bottom (state restored · route · hermes online · conversation), so a longer wait reads as work instead of a hang.
|
||||
|
||||
- **Terminal and Settings headers gained back buttons.** Both are pushed destinations (reached from the Chat/Manage header chrome), but neither offered a way back except the system gesture; they now carry the same header back arrow as every other pushed screen. The footer status pill also hugs the bottom edge slightly tighter.
|
||||
|
||||
@@ -3,7 +3,7 @@ import { defineConfig } from 'vitepress'
|
||||
export default defineConfig({
|
||||
base: '/hermes-relay/',
|
||||
title: 'Hermes-Relay',
|
||||
description: 'Hermes-Relay — Android phone control + desktop terminal client for the Hermes agent platform',
|
||||
description: 'Hermes-Relay — the Android companion and remote-hands CLI for your Hermes agent. Runs on your machine; lives on your devices.',
|
||||
|
||||
head: [
|
||||
// Favicon — base path is NOT auto-applied to head entries in VitePress,
|
||||
@@ -17,25 +17,25 @@ export default defineConfig({
|
||||
// Open Graph — crawlers (Facebook, Messenger, Slack, Discord, LinkedIn) need absolute URLs
|
||||
['meta', { property: 'og:type', content: 'website' }],
|
||||
['meta', { property: 'og:site_name', content: 'Hermes-Relay' }],
|
||||
['meta', { property: 'og:title', content: 'Hermes-Relay — Phone control + desktop terminal for Hermes Agent' }],
|
||||
['meta', { property: 'og:description', content: 'Two surfaces for one Hermes agent: a native Android app for phone control + a desktop CLI that lets you use a server-deployed Hermes from your laptop as if it were local.' }],
|
||||
['meta', { property: 'og:title', content: 'Hermes-Relay — give your Hermes agent hands' }],
|
||||
['meta', { property: 'og:description', content: 'Runs on your machine. Lives on your devices. A native Android companion for chat, voice, and phone control — plus a single-binary CLI the agent uses to work on any machine you pair.' }],
|
||||
['meta', { property: 'og:url', content: 'https://codename-11.github.io/hermes-relay/' }],
|
||||
['meta', { property: 'og:image', content: 'https://codename-11.github.io/hermes-relay/og-image.png' }],
|
||||
['meta', { property: 'og:image:secure_url', content: 'https://codename-11.github.io/hermes-relay/og-image.png' }],
|
||||
['meta', { property: 'og:image:type', content: 'image/png' }],
|
||||
['meta', { property: 'og:image:width', content: '1024' }],
|
||||
['meta', { property: 'og:image:height', content: '500' }],
|
||||
['meta', { property: 'og:image:alt', content: 'Hermes-Relay — Phone control + desktop terminal for Hermes Agent.' }],
|
||||
['meta', { property: 'og:image:alt', content: 'Hermes-Relay — give your Hermes agent hands.' }],
|
||||
|
||||
// Twitter / X Card
|
||||
['meta', { name: 'twitter:card', content: 'summary_large_image' }],
|
||||
['meta', { name: 'twitter:title', content: 'Hermes-Relay — Phone control + desktop terminal for Hermes Agent' }],
|
||||
['meta', { name: 'twitter:description', content: 'Android remote-control app + desktop CLI for the Hermes agent platform — one pair, two surfaces.' }],
|
||||
['meta', { name: 'twitter:title', content: 'Hermes-Relay — give your Hermes agent hands' }],
|
||||
['meta', { name: 'twitter:description', content: 'Android companion for chat, voice, and phone control + a single-binary CLI that gives your Hermes agent hands on any machine. One pair, every device.' }],
|
||||
['meta', { name: 'twitter:image', content: 'https://codename-11.github.io/hermes-relay/og-image.png' }],
|
||||
['meta', { name: 'twitter:image:alt', content: 'Hermes-Relay — Phone control + desktop terminal for Hermes Agent.' }],
|
||||
['meta', { name: 'twitter:image:alt', content: 'Hermes-Relay — give your Hermes agent hands.' }],
|
||||
|
||||
// Theme
|
||||
['meta', { name: 'theme-color', content: '#000000' }],
|
||||
// Theme — RelayRefresh.Background
|
||||
['meta', { name: 'theme-color', content: '#08090D' }],
|
||||
|
||||
// Fonts — Space Grotesk + Space Mono (shared DNA with ARC docs)
|
||||
['link', { rel: 'preconnect', href: 'https://fonts.googleapis.com' }],
|
||||
@@ -50,8 +50,8 @@ export default defineConfig({
|
||||
{
|
||||
text: 'Surfaces',
|
||||
items: [
|
||||
{ text: 'Android (phone control)', link: '/guide/' },
|
||||
{ text: 'Desktop CLI (terminal)', link: '/desktop/' },
|
||||
{ text: 'Android (companion)', link: '/guide/' },
|
||||
{ text: 'CLI (remote hands)', link: '/desktop/' },
|
||||
],
|
||||
},
|
||||
{ text: 'Features', link: '/features/' },
|
||||
@@ -77,9 +77,9 @@ export default defineConfig({
|
||||
],
|
||||
},
|
||||
{
|
||||
text: 'Looking for the desktop CLI?',
|
||||
text: 'Looking for the CLI?',
|
||||
items: [
|
||||
{ text: 'Desktop CLI overview', link: '/desktop/' },
|
||||
{ text: 'CLI overview', link: '/desktop/' },
|
||||
],
|
||||
},
|
||||
],
|
||||
@@ -94,8 +94,11 @@ export default defineConfig({
|
||||
{ text: 'Connections', link: '/features/connections' },
|
||||
{ text: 'Profiles', link: '/features/profiles' },
|
||||
{ text: 'Personalities', link: '/features/personalities' },
|
||||
{ text: 'Voice Mode', link: '/features/voice' },
|
||||
{ text: 'Voice Intents', link: '/features/voice-intents' },
|
||||
{ text: 'Token Tracking', link: '/features/tokens' },
|
||||
{ text: 'Tool Progress', link: '/features/tools' },
|
||||
{ text: 'Phone Control Tools', link: '/features/phone-control-tools' },
|
||||
{ text: 'Dashboard Plugin', link: '/features/dashboard' },
|
||||
],
|
||||
},
|
||||
@@ -106,6 +109,7 @@ export default defineConfig({
|
||||
items: [
|
||||
{ text: 'Overview', link: '/architecture/' },
|
||||
{ text: 'Relay Architecture Spec', link: '/architecture/relay-architecture-spec' },
|
||||
{ text: 'Flavor Differences', link: '/architecture/flavor-differences' },
|
||||
{ text: 'Decisions', link: '/architecture/decisions' },
|
||||
{ text: 'Security', link: '/architecture/security' },
|
||||
{ text: 'Privacy', link: '/architecture/privacy' },
|
||||
@@ -118,6 +122,7 @@ export default defineConfig({
|
||||
items: [
|
||||
{ text: 'Hermes API', link: '/reference/api' },
|
||||
{ text: 'Configuration', link: '/reference/configuration' },
|
||||
{ text: 'Relay Server', link: '/reference/relay-server' },
|
||||
],
|
||||
},
|
||||
],
|
||||
@@ -125,7 +130,7 @@ export default defineConfig({
|
||||
{
|
||||
// Sidebar header carries the experimental marker so it's visible
|
||||
// on every page in the section, not just the overview.
|
||||
text: 'Desktop CLI (terminal) · Experimental',
|
||||
text: 'CLI (remote hands) · Experimental',
|
||||
items: [
|
||||
{ text: 'Overview', link: '/desktop/' },
|
||||
{ text: 'Installation', link: '/desktop/installation' },
|
||||
|
||||
@@ -42,15 +42,15 @@ const ariaLabel = computed(() => `${label.value} feature`)
|
||||
box-shadow: 0 0 0 2px rgba(180, 83, 9, 0.18);
|
||||
}
|
||||
|
||||
/* Dark theme — VitePress sets `.dark` on <html>. Use a slightly brighter
|
||||
* amber so the badge stays legible against the dark surface. */
|
||||
/* Dark theme — VitePress sets `.dark` on <html>. RelayRefresh.Amber so the
|
||||
* badge matches the app's status palette against the navy surface. */
|
||||
.dark .experimental-badge {
|
||||
background: rgba(251, 191, 36, 0.10);
|
||||
color: #fbbf24;
|
||||
border-color: rgba(251, 191, 36, 0.30);
|
||||
background: rgba(242, 177, 75, 0.10);
|
||||
color: #F2B14B;
|
||||
border-color: rgba(242, 177, 75, 0.30);
|
||||
}
|
||||
.dark .experimental-badge .dot {
|
||||
background: #fbbf24;
|
||||
box-shadow: 0 0 0 2px rgba(251, 191, 36, 0.18);
|
||||
background: #F2B14B;
|
||||
box-shadow: 0 0 0 2px rgba(242, 177, 75, 0.18);
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -383,8 +383,8 @@ export default {}
|
||||
<style scoped>
|
||||
/* ══════════════════════════════════════════════════════════════════════
|
||||
FeatureMatrix — flat, border-separated, single accent.
|
||||
Matches the Nothing-inspired theme used by HermesFlow / InstallSection /
|
||||
HeroDemo: Space Grotesk + Space Mono, --vp-c-brand-1 purple accent, no
|
||||
Matches the relay cockpit theme used by HermesFlow / InstallSection /
|
||||
HeroDemo: Space Grotesk + Space Mono, --vp-c-brand-1 Relay accent, no
|
||||
gradients, no shadows, no blur.
|
||||
══════════════════════════════════════════════════════════════════════ */
|
||||
|
||||
@@ -571,7 +571,7 @@ export default {}
|
||||
letter-spacing: 0.04em;
|
||||
}
|
||||
.fm-cell-track--sl {
|
||||
background: rgba(124, 58, 237, 0.04);
|
||||
background: rgba(110, 124, 255, 0.06);
|
||||
}
|
||||
|
||||
/* Support states — color, opacity, weight */
|
||||
|
||||
@@ -32,8 +32,8 @@ interface DiagramDef {
|
||||
}>
|
||||
}
|
||||
|
||||
const edgeStyle = { stroke: '#7C3AED', strokeWidth: 1.5 }
|
||||
const edgeDim = { stroke: '#333', strokeWidth: 1 }
|
||||
const edgeStyle = { stroke: '#6E7CFF', strokeWidth: 1.5 }
|
||||
const edgeDim = { stroke: '#33365A', strokeWidth: 1 }
|
||||
const edgeAnimated = true
|
||||
|
||||
const diagrams: Record<string, DiagramDef> = {
|
||||
@@ -119,7 +119,7 @@ const diagrams: Record<string, DiagramDef> = {
|
||||
// Completion
|
||||
{ id: 'e9', source: 'delta', target: 'complete', style: edgeStyle },
|
||||
{ id: 'e10', source: 'tool-pending', target: 'tool-completed', style: edgeDim, label: 'success' },
|
||||
{ id: 'e11', source: 'tool-started', target: 'tool-failed', style: { stroke: '#EF4444', strokeWidth: 1 }, label: 'error' },
|
||||
{ id: 'e11', source: 'tool-started', target: 'tool-failed', style: { stroke: '#FF6B78', strokeWidth: 1 }, label: 'error' },
|
||||
{ id: 'e12', source: 'complete', target: 'run', animated: edgeAnimated, style: edgeStyle },
|
||||
{ id: 'e13', source: 'tool-completed', target: 'run', style: edgeDim },
|
||||
],
|
||||
@@ -202,27 +202,30 @@ function handleFitView() {
|
||||
<style>
|
||||
@import '@vue-flow/core/dist/style.css';
|
||||
|
||||
/* Diagrams stay dark in both modes — cockpit instrument panels, like
|
||||
terminal screenshots. Palette mirrors RelayRefresh (navy, warm-white
|
||||
hairlines, Relay periwinkle accent). */
|
||||
.hermes-flow-wrapper {
|
||||
position: relative;
|
||||
border: 1px solid #222;
|
||||
border: 1px solid rgba(247, 246, 240, 0.14);
|
||||
border-radius: 8px;
|
||||
background: #0A0A0A;
|
||||
background: #0B0C12;
|
||||
margin: 16px 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* Override Vue Flow background */
|
||||
.hermes-flow-wrapper .vue-flow {
|
||||
background: #0A0A0A !important;
|
||||
background: #0B0C12 !important;
|
||||
}
|
||||
|
||||
/* Edge labels */
|
||||
.hermes-flow-wrapper .vue-flow__edge-textbg {
|
||||
fill: #0A0A0A;
|
||||
fill: #0B0C12;
|
||||
}
|
||||
|
||||
.hermes-flow-wrapper .vue-flow__edge-text {
|
||||
fill: #999;
|
||||
fill: #A7A4B7;
|
||||
font-family: 'Space Mono', monospace;
|
||||
font-size: 10px;
|
||||
}
|
||||
@@ -258,17 +261,17 @@ function handleFitView() {
|
||||
justify-content: center;
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
background: #1A1A1A;
|
||||
border: 1px solid #333;
|
||||
background: #191B31;
|
||||
border: 1px solid rgba(247, 246, 240, 0.18);
|
||||
border-radius: 4px;
|
||||
color: #999;
|
||||
color: #A7A4B7;
|
||||
cursor: pointer;
|
||||
transition: border-color 0.2s, color 0.2s;
|
||||
}
|
||||
|
||||
.hermes-flow-btn:hover {
|
||||
border-color: #7C3AED;
|
||||
color: #E8E8E8;
|
||||
border-color: #6E7CFF;
|
||||
color: #F7F6F0;
|
||||
}
|
||||
|
||||
/* Pan cursor */
|
||||
@@ -281,7 +284,7 @@ function handleFitView() {
|
||||
}
|
||||
|
||||
.hermes-flow-fallback {
|
||||
color: #666;
|
||||
color: #68647D;
|
||||
font-family: 'Space Mono', monospace;
|
||||
font-size: 12px;
|
||||
padding: 24px;
|
||||
|
||||
@@ -22,25 +22,25 @@ const props = defineProps<{
|
||||
|
||||
<style>
|
||||
.hermes-flow-node {
|
||||
background: #0A0A0A;
|
||||
border: 1px solid #2A2A2A;
|
||||
background: #121426;
|
||||
border: 1px solid rgba(247, 246, 240, 0.16);
|
||||
border-radius: 8px;
|
||||
padding: 8px 16px;
|
||||
font-family: 'Space Mono', monospace;
|
||||
font-size: 11px;
|
||||
letter-spacing: 0.03em;
|
||||
color: #E8E8E8;
|
||||
color: #F7F6F0;
|
||||
white-space: nowrap;
|
||||
transition: border-color 0.2s;
|
||||
}
|
||||
|
||||
.hermes-flow-node:hover {
|
||||
border-color: #7C3AED;
|
||||
border-color: #6E7CFF;
|
||||
}
|
||||
|
||||
.hermes-flow-node--accent {
|
||||
border-color: #7C3AED;
|
||||
box-shadow: 0 0 12px rgba(124, 58, 237, 0.15);
|
||||
border-color: #6E7CFF;
|
||||
box-shadow: 0 0 12px rgba(110, 124, 255, 0.22);
|
||||
}
|
||||
|
||||
.hermes-flow-node__label {
|
||||
@@ -52,12 +52,12 @@ const props = defineProps<{
|
||||
.hermes-flow-node .vue-flow__handle {
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
background: #444;
|
||||
background: #33365A;
|
||||
border: none;
|
||||
transition: background 0.2s;
|
||||
}
|
||||
|
||||
.hermes-flow-node:hover .vue-flow__handle {
|
||||
background: #7C3AED;
|
||||
background: #6E7CFF;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -65,7 +65,7 @@ onBeforeUnmount(() => {
|
||||
display: block;
|
||||
padding: 10px;
|
||||
border-radius: 36px;
|
||||
background: linear-gradient(160deg, #1a1a1a 0%, #0a0a0a 100%);
|
||||
background: linear-gradient(160deg, #191B31 0%, #0B0C12 100%);
|
||||
box-shadow:
|
||||
0 0 0 1px var(--vp-c-divider),
|
||||
0 30px 60px -20px rgba(0, 0, 0, 0.5),
|
||||
@@ -93,7 +93,7 @@ onBeforeUnmount(() => {
|
||||
display: block;
|
||||
width: 100%;
|
||||
border-radius: 28px;
|
||||
background: #000;
|
||||
background: #08090D;
|
||||
}
|
||||
.hero-demo-video {
|
||||
height: auto;
|
||||
@@ -105,14 +105,14 @@ onBeforeUnmount(() => {
|
||||
justify-content: center;
|
||||
background: radial-gradient(
|
||||
ellipse at center,
|
||||
rgba(155, 107, 240, 0.18) 0%,
|
||||
rgba(0, 0, 0, 1) 70%
|
||||
rgba(110, 124, 255, 0.16) 0%,
|
||||
rgba(8, 9, 13, 1) 70%
|
||||
);
|
||||
}
|
||||
.hero-demo-logo img {
|
||||
width: 55%;
|
||||
height: auto;
|
||||
filter: drop-shadow(0 0 24px rgba(155, 107, 240, 0.55));
|
||||
filter: drop-shadow(0 0 24px rgba(110, 124, 255, 0.5));
|
||||
}
|
||||
|
||||
/* Crossfade between video and logo */
|
||||
|
||||
@@ -0,0 +1,94 @@
|
||||
<script setup lang="ts">
|
||||
// Three-step "how it works" strip — sits between the sphere mark and the
|
||||
// install section on the home page (slotted via theme/index.ts, since
|
||||
// markdown body content always renders after the VPFeatures grid).
|
||||
const steps = [
|
||||
{
|
||||
num: '01 · Run',
|
||||
title: 'Hermes on your machine',
|
||||
body: 'Any box that runs Hermes — server, desktop, laptop. Its API serves chat; its dashboard serves Manage and voice. Nothing extra to install.',
|
||||
},
|
||||
{
|
||||
num: '02 · Install',
|
||||
title: 'The app on your phone',
|
||||
body: 'Google Play or sideload — two tracks, one app. The sideload track adds full Device Control later if you want it.',
|
||||
},
|
||||
{
|
||||
num: '03 · Connect',
|
||||
title: 'Point it at your Hermes',
|
||||
body: 'The wizard scans your LAN, accepts a setup QR, or takes a plain URL. Chat, voice, and Manage light up.',
|
||||
},
|
||||
]
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<section class="how-strip" aria-label="How Hermes-Relay works">
|
||||
<div class="how-strip-label">How it works</div>
|
||||
<div class="how-steps">
|
||||
<div v-for="step in steps" :key="step.num" class="how-step">
|
||||
<span class="num">{{ step.num }}</span>
|
||||
<h4>{{ step.title }}</h4>
|
||||
<p>{{ step.body }}</p>
|
||||
</div>
|
||||
</div>
|
||||
<p class="how-power-note">
|
||||
Want phone control, terminal, or hands on your computers? Add the relay plugin — the power path below.
|
||||
</p>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.how-strip {
|
||||
max-width: 960px;
|
||||
margin: 3rem auto 0;
|
||||
padding: 0 24px;
|
||||
}
|
||||
.how-strip-label {
|
||||
text-align: center;
|
||||
font-family: var(--vp-font-family-mono);
|
||||
font-size: 0.72rem;
|
||||
letter-spacing: 0.1em;
|
||||
text-transform: uppercase;
|
||||
color: var(--vp-c-text-3);
|
||||
margin-bottom: 1.25rem;
|
||||
}
|
||||
.how-steps {
|
||||
display: grid;
|
||||
gap: 1rem;
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
@media (min-width: 768px) {
|
||||
.how-steps { grid-template-columns: 1fr 1fr 1fr; }
|
||||
}
|
||||
.how-step {
|
||||
background: var(--vp-c-bg-alt);
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
border-radius: 8px;
|
||||
padding: 1.1rem 1.25rem;
|
||||
}
|
||||
.how-step .num {
|
||||
font-family: var(--vp-font-family-mono);
|
||||
font-size: 0.72rem;
|
||||
letter-spacing: 0.08em;
|
||||
text-transform: uppercase;
|
||||
color: var(--vp-c-brand-1);
|
||||
}
|
||||
.how-step h4 {
|
||||
margin: 0.35rem 0 0.3rem;
|
||||
font-size: 1rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
.how-step p {
|
||||
margin: 0;
|
||||
font-size: 0.85rem;
|
||||
line-height: 1.55;
|
||||
color: var(--vp-c-text-2);
|
||||
}
|
||||
.how-power-note {
|
||||
text-align: center;
|
||||
font-size: 0.85rem;
|
||||
color: var(--vp-c-text-3);
|
||||
margin: 1.25rem auto 0;
|
||||
max-width: 640px;
|
||||
}
|
||||
</style>
|
||||
@@ -4,13 +4,12 @@ import { withBase } from 'vitepress'
|
||||
|
||||
const installCommand = 'curl -fsSL https://raw.githubusercontent.com/Codename-11/hermes-relay/main/install.sh | bash'
|
||||
const pairCommand = 'hermes-pair'
|
||||
const desktopInstallShell = 'curl -fsSL https://raw.githubusercontent.com/Codename-11/hermes-relay/main/desktop/scripts/install.sh | sh'
|
||||
const desktopInstallPwsh = 'irm https://raw.githubusercontent.com/Codename-11/hermes-relay/main/desktop/scripts/install.ps1 | iex'
|
||||
|
||||
// Copy-paste prompt block for AI agents (Claude, GPT, etc.) — points the
|
||||
// agent at the canonical SKILL.md raw URL, no inline recipe duplication.
|
||||
// Single source of truth lives at skills/devops/hermes-relay-self-setup/SKILL.md.
|
||||
const agentPrompt = `You are helping me install and maintain Hermes-Relay (https://github.com/Codename-11/hermes-relay) — a native Android client + a desktop CLI + a Python plugin for the Hermes AI agent platform.
|
||||
const agentPrompt = `You are helping me install and maintain Hermes-Relay (https://github.com/Codename-11/hermes-relay) — a native Android client + a CLI + a Python plugin for the Hermes AI agent platform.
|
||||
|
||||
Read the canonical setup recipe before acting:
|
||||
https://raw.githubusercontent.com/Codename-11/hermes-relay/main/skills/devops/hermes-relay-self-setup/SKILL.md
|
||||
@@ -18,7 +17,7 @@ Read the canonical setup recipe before acting:
|
||||
Then guide me through:
|
||||
- Verifying hermes-agent is already installed (it's a prerequisite — Hermes-Relay is a plugin, not standalone)
|
||||
- Running the install one-liner: \`curl -fsSL https://raw.githubusercontent.com/Codename-11/hermes-relay/main/install.sh | bash\`
|
||||
- Pairing my phone via \`hermes-pair\` or \`/hermes-relay-pair\` (Android), or my laptop via the \`hermes-relay\` desktop CLI (binary one-liner: \`curl -fsSL https://raw.githubusercontent.com/Codename-11/hermes-relay/main/desktop/scripts/install.sh | sh\` or \`irm .../install.ps1 | iex\` on Windows)
|
||||
- Pairing my phone via \`hermes-pair\` or \`/hermes-relay-pair\` (Android), or another machine via the \`hermes-relay\` CLI (Windows binary one-liner: \`irm https://raw.githubusercontent.com/Codename-11/hermes-relay/main/desktop/scripts/install.ps1 | iex\`; macOS/Linux builds coming soon)
|
||||
- Verifying with \`hermes-status\`
|
||||
|
||||
Always confirm before running shell commands. Never restart hermes-gateway without asking. If any step fails, consult the Troubleshooting section in the SKILL.md and ask me for the exact error.`
|
||||
@@ -42,117 +41,125 @@ async function copy(key: string, text: string) {
|
||||
|
||||
<template>
|
||||
<section class="install-section">
|
||||
<h2>Install in 30 seconds</h2>
|
||||
<h2>Get started</h2>
|
||||
<p class="install-tagline">
|
||||
Step 1 — install the server plugin into your Hermes agent. After restarting hermes, run <code>hermes-pair</code> (or type <code>/hermes-relay-pair</code> in any Hermes chat surface) to generate a QR / 6-char code that either the <a :href="withBase('/guide/getting-started')">Android app</a> or the <a :href="withBase('/desktop/')">desktop CLI</a> can use to pair.
|
||||
Two paths. Most people start with the first — the second adds the agent's hands when you want them.
|
||||
</p>
|
||||
|
||||
<div class="install-code">
|
||||
<div class="install-code-scroll">
|
||||
<pre><code>{{ installCommand }}</code></pre>
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
class="copy-btn"
|
||||
:class="{ copied: copiedKey === 'install' }"
|
||||
:aria-label="copiedKey === 'install' ? 'Copied' : 'Copy install command'"
|
||||
@click="copy('install', installCommand)"
|
||||
>
|
||||
<span class="copy-btn-label">{{ copiedKey === 'install' ? 'Copied!' : 'Copy' }}</span>
|
||||
</button>
|
||||
<!-- ── Quick path — no server install ─────────────────────────────── -->
|
||||
<div class="path-card">
|
||||
<div class="path-tag">Quick path · No server install</div>
|
||||
<h3>Just connect</h3>
|
||||
<p>
|
||||
Already running <a href="https://hermes-agent.nousresearch.com" target="_blank" rel="noopener">Hermes</a>? Then the server side is done. Install the Android app, open it, and point it at your Hermes — the wizard scans your LAN, accepts a setup QR, or takes a plain URL. Chat, voice, and Manage light up.
|
||||
</p>
|
||||
<p class="path-note">
|
||||
Chat talks to the Hermes API server; Manage and voice use the dashboard — most setups run both. Pure chat technically needs only the API. No relay plugin required.
|
||||
</p>
|
||||
<p class="install-extra-actions">
|
||||
<a :href="withBase('/guide/getting-started')" class="install-cta-link">Android setup →</a>
|
||||
<a
|
||||
href="https://play.google.com/store/apps/details?id=com.axiomlabs.hermesrelay"
|
||||
class="install-extra-secondary"
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
>Google Play</a>
|
||||
<a
|
||||
href="https://github.com/Codename-11/hermes-relay/releases/latest"
|
||||
class="install-extra-secondary"
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
>Sideload APK</a>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<p class="install-then">Then mint a pairing code:</p>
|
||||
<!-- ── Power path — relay plugin ──────────────────────────────────── -->
|
||||
<div class="path-card">
|
||||
<div class="path-tag">Power path · Relay plugin</div>
|
||||
<h3>Give it hands</h3>
|
||||
<p>
|
||||
One install on your Hermes host unlocks the rest: phone control on the sideload track, terminal and relay sessions on Android, and the CLI that puts hands on your computers.
|
||||
</p>
|
||||
|
||||
<div class="install-code">
|
||||
<div class="install-code-scroll">
|
||||
<pre><code>{{ pairCommand }}</code></pre>
|
||||
<div class="install-code">
|
||||
<div class="install-code-scroll">
|
||||
<pre><code>{{ installCommand }}</code></pre>
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
class="copy-btn"
|
||||
:class="{ copied: copiedKey === 'install' }"
|
||||
:aria-label="copiedKey === 'install' ? 'Copied' : 'Copy install command'"
|
||||
@click="copy('install', installCommand)"
|
||||
>
|
||||
<span class="copy-btn-label">{{ copiedKey === 'install' ? 'Copied!' : 'Copy' }}</span>
|
||||
</button>
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
class="copy-btn"
|
||||
:class="{ copied: copiedKey === 'pair' }"
|
||||
:aria-label="copiedKey === 'pair' ? 'Copied' : 'Copy pair command'"
|
||||
@click="copy('pair', pairCommand)"
|
||||
>
|
||||
<span class="copy-btn-label">{{ copiedKey === 'pair' ? 'Copied!' : 'Copy' }}</span>
|
||||
</button>
|
||||
|
||||
<p class="install-then">Then mint a pairing QR / 6-char code and scan it from the app:</p>
|
||||
|
||||
<div class="install-code">
|
||||
<div class="install-code-scroll">
|
||||
<pre><code>{{ pairCommand }}</code></pre>
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
class="copy-btn"
|
||||
:class="{ copied: copiedKey === 'pair' }"
|
||||
:aria-label="copiedKey === 'pair' ? 'Copied' : 'Copy pair command'"
|
||||
@click="copy('pair', pairCommand)"
|
||||
>
|
||||
<span class="copy-btn-label">{{ copiedKey === 'pair' ? 'Copied!' : 'Copy' }}</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<p class="path-note">
|
||||
Installs the 18 <code>android_*</code> + 9 <code>desktop_*</code> tool surfaces, the <code>/hermes-relay-pair</code> skill, and a <code>hermes-pair</code> shell shim. Requires hermes-agent v0.8.0+ and Python 3.11+.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
For hands on a computer, add the CLI:
|
||||
</p>
|
||||
|
||||
<div class="install-code">
|
||||
<div class="install-code-scroll">
|
||||
<pre><code>{{ desktopInstallPwsh }}</code></pre>
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
class="copy-btn"
|
||||
:class="{ copied: copiedKey === 'desktop-pwsh' }"
|
||||
:aria-label="copiedKey === 'desktop-pwsh' ? 'Copied' : 'Copy CLI install (pwsh)'"
|
||||
@click="copy('desktop-pwsh', desktopInstallPwsh)"
|
||||
>
|
||||
<span class="copy-btn-label">{{ copiedKey === 'desktop-pwsh' ? 'Copied!' : 'Copy' }}</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<p class="path-note">
|
||||
Windows today — the macOS / Linux one-liner lands with those builds. Track it on <a :href="withBase('/desktop/installation')">CLI setup</a>.
|
||||
</p>
|
||||
|
||||
<p class="install-extra-actions">
|
||||
<a :href="withBase('/desktop/')" class="install-cta-link">CLI setup →</a>
|
||||
<a :href="withBase('/features/phone-control-tools')" class="install-extra-secondary">Phone control tools</a>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<p class="install-note">
|
||||
Installs the 18 <code>android_*</code> + 9 <code>desktop_*</code> tool surfaces, the <code>/hermes-relay-pair</code> skill, and a <code>hermes-pair</code> shell shim. Requires hermes-agent v0.8.0+ and Python 3.11+.
|
||||
</p>
|
||||
|
||||
<p class="install-cta">
|
||||
<a :href="withBase('/guide/getting-started')" class="install-cta-link">Android setup →</a>
|
||||
<a :href="withBase('/desktop/')" class="install-cta-link">Desktop CLI setup →</a>
|
||||
</p>
|
||||
|
||||
<div class="install-extras">
|
||||
<div class="install-extra-card">
|
||||
<h3>Step 2 — your client</h3>
|
||||
<p>
|
||||
Pair the <strong>Android app</strong> (sideload the file ending in <code>-sideload-release.apk</code> from the latest GitHub Release for the full-featured build, or wait for Google Play), <strong>or</strong> install the <strong>desktop CLI</strong> binary with one of the one-liners below — same pair, either client. Bun-compiled binary, no Node required.
|
||||
</p>
|
||||
|
||||
<div class="install-code">
|
||||
<div class="install-code-scroll">
|
||||
<pre><code>{{ desktopInstallShell }}</code></pre>
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
class="copy-btn"
|
||||
:class="{ copied: copiedKey === 'desktop-shell' }"
|
||||
:aria-label="copiedKey === 'desktop-shell' ? 'Copied' : 'Copy desktop install (sh)'"
|
||||
@click="copy('desktop-shell', desktopInstallShell)"
|
||||
>
|
||||
<span class="copy-btn-label">{{ copiedKey === 'desktop-shell' ? 'Copied!' : 'Copy' }}</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="install-code">
|
||||
<div class="install-code-scroll">
|
||||
<pre><code>{{ desktopInstallPwsh }}</code></pre>
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
class="copy-btn"
|
||||
:class="{ copied: copiedKey === 'desktop-pwsh' }"
|
||||
:aria-label="copiedKey === 'desktop-pwsh' ? 'Copied' : 'Copy desktop install (pwsh)'"
|
||||
@click="copy('desktop-pwsh', desktopInstallPwsh)"
|
||||
>
|
||||
<span class="copy-btn-label">{{ copiedKey === 'desktop-pwsh' ? 'Copied!' : 'Copy' }}</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<p class="install-extra-actions">
|
||||
<a
|
||||
href="https://github.com/Codename-11/hermes-relay/releases/latest"
|
||||
class="install-cta-link"
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
>Android APK →</a>
|
||||
<a
|
||||
:href="withBase('/desktop/')"
|
||||
class="install-extra-secondary"
|
||||
>Desktop CLI guide</a>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="install-extra-card">
|
||||
<h3>Found a bug? We'd love to hear about it.</h3>
|
||||
<p>
|
||||
This is an indie project and every report helps. If something feels off, broken, or just weird, open an issue — we read every one.
|
||||
</p>
|
||||
<p class="install-extra-actions">
|
||||
<a
|
||||
href="https://github.com/Codename-11/hermes-relay/issues/new"
|
||||
class="install-cta-link"
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
>Open an issue →</a>
|
||||
</p>
|
||||
</div>
|
||||
<!-- ── Bug-report card ────────────────────────────────────────────── -->
|
||||
<div class="path-card path-card--quiet">
|
||||
<h3>Found a bug? We'd love to hear about it.</h3>
|
||||
<p>
|
||||
This is an indie project and every report helps. If something feels off, broken, or just weird, open an issue — we read every one.
|
||||
</p>
|
||||
<p class="install-extra-actions">
|
||||
<a
|
||||
href="https://github.com/Codename-11/hermes-relay/issues/new"
|
||||
class="install-cta-link"
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
>Open an issue →</a>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- For AI Agents — copy-paste block that points an LLM at the canonical
|
||||
@@ -160,7 +167,7 @@ async function copy(key: string, text: string) {
|
||||
<div class="agent-section">
|
||||
<h3 class="agent-section-title">For AI Agents</h3>
|
||||
<p class="agent-section-tagline">
|
||||
Have an AI assistant install + maintain Hermes-Relay for you. Paste this block into Claude, GPT, or any agent — it'll fetch the canonical setup recipe and walk you through verification, pairing, and troubleshooting.
|
||||
On the power path? Have an AI assistant install + maintain the relay plugin for you. Paste this block into Claude, GPT, or any agent — it'll fetch the canonical setup recipe and walk you through verification, pairing, and troubleshooting.
|
||||
</p>
|
||||
|
||||
<div class="install-code agent-prompt-code">
|
||||
@@ -199,32 +206,88 @@ async function copy(key: string, text: string) {
|
||||
}
|
||||
.install-tagline,
|
||||
.install-then {
|
||||
text-align: center;
|
||||
color: var(--vp-c-text-2);
|
||||
max-width: 640px;
|
||||
margin: 1rem auto;
|
||||
margin: 1rem 0;
|
||||
}
|
||||
.install-code {
|
||||
position: relative;
|
||||
.install-tagline {
|
||||
text-align: center;
|
||||
max-width: 640px;
|
||||
margin: 1rem auto 1.75rem;
|
||||
}
|
||||
|
||||
/* ── Path cards — stacked, Standard first ─────────────────────────── */
|
||||
.path-card {
|
||||
max-width: 720px;
|
||||
min-width: 0;
|
||||
margin: 1rem auto;
|
||||
background: var(--vp-c-bg-alt);
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
border-radius: 8px;
|
||||
padding: 1.5rem 1.5rem 1.25rem;
|
||||
}
|
||||
.path-card h3 {
|
||||
margin: 0.35rem 0 0.5rem;
|
||||
font-size: 1.35rem;
|
||||
border-top: none;
|
||||
padding-top: 0;
|
||||
letter-spacing: -0.01em;
|
||||
}
|
||||
.path-card p {
|
||||
color: var(--vp-c-text-2);
|
||||
font-size: 0.95rem;
|
||||
line-height: 1.6;
|
||||
margin: 0.5rem 0;
|
||||
}
|
||||
.path-card code {
|
||||
font-size: 0.8125rem;
|
||||
}
|
||||
.path-tag {
|
||||
font-family: var(--vp-font-family-mono);
|
||||
font-size: 0.7rem;
|
||||
letter-spacing: 0.08em;
|
||||
text-transform: uppercase;
|
||||
color: var(--vp-c-brand-1);
|
||||
}
|
||||
.path-note {
|
||||
font-size: 0.8125rem !important;
|
||||
color: var(--vp-c-text-3) !important;
|
||||
}
|
||||
.path-platform {
|
||||
font-family: var(--vp-font-family-mono);
|
||||
font-size: 0.72rem;
|
||||
letter-spacing: 0.04em;
|
||||
text-transform: uppercase;
|
||||
color: var(--vp-c-text-3);
|
||||
}
|
||||
.path-card--quiet h3 {
|
||||
font-size: 1.05rem;
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
/* ── Code blocks ──────────────────────────────────────────────────── */
|
||||
.install-code {
|
||||
position: relative;
|
||||
min-width: 0;
|
||||
margin: 1rem 0;
|
||||
background: var(--vp-c-bg);
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
border-radius: 8px;
|
||||
}
|
||||
/* Scrolling lives on the inner container so the absolutely-positioned copy
|
||||
button stays pinned to the visible top-right corner of .install-code,
|
||||
instead of scrolling out of view with the overflowing command text. */
|
||||
instead of scrolling out of view with the overflowing command text.
|
||||
(overflow-x is a fallback — commands wrap below, so it rarely engages.) */
|
||||
.install-code-scroll {
|
||||
min-width: 0;
|
||||
overflow-x: auto;
|
||||
padding: 14px 58px 14px 18px;
|
||||
}
|
||||
/* Wrap one-liners instead of horizontal-scrolling them — a curl command on
|
||||
two lines reads better than a scrollbar hiding half the command. */
|
||||
.install-code pre {
|
||||
margin: 0;
|
||||
background: transparent;
|
||||
white-space: pre;
|
||||
white-space: pre-wrap;
|
||||
word-break: break-all;
|
||||
}
|
||||
.install-code code {
|
||||
font-family: var(--vp-font-family-mono);
|
||||
@@ -245,7 +308,7 @@ async function copy(key: string, text: string) {
|
||||
padding: 0 10px;
|
||||
border-radius: 6px;
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
background: var(--vp-c-bg);
|
||||
background: var(--vp-c-bg-alt);
|
||||
color: var(--vp-c-text-2);
|
||||
font-size: 0.75rem;
|
||||
font-family: var(--vp-font-family-base);
|
||||
@@ -267,19 +330,14 @@ async function copy(key: string, text: string) {
|
||||
.copy-btn-label {
|
||||
line-height: 1;
|
||||
}
|
||||
.install-note {
|
||||
font-size: 0.8125rem;
|
||||
color: var(--vp-c-text-3);
|
||||
text-align: center;
|
||||
margin: 1.25rem auto 0;
|
||||
max-width: 640px;
|
||||
}
|
||||
.install-note code {
|
||||
font-size: 0.8125rem;
|
||||
}
|
||||
.install-cta {
|
||||
text-align: center;
|
||||
margin-top: 1.75rem;
|
||||
|
||||
/* ── Action links ─────────────────────────────────────────────────── */
|
||||
.install-extra-actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.75rem;
|
||||
margin-top: 1rem !important;
|
||||
}
|
||||
.install-cta-link {
|
||||
display: inline-block;
|
||||
@@ -295,51 +353,6 @@ async function copy(key: string, text: string) {
|
||||
background: var(--vp-c-brand-1);
|
||||
color: var(--vp-c-white);
|
||||
}
|
||||
.install-extras {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0, 1fr);
|
||||
align-items: start;
|
||||
gap: 1rem;
|
||||
max-width: 720px;
|
||||
margin: 2.5rem auto 0;
|
||||
}
|
||||
@media (min-width: 720px) {
|
||||
.install-extras {
|
||||
grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
|
||||
}
|
||||
}
|
||||
.install-extra-card {
|
||||
min-width: 0;
|
||||
align-self: start;
|
||||
background: var(--vp-c-bg-alt);
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
border-radius: 8px;
|
||||
padding: 1.25rem 1.25rem 1rem;
|
||||
}
|
||||
.install-extra-card .install-code {
|
||||
max-width: 100%;
|
||||
}
|
||||
.install-extra-card h3 {
|
||||
margin: 0 0 0.5rem;
|
||||
font-size: 1.05rem;
|
||||
border-top: none;
|
||||
padding-top: 0;
|
||||
}
|
||||
.install-extra-card p {
|
||||
color: var(--vp-c-text-2);
|
||||
font-size: 0.9rem;
|
||||
margin: 0.5rem 0;
|
||||
}
|
||||
.install-extra-card code {
|
||||
font-size: 0.8125rem;
|
||||
}
|
||||
.install-extra-actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.75rem;
|
||||
margin-top: 0.9rem !important;
|
||||
}
|
||||
.install-extra-secondary {
|
||||
font-size: 0.85rem;
|
||||
color: var(--vp-c-text-2);
|
||||
@@ -352,7 +365,7 @@ async function copy(key: string, text: string) {
|
||||
border-bottom-color: var(--vp-c-brand-1);
|
||||
}
|
||||
|
||||
/* For AI Agents — sits cleanly below the install-extras grid */
|
||||
/* For AI Agents — sits cleanly below the path cards */
|
||||
.agent-section {
|
||||
max-width: 720px;
|
||||
margin: 3rem auto 0;
|
||||
@@ -374,8 +387,6 @@ async function copy(key: string, text: string) {
|
||||
margin: 0 auto 1.25rem;
|
||||
}
|
||||
.agent-prompt-code {
|
||||
/* Override the install-code single-line style for the multi-line agent
|
||||
prompt — let it wrap and grow vertically instead of horizontal scroll. */
|
||||
max-width: 720px;
|
||||
padding: 18px 58px 18px 22px;
|
||||
}
|
||||
|
||||
@@ -146,6 +146,10 @@ function retargetTo(state: string, nowSec: number) {
|
||||
}
|
||||
|
||||
function onPointerMove(e: PointerEvent) {
|
||||
// Mouse pointers only. Touch "moves" end when the finger lifts — a lifted
|
||||
// touch isn't a hover, and its last position would park a phantom cursor
|
||||
// the eye stares at forever. Touch users get the scroll-gaze baseline.
|
||||
if (e.pointerType !== 'mouse') return
|
||||
mouse.x = e.clientX
|
||||
mouse.y = e.clientY
|
||||
mouse.valid = true
|
||||
@@ -226,6 +230,12 @@ function render() {
|
||||
// already looking straight down at it. Anchors the animation to the reader's
|
||||
// semantic focus, not to viewport geometry.
|
||||
const viewportH = window.innerHeight
|
||||
// Re-query if the cached node was replaced (Vue HMR, route round-trips) —
|
||||
// a detached element's getBoundingClientRect() is all zeros, which reads as
|
||||
// installGap = -viewportH and locks the eye staring straight down forever.
|
||||
if (installEl && !installEl.isConnected) {
|
||||
installEl = null
|
||||
}
|
||||
if (!installEl) {
|
||||
installEl = document.querySelector<HTMLElement>(INSTALL_SELECTOR)
|
||||
}
|
||||
@@ -303,11 +313,15 @@ function render() {
|
||||
const lookVx = smoothLookVx
|
||||
const lookVy = smoothLookVy
|
||||
|
||||
// Ambient wander — tiny fbm offset so the eye has a little breathing motion
|
||||
// when neither scroll nor cursor is moving. Small enough it can't compete
|
||||
// with the coherent target, big enough you feel the sphere's alive.
|
||||
const wanderX = (fbm(nowSec * 0.05 + 2.1, 7.7, 2) * 2 - 1) * 0.07
|
||||
const wanderY = (fbm(3.3, nowSec * 0.06 + 11.1, 2) * 2 - 1) * 0.07
|
||||
// Ambient wander — fbm offset so the eye has breathing motion when neither
|
||||
// scroll nor cursor is moving. With lightAngleBlend pinned at 1 (below),
|
||||
// this wander IS the light's ambient life — the core's natural orbit no
|
||||
// longer contributes. Amplitude eases down as the cursor engages, so hover
|
||||
// reads as locked attention and idle reads as alive.
|
||||
const engagement = Math.min(1, Math.max(0, (proximity - 0.75) * 4))
|
||||
const wanderAmp = 0.12 - 0.07 * engagement
|
||||
const wanderX = (fbm(nowSec * 0.05 + 2.1, 7.7, 2) * 2 - 1) * wanderAmp
|
||||
const wanderY = (fbm(3.3, nowSec * 0.06 + 11.1, 2) * 2 - 1) * wanderAmp
|
||||
|
||||
// Convert to light angles. The algorithm uses:
|
||||
// lx = sin(lightAngle1) * 0.65 ly = cos(lightAngle2) * 0.65
|
||||
@@ -321,10 +335,19 @@ function render() {
|
||||
const gazeAngleX = Math.asin(tvx)
|
||||
const gazeAngleY = Math.acos(tvy)
|
||||
|
||||
// How hard to lock the light to the bias. proximity is in [0.75, 1.0] now
|
||||
// (scroll-baseline → cursor-lock), so gazeBlend lives in [0.76, 0.90] —
|
||||
// always locked to the target. Reduced motion zeroes it.
|
||||
const gazeBlend = reduceMotion ? 0 : 0.35 + 0.55 * proximity
|
||||
// Blend MUST be exactly 1 (or 0): the core mixes angles linearly —
|
||||
// lightAngle = naturalAngle * (1 - blend) + bias * blend
|
||||
// and naturalAngle = time * lightSpeed grows WITHOUT BOUND. Any partial
|
||||
// blend leaks (1 - blend) * ω·t into the gaze: a slow continuous orbit
|
||||
// that drags the eye off the cursor over minutes, plus a visible snap
|
||||
// whenever proximity moved the blend (two different fractions of a huge
|
||||
// angle). That was the "tracking gets confused over time / when switching
|
||||
// between scroll and cursor" bug. At blend 1 the asin/acos mapping also
|
||||
// collapses to exactly linear (sin(asin(x)) = x), so gaze is
|
||||
// pixel-faithful; the proximity feel lives in the wander amplitude above
|
||||
// instead. Reduced motion zeroes the blend (time is frozen there, so the
|
||||
// natural angle is static anyway).
|
||||
const gazeBlend = reduceMotion ? 0 : 1
|
||||
|
||||
// Intensity + state respond to cursor engagement specifically, not to the
|
||||
// scroll-watching baseline. Use cursorWeight (raw, not smoothed) so the
|
||||
@@ -444,13 +467,36 @@ onBeforeUnmount(() => {
|
||||
margin-top: -32px;
|
||||
}
|
||||
.sphere-mark {
|
||||
/* Floor at 240px (down from 280px) so mobile — where clamp() lands on the
|
||||
floor because viewport width × 45vw is below it — gets a smaller canvas
|
||||
and therefore a smaller vertical footprint. Desktop caps at 380px. */
|
||||
width: clamp(240px, 45vw, 380px);
|
||||
/* Mobile fills ~78% of the viewport width (floor 280px on very narrow
|
||||
phones); desktop caps at 380px as before — the wider vw share only
|
||||
changes what phones and small tablets see. */
|
||||
width: clamp(280px, 82vw, 380px);
|
||||
aspect-ratio: 1 / 1;
|
||||
display: block;
|
||||
}
|
||||
/* Tighten the vertical envelope on phones — the canvas carries its own dark
|
||||
margin (the lit body sits inside the 0.60 baseRadius envelope), so the
|
||||
wrap can tuck well into the hero above and the How-it-works strip below
|
||||
without anything visually touching. */
|
||||
@media (max-width: 640px) {
|
||||
.sphere-mark-wrap {
|
||||
margin-top: -64px;
|
||||
margin-bottom: -28px;
|
||||
}
|
||||
}
|
||||
/* Occlusion halo — the home surface carries the cockpit dot-grid texture,
|
||||
which visually competes with the sphere's own cell lattice. Paint the page
|
||||
background back over it: solid behind the sphere body, dimming through the
|
||||
ring, transparent past the rim. Dark mode only (light has no texture). */
|
||||
.dark .sphere-mark {
|
||||
background: radial-gradient(
|
||||
circle closest-side,
|
||||
#08090D 0%,
|
||||
#08090D 52%,
|
||||
rgba(8, 9, 13, 0.82) 72%,
|
||||
rgba(8, 9, 13, 0) 100%
|
||||
);
|
||||
}
|
||||
.sphere-mark-canvas {
|
||||
display: block;
|
||||
width: 100%;
|
||||
|
||||
@@ -0,0 +1,98 @@
|
||||
<script setup lang="ts">
|
||||
// "Meet the two surfaces" — sits between the How-it-works strip and the
|
||||
// Get-started path cards (slotted via theme/index.ts), so readers know what
|
||||
// the app and the CLI each are before they see install commands.
|
||||
import { withBase } from 'vitepress'
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<section class="surface-section" aria-label="The two surfaces">
|
||||
<div class="surface-label">The two surfaces</div>
|
||||
<div class="surface-grid">
|
||||
<div class="surface-card">
|
||||
<div class="surface-tag">The companion · Android</div>
|
||||
<h3>Your agent, in your pocket</h3>
|
||||
<p>A native app that makes your Hermes agent feel local: streaming chat, hands-free voice, and the Manage surface for skills, cron, and profiles. The quick path needs nothing installed on your Hermes — Relay pairing is an optional power-up for terminal and phone control.</p>
|
||||
<ul>
|
||||
<li>Streaming chat, multi-Connection, agent profiles, personalities</li>
|
||||
<li>Voice mode with your server's TTS/STT — or the opt-in Realtime Agent</li>
|
||||
<li>Google Play track: chat, voice, Manage, terminal/TUI, notifications, media</li>
|
||||
<li>Sideload track: full Device Control — screen reading, taps, typing — behind a blocklist, confirmations, and auto-disable</li>
|
||||
</ul>
|
||||
<a class="cta" :href="withBase('/guide/getting-started')">Install the Android app →</a>
|
||||
</div>
|
||||
<div class="surface-card">
|
||||
<div class="surface-tag">The hand · CLI · Experimental</div>
|
||||
<h3>Give your agent hands on any machine</h3>
|
||||
<p>Not another chat app. A single binary you drop on a machine — desktop, laptop, or headless box — so your Hermes agent can <em>work there</em>: filesystem, terminal, processes, clipboard, screenshots — every action consent-gated per device.</p>
|
||||
<ul>
|
||||
<li>Agent-callable tools: read, write, patch, search, run, capture</li>
|
||||
<li><code>daemon</code> mode keeps hands available with no window open</li>
|
||||
<li>Terminal escape hatch: <code>hermes-relay</code> attaches your server's own TUI when <em>you</em> want to drive</li>
|
||||
<li>Windows today · macOS / Linux coming soon — one Bun-compiled binary, SHA256-verified install</li>
|
||||
</ul>
|
||||
<a class="cta" :href="withBase('/desktop/')">Put a hand on this machine →</a>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.surface-section {
|
||||
max-width: 1152px;
|
||||
margin: 3rem auto 0;
|
||||
padding: 0 1.5rem;
|
||||
}
|
||||
.surface-label {
|
||||
text-align: center;
|
||||
font-family: var(--vp-font-family-mono);
|
||||
font-size: 0.72rem;
|
||||
letter-spacing: 0.1em;
|
||||
text-transform: uppercase;
|
||||
color: var(--vp-c-text-3);
|
||||
margin-bottom: 1.25rem;
|
||||
}
|
||||
.surface-grid {
|
||||
display: grid;
|
||||
gap: 1.25rem;
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
@media (min-width: 768px) {
|
||||
.surface-grid { grid-template-columns: 1fr 1fr; }
|
||||
}
|
||||
.surface-card {
|
||||
background: var(--vp-c-bg-alt);
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
border-radius: 12px;
|
||||
padding: 1.5rem;
|
||||
}
|
||||
.surface-card h3 {
|
||||
margin: 0 0 .25rem;
|
||||
font-size: 1.25rem;
|
||||
border-top: none;
|
||||
padding-top: 0;
|
||||
}
|
||||
.surface-card .surface-tag {
|
||||
font-size: .8rem;
|
||||
font-weight: 500;
|
||||
letter-spacing: .04em;
|
||||
text-transform: uppercase;
|
||||
color: var(--vp-c-text-2);
|
||||
}
|
||||
.surface-card p {
|
||||
margin: .75rem 0;
|
||||
line-height: 1.55;
|
||||
color: var(--vp-c-text-2);
|
||||
}
|
||||
.surface-card ul {
|
||||
padding-left: 1.1rem;
|
||||
margin: .5rem 0 1rem;
|
||||
color: var(--vp-c-text-2);
|
||||
}
|
||||
.surface-card li { margin: .25rem 0; }
|
||||
.surface-card .cta {
|
||||
display: inline-block;
|
||||
font-weight: 500;
|
||||
margin-top: .5rem;
|
||||
}
|
||||
</style>
|
||||
@@ -1,62 +1,84 @@
|
||||
/* ==========================================================================
|
||||
Hermes-Relay — Nothing-Inspired Documentation Theme
|
||||
Hermes-Relay — Relay Cockpit Documentation Theme
|
||||
|
||||
Shared DNA with ARC docs (arc-cli.dev):
|
||||
Mirrors the Android app's relay cockpit refresh (ui/theme/RelayRefresh.kt):
|
||||
Fonts: Space Grotesk (body/UI) + Space Mono (code/labels)
|
||||
Brand: #7C3AED (purple) — single accent
|
||||
Dark: OLED black (#000), neutral gray surfaces (#111, #1A1A1A)
|
||||
Light: Off-white (#F5F5F5), white surfaces
|
||||
No gradients. No shadows. No blur. Flat + border separation.
|
||||
Dark: navy-black #08090D base, navy panels (#121426 / #191B31 / #22243C),
|
||||
warm-white ink #F7F6F0, hairline warm-white borders
|
||||
Light: warm paper #F7F3EA, white surfaces
|
||||
Accents: Electric indigo — #6E7CFF for links/active text (a brightened
|
||||
#111DFF that stays readable on navy), #4F5BD5 for fills
|
||||
(full-strength #111DFF reads as glare on large surfaces — same
|
||||
lesson as the app's ElectricMuted). Relay periwinkle #AEBFFF
|
||||
survives only in the dot texture, mirroring the app. Green/amber/
|
||||
red reserved for status meaning.
|
||||
Texture: faint 42px grid + 10px Relay dot lattice on the home surface,
|
||||
mirroring relayGridTexture().
|
||||
No shadows. No blur. Flat + border separation.
|
||||
========================================================================== */
|
||||
|
||||
/* ── Shared Tokens ─────────────────────────────────────────────────────── */
|
||||
|
||||
:root {
|
||||
/* Brand — purple */
|
||||
--vp-c-brand-1: #9B6BF0;
|
||||
--vp-c-brand-2: #7C3AED;
|
||||
--vp-c-brand-3: #6B35E8;
|
||||
--vp-c-brand-soft: rgba(124, 58, 237, 0.14);
|
||||
/* Brand — Electric indigo, muted for contrast on warm paper */
|
||||
--vp-c-brand-1: #4F5BD5;
|
||||
--vp-c-brand-2: #4F5BD5;
|
||||
--vp-c-brand-3: #3A44B8;
|
||||
--vp-c-brand-soft: rgba(79, 91, 213, 0.12);
|
||||
|
||||
/* Fonts — same as ARC */
|
||||
--vp-font-family-base: 'Space Grotesk', 'DM Sans', system-ui, sans-serif;
|
||||
--vp-font-family-mono: 'Space Mono', 'JetBrains Mono', 'SF Mono', monospace;
|
||||
|
||||
/* Nothing border tokens */
|
||||
--nd-border: #222222;
|
||||
--nd-border-visible: #333333;
|
||||
/* Cockpit hairlines — RelayRefresh.Line / LineStrong (mode-aware) */
|
||||
--hr-line: #E8E2D5;
|
||||
--hr-line-strong: #D6CFC0;
|
||||
|
||||
/* Status — RelayRefresh.{Green, Amber, Danger} */
|
||||
--hr-green: #58D36F;
|
||||
--hr-amber: #F2B14B;
|
||||
--hr-danger: #FF6B78;
|
||||
}
|
||||
|
||||
/* ── Dark Mode — neutral grays ───────────────────────────────────────── */
|
||||
/* ── Dark Mode — navy cockpit ────────────────────────────────────────── */
|
||||
|
||||
.dark {
|
||||
--vp-c-bg: #000000;
|
||||
--vp-c-bg-alt: #111111;
|
||||
--vp-c-bg-elv: #1A1A1A;
|
||||
--vp-c-bg-soft: #111111;
|
||||
/* Electric indigo accents text; Electric muted fills surfaces */
|
||||
--vp-c-brand-1: #6E7CFF;
|
||||
--vp-c-brand-2: #4F5BD5;
|
||||
--vp-c-brand-3: #3A44B8;
|
||||
--vp-c-brand-soft: rgba(110, 124, 255, 0.16);
|
||||
|
||||
--vp-c-divider: #222222;
|
||||
--vp-c-gutter: #111111;
|
||||
--hr-line: rgba(247, 246, 240, 0.14);
|
||||
--hr-line-strong: rgba(247, 246, 240, 0.28);
|
||||
|
||||
--vp-c-text-1: #E8E8E8;
|
||||
--vp-c-text-2: #999999;
|
||||
--vp-c-text-3: #666666;
|
||||
--vp-c-bg: #08090D;
|
||||
--vp-c-bg-alt: #121426;
|
||||
--vp-c-bg-elv: #191B31;
|
||||
--vp-c-bg-soft: #121426;
|
||||
|
||||
--vp-c-divider: rgba(247, 246, 240, 0.14);
|
||||
--vp-c-gutter: #121426;
|
||||
|
||||
--vp-c-text-1: #F7F6F0;
|
||||
--vp-c-text-2: #A7A4B7;
|
||||
--vp-c-text-3: #68647D;
|
||||
}
|
||||
|
||||
/* ── Light Mode — warm off-white ─────────────────────────────────────── */
|
||||
/* ── Light Mode — warm paper ─────────────────────────────────────────── */
|
||||
|
||||
html:not(.dark) {
|
||||
--vp-c-bg: #F5F5F5;
|
||||
--vp-c-bg: #F7F3EA;
|
||||
--vp-c-bg-alt: #FFFFFF;
|
||||
--vp-c-bg-elv: #FFFFFF;
|
||||
--vp-c-bg-soft: #F0F0F0;
|
||||
--vp-c-bg-soft: #F1ECDF;
|
||||
|
||||
--vp-c-divider: #E8E8E8;
|
||||
--vp-c-gutter: #F0F0F0;
|
||||
--vp-c-divider: #E8E2D5;
|
||||
--vp-c-gutter: #F1ECDF;
|
||||
|
||||
--vp-c-text-1: #1A1A1A;
|
||||
--vp-c-text-2: #666666;
|
||||
--vp-c-text-3: #999999;
|
||||
--vp-c-text-1: #14151F;
|
||||
--vp-c-text-2: #565264;
|
||||
--vp-c-text-3: #8B879B;
|
||||
}
|
||||
|
||||
/* ══════════════════════════════════════════════════════════════════════════
|
||||
@@ -64,17 +86,16 @@ html:not(.dark) {
|
||||
══════════════════════════════════════════════════════════════════════════ */
|
||||
|
||||
.VPNavBar {
|
||||
border-bottom: 1px solid var(--nd-border) !important;
|
||||
border-bottom: 1px solid var(--hr-line) !important;
|
||||
}
|
||||
|
||||
.dark .VPNavBar {
|
||||
background: #000000 !important;
|
||||
background: #08090D !important;
|
||||
backdrop-filter: none !important;
|
||||
}
|
||||
|
||||
html:not(.dark) .VPNavBar {
|
||||
background: #F5F5F5 !important;
|
||||
border-bottom-color: #E8E8E8 !important;
|
||||
background: #F7F3EA !important;
|
||||
backdrop-filter: none !important;
|
||||
}
|
||||
|
||||
@@ -96,15 +117,23 @@ html:not(.dark) .VPNavBar {
|
||||
text-transform: uppercase !important;
|
||||
}
|
||||
|
||||
/* Search box: align with Nothing aesthetic */
|
||||
/* Search box: align with cockpit aesthetic */
|
||||
.VPNavBarSearch .DocSearch-Button {
|
||||
border: 1px solid var(--nd-border-visible) !important;
|
||||
border: 1px solid var(--hr-line-strong) !important;
|
||||
background: transparent !important;
|
||||
border-radius: 4px !important;
|
||||
}
|
||||
|
||||
.dark .VPNavBarSearch .DocSearch-Button {
|
||||
border-color: var(--nd-border-visible) !important;
|
||||
/* ══════════════════════════════════════════════════════════════════════════
|
||||
HOME SURFACE — cockpit grid texture (mirrors relayGridTexture())
|
||||
══════════════════════════════════════════════════════════════════════════ */
|
||||
|
||||
.dark .VPHome {
|
||||
background-image:
|
||||
linear-gradient(rgba(247, 246, 240, 0.02) 1px, transparent 1px),
|
||||
linear-gradient(90deg, rgba(247, 246, 240, 0.016) 1px, transparent 1px),
|
||||
radial-gradient(circle, rgba(174, 191, 255, 0.11) 1.1px, transparent 1.4px);
|
||||
background-size: 42px 42px, 42px 42px, 10px 10px;
|
||||
}
|
||||
|
||||
/* ══════════════════════════════════════════════════════════════════════════
|
||||
@@ -131,6 +160,21 @@ html:not(.dark) .VPNavBar {
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
/* Platform-availability note under the hero action buttons */
|
||||
.hero-platform-note {
|
||||
margin: 14px 0 0;
|
||||
font-family: var(--vp-font-family-mono);
|
||||
font-size: 11px;
|
||||
letter-spacing: 0.06em;
|
||||
text-transform: uppercase;
|
||||
color: var(--vp-c-text-3);
|
||||
}
|
||||
@media (max-width: 959px) {
|
||||
.hero-platform-note {
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
|
||||
/* Hero image — flat, border separation */
|
||||
.VPHero .image-container .image-bg {
|
||||
display: none !important;
|
||||
@@ -158,23 +202,18 @@ html:not(.dark) .VPNavBar {
|
||||
}
|
||||
|
||||
/* ══════════════════════════════════════════════════════════════════════════
|
||||
FEATURE CARDS — subtle border, hover border only
|
||||
FEATURE CARDS — navy panel, hairline border, hover border only
|
||||
══════════════════════════════════════════════════════════════════════════ */
|
||||
|
||||
.VPFeature {
|
||||
border: 1px solid var(--nd-border) !important;
|
||||
background: var(--vp-c-bg) !important;
|
||||
border: 1px solid var(--hr-line) !important;
|
||||
background: var(--vp-c-bg-alt) !important;
|
||||
border-radius: 8px !important;
|
||||
transition: border-color 0.2s cubic-bezier(0.25, 0.1, 0.25, 1) !important;
|
||||
}
|
||||
|
||||
html:not(.dark) .VPFeature {
|
||||
border-color: #E8E8E8 !important;
|
||||
background: #FFFFFF !important;
|
||||
}
|
||||
|
||||
.VPFeature:hover {
|
||||
border-color: var(--nd-border-visible) !important;
|
||||
border-color: var(--hr-line-strong) !important;
|
||||
}
|
||||
|
||||
.VPFeature .title {
|
||||
@@ -221,15 +260,11 @@ html:not(.dark) .VPFeature {
|
||||
}
|
||||
|
||||
.VPButton.alt {
|
||||
border-color: var(--nd-border-visible) !important;
|
||||
border-color: var(--hr-line-strong) !important;
|
||||
background: transparent !important;
|
||||
transition: border-color 0.2s cubic-bezier(0.25, 0.1, 0.25, 1) !important;
|
||||
}
|
||||
|
||||
html:not(.dark) .VPButton.alt {
|
||||
border-color: #CCCCCC !important;
|
||||
}
|
||||
|
||||
.VPButton.alt:hover {
|
||||
border-color: var(--vp-c-text-2) !important;
|
||||
}
|
||||
@@ -240,14 +275,7 @@ html:not(.dark) .VPButton.alt {
|
||||
|
||||
.VPSidebar {
|
||||
background: var(--vp-c-bg) !important;
|
||||
}
|
||||
|
||||
.dark .VPSidebar {
|
||||
border-right: 1px solid var(--nd-border) !important;
|
||||
}
|
||||
|
||||
html:not(.dark) .VPSidebar {
|
||||
border-right: 1px solid #E8E8E8 !important;
|
||||
border-right: 1px solid var(--hr-line) !important;
|
||||
}
|
||||
|
||||
.VPSidebarItem.is-active > .item > .link > .text {
|
||||
@@ -264,7 +292,7 @@ html:not(.dark) .VPSidebar {
|
||||
}
|
||||
|
||||
/* ══════════════════════════════════════════════════════════════════════════
|
||||
CODE BLOCKS — near-black, border separation
|
||||
CODE BLOCKS — sunken navy panel, border separation
|
||||
══════════════════════════════════════════════════════════════════════════ */
|
||||
|
||||
.vp-code-group .tabs label.active {
|
||||
@@ -272,15 +300,15 @@ html:not(.dark) .VPSidebar {
|
||||
}
|
||||
|
||||
.dark .vp-doc div[class*='language-'] {
|
||||
background: #0A0A0A !important;
|
||||
border: 1px solid var(--nd-border);
|
||||
border-radius: 4px;
|
||||
background: #0B0C12 !important;
|
||||
border: 1px solid var(--hr-line);
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
html:not(.dark) .vp-doc div[class*='language-'] {
|
||||
background: #FFFFFF !important;
|
||||
border: 1px solid #E8E8E8;
|
||||
border-radius: 4px;
|
||||
border: 1px solid var(--hr-line);
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
/* ══════════════════════════════════════════════════════════════════════════
|
||||
@@ -310,44 +338,54 @@ html:not(.dark) .vp-doc div[class*='language-'] {
|
||||
}
|
||||
|
||||
/* ══════════════════════════════════════════════════════════════════════════
|
||||
INLINE CODE
|
||||
INLINE CODE — periwinkle on navy
|
||||
══════════════════════════════════════════════════════════════════════════ */
|
||||
|
||||
.dark .vp-doc :not(pre) > code {
|
||||
background: #1A1A1A;
|
||||
color: #C4B5FD;
|
||||
background: #191B31;
|
||||
color: #9FACFF;
|
||||
}
|
||||
|
||||
html:not(.dark) .vp-doc :not(pre) > code {
|
||||
background: #F0F0F0;
|
||||
color: #5B21B6;
|
||||
background: #EDE8DC;
|
||||
color: #3A44B8;
|
||||
}
|
||||
|
||||
/* ══════════════════════════════════════════════════════════════════════════
|
||||
BADGES / TIPS / WARNINGS — restrained
|
||||
BADGES / TIPS / WARNINGS — restrained, status colors mean status
|
||||
══════════════════════════════════════════════════════════════════════════ */
|
||||
|
||||
.VPBadge.tip {
|
||||
background-color: var(--vp-c-brand-soft) !important;
|
||||
color: #C4B5FD !important;
|
||||
color: #9FACFF !important;
|
||||
font-family: var(--vp-font-family-mono) !important;
|
||||
font-size: 11px !important;
|
||||
letter-spacing: 0.04em !important;
|
||||
text-transform: uppercase !important;
|
||||
}
|
||||
|
||||
html:not(.dark) .VPBadge.tip {
|
||||
color: #3A44B8 !important;
|
||||
}
|
||||
|
||||
.dark .vp-doc .custom-block.tip {
|
||||
border-color: rgba(124, 58, 237, 0.3);
|
||||
background: rgba(124, 58, 237, 0.04);
|
||||
border-color: rgba(110, 124, 255, 0.35);
|
||||
background: rgba(110, 124, 255, 0.05);
|
||||
}
|
||||
|
||||
.dark .vp-doc .custom-block.warning {
|
||||
background: rgba(234, 179, 8, 0.04);
|
||||
border-color: rgba(242, 177, 75, 0.3);
|
||||
background: rgba(242, 177, 75, 0.04);
|
||||
}
|
||||
|
||||
.dark .vp-doc .custom-block.danger {
|
||||
border-color: rgba(255, 107, 120, 0.3);
|
||||
background: rgba(255, 107, 120, 0.04);
|
||||
}
|
||||
|
||||
html:not(.dark) .vp-doc .custom-block.tip {
|
||||
border-color: rgba(124, 58, 237, 0.3);
|
||||
background: rgba(124, 58, 237, 0.04);
|
||||
border-color: rgba(79, 91, 213, 0.3);
|
||||
background: rgba(79, 91, 213, 0.04);
|
||||
}
|
||||
|
||||
/* ══════════════════════════════════════════════════════════════════════════
|
||||
@@ -355,11 +393,7 @@ html:not(.dark) .vp-doc .custom-block.tip {
|
||||
══════════════════════════════════════════════════════════════════════════ */
|
||||
|
||||
.VPFooter {
|
||||
border-top: 1px solid var(--nd-border) !important;
|
||||
}
|
||||
|
||||
html:not(.dark) .VPFooter {
|
||||
border-top-color: #E8E8E8 !important;
|
||||
border-top: 1px solid var(--hr-line) !important;
|
||||
}
|
||||
|
||||
/* ══════════════════════════════════════════════════════════════════════════
|
||||
@@ -367,11 +401,11 @@ html:not(.dark) .VPFooter {
|
||||
══════════════════════════════════════════════════════════════════════════ */
|
||||
|
||||
::-webkit-scrollbar { width: 4px; }
|
||||
.dark ::-webkit-scrollbar-track { background: #000000; }
|
||||
.dark ::-webkit-scrollbar-thumb { background: var(--nd-border-visible); border-radius: 2px; }
|
||||
.dark ::-webkit-scrollbar-thumb:hover { background: #666666; }
|
||||
html:not(.dark) ::-webkit-scrollbar-track { background: #F5F5F5; }
|
||||
html:not(.dark) ::-webkit-scrollbar-thumb { background: #CCCCCC; border-radius: 2px; }
|
||||
.dark ::-webkit-scrollbar-track { background: #08090D; }
|
||||
.dark ::-webkit-scrollbar-thumb { background: var(--hr-line-strong); border-radius: 2px; }
|
||||
.dark ::-webkit-scrollbar-thumb:hover { background: #68647D; }
|
||||
html:not(.dark) ::-webkit-scrollbar-track { background: #F7F3EA; }
|
||||
html:not(.dark) ::-webkit-scrollbar-thumb { background: #D6CFC0; border-radius: 2px; }
|
||||
|
||||
/* ══════════════════════════════════════════════════════════════════════════
|
||||
DOC FOOTER CTA — per-page links
|
||||
@@ -388,14 +422,10 @@ html:not(.dark) ::-webkit-scrollbar-thumb { background: #CCCCCC; border-radius:
|
||||
|
||||
.doc-footer-cta hr {
|
||||
border: none;
|
||||
border-top: 1px solid var(--nd-border);
|
||||
border-top: 1px solid var(--hr-line);
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
html:not(.dark) .doc-footer-cta hr {
|
||||
border-top-color: #E8E8E8;
|
||||
}
|
||||
|
||||
.doc-footer-cta a {
|
||||
color: var(--vp-c-brand-1);
|
||||
}
|
||||
|
||||
@@ -8,6 +8,8 @@ import HeroDemo from './components/HeroDemo.vue';
|
||||
import FeatureMatrix from './components/FeatureMatrix.vue';
|
||||
import SphereMark from './components/SphereMark.vue';
|
||||
import ExperimentalBadge from './components/ExperimentalBadge.vue';
|
||||
import HowItWorks from './components/HowItWorks.vue';
|
||||
import SurfaceCards from './components/SurfaceCards.vue';
|
||||
|
||||
export default {
|
||||
extends: DefaultTheme,
|
||||
@@ -19,7 +21,9 @@ export default {
|
||||
Layout() {
|
||||
return h(DefaultTheme.Layout, null, {
|
||||
'home-hero-image': () => h(HeroDemo),
|
||||
'home-hero-after': () => [h(SphereMark), h(InstallSection)],
|
||||
'home-hero-actions-after': () =>
|
||||
h('p', { class: 'hero-platform-note' }, 'CLI: Windows today · macOS / Linux coming soon'),
|
||||
'home-hero-after': () => [h(SphereMark), h(HowItWorks), h(SurfaceCards), h(InstallSection)],
|
||||
'doc-after': () =>
|
||||
h('div', { class: 'doc-footer-cta' }, [
|
||||
h('hr'),
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Privacy & Data Handling
|
||||
|
||||
Hermes-Relay is a self-hosted app. It connects only to your own servers — no cloud accounts, no hosted Hermes-Relay backend, no ads, and no third-party analytics.
|
||||
Hermes-Relay connects only to your own machines — no cloud accounts, no hosted Hermes-Relay backend, no ads, and no third-party analytics.
|
||||
|
||||
## Track split
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
You don't need to. Native local-Hermes already has the same `/paste`, `/image <path>`, drag-drop a file from Explorer, and `Alt+V` paths — they ship in upstream hermes-agent and the Ink TUI directly. Hermes-Relay is specifically for the **remote-server** case: when you want to use a Hermes that lives somewhere else (a home server, a GPU box, a cloud VM) from your laptop, with the same UX as a local install. If everything's already on the same machine, run `hermes` directly and skip the relay.
|
||||
|
||||
The two paths are complements, not alternatives. You'd use Hermes-Relay's desktop CLI when:
|
||||
The two paths are complements, not alternatives. You'd use the Hermes-Relay CLI when:
|
||||
- The agent's compute, models, or secrets need to live somewhere other than your daily-driver laptop.
|
||||
- You want the same agent / sessions / memory accessible from multiple devices.
|
||||
- You're sharing a GPU or model API key across machines.
|
||||
@@ -26,7 +26,7 @@ You'd use SSH *instead* of this if you want to type commands yourself. You'd use
|
||||
|
||||
## Can I use it offline?
|
||||
|
||||
No. The whole point is the agent lives on a server reachable over the network. If your laptop is offline, you can't reach the Hermes host. (You can of course run Hermes itself on your laptop and skip the relay — but then the desktop CLI is redundant.)
|
||||
No. The whole point is the agent lives on a server reachable over the network. If your laptop is offline, you can't reach the Hermes host. (You can of course run Hermes itself on your laptop and skip the relay — but then the CLI is redundant.)
|
||||
|
||||
## How do I revoke access from one of my machines?
|
||||
|
||||
@@ -125,7 +125,7 @@ v1.0. Tracked in [ROADMAP.md](https://github.com/Codename-11/hermes-relay/blob/m
|
||||
|
||||
Until then: keep a `hermes-relay` session open in a spare terminal tab for the agent to dispatch tool calls into.
|
||||
|
||||
## Can multiple people use the same Hermes host from different desktop CLIs?
|
||||
## Can multiple people use the same Hermes host from different CLI clients?
|
||||
|
||||
Right now the server tracks a single "active" desktop client per relay — if you pair from two machines, the most recently connected wins routing. v1.0 adds per-session-token routing (each hermes session binds to a specific desktop client) so multi-client is clean.
|
||||
|
||||
@@ -133,7 +133,7 @@ For now: one desktop attached at a time. Or two if you pair them with different
|
||||
|
||||
## Is there voice mode?
|
||||
|
||||
Not in the desktop CLI. The Android client has voice mode. The CLI is text-first.
|
||||
Not in the CLI. The Android client has voice mode. The CLI is text-first.
|
||||
|
||||
## Is there a Windows-on-ARM build?
|
||||
|
||||
|
||||
+31
-26
@@ -1,16 +1,28 @@
|
||||
# Desktop CLI <ExperimentalBadge />
|
||||
# Hermes-Relay CLI <ExperimentalBadge />
|
||||
|
||||
**Run Hermes on your server. Use it from your laptop. It feels native.**
|
||||
**A hand for your agent, on any computer you pair.**
|
||||
|
||||
`hermes-relay` is a single-binary desktop client that talks to a server-deployed Hermes agent over WSS — same shell, same Ink TUI, same `/paste` an image into the conversation, same session continuity. The agent brain (LLM, tools, memory, sessions) stays on your Hermes host. Your laptop is the keyboard and the eyes.
|
||||
`hermes-relay` is a single binary you drop on a machine — desktop, laptop, or headless box — so your Hermes agent can work there: read and write files, search a codebase, run commands, manage processes, read the clipboard, capture screenshots — all over the same WSS relay, all consent-gated per device. The brain (LLM, tools, memory, sessions) never leaves your Hermes host. This binary is the hand it reaches with.
|
||||
|
||||
It also includes a terminal escape hatch for when *you* want to drive: bare `hermes-relay` attaches your server's own Hermes TUI over a PTY, tmux-backed so disconnects lose nothing.
|
||||
|
||||
::: warning Experimental phase
|
||||
Binaries are unsigned (SmartScreen / Gatekeeper warnings are expected — the installers print the escape hatches). Wire protocol may shift between alphas. Multi-client routing is single-client MVP. Code-signed releases land with v1.0. Safe to use today — just expect occasional friction and [file an issue](https://github.com/Codename-11/hermes-relay/issues) when you hit one.
|
||||
**Windows today — macOS / Linux builds coming soon.** Binaries are unsigned (SmartScreen warnings are expected — the installer prints the escape hatch). Wire protocol may shift between alphas. Multi-client routing is single-client MVP. Code-signed releases land with v1.0. Safe to use today — just expect occasional friction and [file an issue](https://github.com/Codename-11/hermes-relay/issues) when you hit one.
|
||||
:::
|
||||
|
||||
## The killer demo — paste a screenshot in your TUI
|
||||
::: info Where this track is headed
|
||||
This surface is focusing into a **remote-hands connector** — remote control, filesystem, and terminal access for the agent on machines you install it to. Desktop chat and management UX belong to [hermes-desktop](https://github.com/NousResearch/hermes-agent); this CLI's `chat` mode keeps working for scripting but isn't where new features land. "Desktop" is shorthand, not a constraint — the same binary runs on laptops and headless servers (`daemon` mode needs no display at all), and the track naming will be reframed to match in a later refactor.
|
||||
:::
|
||||
|
||||
This is the experience that earns the "feels-native" claim. You are inside `hermes-relay` (bare invocation drops you straight into the Hermes Ink TUI over a PTY — no subcommand needed), talking to your remote Hermes the same way you would a local one.
|
||||
## The point — the agent works on *your* machine
|
||||
|
||||
Ask your agent — from your phone, from the attached TUI, from anywhere — to "check whether that build passes on my desktop" or "grab the error from my clipboard," and it reaches through the relay to do it: read your notes, grep your codebase, run a build, patch a file, capture a screenshot — while the brain and conversation state stay on the host. [Read how →](./tools.md)
|
||||
|
||||
This mirrors how the Android client hands the agent `android_tap` / `android_screenshot`. Zero hermes-agent core changes — the `desktop_*` tools register via the standard plugin system, same pattern as `android_*`. Run `hermes-relay daemon` and the hand stays available with no window open.
|
||||
|
||||
## Demo — native paste into the attached TUI
|
||||
|
||||
The escape hatch earns its keep too. You are inside `hermes-relay` (bare invocation drops you straight into the Hermes Ink TUI over a PTY — no subcommand needed), talking to your remote Hermes the same way you would a local one.
|
||||
|
||||
```text
|
||||
Win+Shift+S # Windows snipping tool — screenshot goes to the clipboard
|
||||
@@ -23,7 +35,7 @@ type your prompt # Send normally. The vision-capable model sees image + text
|
||||
# in one turn.
|
||||
```
|
||||
|
||||
Identical UX to native local-Hermes paste. That's the whole pitch. The clipboard read happens on your machine, the file lives on the server, the model sees both — one round-trip, no SSH, no SCP, no manual upload.
|
||||
Identical UX to native local-Hermes paste — and a small taste of the hands model: the clipboard read happens on your machine, the file lives on the server, the model sees both. One round-trip, no SSH, no SCP, no manual upload.
|
||||
|
||||
The same chord set works on macOS (`Cmd+Shift+4` → screenshot to clipboard → `Ctrl+A v`) and on Linux (Wayland `wl-paste` / X11 `xclip` are detected automatically).
|
||||
|
||||
@@ -31,10 +43,10 @@ The same chord set works on macOS (`Cmd+Shift+4` → screenshot to clipboard →
|
||||
|
||||
| Mode | Command | Best for |
|
||||
|------|---------|----------|
|
||||
| **Shell** (default) | `hermes-relay` | Full Hermes Ink TUI over a PTY — banner, Victor, slash commands, the whole experience. Uses tmux on the host so disconnects preserve state. |
|
||||
| **Chat (structured)** | `hermes-relay chat "<prompt>"` / `hermes-relay "<prompt>"` | Scriptable, one-shot, pipes stdin. `--json` emits `GatewayEvent`s per line for `jq` / automation. REPL supports `/paste`, `/screenshot`, `/image <path>`. |
|
||||
| **Tools** | Automatic, in-session | The remote agent can call `desktop_read_file`, `desktop_write_file`, `desktop_terminal`, `desktop_search_files`, `desktop_patch`, `desktop_clipboard_read/write`, `desktop_screenshot`, `desktop_open_in_editor` — **executed on your machine**, not the server. One-time per-URL consent gate. |
|
||||
| **Tools (the hand)** | Automatic, in-session | The remote agent can call `desktop_read_file`, `desktop_write_file`, `desktop_terminal`, `desktop_search_files`, `desktop_patch`, `desktop_clipboard_read/write`, `desktop_screenshot`, `desktop_open_in_editor` — **executed on your machine**, not the server. One-time per-URL consent gate. |
|
||||
| **Daemon** | `hermes-relay daemon` | Headless tool router. Keeps the agent's hands available even when no shell is open. JSON-line lifecycle logs. |
|
||||
| **Shell** (default) | `hermes-relay` | The escape hatch: full Hermes Ink TUI over a PTY — banner, Victor, slash commands, the whole experience. Uses tmux on the host so disconnects preserve state. |
|
||||
| **Chat (structured)** | `hermes-relay chat "<prompt>"` / `hermes-relay "<prompt>"` | Scriptable, one-shot, pipes stdin. `--json` emits `GatewayEvent`s per line for `jq` / automation. Maintained for scripting; not a growth surface. |
|
||||
| **Surface plugins** | `hermes-relay plugins` | Install and launch terminal dashboard surfaces. Herm is built in as an installable `herm-tui` plugin with external-terminal and embedded-tray launch paths. |
|
||||
| **Pair / Sessions / Status / Tools / Devices / Doctor / Update / Workspace / Paste** | `hermes-relay <verb>` | First-time setup, TUI tmux session management, session inventory, server-side toolset introspection, paired-device management, local diagnostics, self-update, workspace-context inspection, one-shot clipboard staging. See [Subcommands](./subcommands.md). |
|
||||
|
||||
@@ -46,7 +58,7 @@ While inside the shell/TUI session (bare `hermes-relay`, the default mode), `Ctr
|
||||
|-------|--------|
|
||||
| `Ctrl+A .` | Detach cleanly. tmux session persists on the server; next `hermes-relay` re-attaches with full state. |
|
||||
| `Ctrl+A k` | Destroy the tmux session. Fresh hermes on next run. |
|
||||
| `Ctrl+A v` | [Stage clipboard image to server inbox + auto-type `/paste`](#the-killer-demo-paste-a-screenshot-in-your-tui). |
|
||||
| `Ctrl+A v` | [Stage clipboard image to server inbox + auto-type `/paste`](#demo-native-paste-into-the-attached-tui). |
|
||||
| `Ctrl+A ?` (or `Ctrl+A h`) | Re-print the chord-help banner. The attach-time banner scrolls off as soon as anything writes — this is the way back. |
|
||||
| `Ctrl+A Ctrl+A` | Forward a literal `Ctrl+A` (for nested tmux). |
|
||||
|
||||
@@ -55,7 +67,6 @@ While inside the shell/TUI session (bare `hermes-relay`, the default mode), `Ctr
|
||||
## Headline features
|
||||
|
||||
- **[Native paste / screenshot / image](./subcommands.md)** — the chord set above, plus REPL slash commands `/paste`, `/screenshot`, `/screenshot primary`, `/screenshot 1`, `/image <path>`. Multi-monitor aware: `/screenshot` defaults to the virtual-screen union; `primary` / a 1-indexed display narrows. Identical wire format to a local Hermes paste.
|
||||
- **Tray Chat** — the desktop dashboard has a Chat tab. Paired installs stream through the saved relay session; unpaired/chat-only installs can enter a direct Hermes WebAPI URL and optional in-memory API key.
|
||||
- **[Local tool routing](./tools.md)** — agent-callable file I/O, shell exec, ripgrep, clipboard, screenshot, editor-launcher, and unified-diff patching. Strict consent gate per relay URL; non-TTY stdin fails closed.
|
||||
- **[Self-update](./subcommands.md#hermes-relay-update)** — `hermes-relay update` polls GitHub Releases, semver-compares, downloads + verifies SHA256, and atomic-swaps the binary. POSIX renames in place; Windows uses cooperative `.new.exe` swap on next start.
|
||||
- **[Surface plugins](./subcommands.md#hermes-relay-plugins)** — install, update, launch, or embed terminal dashboard plugins from the tray or CLI. The first built-in plugin is [Herm](https://github.com/liftaris/herm), installed as `herm-tui` and resumed with `herm -c`.
|
||||
@@ -68,13 +79,13 @@ While inside the shell/TUI session (bare `hermes-relay`, the default mode), `Ctr
|
||||
- **[Reconnect-on-drop + TOFU cert pinning](./pairing.md)** — exponential backoff (1 s → 30 s, 5 min on 429), per-host SPKI sha256 pin captured first-time and verified every reconnect.
|
||||
- **[Bun-compiled native binary, no Node required](./installation.md)** — curl/irm one-liners install a self-contained binary. Version-aware (`upgrading X → Y` readback), collision-safe `hermes` alias, `~/.hermes/bin/` on PATH.
|
||||
|
||||
## When to use the desktop CLI vs. installing Hermes locally
|
||||
## When to use the CLI vs. installing Hermes locally
|
||||
|
||||
Both are valid. Pick based on where the agent's compute, models, and state should live.
|
||||
|
||||
| Setup | When it fits | What lives where |
|
||||
|-------|--------------|------------------|
|
||||
| **Server-deployed Hermes + Hermes-Relay desktop CLI** | Multiple devices, shared sessions, GPU on a different box, model API keys you don't want spread across machines. | Compute, models, secrets, sessions, memory all live on the Hermes host. The CLI is a thin client. Pair from laptop, desktop, work box — same agent, shared state. |
|
||||
| **Hermes on its own host + Hermes-Relay CLI** | Multiple devices, shared sessions, GPU on a different box, model API keys you don't want spread across machines. | Compute, models, secrets, sessions, memory all live on the Hermes host. The CLI is a thin client. Pair from laptop, desktop, work box, headless server — same agent, shared state. |
|
||||
| **Native local Hermes install** | Single machine, willing to manage Python venv + model API keys yourself, no cross-device session continuity needed. | Everything on your laptop. Model API calls go directly from your machine. No relay involved. |
|
||||
|
||||
Hermes-Relay is for the first case. If you're in the second case, you don't need this CLI at all — just install hermes-agent and use `hermes` directly. The two paths are complements, not alternatives.
|
||||
@@ -89,7 +100,7 @@ hermes-relay pair --remote ws://<host>:8767
|
||||
hermes-relay
|
||||
```
|
||||
|
||||
```bash [macOS / Linux]
|
||||
```bash [macOS / Linux — coming soon]
|
||||
curl -fsSL https://raw.githubusercontent.com/Codename-11/hermes-relay/main/desktop/scripts/install.sh | sh
|
||||
hermes-relay pair --remote ws://<host>:8767
|
||||
hermes-relay
|
||||
@@ -97,31 +108,25 @@ hermes-relay
|
||||
|
||||
:::
|
||||
|
||||
The third command (`hermes-relay` with no args) drops you into `shell` mode — the full Hermes TUI verbatim, running in tmux on the server. Try `Ctrl+A v` after a `Win+Shift+S` (or `Cmd+Shift+4`) and you'll see the killer demo in action.
|
||||
The third command (`hermes-relay` with no args) drops you into `shell` mode — the full Hermes TUI verbatim, running in tmux on the server. Try `Ctrl+A v` after a `Win+Shift+S` and you'll see the [native-paste demo](#demo-native-paste-into-the-attached-tui) in action.
|
||||
|
||||
See **[Installation](./installation.md)** for the full walkthrough (Bun-compiled binaries, version-aware install, `hermes` alias, self-update flow) and **[Pairing](./pairing.md)** for minting a 6-char code on the server.
|
||||
|
||||
The tray app follows the same rule as the CLI for relay-backed desktop control: daemon, devices, grants, and TUI controls unlock only after a paired session token exists in `~/.hermes/remote-sessions.json`. A raw Advanced relay URL is only an override hint, not a pairing, so fresh or signed-out installs show "Pair first" for those controls until pairing completes. The Chat tab can run in a lighter chat-only mode against a direct Hermes WebAPI URL (`http://host:8642`) with the API key from `API_SERVER_KEY` kept only for the current tray session. The TUI tab runs the experimental embedded terminal: xterm.js renders inside the dashboard while the Rust tray process owns the local PTY and launches the same tmux-backed `hermes-relay` session path. The Plugins tab uses the same embedded terminal host for installable dashboard surfaces such as Herm. Open in an external terminal remains the fallback for PTY focus, resize, and shortcut testing.
|
||||
The tray app is a control surface for the hand, not a chat app (chat and management UX live in [hermes-desktop](https://github.com/NousResearch/hermes-agent)). It follows the same rule as the CLI for relay-backed control: daemon, devices, grants, and TUI controls unlock only after a paired session token exists in `~/.hermes/remote-sessions.json`. A raw Advanced relay URL is only an override hint, not a pairing, so fresh or signed-out installs show "Pair first" for those controls until pairing completes. The TUI tab runs the experimental embedded terminal: xterm.js renders inside the dashboard while the Rust tray process owns the local PTY and launches the same tmux-backed `hermes-relay` session path. The Plugins tab uses the same embedded terminal host for installable dashboard surfaces such as Herm. Open in an external terminal remains the fallback for PTY focus, resize, and shortcut testing.
|
||||
|
||||
## Why both shell AND chat modes?
|
||||
|
||||
They're not the same thing:
|
||||
|
||||
- **`shell`** pipes the host's actual `hermes` CLI through a PTY. You see exactly what `ssh bailey@hermes-host hermes` would show — same banner, same skin, same slash commands. Best for interactive use.
|
||||
- **`chat`** speaks the relay's structured `tui` channel (JSON-RPC-over-WSS), renders events as plain lines. Scriptable, pipeable, survives non-TTY environments. The REPL still has slash-command paste / screenshot / image attach. Best for automation / CI / one-shot queries.
|
||||
- **`chat`** speaks the relay's structured `tui` channel (JSON-RPC-over-WSS), renders events as plain lines. Scriptable, pipeable, survives non-TTY environments. Best for automation / CI / one-shot queries.
|
||||
|
||||
Most users want `shell`. If you're writing a script, use `chat --json`.
|
||||
|
||||
## Local tool routing — the big deal
|
||||
|
||||
The agent on the server can reach through the relay and run tools on **your** machine — read your notes, grep your codebase, run a build, edit a file, capture a screenshot, read your clipboard — while the agent's brain + conversation state stay on the host. [Read how →](./tools.md)
|
||||
|
||||
This mirrors how the Android client exposes `android_tap` / `android_screenshot` to the agent. Zero hermes-agent core changes — the `desktop_*` tools are registered via the standard plugin system, same pattern as `android_*`.
|
||||
Use `shell` when you want to drive interactively; use `chat --json` from scripts. Chat mode is maintained for automation — it isn't where new features land, and it isn't a desktop chat app (that's [hermes-desktop](https://github.com/NousResearch/hermes-agent)'s job).
|
||||
|
||||
## Related
|
||||
|
||||
- [Hermes-Relay Android client](/guide/) — same project, same relay, different surface (phone control, voice, bridge).
|
||||
- [Hermes Agent](https://github.com/NousResearch/hermes-agent) — the agent platform the CLI talks to.
|
||||
- [Herm](https://github.com/liftaris/herm) — OpenTUI dashboard plugin installable from the desktop surface.
|
||||
- [Desktop CLI GitHub source](https://github.com/Codename-11/hermes-relay/tree/main/desktop) — `@hermes-relay/cli` package.
|
||||
- [CLI GitHub source](https://github.com/Codename-11/hermes-relay/tree/main/desktop) — `@hermes-relay/cli` package.
|
||||
- [Release notes](https://github.com/Codename-11/hermes-relay/releases?q=desktop) — tagged `desktop-v*` (separate track from Android).
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Installing the Desktop CLI <ExperimentalBadge />
|
||||
# Installing the CLI <ExperimentalBadge />
|
||||
|
||||
One-liner installs on Windows / macOS / Linux. The installer downloads a prebuilt single-file binary — **no Node required, no Python required**.
|
||||
One-liner install on **Windows today — macOS / Linux builds coming soon**. The installer downloads a prebuilt single-file binary — **no Node required, no Python required**.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
@@ -21,8 +21,8 @@ The script:
|
||||
2. Resolves the **latest** desktop release by querying the GitHub Releases API directly and picking the SemVer-max `desktop-v*` tag — prereleases included, so alpha builds aren't skipped (see CHANGELOG entry on alpha.11 for why this matters).
|
||||
3. Downloads `hermes-relay-win-x64.exe` and verifies SHA256 against the published `SHA256SUMS.txt`.
|
||||
4. Reads the existing binary's `--version` (if present) and prints one of:
|
||||
- `existing install detected: 0.3.0-alpha.13 — upgrading to 0.3.0-alpha.14`
|
||||
- `reinstalling 0.3.0-alpha.14`
|
||||
- `existing install detected: 0.3.0-alpha.17 — upgrading to 0.3.0-alpha.18`
|
||||
- `reinstalling 0.3.0-alpha.18`
|
||||
- `installing fresh`
|
||||
5. Installs to `%USERPROFILE%\.hermes\bin\hermes-relay.exe`.
|
||||
6. Drops a `hermes.cmd` shim next to the binary so `hermes <prompt>` works as a short alias — **collision-safe**: if a `hermes` already exists in the install dir (e.g. from a local hermes-agent install), the installer leaves it untouched and prints a skip notice.
|
||||
@@ -49,11 +49,11 @@ Code signing (EV cert) is a v1.0 milestone — the experimental phase doesn't ju
|
||||
### Pin a specific version
|
||||
|
||||
```powershell
|
||||
$env:HERMES_RELAY_VERSION = 'desktop-v0.3.0-alpha.14'
|
||||
$env:HERMES_RELAY_VERSION = 'desktop-v0.3.0-alpha.18'
|
||||
irm https://raw.githubusercontent.com/Codename-11/hermes-relay/main/desktop/scripts/install.ps1 | iex
|
||||
```
|
||||
|
||||
The version-aware readback compares full SemVer including the prerelease tail, so `desktop-v0.3.0-alpha.13` → `desktop-v0.3.0-alpha.14` is recognized as an upgrade rather than a reinstall.
|
||||
The version-aware readback compares full SemVer including the prerelease tail, so `desktop-v0.3.0-alpha.17` → `desktop-v0.3.0-alpha.18` is recognized as an upgrade rather than a reinstall.
|
||||
|
||||
### Uninstall
|
||||
|
||||
@@ -61,6 +61,10 @@ See [Uninstall](#uninstall) below — the PowerShell one-liner reverses everythi
|
||||
|
||||
## macOS / Linux — curl one-liner
|
||||
|
||||
::: warning Coming soon
|
||||
macOS / Linux binaries aren't published yet — the installer script below is ready and documented ahead of those builds. Watch [Releases](https://github.com/Codename-11/hermes-relay/releases) for the first `darwin`/`linux` assets.
|
||||
:::
|
||||
|
||||
```bash
|
||||
curl -fsSL https://raw.githubusercontent.com/Codename-11/hermes-relay/main/desktop/scripts/install.sh | sh
|
||||
```
|
||||
@@ -103,7 +107,7 @@ Apple Developer ID signing + notarization is a v1.0 milestone.
|
||||
### Pin a specific version
|
||||
|
||||
```bash
|
||||
HERMES_RELAY_VERSION=desktop-v0.3.0-alpha.14 \
|
||||
HERMES_RELAY_VERSION=desktop-v0.3.0-alpha.18 \
|
||||
curl -fsSL https://raw.githubusercontent.com/Codename-11/hermes-relay/main/desktop/scripts/install.sh | sh
|
||||
```
|
||||
|
||||
@@ -151,7 +155,7 @@ npx tsx src/cli.ts --help
|
||||
npx tsx src/cli.ts pair --remote ws://<host>:8767
|
||||
```
|
||||
|
||||
The package name in `desktop/package.json` is local workspace metadata today. The desktop CLI is not published to npm; use GitHub Release binaries or a local clone with `npm link`.
|
||||
The package name in `desktop/package.json` is local workspace metadata today. The CLI is not published to npm; use GitHub Release binaries or a local clone with `npm link`.
|
||||
|
||||
## Uninstall
|
||||
|
||||
|
||||
@@ -183,7 +183,7 @@ hermes-relay tools --remote <url> --json # machine-readable
|
||||
|
||||
## `hermes-relay devices`
|
||||
|
||||
Server-side paired-device management (the relay's `GET/DELETE/PATCH /sessions` HTTP API). Shows all paired devices across all your clients — Android phones, desktop CLIs, Ink TUIs — and lets you revoke or extend them.
|
||||
Server-side paired-device management (the relay's `GET/DELETE/PATCH /sessions` HTTP API). Shows all paired devices across all your clients — Android phones, CLI clients, Ink TUIs — and lets you revoke or extend them.
|
||||
|
||||
```bash
|
||||
hermes-relay devices # list (defaults to the one stored relay)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Troubleshooting <ExperimentalBadge />
|
||||
|
||||
Common errors in the desktop CLI, indexed by exact message. If your problem isn't here, [open an issue](https://github.com/Codename-11/hermes-relay/issues/new).
|
||||
Common errors in the CLI, indexed by exact message. If your problem isn't here, [open an issue](https://github.com/Codename-11/hermes-relay/issues/new).
|
||||
|
||||
## `hermes-relay: command not found` / `is not recognized`
|
||||
|
||||
@@ -137,7 +137,7 @@ Fixed in alpha.12. Pre-alpha.12 `install.sh` and `install.ps1` printed lines lik
|
||||
|
||||
## `timed out after 30ms` (or any millisecond-range timeout on a desktop tool)
|
||||
|
||||
You're running a pre-fix desktop CLI build. The Python side sends `timeout` in seconds; early Node builds treated it as milliseconds — `30` seconds became 30 ms, and every shell command SIGKILL'd instantly.
|
||||
You're running a pre-fix CLI build. The Python side sends `timeout` in seconds; early Node builds treated it as milliseconds — `30` seconds became 30 ms, and every shell command SIGKILL'd instantly.
|
||||
|
||||
Fixed in releases after 2026-04-23. Upgrade:
|
||||
|
||||
@@ -166,7 +166,7 @@ Two layers — check them in order:
|
||||
curl -s "http://127.0.0.1:8767/desktop/_ping?tool=desktop_terminal"
|
||||
```
|
||||
|
||||
**If `connected: false`**: no desktop CLI is attached. Run `hermes-relay` (bare = shell/TUI mode by default) or `hermes-relay chat`; make sure you didn't pass `--no-tools`; make sure you consented on the first-run prompt.
|
||||
**If `connected: false`**: no CLI client is attached. Run `hermes-relay` (bare = shell/TUI mode by default) or `hermes-relay chat`; make sure you didn't pass `--no-tools`; make sure you consented on the first-run prompt.
|
||||
|
||||
**If `connected: true`** but Hermes still can't see the tools: the plugin isn't loaded by the gateway, or the `desktop` toolset isn't enabled for your session.
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ A **<span class="track-badge track-badge--sideload">Sideload only</span>** badge
|
||||
| Feature | Description |
|
||||
|---------|-------------|
|
||||
| [Direct API Connection](/features/direct-api) | HTTP/SSE streaming to Hermes API Server |
|
||||
| [Voice Mode](/features/voice) | Real-time voice conversation — sphere listens, agent speaks back via your server's configured TTS/STT providers |
|
||||
| [Voice Mode](/features/voice) | Real-time voice conversation — you talk, the agent answers aloud via your server's configured TTS/STT providers |
|
||||
| [Markdown Rendering](/features/markdown) | Full markdown with syntax-highlighted code blocks |
|
||||
| [Reasoning Display](/features/reasoning) | Collapsible extended-thinking blocks |
|
||||
| [Connections](/features/connections) | Save multiple Hermes servers — one-tap switch from the top-bar chip |
|
||||
@@ -135,7 +135,7 @@ For the full decision guide and install instructions for each, see [Release trac
|
||||
|---------|--------|
|
||||
| Push Notifications | Future — Agent-initiated alerts |
|
||||
| Memory Viewer | Future — View/edit agent memories |
|
||||
| Cross-device handoff | Future — Hand a task from phone to desktop terminal session |
|
||||
| Cross-device handoff | Future — Hand a task from your phone to a desktop hand |
|
||||
|
||||
<style scoped>
|
||||
.track-badge {
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
|
||||
This section covers the **Android client** for [Hermes Agent](https://hermes-agent.nousresearch.com): standard API/dashboard setup, chat, Manage, voice, optional Relay pairing, terminal/TUI relay, notifications, and optional sideload Device Control.
|
||||
|
||||
::: tip Looking for the desktop CLI?
|
||||
The desktop terminal client (Windows / macOS / Linux) lives in its own section: **[Desktop CLI →](/desktop/)**. Desktop Relay pairing and Android Relay pairing use the same Hermes-Relay server and `~/.hermes/remote-sessions.json`.
|
||||
::: tip Want the agent to have hands on your other machines too?
|
||||
The Hermes-Relay CLI (Windows today; macOS / Linux coming soon) gives your Hermes agent consent-gated filesystem, terminal, and screenshot access on any machine you pair — plus a terminal escape hatch for you: **[CLI →](/desktop/)**. Both surfaces share the same relay pairing and `~/.hermes/remote-sessions.json`.
|
||||
:::
|
||||
|
||||
Hermes-Relay is a native Android app for [Hermes Agent](https://hermes-agent.nousresearch.com). Chat with your agent through the Hermes API server, manage Skills/Cron/MCP/Profile surfaces through the dashboard, use voice, and optionally pair Relay for terminal/TUI and bridge power tools. The Google Play build ships Bridge Core only; sideload builds add AccessibilityService-backed Device Control.
|
||||
@@ -40,7 +40,7 @@ Phone (WSS/HTTP) → Relay Server (:8767) [Bridge Core, terminal, TUI, me
|
||||
|
||||
Chat connects directly to the Hermes API Server using the Sessions API with SSE streaming. The relay server handles Bridge Core, terminal, TUI, media, notification companion, sessions, and `/voice/*` routes. Sideload builds additionally expose Android Device Control routes.
|
||||
|
||||
## Current Status — v0.8.0
|
||||
## Current Status — v0.8.1
|
||||
|
||||
| Feature | Status |
|
||||
|---------|--------|
|
||||
|
||||
+35
-103
@@ -3,136 +3,68 @@ layout: home
|
||||
|
||||
hero:
|
||||
name: Hermes-Relay
|
||||
text: One Hermes agent. Two ways to use it.
|
||||
tagline: An Android companion for self-hosted Hermes chat, voice, relay pairing, and optional sideload phone control, plus a desktop CLI that lets you use a server-deployed Hermes from your laptop as if it were running locally.
|
||||
text: Runs on your machine. Lives on your devices.
|
||||
tagline: Pair your Hermes agent with the devices around you — a native Android companion for chat, voice, and full phone control, plus a single-binary CLI that gives the agent hands on any machine you put it on.
|
||||
actions:
|
||||
- theme: brand
|
||||
text: Use Hermes from my desktop
|
||||
link: /desktop/
|
||||
- theme: alt
|
||||
text: Use Hermes on Android
|
||||
text: Get the Android app
|
||||
link: /guide/getting-started
|
||||
- theme: alt
|
||||
text: Give it hands — CLI
|
||||
link: /desktop/
|
||||
|
||||
features:
|
||||
- icon:
|
||||
src: /icons/chat.svg
|
||||
width: 40
|
||||
height: 40
|
||||
title: Two surfaces, one pair
|
||||
details: Pair once. Both the Android app and the desktop CLI share `~/.hermes/remote-sessions.json` and the same WSS relay — sign in on either, both light up.
|
||||
- icon:
|
||||
src: /icons/sessions.svg
|
||||
width: 40
|
||||
height: 40
|
||||
title: Server-deployed Hermes, native feel
|
||||
details: The agent brain (LLM, tools, memory, sessions) lives on your Hermes host. The desktop CLI gives you the same shell, the same TUI, the same `/paste` flow — over WSS, no Python on your laptop.
|
||||
- icon:
|
||||
src: /icons/reasoning.svg
|
||||
width: 40
|
||||
height: 40
|
||||
title: Native paste in your shell
|
||||
details: "`Win+Shift+S` → `Ctrl+A v` inside `hermes-relay shell` ships the screenshot to the server inbox and types `/paste` into the TUI. Identical UX to a local Hermes install."
|
||||
title: Chat that streams, not spins
|
||||
details: Direct HTTP/SSE to your Hermes API server — markdown, syntax-highlighted code, reasoning blocks, live tool-progress cards. No cloud relay in the path.
|
||||
- icon:
|
||||
src: /icons/personalities.svg
|
||||
width: 40
|
||||
height: 40
|
||||
title: Android Bridge Core
|
||||
details: Google Play keeps chat, voice, terminal/TUI relay, notifications, media, and sessions. Sideload builds add AccessibilityService-backed phone control for screen reading, taps, typing, screenshots, and safety rails.
|
||||
title: Hands-free voice
|
||||
details: Talk to your agent using your server's own TTS/STT — or opt into the provider-native Realtime Agent for low-latency, barge-in conversation.
|
||||
- icon:
|
||||
src: /icons/tokens.svg
|
||||
src: /icons/sessions.svg
|
||||
width: 40
|
||||
height: 40
|
||||
title: Local tool routing (Desktop)
|
||||
details: The remote agent can read, write, search, run shell commands, capture screenshots, and read your clipboard — on your machine — through the same relay it uses for chat. Consent-gated.
|
||||
title: Manage from your pocket
|
||||
details: Skills, cron jobs, profiles, models, and keys — your server's dashboard controls, rebuilt native. One sign-in unlocks Manage and voice.
|
||||
- icon:
|
||||
src: /icons/tools.svg
|
||||
width: 40
|
||||
height: 40
|
||||
title: Multi-endpoint pairing
|
||||
details: One QR carries LAN + Tailscale + public URLs. The client races them in priority order on every connect and on network change, so the same pair just works whether you're at home, on the train, or behind your VPN.
|
||||
title: Your phone, on the agent's toolbelt
|
||||
details: Sideload builds add screen reading, taps, typing, and vision-driven navigation — fenced by a per-app blocklist, destructive-verb confirmation, and auto-disable.
|
||||
- icon:
|
||||
src: /icons/tokens.svg
|
||||
width: 40
|
||||
height: 40
|
||||
title: Hands on any machine
|
||||
details: Install the single-binary CLI — desktop, laptop, or headless box — and the agent can read, write, search, run commands, and capture screens there. Consent-gated per device.
|
||||
- icon:
|
||||
src: /icons/markdown.svg
|
||||
width: 40
|
||||
height: 40
|
||||
title: Streaming chat + structured tools
|
||||
details: Direct SSE to the Hermes API server with markdown rendering, syntax-highlighted code, reasoning blocks, tool-progress cards, and a 29-command searchable palette.
|
||||
title: Works at home and away
|
||||
details: Connections carry LAN, Tailscale, and public routes, and the app picks the best one on every connect and network change — home, train, or VPN, it just works.
|
||||
- icon:
|
||||
src: /icons/reasoning.svg
|
||||
width: 40
|
||||
height: 40
|
||||
title: Notifications and media in the loop
|
||||
details: Forward phone notifications to your agent, and let it hand files, images, and rich cards back into chat — shareable through native Android flows.
|
||||
- icon:
|
||||
src: /icons/security.svg
|
||||
width: 40
|
||||
height: 40
|
||||
title: Secure by default
|
||||
details: QR pairing, session tokens (Android Keystore / mode-0600 file), TOFU cert pinning, per-channel grants, user-chosen TTLs, revocable from any client.
|
||||
title: Private by architecture
|
||||
details: No cloud in the path — QR pairing, Keystore-held tokens, TOFU cert pinning, per-channel grants with TTLs you choose, revocable from any client.
|
||||
---
|
||||
|
||||
<style>
|
||||
:root {
|
||||
--hr-card-bg: var(--vp-c-bg-soft);
|
||||
--hr-card-border: var(--vp-c-divider);
|
||||
}
|
||||
.dark {
|
||||
--hr-card-bg: var(--vp-c-bg-alt);
|
||||
}
|
||||
.surface-grid {
|
||||
display: grid;
|
||||
gap: 1.25rem;
|
||||
grid-template-columns: 1fr;
|
||||
margin: 2rem auto 0;
|
||||
max-width: 1152px;
|
||||
padding: 0 1.5rem;
|
||||
}
|
||||
@media (min-width: 768px) {
|
||||
.surface-grid { grid-template-columns: 1fr 1fr; }
|
||||
}
|
||||
.surface-card {
|
||||
background: var(--hr-card-bg);
|
||||
border: 1px solid var(--hr-card-border);
|
||||
border-radius: 12px;
|
||||
padding: 1.5rem;
|
||||
}
|
||||
.surface-card h3 {
|
||||
margin: 0 0 .25rem;
|
||||
font-size: 1.25rem;
|
||||
}
|
||||
.surface-card .surface-tag {
|
||||
font-size: .8rem;
|
||||
font-weight: 500;
|
||||
letter-spacing: .04em;
|
||||
text-transform: uppercase;
|
||||
color: var(--vp-c-text-2);
|
||||
}
|
||||
.surface-card p { margin: .75rem 0; line-height: 1.55; }
|
||||
.surface-card ul { padding-left: 1.1rem; margin: .5rem 0 1rem; }
|
||||
.surface-card li { margin: .25rem 0; }
|
||||
.surface-card .cta {
|
||||
display: inline-block;
|
||||
font-weight: 500;
|
||||
margin-top: .5rem;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="surface-grid">
|
||||
<div class="surface-card">
|
||||
<div class="surface-tag">Surface 1</div>
|
||||
<h3>Android — Bridge Core</h3>
|
||||
<p>Native app. Talks directly to the Hermes API server for chat, plus a WSS relay for voice, terminal/TUI relay, media, sessions, status, and notification companion.</p>
|
||||
<ul>
|
||||
<li>Streaming chat, multi-Connection, agent profiles, personalities</li>
|
||||
<li>Voice mode with the morphing sphere + your server's TTS/STT</li>
|
||||
<li>Google Play track: no AccessibilityService, screen reading, taps, typing, screenshots, SMS, calls, contacts, location, or unattended control</li>
|
||||
<li>Sideload track: AccessibilityService-backed Device Control with per-app blocklist, destructive-verb confirmation, and auto-disable</li>
|
||||
</ul>
|
||||
<a class="cta" href="./guide/getting-started.html">Install the Android app →</a>
|
||||
</div>
|
||||
<div class="surface-card">
|
||||
<div class="surface-tag">Surface 2 · Experimental</div>
|
||||
<h3>Desktop CLI — terminal</h3>
|
||||
<p>One binary, no Node required. Pipes the full Hermes TUI over a PTY, or streams structured chat events for scripting. The remote agent can also reach back through the relay and run tools on <em>your</em> machine.</p>
|
||||
<ul>
|
||||
<li><code>shell</code> mode — full Hermes CLI verbatim, in tmux on the server</li>
|
||||
<li><code>chat</code> mode — REPL or one-shot, JSON event stream for scripting</li>
|
||||
<li>In-shell <code>Ctrl+A v</code> paste · multi-monitor screenshots · editor tool</li>
|
||||
<li>Daemon mode keeps tools advertised even when no shell is open</li>
|
||||
</ul>
|
||||
<a class="cta" href="./desktop/">Use Hermes from my desktop →</a>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Home body intentionally empty — the sphere, How-it-works strip, surface
|
||||
cards, and Get-started section are slotted via .vitepress/theme/index.ts
|
||||
(markdown body always renders below the VPFeatures grid, which is the
|
||||
wrong place for all of them). -->
|
||||
|
||||
@@ -8,7 +8,7 @@ description: Privacy policy for the Hermes-Relay Android app
|
||||
|
||||
**Hermes-Relay** · Effective date: May 19, 2026
|
||||
|
||||
Hermes-Relay is a native Android app that connects to your self-hosted [Hermes Agent](https://github.com/NousResearch/hermes-agent) server. This policy describes how the app handles your data.
|
||||
Hermes-Relay is a native Android app that connects to your own [Hermes Agent](https://github.com/NousResearch/hermes-agent) host. This policy describes how the app handles your data.
|
||||
|
||||
## Summary
|
||||
|
||||
|
||||
Reference in New Issue
Block a user