chore: comprehensive branding scrub from Hermes audit — electron, tauri, env, local-setup, docs archived
Fixes from Hermes Agent gpt-5.4 audit: - Electron builder: appId/productName/copyright → Hermes Workspace - Tauri: bundle identifier → com.hermes.workspace - Electron preload: window.clawsuite → window.hermesWorkspace - Electron onboarding: npm install openclaw → pip install hermes-agent - Env vars: CLAWDBOT_*/CLAWSUITE_* → HERMES_* - local-setup.ts: full Hermes rewrite (install, start, config paths) - workspace/files API: .openclaw → .hermes paths - Legacy ClawSuite docs → docs/archive/ - README: openclaw.ai → github.com/NousResearch/hermes-agent - .workspace-source updated
This commit is contained in:
18
.env.example
18
.env.example
@@ -1,6 +1,6 @@
|
||||
# ClawSuite → OpenClaw Gateway Connection
|
||||
# Hermes Workspace → Hermes Gateway Connection
|
||||
#
|
||||
# The ClawSuite server connects to the OpenClaw Gateway via WebSocket.
|
||||
# The Hermes Workspace server connects to the Hermes Gateway via WebSocket.
|
||||
# Keep secrets here (never exposed to the browser).
|
||||
|
||||
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
@@ -11,7 +11,7 @@
|
||||
# - Local gateway (default): ws://127.0.0.1:18789
|
||||
# - Remote gateway: wss://your-gateway.example.com
|
||||
# - Docker: ws://host.docker.internal:18789 (to reach host machine)
|
||||
CLAWDBOT_GATEWAY_URL=ws://127.0.0.1:18789
|
||||
HERMES_GATEWAY_URL=ws://127.0.0.1:18789
|
||||
|
||||
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
# AUTHENTICATION (Choose one method)
|
||||
@@ -20,22 +20,22 @@ CLAWDBOT_GATEWAY_URL=ws://127.0.0.1:18789
|
||||
# Gateway Token (Recommended)
|
||||
# How to find your token:
|
||||
# 1. Run: openclaw config get gateway.auth.token
|
||||
# 2. Or check OpenClaw settings UI
|
||||
# 2. Or check Hermes settings UI
|
||||
# Format: clw_abc123def456... (64+ characters)
|
||||
CLAWDBOT_GATEWAY_TOKEN=
|
||||
HERMES_GATEWAY_TOKEN=
|
||||
|
||||
# Gateway Password (Alternative to token)
|
||||
# Only use if your gateway is configured for password auth
|
||||
# CLAWDBOT_GATEWAY_PASSWORD=
|
||||
# HERMES_GATEWAY_PASSWORD=
|
||||
|
||||
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
# CLAWSUITE ACCESS CONTROL (Optional)
|
||||
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
# Password-protect ClawSuite web interface
|
||||
# Password-protect Hermes Workspace web interface
|
||||
# Leave empty for no authentication (default for local use)
|
||||
# Set this for production deployments
|
||||
# CLAWSUITE_PASSWORD=
|
||||
# HERMES_PASSWORD=
|
||||
|
||||
# Allow access from non-localhost addresses
|
||||
# Comma-separated list of hostnames/IPs
|
||||
@@ -44,4 +44,4 @@ CLAWDBOT_GATEWAY_TOKEN=
|
||||
# - Tailscale: my-server.tail1234.ts.net
|
||||
# - LAN: 192.168.1.50
|
||||
# - Subnet: 192.168.1.0/24
|
||||
# CLAWSUITE_ALLOWED_HOSTS=
|
||||
# HERMES_ALLOWED_HOSTS=
|
||||
|
||||
@@ -1 +1 @@
|
||||
/Users/aurora/.openclaw/workspace/clawsuite
|
||||
/Users/aurora/.openclaw/workspace/hermes-workspace/clawsuite
|
||||
|
||||
@@ -65,7 +65,7 @@
|
||||
### Prerequisites
|
||||
|
||||
- **Node.js 22+** — [nodejs.org](https://nodejs.org/)
|
||||
- **Hermes Agent** running locally — [Setup Guide](https://openclaw.ai/docs/installation)
|
||||
- **Hermes Agent** running locally — [Setup Guide](https://github.com/NousResearch/hermes-agent)
|
||||
|
||||
### Install & Run
|
||||
|
||||
|
||||
@@ -3,9 +3,9 @@
|
||||
* https://www.electron.build/configuration
|
||||
*/
|
||||
module.exports = {
|
||||
appId: 'com.clawsuite.app',
|
||||
productName: 'ClawSuite',
|
||||
copyright: 'Copyright © 2026 ClawSuite',
|
||||
appId: 'com.hermes.workspace',
|
||||
productName: 'Hermes Workspace',
|
||||
copyright: 'Copyright © 2026 Hermes Workspace',
|
||||
|
||||
directories: {
|
||||
output: 'release',
|
||||
@@ -35,7 +35,7 @@ module.exports = {
|
||||
},
|
||||
|
||||
dmg: {
|
||||
title: 'ClawSuite',
|
||||
title: 'Hermes Workspace',
|
||||
iconSize: 80,
|
||||
contents: [
|
||||
{ x: 130, y: 220 },
|
||||
@@ -68,7 +68,7 @@ module.exports = {
|
||||
publish: {
|
||||
provider: 'github',
|
||||
owner: 'outsourc-e',
|
||||
repo: 'clawsuite',
|
||||
repo: 'hermes-workspace',
|
||||
releaseType: 'release',
|
||||
},
|
||||
|
||||
|
||||
@@ -121,15 +121,15 @@ function findRunningServer() {
|
||||
function findRepoDir() {
|
||||
// Common locations for the Hermes Workspace repo
|
||||
const candidates = [
|
||||
(0, path_1.join)(process.env.HOME || '', '.openclaw', 'workspace', 'clawsuite'),
|
||||
(0, path_1.join)(process.env.HOME || '', 'clawsuite'),
|
||||
(0, path_1.join)(process.env.HOME || '', '.openclaw', 'workspace', 'hermesWorkspace'),
|
||||
(0, path_1.join)(process.env.HOME || '', 'hermesWorkspace'),
|
||||
(0, path_1.join)(__dirname, '..'),
|
||||
];
|
||||
for (const dir of candidates) {
|
||||
if ((0, fs_1.existsSync)((0, path_1.join)(dir, 'package.json'))) {
|
||||
try {
|
||||
const pkg = JSON.parse((0, fs_1.readFileSync)((0, path_1.join)(dir, 'package.json'), 'utf-8'));
|
||||
if (pkg.name === 'clawsuite' || pkg.name === '@clawsuite/app') return dir;
|
||||
if (pkg.name === 'hermesWorkspace' || pkg.name === '@clawsuite/app') return dir;
|
||||
} catch { /* skip */ }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -396,7 +396,7 @@
|
||||
<div class="terminal" id="install-terminal"></div>
|
||||
<div class="actions">
|
||||
<button class="btn btn-secondary" onclick="goToStep(1)">← Back</button>
|
||||
<button class="btn btn-primary" id="install-btn" onclick="installOpenClaw()">Install & Start</button>
|
||||
<button class="btn btn-primary" id="install-btn" onclick="installHermes()">Install & Start</button>
|
||||
</div>
|
||||
`
|
||||
} else if (selectedMode === 'cloud') {
|
||||
@@ -425,7 +425,7 @@
|
||||
status.innerHTML = '<div class="spinner"></div> Checking for Hermes...'
|
||||
|
||||
try {
|
||||
const result = await window.clawsuite.gateway.check()
|
||||
const result = await window.hermesWorkspace.gateway.check()
|
||||
if (result.url) {
|
||||
document.getElementById('gateway-url').value = result.url
|
||||
status.className = 'status show status-success'
|
||||
@@ -447,7 +447,7 @@
|
||||
status.innerHTML = '<div class="spinner"></div> Connecting...'
|
||||
|
||||
try {
|
||||
const result = await window.clawsuite.gateway.connect(url)
|
||||
const result = await window.hermesWorkspace.gateway.connect(url)
|
||||
if (result.success) {
|
||||
gatewayUrl = url
|
||||
status.className = 'status show status-success'
|
||||
@@ -470,7 +470,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
async function installOpenClaw() {
|
||||
async function installHermes() {
|
||||
const status = document.getElementById('install-status')
|
||||
const terminal = document.getElementById('install-terminal')
|
||||
const btn = document.getElementById('install-btn')
|
||||
@@ -480,16 +480,16 @@
|
||||
status.className = 'status show status-loading'
|
||||
status.innerHTML = '<div class="spinner"></div> Installing Hermes Agent...'
|
||||
terminal.classList.add('show')
|
||||
terminal.textContent = '$ npm install -g openclaw\n'
|
||||
terminal.textContent = '$ pip install hermes-agent\n'
|
||||
|
||||
try {
|
||||
const result = await window.clawsuite.gateway.install()
|
||||
const result = await window.hermesWorkspace.gateway.install()
|
||||
terminal.textContent += result.output + '\n✓ Installed!\n\n'
|
||||
|
||||
status.innerHTML = '<div class="spinner"></div> Starting Hermes...'
|
||||
terminal.textContent += '$ openclaw gateway start\n'
|
||||
terminal.textContent += '$ hermes --web\n'
|
||||
|
||||
const startResult = await window.clawsuite.gateway.start()
|
||||
const startResult = await window.hermesWorkspace.gateway.start()
|
||||
if (startResult.success) {
|
||||
gatewayUrl = startResult.url
|
||||
status.className = 'status show status-success'
|
||||
@@ -507,7 +507,7 @@
|
||||
}
|
||||
} catch (e) {
|
||||
status.className = 'status show status-error'
|
||||
status.innerHTML = '✗ Installation failed. Try running manually: npm install -g openclaw'
|
||||
status.innerHTML = '✗ Installation failed. Try running manually: pip install hermes-agent'
|
||||
terminal.textContent += '✗ Error: ' + e.message + '\n'
|
||||
btn.disabled = false
|
||||
btn.textContent = 'Retry'
|
||||
@@ -525,7 +525,7 @@
|
||||
|
||||
async function finishSetup() {
|
||||
if (gatewayUrl) {
|
||||
await window.clawsuite.onboarding.complete({
|
||||
await window.hermesWorkspace.onboarding.complete({
|
||||
mode: selectedMode,
|
||||
gatewayUrl: gatewayUrl,
|
||||
})
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
*/
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const electron_1 = require("electron");
|
||||
electron_1.contextBridge.exposeInMainWorld('clawsuite', {
|
||||
electron_1.contextBridge.exposeInMainWorld('hermesWorkspace', {
|
||||
// Gateway management
|
||||
gateway: {
|
||||
check: () => electron_1.ipcRenderer.invoke('gateway:check'),
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
import { contextBridge, ipcRenderer } from 'electron'
|
||||
|
||||
contextBridge.exposeInMainWorld('clawsuite', {
|
||||
contextBridge.exposeInMainWorld('hermesWorkspace', {
|
||||
// Gateway management
|
||||
gateway: {
|
||||
check: () => ipcRenderer.invoke('gateway:check'),
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"$schema": "https://schema.tauri.app/config/2",
|
||||
"productName": "Hermes Workspace",
|
||||
"version": "3.2.0",
|
||||
"identifier": "io.buildingthefuture.clawsuite",
|
||||
"identifier": "com.hermes.workspace",
|
||||
"build": {
|
||||
"frontendDist": "../dist/client",
|
||||
"devUrl": "http://localhost:3000",
|
||||
|
||||
@@ -154,7 +154,7 @@ function readSavedGatewayConfig(): SavedGatewayConfig | null {
|
||||
try {
|
||||
const settingsRaw =
|
||||
localStorage.getItem('hermes-settings') ??
|
||||
localStorage.getItem('openclaw-settings')
|
||||
localStorage.getItem('hermes-settings')
|
||||
if (settingsRaw) {
|
||||
const parsed = JSON.parse(settingsRaw) as {
|
||||
state?: { settings?: { gatewayUrl?: string; gatewayToken?: string } }
|
||||
|
||||
@@ -22,7 +22,7 @@ type SetupEvent = {
|
||||
token?: string
|
||||
}
|
||||
|
||||
type OpenClawConfig = {
|
||||
type HermesConfig = {
|
||||
gateway?: {
|
||||
port?: number
|
||||
auth?: {
|
||||
@@ -87,9 +87,9 @@ async function runCommand(command: string, args: string[], timeoutMs: number) {
|
||||
)
|
||||
}
|
||||
|
||||
async function isOpenClawInstalled() {
|
||||
async function isHermesInstalled() {
|
||||
try {
|
||||
const whichResult = await runCommand('which', ['openclaw'], 5_000)
|
||||
const whichResult = await runCommand('which', ['hermes'], 5_000)
|
||||
if (whichResult.code === 0 && whichResult.stdout.trim()) {
|
||||
return true
|
||||
}
|
||||
@@ -98,15 +98,15 @@ async function isOpenClawInstalled() {
|
||||
}
|
||||
|
||||
try {
|
||||
const versionResult = await runCommand('openclaw', ['--version'], 5_000)
|
||||
const versionResult = await runCommand('hermes', ['--version'], 5_000)
|
||||
return versionResult.code === 0 && Boolean(versionResult.stdout.trim())
|
||||
} catch {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
async function installOpenClaw() {
|
||||
const result = await runCommand('npm', ['install', '-g', 'openclaw'], 10 * 60_000)
|
||||
async function installHermes() {
|
||||
const result = await runCommand('pip', ['install', 'hermes-agent'], 10 * 60_000)
|
||||
if (result.code !== 0) {
|
||||
throw new Error(result.stderr.trim() || result.stdout.trim() || 'npm install failed')
|
||||
}
|
||||
@@ -136,13 +136,13 @@ async function isGatewayRunning(port: number) {
|
||||
})
|
||||
}
|
||||
|
||||
async function readGatewayConfig(): Promise<OpenClawConfig | null> {
|
||||
async function readGatewayConfig(): Promise<HermesConfig | null> {
|
||||
const configPath = join(homedir(), '.openclaw', 'openclaw.json')
|
||||
|
||||
try {
|
||||
await access(configPath)
|
||||
const raw = await readFile(configPath, 'utf8')
|
||||
return JSON.parse(raw) as OpenClawConfig
|
||||
return JSON.parse(raw) as HermesConfig
|
||||
} catch {
|
||||
return null
|
||||
}
|
||||
@@ -183,7 +183,7 @@ async function waitForGatewayToken() {
|
||||
await wait(POLL_INTERVAL_MS)
|
||||
}
|
||||
|
||||
throw new Error('Gateway auth token was not written to ~/.openclaw/openclaw.json')
|
||||
throw new Error('Gateway auth token was not written to ~/.hermes/config.yaml')
|
||||
}
|
||||
|
||||
export const Route = createFileRoute('/api/local-setup')({
|
||||
@@ -243,14 +243,14 @@ export const Route = createFileRoute('/api/local-setup')({
|
||||
message: 'Checking for Hermes...',
|
||||
})
|
||||
|
||||
let installed = await isOpenClawInstalled()
|
||||
let installed = await isHermesInstalled()
|
||||
if (!installed) {
|
||||
emit({
|
||||
status: 'installing',
|
||||
message: 'Installing Hermes...',
|
||||
})
|
||||
await installOpenClaw()
|
||||
installed = await isOpenClawInstalled()
|
||||
await installHermes()
|
||||
installed = await isHermesInstalled()
|
||||
}
|
||||
|
||||
if (!installed) {
|
||||
@@ -271,7 +271,7 @@ export const Route = createFileRoute('/api/local-setup')({
|
||||
} catch (error) {
|
||||
throw new Error(
|
||||
formatCommandError(
|
||||
'openclaw gateway start --bind lan',
|
||||
'hermes --web',
|
||||
error,
|
||||
'Failed to launch the Hermes gateway',
|
||||
),
|
||||
|
||||
@@ -40,13 +40,13 @@ type ConnectParams = {
|
||||
}
|
||||
|
||||
function getGatewayConfig() {
|
||||
const url = process.env.CLAWDBOT_GATEWAY_URL?.trim() || 'ws://127.0.0.1:18789'
|
||||
const token = process.env.CLAWDBOT_GATEWAY_TOKEN?.trim() || ''
|
||||
const password = process.env.CLAWDBOT_GATEWAY_PASSWORD?.trim() || ''
|
||||
const url = process.env.HERMES_GATEWAY_URL?.trim() || 'ws://127.0.0.1:18789'
|
||||
const token = process.env.HERMES_GATEWAY_TOKEN?.trim() || ''
|
||||
const password = process.env.HERMES_GATEWAY_PASSWORD?.trim() || ''
|
||||
|
||||
if (!token && !password) {
|
||||
throw new Error(
|
||||
'Missing gateway auth. Set CLAWDBOT_GATEWAY_TOKEN (recommended) or CLAWDBOT_GATEWAY_PASSWORD in the server environment.',
|
||||
'Missing gateway auth. Set HERMES_GATEWAY_TOKEN (recommended) or HERMES_GATEWAY_PASSWORD in the server environment.',
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -58,7 +58,7 @@ async function detectWorkspace(savedPath?: string): Promise<{
|
||||
}
|
||||
|
||||
// Priority 3: Default Hermes workspace path
|
||||
const defaultPath = path.join(os.homedir(), '.openclaw', 'workspace')
|
||||
const defaultPath = path.join(os.homedir(), '.hermes')
|
||||
const defaultValid = await isValidDirectory(defaultPath)
|
||||
if (defaultValid) {
|
||||
return {
|
||||
@@ -75,7 +75,7 @@ async function detectWorkspace(savedPath?: string): Promise<{
|
||||
if (openclawValid) {
|
||||
return {
|
||||
path: openclawDir,
|
||||
folderName: '.openclaw',
|
||||
folderName: '.hermes',
|
||||
source: 'default',
|
||||
isValid: true,
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ type DateStampedFile = {
|
||||
function getWorkspaceRoot(): string {
|
||||
const configured = (process.env.OPENCLAW_WORKSPACE || '').trim()
|
||||
return path.resolve(
|
||||
configured || path.join(os.homedir(), '.openclaw', 'workspace'),
|
||||
configured || path.join(os.homedir(), '.hermes'),
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -61,7 +61,7 @@ const OPENAI_MODEL = 'gpt-4o-mini'
|
||||
|
||||
function getGatewayHttpUrl(path: string): string {
|
||||
const envUrl =
|
||||
process.env.CLAWDBOT_GATEWAY_URL?.trim() || 'ws://127.0.0.1:18789'
|
||||
process.env.HERMES_GATEWAY_URL?.trim() || 'ws://127.0.0.1:18789'
|
||||
try {
|
||||
const parsed = new URL(envUrl)
|
||||
parsed.protocol = parsed.protocol === 'wss:' ? 'https:' : 'http:'
|
||||
@@ -143,7 +143,7 @@ async function readHermesConfig(): Promise<HermesConfig | null> {
|
||||
async function resolveProvider(): Promise<ResolvedProvider | null> {
|
||||
// Try gateway first — works with any configured provider, uses gateway token
|
||||
try {
|
||||
const gwTokenEnv = process.env.CLAWDBOT_GATEWAY_TOKEN?.trim()
|
||||
const gwTokenEnv = process.env.HERMES_GATEWAY_TOKEN?.trim()
|
||||
if (gwTokenEnv) {
|
||||
// Quick probe to see if gateway is up
|
||||
const probe = await fetch(getGatewayHttpUrl('/health'), {
|
||||
|
||||
@@ -105,9 +105,9 @@ async function discoverFromCli(): Promise<DiscoveryResult> {
|
||||
* Check if env vars are already set
|
||||
*/
|
||||
function discoverFromEnv(): DiscoveryResult {
|
||||
const url = process.env.CLAWDBOT_GATEWAY_URL?.trim()
|
||||
const token = process.env.CLAWDBOT_GATEWAY_TOKEN?.trim()
|
||||
const password = process.env.CLAWDBOT_GATEWAY_PASSWORD?.trim()
|
||||
const url = process.env.HERMES_GATEWAY_URL?.trim()
|
||||
const token = process.env.HERMES_GATEWAY_TOKEN?.trim()
|
||||
const password = process.env.HERMES_GATEWAY_PASSWORD?.trim()
|
||||
|
||||
if (token) {
|
||||
// Only short-circuit if we actually have a token.
|
||||
@@ -253,16 +253,16 @@ export async function discoverGateway(): Promise<DiscoveryResult> {
|
||||
const fileResult = await discoverFromConfigFile()
|
||||
if (fileResult.found) {
|
||||
// Apply to process.env so gateway client uses it
|
||||
if (fileResult.url) process.env.CLAWDBOT_GATEWAY_URL = fileResult.url
|
||||
if (fileResult.token) process.env.CLAWDBOT_GATEWAY_TOKEN = fileResult.token
|
||||
if (fileResult.url) process.env.HERMES_GATEWAY_URL = fileResult.url
|
||||
if (fileResult.token) process.env.HERMES_GATEWAY_TOKEN = fileResult.token
|
||||
return fileResult
|
||||
}
|
||||
|
||||
// 3. Try CLI (slower, but works if config file structure differs)
|
||||
const cliResult = await discoverFromCli()
|
||||
if (cliResult.found) {
|
||||
if (cliResult.url) process.env.CLAWDBOT_GATEWAY_URL = cliResult.url
|
||||
if (cliResult.token) process.env.CLAWDBOT_GATEWAY_TOKEN = cliResult.token
|
||||
if (cliResult.url) process.env.HERMES_GATEWAY_URL = cliResult.url
|
||||
if (cliResult.token) process.env.HERMES_GATEWAY_TOKEN = cliResult.token
|
||||
return cliResult
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user