Skip to content

Commit d324f7e

Browse files
authored
Merge branch 'NousResearch:main' into main
2 parents 3414ca3 + 2a14e89 commit d324f7e

1,374 files changed

Lines changed: 165175 additions & 30653 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.dockerignore

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,3 +63,45 @@ data/
6363
# Compose/profile runtime state (bind-mounted; avoid ownership/secret issues)
6464
hermes-config/
6565
runtime/
66+
67+
# ---------- Not needed inside the Docker image ----------
68+
69+
# Desktop app source (Tauri/Electron); never installed in the container
70+
apps/
71+
72+
# Test suite — not shipped in production images
73+
tests/
74+
75+
# Documentation site (Docusaurus) and supplementary docs
76+
website/
77+
docs/
78+
79+
# Assets only used by the GitHub README
80+
assets/
81+
infographic/
82+
83+
# Plugin-level docs (hermes-achievements ships docs/ but the runtime doesn't read them)
84+
plugins/hermes-achievements/docs/
85+
86+
# Nix / Homebrew / AUR packaging metadata — irrelevant to Docker
87+
nix/
88+
flake.nix
89+
flake.lock
90+
packaging/
91+
92+
# Design and planning documents
93+
plans/
94+
.plans/
95+
96+
# ACP registry manifest (icon + agent.json) — not consumed at runtime
97+
acp_registry/
98+
99+
# Repo-level dotfiles that are git-only or dev-tooling config
100+
.env.example
101+
.envrc
102+
.gitattributes
103+
.hadolint.yaml
104+
.mailmap
105+
106+
# Top-level LICENSE (not matched by *.md); not needed inside the container
107+
LICENSE
428 KB
Loading

.github/workflows/deploy-site.yml

Lines changed: 86 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,20 @@ on:
1111
- 'optional-skills/**'
1212
- '.github/workflows/deploy-site.yml'
1313
workflow_dispatch:
14+
inputs:
15+
skills_index_run_id:
16+
description: 'Optional Build Skills Index run ID whose skills-index artifact should be deployed'
17+
required: false
18+
type: string
19+
rebuild_skills_index:
20+
description: 'Force a fresh multi-source crawl instead of reusing the latest healthy index'
21+
required: false
22+
default: false
23+
type: boolean
1424

1525
permissions:
26+
contents: read
27+
actions: read
1628
pages: write
1729
id-token: write
1830

@@ -44,7 +56,7 @@ jobs:
4456

4557
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
4658
with:
47-
node-version: 20
59+
node-version: 22
4860
cache: npm
4961
cache-dependency-path: website/package-lock.json
5062

@@ -55,16 +67,81 @@ jobs:
5567
- name: Install PyYAML for skill extraction
5668
run: pip install pyyaml==6.0.2 httpx==0.28.1
5769

