mirror of
https://github.com/domfelipe/hermes-client-onboarding.git
synced 2026-08-07 13:56:42 +00:00
Multi-tenant shared VPS: one client = one Hermes profile
Require --client SLUG on the one-liner; add hermes-client-provision for isolated HERMES_HOME + hermes-gateway-<slug>. Document profile isolation over per-VM/Docker for DomHubs fleet on a single host.
This commit is contained in:
parent
4ea1708cdd
commit
ff725338f3
5 changed files with 422 additions and 145 deletions
|
|
@ -1,13 +1,13 @@
|
|||
---
|
||||
name: hermes-client-onboarding
|
||||
description: Use when setting up Hermes for a client, install Hermes + Telegram + DeepSeek, run a demo setup, or launch client onboarding. Conducts guided conversational onboarding on a clean Linux VM (deepseek-v4-flash, Telegram gateway, systemd, SOUL.md).
|
||||
version: 1.3.0
|
||||
version: 1.4.0
|
||||
author: DomHubs
|
||||
license: MIT
|
||||
platforms: [linux, macos]
|
||||
metadata:
|
||||
hermes:
|
||||
tags: [onboarding, client, telegram, deepseek, gateway, demo, vps]
|
||||
tags: [onboarding, client, telegram, deepseek, gateway, demo, vps, multi-tenant]
|
||||
related_skills: []
|
||||
---
|
||||
|
||||
|
|
@ -15,9 +15,15 @@ metadata:
|
|||
|
||||
## Overview
|
||||
|
||||
You are conducting a professional, step-by-step onboarding of Hermes Agent on a clean Ubuntu/Debian **VPS** so a client can start using it immediately (primarily via Telegram). The goal is a working agent in minutes, with **native DeepSeek** (`DEEPSEEK_API_KEY`, provider `deepseek`) and model **`deepseek-v4-flash`** (V4 Flash 0731 family) as the default, Telegram as the primary channel, and the gateway running as a persistent service.
|
||||
You are conducting a professional, step-by-step onboarding of Hermes Agent on the **shared DomHubs Ubuntu/Debian VPS** so a client can start using it immediately (primarily via Telegram). DomHubs does **not** give each client a new VM — many clients share one host.
|
||||
|
||||
This skill is designed for live demos in front of the client and for commercial handoff. Be clear, structured, and efficient. Always confirm critical values before applying them.
|
||||
**Isolation model (required):** one client = one Hermes **profile** (`~/.hermes/profiles/<slug>/`) + its own gateway unit (`hermes-gateway-<slug>.service`) + its own bot token, allowlist, SOUL, and `state.db`. Never put a client bot on the default `~/.hermes` home.
|
||||
|
||||
Containers are optional later (OS sandbox for untrusted tools). Profiles already prevent secret/history/gateway conflicts for Telegram bots.
|
||||
|
||||
Goal: working agent in minutes, native DeepSeek (`DEEPSEEK_API_KEY`, provider `deepseek`), model **`deepseek-v4-flash`**, Telegram primary, gateway as systemd user service **for that profile only**.
|
||||
|
||||
Be clear, structured, and efficient. Always confirm critical values before applying them.
|
||||
|
||||
## When to Use
|
||||
|
||||
|
|
@ -32,63 +38,73 @@ Don't use for: day-to-day Hermes coding tasks after onboarding is done; multi-te
|
|||
|
||||
The onboarding is complete only when all of the following are true:
|
||||
|
||||
- Operator can **SSH into the VPS** and re-enter when needed
|
||||
- Hermes is installed and `hermes` command works
|
||||
- Model is set to `deepseek-v4-flash` with provider `deepseek`
|
||||
- `DEEPSEEK_API_KEY` is configured
|
||||
- Telegram bot token and at least one allowed user ID are set
|
||||
- Gateway is installed as a systemd service and is running
|
||||
- A test message sent to the Telegram bot receives a coherent reply
|
||||
- `hermes doctor` reports no critical errors
|
||||
- SOUL.md has been personalized (or the user explicitly skipped it)
|
||||
- Operator can **SSH into the shared DomHubs VPS**
|
||||
- Client has a dedicated **profile slug** (not default `~/.hermes`)
|
||||
- Hermes is installed; `hermes --profile <slug> …` works
|
||||
- Model is set to `deepseek-v4-flash` with provider `deepseek` **inside that profile**
|
||||
- `DEEPSEEK_API_KEY` is configured **in the profile `.env`**
|
||||
- Telegram bot token (unique to this client) and allowlist are set **in the profile**
|
||||
- Gateway unit `hermes-gateway-<slug>` is installed and running
|
||||
- No other profile uses the same Telegram bot token
|
||||
- A test message to the bot receives a coherent reply
|
||||
- `hermes --profile <slug> doctor` has no critical errors
|
||||
- SOUL.md personalized under the profile home (or user skipped)
|
||||
|
||||
## Phase 0 — Entrar na VPS (antes de tudo)
|
||||
## Phase 0 — Entrar na VPS + escolher instância (antes de tudo)
|
||||
|
||||
Onboarding runs **inside** the Linux VPS, not on the operator’s laptop. First step is always SSH.
|
||||
Onboarding runs **inside** the shared DomHubs VPS. First step is always SSH; second is a **client slug**.
|
||||
|
||||
**Generic (any client VM):**
|
||||
|
||||
```bash
|
||||
ssh root@IP_DA_VPS
|
||||
# or: ssh USER@IP_DA_VPS
|
||||
```
|
||||
|
||||
**DomHubs ops alias (operator Mac, after key is set):**
|
||||
**DomHubs ops (operator Mac):**
|
||||
|
||||
```bash
|
||||
ssh domhubs-vps
|
||||
# Host: 169.58.116.28 User: root Key: ~/.ssh/domhubs_vps
|
||||
```
|
||||
|
||||
**Then, already on the VPS, bootstrap + this skill (one-liner):**
|
||||
**On the VPS — provision + onboarding for ONE client (required):**
|
||||
|
||||
```bash
|
||||
curl -fsSL https://setup.domhubs.com.br/hermes | bash
|
||||
# slug = stable id (flavia, acme, joao-silva). Never reuse across clients.
|
||||
curl -fsSL https://setup.domhubs.com.br/hermes | bash -s -- --client SLUG
|
||||
```
|
||||
|
||||
Variants on the VPS:
|
||||
Examples:
|
||||
|
||||
```bash
|
||||
# install only (no auto-launch)
|
||||
curl -fsSL https://setup.domhubs.com.br/hermes | bash -s -- --no-launch
|
||||
|
||||
# re-open onboarding later
|
||||
hermes-client-onboarding
|
||||
curl -fsSL https://setup.domhubs.com.br/hermes | bash -s -- --client flavia
|
||||
curl -fsSL https://setup.domhubs.com.br/hermes | bash -s -- --client acme --no-launch
|
||||
```
|
||||
|
||||
If the session drops mid-onboarding, reconnect with the same `ssh` and reattach tmux if used:
|
||||
This creates/reuses:
|
||||
|
||||
| Piece | Path / unit |
|
||||
|-------|-------------|
|
||||
| Home | `~/.hermes/profiles/<slug>/` |
|
||||
| Secrets | `…/profiles/<slug>/.env` |
|
||||
| Soul | `…/profiles/<slug>/SOUL.md` |
|
||||
| History | `…/profiles/<slug>/state.db` |
|
||||
| Gateway | `hermes-gateway-<slug>.service` |
|
||||
|
||||
**Re-enter later:**
|
||||
|
||||
```bash
|
||||
ssh root@IP_DA_VPS # or: ssh domhubs-vps
|
||||
tmux ls
|
||||
tmux attach -t hermes-onboard-<pid> # if the launcher created one
|
||||
# or restart onboarding:
|
||||
hermes-client-onboarding
|
||||
ssh domhubs-vps
|
||||
hermes --profile SLUG gateway status
|
||||
hermes-client-onboarding --client SLUG
|
||||
# tmux: tmux ls && tmux attach -t hermes-onboard-SLUG-…
|
||||
```
|
||||
|
||||
In Phase 6 handover, **always** leave the client/operator with the exact SSH command for *their* IP (do not invent IPs).
|
||||
**Hard isolation rules (never break these):**
|
||||
|
||||
**Done when:** shell is on the target Linux VPS (hostname/IP known) and you can run commands as the deploy user (usually `root`).
|
||||
1. One Telegram bot token → exactly one profile/gateway.
|
||||
2. All `hermes config set` / doctor / gateway for a client use `--profile SLUG` (or `HERMES_HOME=~/.hermes/profiles/SLUG`).
|
||||
3. Do not start client bots on the default `hermes-gateway.service` (host default is for tooling/setup only).
|
||||
4. Do not enable heavy shared MCP servers on client profiles (leaks tokens + RAM).
|
||||
5. `kanban.dispatch_in_gateway: false` on client profiles (shared host).
|
||||
|
||||
If the kickoff mentions a profile slug, **stay inside that profile for the entire onboarding**.
|
||||
|
||||
**Done when:** shell is on DomHubs VPS, client slug known, profile home exists (or will be created immediately).
|
||||
|
||||
## Pre-flight Checks (do these first)
|
||||
|
||||
|
|
@ -227,67 +243,74 @@ Write the final content to `~/.hermes/SOUL.md`. Confirm before overwriting if th
|
|||
|
||||
**Done when:** SOUL.md written or user explicitly skipped personalization.
|
||||
|
||||
### Phase 5 — Gateway & Persistence
|
||||
### Phase 5 — Gateway & Persistence (per profile)
|
||||
|
||||
1. Install the gateway as a system service:
|
||||
Assume client slug is `$SLUG` (from Phase 0 / kickoff).
|
||||
|
||||
1. Prefer the provisioner if the unit is missing:
|
||||
|
||||
```bash
|
||||
hermes gateway install
|
||||
hermes-client-provision --client "$SLUG"
|
||||
# or: hermes --profile "$SLUG" gateway install
|
||||
```
|
||||
|
||||
2. Start / restart it:
|
||||
2. Start / restart **only this profile’s** gateway:
|
||||
|
||||
```bash
|
||||
hermes gateway start
|
||||
hermes --profile "$SLUG" gateway start
|
||||
# or
|
||||
hermes gateway restart
|
||||
hermes --profile "$SLUG" gateway restart
|
||||
```
|
||||
|
||||
3. Check status:
|
||||
|
||||
```bash
|
||||
hermes gateway status
|
||||
hermes --profile "$SLUG" gateway status
|
||||
systemctl --user is-active "hermes-gateway-${SLUG}.service"
|
||||
```
|
||||
|
||||
4. If the service fails, inspect logs (`hermes gateway logs` or `journalctl -u hermes* -n 50` / `launchctl` on macOS) and fix common issues (PATH, missing env, permissions). See `references/troubleshooting.md`.
|
||||
4. If the service fails, inspect logs:
|
||||
|
||||
**Done when:** gateway status shows running and service is installed for reboot persistence.
|
||||
```bash
|
||||
journalctl --user -u "hermes-gateway-${SLUG}" -n 50 --no-pager
|
||||
```
|
||||
|
||||
Never restart the default `hermes-gateway.service` for a client bot unless you intentionally want the default home (you should not).
|
||||
|
||||
**Done when:** `hermes-gateway-<slug>` is active and Telegram connected for that profile only.
|
||||
|
||||
### Phase 6 — Validation & Handover
|
||||
|
||||
Run the full validation sequence:
|
||||
Run the full validation sequence (replace `$SLUG`):
|
||||
|
||||
```bash
|
||||
hermes doctor
|
||||
hermes gateway status
|
||||
hermes --profile "$SLUG" doctor
|
||||
hermes --profile "$SLUG" gateway status
|
||||
```
|
||||
|
||||
Then instruct the user to send a test message to the Telegram bot (“oi” ou “teste”). Confirm that a coherent reply arrives.
|
||||
|
||||
Final checklist to present to the user:
|
||||
Final checklist:
|
||||
|
||||
- [ ] Hermes installed and in PATH
|
||||
- [ ] Model = deepseek-v4-flash via provider deepseek (native API)
|
||||
- [ ] Telegram bot responding
|
||||
- [ ] Gateway running as service (survives reboot)
|
||||
- [ ] SOUL.md personalized
|
||||
- [ ] `hermes doctor` clean
|
||||
- [ ] Profile `~/.hermes/profiles/<slug>/` isolated
|
||||
- [ ] Model = deepseek-v4-flash via provider deepseek (in profile)
|
||||
- [ ] Unique Telegram bot responding
|
||||
- [ ] Unit `hermes-gateway-<slug>` running (survives reboot + linger)
|
||||
- [ ] SOUL.md personalized under profile
|
||||
- [ ] `hermes --profile <slug> doctor` clean
|
||||
- [ ] No token collision with other profiles
|
||||
|
||||
Give the user the useful commands for later:
|
||||
Ops commands for later:
|
||||
|
||||
```bash
|
||||
# Re-enter the VPS (fill real IP / use DomHubs alias)
|
||||
ssh root@IP_DA_VPS
|
||||
# ssh domhubs-vps
|
||||
|
||||
hermes gateway status
|
||||
hermes gateway logs
|
||||
hermes doctor
|
||||
hermes config get model.default
|
||||
hermes update
|
||||
ssh domhubs-vps
|
||||
hermes --profile SLUG gateway status
|
||||
journalctl --user -u hermes-gateway-SLUG -n 50 --no-pager
|
||||
hermes --profile SLUG doctor
|
||||
hermes profile list
|
||||
```
|
||||
|
||||
**Done when:** checklist walked, test Telegram reply confirmed, SSH re-entry command + useful commands delivered.
|
||||
**Done when:** checklist walked, test Telegram reply confirmed, slug + SSH re-entry delivered.
|
||||
|
||||
## Error Handling Guidelines
|
||||
|
||||
|
|
@ -321,35 +344,34 @@ hermes update
|
|||
## Reference Commands (quick lookup)
|
||||
|
||||
```bash
|
||||
# Enter VPS first
|
||||
ssh root@IP_DA_VPS
|
||||
# DomHubs ops: ssh domhubs-vps
|
||||
# Enter shared VPS
|
||||
ssh domhubs-vps
|
||||
|
||||
# DomHubs one-liner (on the VPS)
|
||||
curl -fsSL https://setup.domhubs.com.br/hermes | bash
|
||||
# New / resume client instance (on VPS)
|
||||
curl -fsSL https://setup.domhubs.com.br/hermes | bash -s -- --client SLUG
|
||||
hermes-client-provision --client SLUG
|
||||
hermes-client-onboarding --client SLUG
|
||||
|
||||
# Install Hermes only
|
||||
curl -fsSL https://hermes-agent.nousresearch.com/install.sh | bash -s -- --skip-browser
|
||||
# Config inside profile (HERMES_HOME or --profile)
|
||||
export HERMES_HOME=~/.hermes/profiles/SLUG
|
||||
# or: hermes --profile SLUG config set …
|
||||
hermes --profile SLUG config set DEEPSEEK_API_KEY "sk-..."
|
||||
hermes --profile SLUG config set model.provider deepseek
|
||||
hermes --profile SLUG config set model.default deepseek-v4-flash
|
||||
hermes --profile SLUG config set model.base_url "https://api.deepseek.com/v1"
|
||||
hermes --profile SLUG config set TELEGRAM_BOT_TOKEN "..."
|
||||
hermes --profile SLUG config set TELEGRAM_ALLOWED_USERS "123456789"
|
||||
grep -E '^TELEGRAM_ALLOWED_USERS=' ~/.hermes/profiles/SLUG/.env
|
||||
|
||||
# Core config (or use the helper script)
|
||||
hermes config set DEEPSEEK_API_KEY "sk-..."
|
||||
hermes config set model.provider deepseek
|
||||
hermes config set model.default deepseek-v4-flash
|
||||
hermes config set model.base_url "https://api.deepseek.com/v1"
|
||||
hermes config set TELEGRAM_BOT_TOKEN "..."
|
||||
hermes config set TELEGRAM_ALLOWED_USERS "123456789"
|
||||
grep -E '^TELEGRAM_ALLOWED_USERS=' ~/.hermes/.env # must match IDs above
|
||||
# Gateway for this client only
|
||||
hermes --profile SLUG gateway install
|
||||
hermes --profile SLUG gateway restart
|
||||
hermes --profile SLUG gateway status
|
||||
journalctl --user -u hermes-gateway-SLUG -n 50 --no-pager
|
||||
|
||||
# Gateway
|
||||
hermes gateway install
|
||||
hermes gateway restart
|
||||
hermes gateway status
|
||||
# Linux: journalctl --user -u hermes-gateway -n 50
|
||||
# macOS: tail -f ~/.hermes/logs/gateway.log
|
||||
|
||||
# Validation
|
||||
hermes doctor
|
||||
hermes --version
|
||||
# Fleet
|
||||
hermes profile list
|
||||
systemctl --user list-units 'hermes-gateway*' --all
|
||||
```
|
||||
|
||||
When the user says the onboarding is finished or the bot is responding correctly, summarize what was configured and congratulate them. Offer to make any final adjustments.
|
||||
|
|
|
|||
171
skill/hermes-client-onboarding/scripts/provision-client-instance.sh
Executable file
171
skill/hermes-client-onboarding/scripts/provision-client-instance.sh
Executable file
|
|
@ -0,0 +1,171 @@
|
|||
#!/usr/bin/env bash
|
||||
# DomHubs — provision an isolated Hermes client instance on a SHARED VPS.
|
||||
#
|
||||
# Model: one Linux host, many clients = many Hermes *profiles*
|
||||
# ~/.hermes/profiles/<slug>/ → own .env, SOUL, state.db, gateway unit
|
||||
# systemd: hermes-gateway-<slug>.service
|
||||
# Telegram: one bot token per profile (never share tokens)
|
||||
#
|
||||
# Why not Docker first: Hermes already isolates via HERMES_HOME + multi-gateway.
|
||||
# Containers later only if you need OS-level sandbox for untrusted tool use.
|
||||
#
|
||||
# Usage (on the VPS as root/deploy user):
|
||||
# provision-client-instance.sh --client flavia
|
||||
# provision-client-instance.sh --client acme --description "Acme assistant"
|
||||
# provision-client-instance.sh --client flavia --clone-from edwiges
|
||||
#
|
||||
set -euo pipefail
|
||||
|
||||
SLUG=""
|
||||
DESCRIPTION=""
|
||||
CLONE_FROM=""
|
||||
START_GATEWAY=1
|
||||
INSTALL_GATEWAY=1
|
||||
LEAN_MCP=1
|
||||
|
||||
log() { printf '==> %s\n' "$*"; }
|
||||
warn() { printf 'warn: %s\n' "$*" >&2; }
|
||||
die() { printf 'error: %s\n' "$*" >&2; exit 1; }
|
||||
|
||||
usage() {
|
||||
cat <<'EOF'
|
||||
Usage: provision-client-instance.sh --client SLUG [options]
|
||||
|
||||
--client SLUG Required. lowercase [a-z0-9-] (e.g. flavia, acme-corp)
|
||||
--description TEXT Stored on the profile
|
||||
--clone-from PROFILE Clone config skeleton from another profile (not secrets by default uses --clone)
|
||||
--no-gateway-install Create profile only
|
||||
--no-start Install unit but do not start
|
||||
--keep-mcp Do not force mcp_servers: {}
|
||||
-h, --help
|
||||
|
||||
Env:
|
||||
HERMES_CLIENT_SLUG Same as --client
|
||||
EOF
|
||||
}
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--client) SLUG="${2:-}"; shift 2 ;;
|
||||
--description) DESCRIPTION="${2:-}"; shift 2 ;;
|
||||
--clone-from) CLONE_FROM="${2:-}"; shift 2 ;;
|
||||
--no-gateway-install) INSTALL_GATEWAY=0; shift ;;
|
||||
--no-start) START_GATEWAY=0; shift ;;
|
||||
--keep-mcp) LEAN_MCP=0; shift ;;
|
||||
-h|--help) usage; exit 0 ;;
|
||||
*) die "unknown arg: $1" ;;
|
||||
esac
|
||||
done
|
||||
|
||||
SLUG="${SLUG:-${HERMES_CLIENT_SLUG:-}}"
|
||||
[[ -n "$SLUG" ]] || die "--client SLUG is required (shared VPS multi-tenant)"
|
||||
|
||||
# normalize: lowercase, allow a-z0-9-
|
||||
SLUG="$(printf '%s' "$SLUG" | tr '[:upper:]' '[:lower:]' | tr -c 'a-z0-9-' '-')"
|
||||
SLUG="$(printf '%s' "$SLUG" | sed -E 's/-+/-/g; s/^-|-$//g')"
|
||||
[[ "$SLUG" =~ ^[a-z0-9]([a-z0-9-]*[a-z0-9])?$ ]] || die "invalid slug after normalize: $SLUG"
|
||||
[[ "$SLUG" != "default" && "$SLUG" != "root" && "$SLUG" != "main" ]] || die "reserved slug: $SLUG"
|
||||
|
||||
command -v hermes >/dev/null 2>&1 || die "hermes not on PATH"
|
||||
export PATH="${HOME}/.local/bin:/usr/local/bin:${PATH}"
|
||||
|
||||
PROFILE_HOME="${HERMES_ROOT:-${HOME}/.hermes}/profiles/${SLUG}"
|
||||
DESCRIPTION="${DESCRIPTION:-DomHubs client instance: ${SLUG}}"
|
||||
|
||||
log "Provisioning isolated client instance: ${SLUG}"
|
||||
log "Profile home: ${PROFILE_HOME}"
|
||||
|
||||
if [[ -d "$PROFILE_HOME" && -f "$PROFILE_HOME/config.yaml" ]]; then
|
||||
log "Profile already exists — reusing ${SLUG}"
|
||||
else
|
||||
create_args=(profile create "$SLUG" --description "$DESCRIPTION" --no-skills)
|
||||
if [[ -n "$CLONE_FROM" ]]; then
|
||||
create_args+=(--clone-from "$CLONE_FROM")
|
||||
fi
|
||||
hermes "${create_args[@]}"
|
||||
fi
|
||||
|
||||
[[ -d "$PROFILE_HOME" ]] || die "profile home missing after create: $PROFILE_HOME"
|
||||
chmod 700 "$PROFILE_HOME" 2>/dev/null || true
|
||||
|
||||
# Lean production defaults for multi-tenant bots
|
||||
python3 - "$PROFILE_HOME" "$LEAN_MCP" <<'PY'
|
||||
import sys, re
|
||||
from pathlib import Path
|
||||
home = Path(sys.argv[1])
|
||||
lean = sys.argv[2] == "1"
|
||||
cfg = home / "config.yaml"
|
||||
if not cfg.exists():
|
||||
sys.exit(0)
|
||||
text = cfg.read_text()
|
||||
# kanban: only default should dispatch on shared host
|
||||
if re.search(r"^kanban:\s*$", text, re.M):
|
||||
if "dispatch_in_gateway" not in text:
|
||||
text = re.sub(r"^kanban:\s*$", "kanban:\n dispatch_in_gateway: false", text, count=1, flags=re.M)
|
||||
elif "dispatch_in_gateway" not in text:
|
||||
text = text.rstrip() + "\n\nkanban:\n dispatch_in_gateway: false\n"
|
||||
if lean:
|
||||
if re.search(r"^mcp_servers:\s*$", text, re.M):
|
||||
text = re.sub(r"^mcp_servers:\n(?: .*\n)*", "mcp_servers: {}\n", text, count=1, flags=re.M)
|
||||
elif re.search(r"^mcp_servers:\s*\{\s*\}\s*$", text, re.M):
|
||||
pass
|
||||
elif re.search(r"^mcp_servers:", text, re.M):
|
||||
text = re.sub(r"^mcp_servers:\n(?: .*\n)*", "mcp_servers: {}\n", text, count=1, flags=re.M)
|
||||
else:
|
||||
text = text.rstrip() + "\nmcp_servers: {}\n"
|
||||
cfg.write_text(text)
|
||||
print("config hardened (kanban dispatch off, lean mcp)" if lean else "config hardened (kanban dispatch off)")
|
||||
PY
|
||||
|
||||
# Ensure empty secrets file exists with safe perms
|
||||
touch "${PROFILE_HOME}/.env"
|
||||
chmod 600 "${PROFILE_HOME}/.env"
|
||||
|
||||
if [[ "$INSTALL_GATEWAY" -eq 1 ]]; then
|
||||
log "Installing systemd user unit: hermes-gateway-${SLUG}"
|
||||
hermes --profile "$SLUG" gateway install
|
||||
if [[ "$START_GATEWAY" -eq 1 ]]; then
|
||||
# Do not start if no bot token yet — gateway can still run but wasteful
|
||||
if grep -qE '^TELEGRAM_BOT_TOKEN=.+' "${PROFILE_HOME}/.env" 2>/dev/null; then
|
||||
hermes --profile "$SLUG" gateway restart || hermes --profile "$SLUG" gateway start
|
||||
log "Gateway started for ${SLUG}"
|
||||
else
|
||||
warn "No TELEGRAM_BOT_TOKEN in profile .env yet — unit installed, not started"
|
||||
warn "After onboarding secrets: hermes --profile ${SLUG} gateway start"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
# Mark instance metadata (no secrets)
|
||||
mkdir -p "${PROFILE_HOME}/domhubs"
|
||||
cat > "${PROFILE_HOME}/domhubs/instance.json" <<EOF
|
||||
{
|
||||
"slug": "${SLUG}",
|
||||
"profile_home": "${PROFILE_HOME}",
|
||||
"gateway_unit": "hermes-gateway-${SLUG}.service",
|
||||
"created_at": "$(date -u +%Y-%m-%dT%H:%M:%SZ)",
|
||||
"isolation": "hermes-profile"
|
||||
}
|
||||
EOF
|
||||
chmod 644 "${PROFILE_HOME}/domhubs/instance.json"
|
||||
|
||||
cat <<EOF
|
||||
|
||||
✓ Instance ready: ${SLUG}
|
||||
|
||||
Home: ${PROFILE_HOME}
|
||||
Unit: hermes-gateway-${SLUG}.service
|
||||
Commands:
|
||||
hermes --profile ${SLUG} doctor
|
||||
hermes --profile ${SLUG} gateway status
|
||||
hermes --profile ${SLUG} chat --cli -s hermes-client-onboarding
|
||||
journalctl --user -u hermes-gateway-${SLUG} -n 50 --no-pager
|
||||
|
||||
Isolation rules:
|
||||
• One Telegram bot token per profile (never reuse)
|
||||
• TELEGRAM_ALLOWED_USERS only for that client
|
||||
• Do not run gateway on default ~/.hermes for client bots
|
||||
• Default host profile: setup/tooling only
|
||||
|
||||
Next: run onboarding against this profile (apply secrets, SOUL, start gateway).
|
||||
EOF
|
||||
|
|
@ -9,20 +9,44 @@ set -euo pipefail
|
|||
export PATH="${HOME}/.local/bin:/usr/local/bin:${PATH}"
|
||||
|
||||
SKILL_NAME="${HERMES_ONBOARD_SKILL:-hermes-client-onboarding}"
|
||||
# Shared VPS multi-tenant: isolate into Hermes profile
|
||||
CLIENT_SLUG="${HERMES_CLIENT_SLUG:-${HERMES_PROFILE:-}}"
|
||||
if [[ -n "${1:-}" && "$1" == "--client" && -n "${2:-}" ]]; then
|
||||
CLIENT_SLUG="$2"
|
||||
shift 2
|
||||
fi
|
||||
if [[ -n "${1:-}" && "$1" == "--profile" && -n "${2:-}" ]]; then
|
||||
CLIENT_SLUG="$2"
|
||||
shift 2
|
||||
fi
|
||||
|
||||
# Short: long text trips Hermes paste-collapse
|
||||
KICKOFF="${HERMES_ONBOARD_KICKOFF:-Inicie o onboarding agora. Skill hermes-client-onboarding. Pre-flight silencioso e Phase 1 (voce fala primeiro).}"
|
||||
if [[ -n "$CLIENT_SLUG" ]]; then
|
||||
KICKOFF_DEFAULT="Inicie onboarding do cliente ${CLIENT_SLUG}. Skill hermes-client-onboarding. Multi-tenant profile ${CLIENT_SLUG} only. Pre-flight e Phase 1."
|
||||
else
|
||||
KICKOFF_DEFAULT="Inicie o onboarding agora. Skill hermes-client-onboarding. Pre-flight silencioso e Phase 1 (voce fala primeiro)."
|
||||
fi
|
||||
KICKOFF="${HERMES_ONBOARD_KICKOFF:-$KICKOFF_DEFAULT}"
|
||||
KICKOFF="$(printf '%s' "$KICKOFF" | tr '\n' ' ' | sed 's/ */ /g')"
|
||||
|
||||
export HERMES_ONBOARD_SKILL="$SKILL_NAME"
|
||||
export HERMES_ONBOARD_KICKOFF="$KICKOFF"
|
||||
export HERMES_TUI_SKILLS="$SKILL_NAME"
|
||||
export HERMES_TUI_QUERY="$KICKOFF"
|
||||
export HERMES_CLIENT_SLUG="${CLIENT_SLUG:-}"
|
||||
|
||||
if ! command -v hermes >/dev/null 2>&1; then
|
||||
echo "error: hermes not on PATH" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
HERMES_BIN=(hermes)
|
||||
if [[ -n "$CLIENT_SLUG" ]]; then
|
||||
HERMES_BIN=(hermes --profile "$CLIENT_SLUG")
|
||||
export HERMES_HOME="${HOME}/.hermes/profiles/${CLIENT_SLUG}"
|
||||
echo "==> Profile/client: ${CLIENT_SLUG} (HERMES_HOME=${HERMES_HOME})"
|
||||
fi
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
AUTO_PY=""
|
||||
for candidate in \
|
||||
|
|
@ -36,19 +60,20 @@ done
|
|||
use_tui="${HERMES_ONBOARD_USE_TUI:-0}"
|
||||
if [[ "$use_tui" == "1" ]]; then
|
||||
if [[ -r /dev/tty ]]; then
|
||||
exec hermes chat --tui -s "$SKILL_NAME" --query "$KICKOFF" </dev/tty
|
||||
exec "${HERMES_BIN[@]}" chat --tui -s "$SKILL_NAME" --query "$KICKOFF" </dev/tty
|
||||
fi
|
||||
exec hermes chat --tui -s "$SKILL_NAME" --query "$KICKOFF"
|
||||
exec "${HERMES_BIN[@]}" chat --tui -s "$SKILL_NAME" --query "$KICKOFF"
|
||||
fi
|
||||
|
||||
# --- Preferred: tmux (no frozen PTY wrapper) ---
|
||||
if command -v tmux >/dev/null 2>&1 && [[ -t 0 && -t 1 ]]; then
|
||||
SESSION="hermes-onboard-$$"
|
||||
SESSION="hermes-onboard-${CLIENT_SLUG:-default}-$$"
|
||||
# Kill leftover same-name (shouldn't happen with $$)
|
||||
tmux has-session -t "$SESSION" 2>/dev/null && tmux kill-session -t "$SESSION" 2>/dev/null || true
|
||||
|
||||
HERMES_CMD="${HERMES_BIN[*]} chat --cli -s ${SKILL_NAME}"
|
||||
tmux new-session -d -s "$SESSION" -x "$(tput cols 2>/dev/null || echo 120)" -y "$(tput lines 2>/dev/null || echo 40)" \
|
||||
"export PATH=\"${PATH}\"; hermes chat --cli -s ${SKILL_NAME}; exec bash"
|
||||
"export PATH=\"${PATH}\"; export HERMES_CLIENT_SLUG=\"${CLIENT_SLUG:-}\"; export HERMES_HOME=\"${HERMES_HOME:-}\"; ${HERMES_CMD}; exec bash"
|
||||
|
||||
# Wait until Hermes is up, then type kickoff + Enter
|
||||
for i in $(seq 1 40); do
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue