はじめに
Apple containerがでたので、MacOSでHermes Agentなど常駐させるには、アイドル時のリソース消費がほぼゼロ、起動が高速のApple Containerを使って隔離した環境で実行するのがいいので試してみました。
Apple Containerで起動
container run \
--name hermes \
--detach \
--cpus 4 \
--memory 8G \
--shm-size 1G \
-v "$HOME/.hermes:/opt/data" \
-p 127.0.0.1:8642:8642 \
docker.io/nousresearch/hermes-agent:latest \
gateway run
hermes agent 基本設定
container exec -it hermes hermes setup
自分のLLM Providerを設定する
Tool for CLIの設定
設定完了
All your files are in ~/./:
Settings: /opt/data/config.yaml
API Keys: /opt/data/.env
Data: /opt/data/cron/, sessions/, logs/
設定ファイルは、 $HOME/.hermes を /opt/data にマウントしてるので、ホストマシン側で設定ファイルは確認できるようになっている。
確認
container exec -it hermes hermes auth list
openai-codex (1 credentials):
#1 openai-codex-oauth-1 oauth device_code exhausted (16m 48s left)
chat
container exec -it hermes hermes chat
Slack 連携
container exec -it hermes hermes slack manifest --write
Slack manifest written to: /opt/data/slack-manifest.json
Next steps:
1. Open https://api.slack.com/apps and pick your Hermes app
(or create a new one: Create New App → From an app manifest).
2. Features → App Manifest → paste the contents of
/opt/data/slack-manifest.json
3. Save; Slack will prompt to reinstall the app if scopes or
slash commands changed.
4. Make sure Socket Mode is enabled and you have a bot token
(xoxb-...) and app token (xapp-...) configured via
`hermes setup`.
ホストマシンの~/.hermes/slack-manifest.json に作成されているので、いくつか修正する。
- App nameはDefaultで
Hermesとなっているので、自分の好きな名前にする - Dockerでローカルで作っている場合は、slash command のエンドポイントはそのまま使えないので消したい場合は一旦消す
details
{
"_metadata": {
"major_version": 1,
"minor_version": 1
},
"display_information": {
"name": "Hermes",
"description": "Your Hermes agent on Slack",
"background_color": "#1a1a2e"
},
"features": {
"app_home": {
"home_tab_enabled": false,
"messages_tab_enabled": true,
"messages_tab_read_only_enabled": false
},
"bot_user": {
"display_name": "Hermes",
"always_online": true
},
"slash_commands": [
{
"command": "/hermes",
"description": "Talk to Hermes or run a subcommand",
"should_escape": false,
"url": "https://hermes-agent.local/slack/commands",
"usage_hint": "[subcommand] [args]"
},
{
"command": "/btw",
"description": "Alias for /background — Run a prompt in the background",
"should_escape": false,
"url": "https://hermes-agent.local/slack/commands",
"usage_hint": "<prompt>"
},
{
"command": "/bg",
"description": "Alias for /background — Run a prompt in the background",
"should_escape": false,
"url": "https://hermes-agent.local/slack/commands",
"usage_hint": "<prompt>"
},
{
"command": "/start",
"description": "Acknowledge platform start pings without a reply",
"should_escape": false,
"url": "https://hermes-agent.local/slack/commands"
},
{
"command": "/new",
"description": "Start a new session (fresh session ID + history)",
"should_escape": false,
"url": "https://hermes-agent.local/slack/commands",
"usage_hint": "[name]"
},
{
"command": "/retry",
"description": "Retry the last message (resend to agent)",
"should_escape": false,
"url": "https://hermes-agent.local/slack/commands"
},
{
"command": "/undo",
"description": "Back up N user turns and re-prompt (default 1)",
"should_escape": false,
"url": "https://hermes-agent.local/slack/commands",
"usage_hint": "[N]"
},
{
"command": "/title",
"description": "Set a title for the current session",
"should_escape": false,
"url": "https://hermes-agent.local/slack/commands",
"usage_hint": "[name]"
},
{
"command": "/branch",
"description": "Branch the current session (explore a different path)",
"should_escape": false,
"url": "https://hermes-agent.local/slack/commands",
"usage_hint": "[name]"
},
{
"command": "/compress",
"description": "Compress conversation context (add 'here [N]' to keep recent N turns; --preview shows what would happen)",
"should_escape": false,
"url": "https://hermes-agent.local/slack/commands",
"usage_hint": "[here [N] | focus topic | --preview|--dry-run]"
},
{
"command": "/rollback",
"description": "List or restore filesystem checkpoints",
"should_escape": false,
"url": "https://hermes-agent.local/slack/commands",
"usage_hint": "[number]"
},
{
"command": "/stop",
"description": "Kill all running background processes",
"should_escape": false,
"url": "https://hermes-agent.local/slack/commands"
},
{
"command": "/approve",
"description": "Approve a pending dangerous command",
"should_escape": false,
"url": "https://hermes-agent.local/slack/commands",
"usage_hint": "[session|always]"
},
{
"command": "/deny",
"description": "Deny a pending dangerous command (optionally with a reason)",
"should_escape": false,
"url": "https://hermes-agent.local/slack/commands",
"usage_hint": "[all] [reason]"
},
{
"command": "/background",
"description": "Run a prompt in the background",
"should_escape": false,
"url": "https://hermes-agent.local/slack/commands",
"usage_hint": "<prompt>"
},
{
"command": "/agents",
"description": "Show active agents and running tasks",
"should_escape": false,
"url": "https://hermes-agent.local/slack/commands"
},
{
"command": "/queue",
"description": "Queue a prompt for the next turn (doesn't interrupt)",
"should_escape": false,
"url": "https://hermes-agent.local/slack/commands",
"usage_hint": "<prompt>"
},
{
"command": "/steer",
"description": "Inject a message after the next tool call without interrupting",
"should_escape": false,
"url": "https://hermes-agent.local/slack/commands",
"usage_hint": "<prompt>"
},
{
"command": "/goal",
"description": "Set a standing goal Hermes works on across turns until achieved",
"should_escape": false,
"url": "https://hermes-agent.local/slack/commands",
"usage_hint": "[text | draft <text> | show | pause | resume | clear | status | wait <pid> | unwait]"
},
{
"command": "/subgoal",
"description": "Add or manage extra criteria on the active goal",
"should_escape": false,
"url": "https://hermes-agent.local/slack/commands",
"usage_hint": "[text | remove N | clear]"
},
{
"command": "/whoami",
"description": "Show your slash command access (admin / user)",
"should_escape": false,
"url": "https://hermes-agent.local/slack/commands"
},
{
"command": "/profile",
"description": "Show active profile name and home directory",
"should_escape": false,
"url": "https://hermes-agent.local/slack/commands"
},
{
"command": "/sethome",
"description": "Set this chat as the home channel",
"should_escape": false,
"url": "https://hermes-agent.local/slack/commands"
},
{
"command": "/resume",
"description": "Resume a previously-named session",
"should_escape": false,
"url": "https://hermes-agent.local/slack/commands",
"usage_hint": "[name]"
},
{
"command": "/sessions",
"description": "Browse and resume previous sessions",
"should_escape": false,
"url": "https://hermes-agent.local/slack/commands"
},
{
"command": "/model",
"description": "Switch model (persists by default)",
"should_escape": false,
"url": "https://hermes-agent.local/slack/commands",
"usage_hint": "[model] [--provider name] [--global|--session] [--refresh]"
},
{
"command": "/codex-runtime",
"description": "Toggle codex app-server runtime for OpenAI/Codex models",
"should_escape": false,
"url": "https://hermes-agent.local/slack/commands",
"usage_hint": "[auto|codex_app_server]"
},
{
"command": "/personality",
"description": "Set a predefined personality",
"should_escape": false,
"url": "https://hermes-agent.local/slack/commands",
"usage_hint": "[name]"
},
{
"command": "/footer",
"description": "Toggle gateway runtime-metadata footer on final replies",
"should_escape": false,
"url": "https://hermes-agent.local/slack/commands",
"usage_hint": "[on|off|status]"
},
{
"command": "/yolo",
"description": "Toggle YOLO mode (skip all dangerous command approvals)",
"should_escape": false,
"url": "https://hermes-agent.local/slack/commands"
},
{
"command": "/reasoning",
"description": "Manage reasoning effort and display",
"should_escape": false,
"url": "https://hermes-agent.local/slack/commands",
"usage_hint": "[level|show|hide|full|clamp]"
},
{
"command": "/fast",
"description": "Toggle fast mode — OpenAI Priority Processing / Anthropic Fast Mode (Normal/Fast)",
"should_escape": false,
"url": "https://hermes-agent.local/slack/commands",
"usage_hint": "[normal|fast|status]"
},
{
"command": "/voice",
"description": "Toggle voice mode",
"should_escape": false,
"url": "https://hermes-agent.local/slack/commands",
"usage_hint": "[on|off|tts|status]"
},
{
"command": "/memory",
"description": "Review pending memory writes / toggle the approval gate",
"should_escape": false,
"url": "https://hermes-agent.local/slack/commands",
"usage_hint": "[pending|approve|reject|approval] [id|on|off]"
},
{
"command": "/bundles",
"description": "List skill bundles (aliases /<name> for multiple skills)",
"should_escape": false,
"url": "https://hermes-agent.local/slack/commands"
},
{
"command": "/learn",
"description": "Learn a reusable skill from anything you describe (dirs, URLs, this chat, notes)",
"should_escape": false,
"url": "https://hermes-agent.local/slack/commands",
"usage_hint": "<what to learn from>"
},
{
"command": "/suggestions",
"description": "Review suggested automations (accept/dismiss)",
"should_escape": false,
"url": "https://hermes-agent.local/slack/commands",
"usage_hint": "[accept|dismiss N | catalog]"
},
{
"command": "/blueprint",
"description": "Set up an automation from a blueprint template",
"should_escape": false,
"url": "https://hermes-agent.local/slack/commands",
"usage_hint": "[name] [slot=value ...]"
},
{
"command": "/curator",
"description": "Background skill maintenance (status, run, pin, archive, list-archived)",
"should_escape": false,
"url": "https://hermes-agent.local/slack/commands",
"usage_hint": "[subcommand]"
},
{
"command": "/kanban",
"description": "Multi-profile collaboration board (tasks, links, comments)",
"should_escape": false,
"url": "https://hermes-agent.local/slack/commands",
"usage_hint": "[subcommand]"
},
{
"command": "/reload-mcp",
"description": "Reload MCP servers from config",
"should_escape": false,
"url": "https://hermes-agent.local/slack/commands"
},
{
"command": "/reload-skills",
"description": "Re-scan ~/.hermes/skills/ for newly installed or removed skills",
"should_escape": false,
"url": "https://hermes-agent.local/slack/commands"
},
{
"command": "/commands",
"description": "Browse all commands and skills (paginated)",
"should_escape": false,
"url": "https://hermes-agent.local/slack/commands",
"usage_hint": "[page]"
},
{
"command": "/help",
"description": "Show available commands",
"should_escape": false,
"url": "https://hermes-agent.local/slack/commands"
},
{
"command": "/restart",
"description": "Gracefully restart the gateway after draining active runs",
"should_escape": false,
"url": "https://hermes-agent.local/slack/commands"
},
{
"command": "/usage",
"description": "Show token usage and rate limits for the current session",
"should_escape": false,
"url": "https://hermes-agent.local/slack/commands"
},
{
"command": "/insights",
"description": "Show usage insights and analytics",
"should_escape": false,
"url": "https://hermes-agent.local/slack/commands",
"usage_hint": "[days]"
},
{
"command": "/platform",
"description": "Pause, resume, or list a failing gateway platform",
"should_escape": false,
"url": "https://hermes-agent.local/slack/commands",
"usage_hint": "<pause|resume|list> [name]"
},
{
"command": "/update",
"description": "Update Hermes Agent to the latest version",
"should_escape": false,
"url": "https://hermes-agent.local/slack/commands"
},
{
"command": "/version",
"description": "Show Hermes Agent version",
"should_escape": false,
"url": "https://hermes-agent.local/slack/commands"
}
],
"assistant_view": {
"assistant_description": "Chat with Hermes in threads and DMs."
}
},
"oauth_config": {
"scopes": {
"bot": [
"app_mentions:read",
"assistant:write",
"channels:history",
"channels:read",
"chat:write",
"commands",
"files:read",
"files:write",
"groups:history",
"groups:read",
"im:history",
"im:read",
"im:write",
"mpim:history",
"mpim:read",
"users:read"
]
}
},
"settings": {
"event_subscriptions": {
"bot_events": [
"app_mention",
"assistant_thread_context_changed",
"assistant_thread_started",
"message.channels",
"message.groups",
"message.im",
"message.mpim"
]
},
"interactivity": {
"is_enabled": true
},
"org_deploy_enabled": false,
"socket_mode_enabled": true,
"token_rotation_enabled": false
}
}
Slack マニフェストからSlack Appを作成しインストールして、Bot tokenとApp tokenを取得する
- Bot token: xoxb-xxxxx
- App token: xapp-xxxxx
Hermes Agentに設定する
container exec -it hermes hermes setup
上で取得したトークンをセットする
Gatewayをリスタートすると
container exec -it hermes hermes gateway restart
Default Slackチャンネルにメッセージが投稿され連携が確認できる。
話しかけてみると返事が返ってくる
GitHub設定
今回はgh コマンドを使った接続をしてみます。
hermes agent のイメージにはgh コマンドが入ってないのでDockerfileを用意する
FROM docker.io/nousresearch/hermes-agent:latest
# TARGETARCH は BuildKit が自動で埋める (Apple Silicon なら arm64)
ARG TARGETARCH=arm64
ARG GH_VERSION=2.97.0
ARG GH_SHA256_arm64=73ea440ecad9c9e284429997ee6f93577bc6f7bc6fba357ef62c53ad8fb641a5
ARG GH_SHA256_amd64=a2c9b8497e1f85b1ad0dfcb78b5a622e098801b8e461e459e88e1ee12f018112
RUN set -eux; \
case "${TARGETARCH}" in \
arm64) gh_sha="${GH_SHA256_arm64}" ;; \
amd64) gh_sha="${GH_SHA256_amd64}" ;; \
*) echo "unsupported TARGETARCH: ${TARGETARCH}" >&2; exit 1 ;; \
esac; \
tarball="gh_${GH_VERSION}_linux_${TARGETARCH}.tar.gz"; \
curl -fsSL -o "/tmp/${tarball}" \
"https://github.com/cli/cli/releases/download/v${GH_VERSION}/${tarball}"; \
echo "${gh_sha} /tmp/${tarball}" | sha256sum -c -; \
tar -xzf "/tmp/${tarball}" -C /tmp; \
install -m 0755 "/tmp/gh_${GH_VERSION}_linux_${TARGETARCH}/bin/gh" /usr/local/bin/gh; \
rm -rf "/tmp/${tarball}" "/tmp/gh_${GH_VERSION}_linux_${TARGETARCH}"; \
gh --version
ENV GH_CONFIG_DIR=/opt/data/gh
これでbuildして再起動します。
gh 認証:
container exec -it hermes gh auth login
? Where do you use GitHub? GitHub.com
? What is your preferred protocol for Git operations on this host? HTTPS
? Authenticate Git with your GitHub credentials? Yes
? How would you like to authenticate GitHub CLI? Login with a web browser
! First copy your one-time code: XXXX-XXXX
Press Enter to open https://github.com/login/device in your browser...
✓ Authentication complete.
- gh config set -h github.com git_protocol https
✓ Configured git protocol
! Authentication credentials saved in plain text
✓ Logged in as youraccount
chat から聞いて確認:
container exec -it hermes hermes chat
────────────────────────────────────────
● 私が最近作成したprは?
Initializing agent...
────────────────────────────────────────
┊ 📚 skill github-pr-workflow 0.1s
┊ 📚 skill github-auth 0.1s
┊ 💻 $ true) printf '\n-- gh version --\n' (gh --version + 1 command 0.7s
┊ 💻 $ git branch --show-current 2>/dev/null + 1 command 0.1s
┊ 💻 $ set -e gh search prs --author @me --sort created --order desc --limit 10 --json number,title,url,repository,createdAt,state 1.7s
╭─ ⚕ Hermes ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
あなたの GitHub アカウントで最近作成した PR の上位10件です(作成日時の新しい順):
...
これで接続確認完了
GitHubの連携はGitHub Appを用いたほうが好ましいですが、今回はローカルで接続確認のために gh auth loginで認証しています。
まとめ
一旦SlackとGitHubがつながったので、Slack側から自分のAI personal assistantに何かGitHubへの操作などを依頼できるようになりました。