58-
- name: Build skills index (unified multi-source catalog)
70+
- name: Prepare skills index (unified multi-source catalog)
5971
env:
60-
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
72+
GH_TOKEN: ${{ github.token }}
73+
GITHUB_TOKEN: ${{ github.token }}
74+
SKILLS_INDEX_RUN_ID: ${{ github.event.inputs.skills_index_run_id || '' }}
75+
REBUILD_SKILLS_INDEX: ${{ github.event.inputs.rebuild_skills_index || 'false' }}
6176
run: |
62-
# Always rebuild — the file isn't committed (gitignored), so a
63-
# fresh checkout starts without it and we want the freshest crawl
64-
# in every deploy. Failure is non-fatal: extract-skills.py will
65-
# fall back to the legacy snapshot cache and the Skills Hub page
66-
# still renders, just without the latest community catalog.
67-
python3 scripts/build_skills_index.py || echo "Skills index build failed (non-fatal)"
77+
# The unified external catalog is expensive to crawl and can burn
78+
# through the repository installation's GitHub API quota when several
79+
# docs deploys land close together. Normal docs deploys therefore
80+
# reuse the latest healthy catalog: first the artifact from a
81+
# scheduled skills-index run, then the currently live index. Only a
82+
# manual force rebuild does a fresh crawl here.
83+
#
84+
# If we do crawl, the build remains fatal. build_skills_index.py runs
85+
# the health check BEFORE writing and exits non-zero on source
86+
# collapse, keeping the last good Pages deployment live instead of
87+
# publishing a degenerate catalog.
88+
set -euo pipefail
89+
INDEX_PATH="website/static/api/skills-index.json"
90+
mkdir -p "$(dirname "$INDEX_PATH")"
91+
92+
validate_index() {
93+
python3 - "$INDEX_PATH" <<'PY'
94+
import json
95+
import sys
96+
from pathlib import Path
97+
98+
path = Path(sys.argv[1])
99+
try:
100+
data = json.loads(path.read_text(encoding="utf-8"))
101+
except Exception as exc:
102+
print(f"invalid skills index JSON: {exc}", file=sys.stderr)
103+
sys.exit(1)
104+
skills = data.get("skills")
105+
if not isinstance(skills, list) or len(skills) < 1500:
106+
count = len(skills) if isinstance(skills, list) else "missing"
107+
print(f"skills index too small: {count}", file=sys.stderr)
108+
sys.exit(1)
109+
print(f"skills index ready: {len(skills)} skills")
110+
PY
111+
}
112+
113+
if [ "$REBUILD_SKILLS_INDEX" = "true" ]; then
114+
python3 scripts/build_skills_index.py
115+
validate_index
116+
exit 0
117+
fi
118+
119+
if [ -n "$SKILLS_INDEX_RUN_ID" ]; then
120+
tmpdir="$(mktemp -d)"
121+
echo "Downloading skills-index artifact from run $SKILLS_INDEX_RUN_ID"
122+
if gh run download "$SKILLS_INDEX_RUN_ID" --name skills-index --dir "$tmpdir"; then
123+
candidate="$(find "$tmpdir" -name skills-index.json -type f | head -n 1 || true)"
124+
if [ -n "$candidate" ]; then
125+
cp "$candidate" "$INDEX_PATH"
126+
if validate_index; then
127+
exit 0
128+
fi
129+
fi
130+
fi
131+
echo "::warning::Could not use skills-index artifact from run $SKILLS_INDEX_RUN_ID; trying live index"
132+
fi
133+
134+
echo "Downloading currently live skills index"
135+
if curl -fsSL --retry 3 --retry-delay 5 \
136+
"https://hermes-agent.nousresearch.com/docs/api/skills-index.json" \
137+
-o "$INDEX_PATH" && validate_index; then
138+
exit 0
139+
fi
140+
141+
echo "::warning::Live skills index unavailable or unhealthy; falling back to a fresh crawl"
142+
rm -f "$INDEX_PATH"
143+
python3 scripts/build_skills_index.py
144+
validate_index
68145
69146
- name: Extract skill metadata for dashboard
70147
run: python3 website/scripts/extract-skills.py

.github/workflows/docker-publish.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ jobs:
9090
# (see `_SKIP_PARTS` in scripts/run_tests_parallel.py) because each
9191
# shard would otherwise reach the session-scoped ``built_image``
9292
# fixture in ``tests/docker/conftest.py`` and start a 3-7min
93-
# ``docker build`` under a 180s pytest-timeout cap — guaranteed to
93+
# ``docker build`` — guaranteed to
9494
# die in fixture setup.
9595
#
9696
# Piggybacking here avoids a second image build: the smoke test
@@ -114,7 +114,7 @@ jobs:
114114
run: |
115115
uv venv .venv --python 3.11
116116
source .venv/bin/activate
117-
# ``dev`` extra pulls in pytest, pytest-asyncio, pytest-timeout
117+
# ``dev`` extra pulls in pytest, pytest-asyncio —
118118
# everything tests/docker/ needs. We deliberately avoid ``all``
119119
# here because the docker tests only drive the container via
120120
# subprocess and don't import hermes_agent's optional deps.

.github/workflows/docs-site-checks.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ jobs:
1818

1919
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
2020
with:
21-
node-version: 20
21+
node-version: 22
2222
cache: npm
2323
cache-dependency-path: website/package-lock.json
2424

.github/workflows/nix-lockfile-fix.yml

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -75,9 +75,10 @@ jobs:
7575
run: |
7676
set -euo pipefail
7777
78-
# Ensure only nix files were modified — prevents accidental
79-
# self-triggering if fix-lockfiles ever touches package files.
80-
unexpected="$(git diff --name-only | grep -Ev '^nix/(tui|web)\.nix$' || true)"
78+
# Ensure only nix/lib.nix (home of the single npmDepsHash) was
79+
# modified — prevents accidental self-triggering if fix-lockfiles
80+
# ever touches package files.
81+
unexpected="$(git diff --name-only | grep -Ev '^nix/lib\.nix$' || true)"
8182
if [ -n "$unexpected" ]; then
8283
echo "::error::Unexpected modified files: $unexpected"
8384
exit 1
@@ -89,7 +90,7 @@ jobs:
8990
9091
git config user.name 'github-actions[bot]'
9192
git config user.email '41898282+github-actions[bot]@users.noreply.github.com'
92-
git add nix/tui.nix nix/web.nix
93+
git add nix/lib.nix
9394
git commit -m "fix(nix): auto-refresh npm lockfile hashes" \
9495
-m "Source: $GITHUB_SHA" \
9596
-m "Run: $GITHUB_SERVER_URL/$GITHUB_REPOSITORY/actions/runs/$GITHUB_RUN_ID"
@@ -216,7 +217,7 @@ jobs:
216217
set -euo pipefail
217218
git config user.name 'github-actions[bot]'
218219
git config user.email '41898282+github-actions[bot]@users.noreply.github.com'
219-
git add nix/tui.nix nix/web.nix
220+
git add nix/lib.nix
220221
git commit -m "fix(nix): refresh npm lockfile hashes"
221222
git push
222223

.github/workflows/skills-index.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,4 +53,4 @@ jobs:
5353
- name: Trigger Deploy Site workflow
5454
env:
5555
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
56-
run: gh workflow run deploy-site.yml --repo ${{ github.repository }}
56+
run: gh workflow run deploy-site.yml --repo ${{ github.repository }} -f skills_index_run_id=${{ github.run_id }}

.github/workflows/supply-chain-audit.yml

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ jobs:
2929
scan: ${{ steps.filter.outputs.scan }}
3030
# True when pyproject.toml changed in this PR
3131
deps: ${{ steps.filter.outputs.deps }}
32+
# True when the curated MCP catalog / bundled MCP manifests changed.
33+
mcp_catalog: ${{ steps.filter.outputs.mcp_catalog }}
3234
steps:
3335
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
3436
with:
@@ -54,6 +56,14 @@ jobs:
5456
else
5557
echo "deps=false" >> "$GITHUB_OUTPUT"
5658
fi
59+
MCP_CATALOG_FILES=$(git diff --name-only "$BASE"..."$HEAD" -- \
60+
'optional-mcps/**' \
61+
'hermes_cli/mcp_catalog.py' || true)
62+
if [ -n "$MCP_CATALOG_FILES" ]; then
63+
echo "mcp_catalog=true" >> "$GITHUB_OUTPUT"
64+
else
65+
echo "mcp_catalog=false" >> "$GITHUB_OUTPUT"
66+
fi
5767
5868
scan:
5969
name: Scan PR for critical supply chain risks
@@ -268,3 +278,50 @@ jobs:
268278
runs-on: ubuntu-latest
269279
steps:
270280
- run: echo "No pyproject.toml changes, skipping dependency bounds check."
281+
282+
mcp-catalog-review:
283+
name: MCP catalog security review
284+
needs: changes
285+
if: needs.changes.outputs.mcp_catalog == 'true'
286+
runs-on: ubuntu-latest
287+
steps:
288+
- name: Checkout
289+
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
290+
with:
291+
fetch-depth: 0
292+
293+
- name: Require explicit MCP catalog review label
294+
env:
295+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
296+
run: |
297+
set -euo pipefail
298+
PR="${{ github.event.pull_request.number }}"
299+
LABELS=$(gh pr view "$PR" --json labels --jq '.labels[].name' || true)
300+
if echo "$LABELS" | grep -Fxq 'mcp-catalog-reviewed'; then
301+
echo "MCP catalog review label present."
302+
exit 0
303+
fi
304+
305+
BODY="## ⚠️ MCP catalog security review required
306+
307+
This PR changes the bundled MCP catalog or MCP catalog installer code. MCP entries can define local commands that users later install into \`mcp_servers\`, so this needs explicit maintainer review before merge.
308+
309+
A maintainer should verify:
310+
- any new/changed \`optional-mcps/**/manifest.yaml\` command and args are expected,
311+
- stdio transports do not use shell+egress/exfiltration payloads,
312+
- git install refs are pinned and bootstrap commands are minimal,
313+
- requested env vars/secrets match the upstream MCP's documented needs.
314+
315+
After review, add the \`mcp-catalog-reviewed\` label and re-run this check."
316+
317+
gh pr comment "$PR" --body "$BODY" || echo "::warning::Could not post PR comment (expected for fork PRs)"
318+
echo "::error::MCP catalog changes require the mcp-catalog-reviewed label."
319+
exit 1
320+
321+
mcp-catalog-review-gate:
322+
name: MCP catalog security review
323+
needs: changes
324+
if: always() && needs.changes.outputs.mcp_catalog != 'true'
325+
runs-on: ubuntu-latest
326+
steps:
327+
- run: echo "No MCP catalog changes, skipping MCP catalog security review."

0 commit comments

Comments
 (0)