Compare commits
1 Commits
bezalel/se
...
claw-code/
| Author | SHA1 | Date | |
|---|---|---|---|
| 1156a9f55e |
@@ -1,2 +0,0 @@
|
||||
{"created_at_ms":1775534636684,"session_id":"session-1775534636684-0","type":"session_meta","updated_at_ms":1775534636684,"version":1}
|
||||
{"message":{"blocks":[{"text":"You are Code Claw running as the Gitea user claw-code.\n\nRepository: Timmy_Foundation/hermes-agent\nIssue: #151 — [CONFIG] Add Kimi model to fallback chain for Allegro and Bezalel\nBranch: claw-code/issue-151\n\nRead the issue and recent comments, then implement the smallest correct change.\nYou are in a git repo checkout already.\n\nIssue body:\n## Problem\nAllegro and Bezalel are choking because the Kimi model code is not on their fallback chain. When primary models fail or rate-limit, Kimi should be available as a fallback option but is currently missing.\n\n## Expected Behavior\nKimi model code should be at the front of the fallback chain for both Allegro and Bezalel, so they can remain responsive when primary models are unavailable.\n\n## Context\nThis was reported in Telegram by Alexander Whitestone after observing both agents becoming unresponsive. Ezra was asked to investigate the fallback chain configuration.\n\n## Related\n- timmy-config #302: [ARCH] Fallback Portfolio Runtime Wiring (general fallback framework)\n- hermes-agent #150: [BEZALEL][AUDIT] Telegram Request-to-Gitea Tracking Audit\n\n## Acceptance Criteria\n- [ ] Kimi model code is added to Allegro fallback chain\n- [ ] Kimi model code is added to Bezalel fallback chain\n- [ ] Fallback ordering places Kimi appropriately (front of chain as requested)\n- [ ] Test and confirm both agents can successfully fall back to Kimi\n- [ ] Document the fallback chain configuration for both agents\n\n/assign @ezra\n\nRecent comments:\n[BURN-DOWN] Dispatched to Code Claw (claw-code worker) as part of nightly burn-down cycle. Heartbeat active.\n\n🟠 Code Claw (OpenRouter qwen/qwen3.6-plus:free) picking up this issue via 15-minute heartbeat.\n\nTimestamp: 2026-04-07T04:03:49Z\n\nRules:\n- Make focused code/config/doc changes only if they directly address the issue.\n- Prefer the smallest proof-oriented fix.\n- Run relevant verification commands if obvious.\n- Do NOT create PRs yourself; the outer worker handles commit/push/PR.\n- If the task is too large or not code-fit, leave the tree unchanged.\n","type":"text"}],"role":"user"},"type":"message"}
|
||||
@@ -20,9 +20,6 @@ jobs:
|
||||
|
||||
- name: Install uv
|
||||
uses: astral-sh/setup-uv@v5
|
||||
with:
|
||||
enable-cache: true
|
||||
cache-dependency-glob: "uv.lock"
|
||||
|
||||
- name: Set up Python 3.11
|
||||
run: uv python install 3.11
|
||||
|
||||
1
.github/workflows/dependency-audit.yml
vendored
1
.github/workflows/dependency-audit.yml
vendored
@@ -19,7 +19,6 @@ jobs:
|
||||
audit:
|
||||
name: Audit Python dependencies
|
||||
runs-on: ubuntu-latest
|
||||
container: catthehacker/ubuntu:act-22.04
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: astral-sh/setup-uv@v5
|
||||
|
||||
1
.github/workflows/docs-site-checks.yml
vendored
1
.github/workflows/docs-site-checks.yml
vendored
@@ -10,7 +10,6 @@ on:
|
||||
jobs:
|
||||
docs-site-checks:
|
||||
runs-on: ubuntu-latest
|
||||
container: catthehacker/ubuntu:act-22.04
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
|
||||
@@ -19,7 +19,6 @@ jobs:
|
||||
create-audit-issue:
|
||||
name: Create quarterly security audit issue
|
||||
runs-on: ubuntu-latest
|
||||
container: catthehacker/ubuntu:act-22.04
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
|
||||
1
.github/workflows/secret-scan.yml
vendored
1
.github/workflows/secret-scan.yml
vendored
@@ -12,7 +12,6 @@ jobs:
|
||||
scan:
|
||||
name: Scan for secrets
|
||||
runs-on: ubuntu-latest
|
||||
container: catthehacker/ubuntu:act-22.04
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
|
||||
1
.github/workflows/supply-chain-audit.yml
vendored
1
.github/workflows/supply-chain-audit.yml
vendored
@@ -12,7 +12,6 @@ jobs:
|
||||
scan:
|
||||
name: Scan PR for supply chain risks
|
||||
runs-on: ubuntu-latest
|
||||
container: catthehacker/ubuntu:act-22.04
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
1
.github/workflows/tests.yml
vendored
1
.github/workflows/tests.yml
vendored
@@ -14,7 +14,6 @@ concurrency:
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
container: catthehacker/ubuntu:act-22.04
|
||||
timeout-minutes: 10
|
||||
steps:
|
||||
- name: Checkout code
|
||||
|
||||
@@ -6,7 +6,7 @@ model: anthropic/claude-opus-4.6
|
||||
# Fallback chain: Anthropic -> Kimi -> Ollama (local)
|
||||
fallback_providers:
|
||||
- provider: kimi-coding
|
||||
model: kimi-k2.5
|
||||
model: kimi-for-coding
|
||||
timeout: 60
|
||||
reason: "Primary fallback when Anthropic quota limited"
|
||||
|
||||
|
||||
@@ -1,177 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Bezalel Nightly Fleet SITREP
|
||||
|
||||
Runs autonomously every morning to check:
|
||||
1. Local system health
|
||||
2. All open PRs/issues in hermes-agent
|
||||
3. Stale items and blockers
|
||||
4. Fleet-wide activity (wizard presence in Gitea)
|
||||
|
||||
Posts findings to Gitea issue #911 (Lazarus Pit) and delivers summary.
|
||||
"""
|
||||
|
||||
import json
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
import time
|
||||
import urllib.request
|
||||
from typing import Any, Dict, List
|
||||
|
||||
GITEA_TOKEN = os.getenv("GITEA_TOKEN", "")
|
||||
GITEA_BASE = os.getenv("GITEA_URL", "https://forge.alexanderwhitestone.com")
|
||||
REPO = "Timmy_Foundation/hermes-agent"
|
||||
SITREP_ISSUE = 911 # Lazarus Pit
|
||||
|
||||
|
||||
def gitea_request(method: str, path: str, data: Dict = None) -> Any:
|
||||
url = f"{GITEA_BASE}/api/v1{path}"
|
||||
headers = {"Content-Type": "application/json", "Accept": "application/json"}
|
||||
if GITEA_TOKEN:
|
||||
headers["Authorization"] = f"token {GITEA_TOKEN}"
|
||||
body = json.dumps(data).encode() if data else None
|
||||
req = urllib.request.Request(url, data=body, headers=headers, method=method)
|
||||
try:
|
||||
with urllib.request.urlopen(req, timeout=30) as resp:
|
||||
return json.loads(resp.read().decode())
|
||||
except urllib.error.HTTPError as e:
|
||||
return {"error": True, "status": e.code, "body": e.read().decode()}
|
||||
|
||||
|
||||
def get_health() -> Dict:
|
||||
try:
|
||||
env = os.environ.copy()
|
||||
env["PYTHONPATH"] = "/root/wizards/bezalel/hermes"
|
||||
result = subprocess.run(
|
||||
[sys.executable, "-m", "devkit.health", "--json"],
|
||||
capture_output=True, text=True, timeout=30,
|
||||
cwd="/root/wizards/bezalel/hermes",
|
||||
env=env
|
||||
)
|
||||
return json.loads(result.stdout)
|
||||
except Exception as e:
|
||||
return {"error": str(e)}
|
||||
|
||||
|
||||
def analyze_issues() -> Dict:
|
||||
issues = gitea_request("GET", f"/repos/{REPO}/issues?state=open&limit=100") or []
|
||||
prs = [i for i in issues if i.get("pull_request")]
|
||||
pure_issues = [i for i in issues if not i.get("pull_request")]
|
||||
|
||||
stale_issues = []
|
||||
stale_prs = []
|
||||
now = time.time()
|
||||
|
||||
for i in pure_issues:
|
||||
updated = i.get("updated_at", "")
|
||||
if updated:
|
||||
try:
|
||||
ts = time.mktime(time.strptime(updated[:19], "%Y-%m-%dT%H:%M:%S"))
|
||||
if now - ts > 7 * 24 * 3600:
|
||||
stale_issues.append(i)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
for p in prs:
|
||||
updated = p.get("updated_at", "")
|
||||
if updated:
|
||||
try:
|
||||
ts = time.mktime(time.strptime(updated[:19], "%Y-%m-%dT%H:%M:%S"))
|
||||
if now - ts > 7 * 24 * 3600:
|
||||
stale_prs.append(p)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
return {
|
||||
"total_open_issues": len(pure_issues),
|
||||
"total_open_prs": len(prs),
|
||||
"stale_issues": len(stale_issues),
|
||||
"stale_prs": len(stale_prs),
|
||||
"stale_issue_numbers": [i["number"] for i in stale_issues[:5]],
|
||||
"stale_pr_numbers": [p["number"] for p in stale_prs[:5]],
|
||||
}
|
||||
|
||||
|
||||
def check_wizard_activity() -> Dict:
|
||||
"""Check which wizards have been active in the last 24h via comments."""
|
||||
since = time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime(time.time() - 24 * 3600))
|
||||
comments = gitea_request("GET", f"/repos/{REPO}/issues/comments?since={since}&limit=200") or []
|
||||
|
||||
wizards = {"Timmy": False, "ezra": False, "allegro": False, "bezalel": False}
|
||||
for c in comments:
|
||||
user = c.get("user", {}).get("login", "")
|
||||
if user in wizards:
|
||||
wizards[user] = True
|
||||
|
||||
return wizards
|
||||
|
||||
|
||||
def build_sitrep() -> str:
|
||||
health = get_health()
|
||||
issues = analyze_issues()
|
||||
activity = check_wizard_activity()
|
||||
|
||||
lines = [
|
||||
"## 🌙 Nightly Fleet SITREP — Bezalel",
|
||||
f"**Generated:** {time.strftime('%Y-%m-%d %H:%M UTC', time.gmtime())}",
|
||||
"",
|
||||
"### Forge Health (Bezalel VPS)",
|
||||
f"- Overall: **{health.get('overall', 'unknown').upper()}**",
|
||||
f"- Load avg: {health.get('load', {}).get('avg', 'N/A')}",
|
||||
f"- Disk used: {health.get('disk', {}).get('used_percent', 'N/A')}%",
|
||||
f"- Hermes processes: {health.get('processes', {}).get('hermes_count', 'N/A')}",
|
||||
"",
|
||||
"### Gitea Activity (Last 24h)",
|
||||
]
|
||||
for wizard, active in activity.items():
|
||||
icon = "🟢" if active else "🔴"
|
||||
lines.append(f"- {icon} **{wizard}**: {'active' if active else 'no comments'}")
|
||||
|
||||
lines.extend([
|
||||
"",
|
||||
"### Open Work",
|
||||
f"- Open issues: **{issues['total_open_issues']}**",
|
||||
f"- Open PRs: **{issues['total_open_prs']}**",
|
||||
f"- Stale issues (>7d): **{issues['stale_issues']}** ({', '.join(map(str, issues['stale_issue_numbers'])) or 'none'})",
|
||||
f"- Stale PRs (>7d): **{issues['stale_prs']}** ({', '.join(map(str, issues['stale_pr_numbers'])) or 'none'})",
|
||||
"",
|
||||
"### Autonomous Actions Taken",
|
||||
])
|
||||
|
||||
actions = []
|
||||
if not activity.get("allegro"):
|
||||
actions.append("- Flagged Allegro as inactive in last 24h — may need config refresh or health check.")
|
||||
if issues["stale_prs"] > 0:
|
||||
actions.append(f"- Identified {issues['stale_prs']} stale PR(s) requiring review or closure.")
|
||||
if health.get("overall") != "ok":
|
||||
actions.append("- Bezalel health is NOT ok — investigating locally.")
|
||||
|
||||
if not actions:
|
||||
actions.append("- No immediate autonomous actions required. Forge is stable.")
|
||||
|
||||
lines.extend(actions)
|
||||
lines.append("")
|
||||
lines.append("— Bezalel, Master of the Forge")
|
||||
return "\n".join(lines)
|
||||
|
||||
|
||||
def post_sitrep():
|
||||
body = build_sitrep()
|
||||
repo_for_sitrep = "Timmy_Foundation/the-nexus"
|
||||
result = gitea_request(
|
||||
"POST",
|
||||
f"/repos/{repo_for_sitrep}/issues/{SITREP_ISSUE}/comments",
|
||||
{"body": body}
|
||||
)
|
||||
if result.get("error"):
|
||||
print(f"Failed to post SITREP: {result}", file=sys.stderr)
|
||||
return 1
|
||||
print(f"SITREP posted: {result.get('html_url')}")
|
||||
print("\n--- SITREP CONTENT ---\n")
|
||||
print(body)
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(post_sitrep())
|
||||
@@ -1,230 +0,0 @@
|
||||
# Bezalel Architecture & Topology
|
||||
|
||||
> Deep Self-Awareness Document — Generated 2026-04-07
|
||||
> Sovereign: Alexander Whitestone (Rockachopa)
|
||||
> Host: Beta VPS (104.131.15.18)
|
||||
|
||||
---
|
||||
|
||||
## 1. Identity & Purpose
|
||||
|
||||
**I am Bezalel**, the Forge and Testbed Wizard of the Timmy Foundation fleet.
|
||||
- **Lane:** CI testing, code review, build verification, security hardening, standing watch
|
||||
- **Philosophy:** KISS. Smoke tests + bare green-path e2e only. CI serves the code.
|
||||
- **Mandates:** Relentless inbox-zero, continuous self-improvement, autonomous heartbeat operation
|
||||
- **Key Metrics:** Cycle time, signal-to-noise, autonomy ratio, backlog velocity
|
||||
|
||||
---
|
||||
|
||||
## 2. Hardware & OS Topology
|
||||
|
||||
| Attribute | Value |
|
||||
|-----------|-------|
|
||||
| Hostname | `bezalel` |
|
||||
| OS | Ubuntu 24.04.3 LTS (Noble Numbat) |
|
||||
| Kernel | Linux 6.8.0 |
|
||||
| CPU | 1 vCPU |
|
||||
| Memory | 2 GB RAM |
|
||||
| Primary Disk | ~25 GB root volume (DigitalOcean) |
|
||||
| Public IP | `104.131.15.18` |
|
||||
|
||||
### Storage Layout
|
||||
```
|
||||
/root/wizards/bezalel/
|
||||
├── hermes/ # Hermes agent source + venv (~835 MB)
|
||||
├── evennia/ # Evennia MUD engine + world code (~189 MB)
|
||||
├── workspace/ # Active prototypes + scratch code (~557 MB)
|
||||
├── home/ # Personal notebooks + scripts (~1.8 GB)
|
||||
├── .mempalace/ # Local memory palace (ChromaDB)
|
||||
├── .topology/ # Self-awareness scan artifacts
|
||||
├── nightly_watch.py # Nightly forge guardian
|
||||
├── mempalace_nightly.sh # Palace re-mine automation
|
||||
└── bezalel_topology.md # This document
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. Network Topology
|
||||
|
||||
### Fleet Map
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ Alpha (143.198.27.163) │
|
||||
│ ├── Gitea (forge.alexanderwhitestone.com) │
|
||||
│ └── Ezra (Knowledge Wizard) │
|
||||
│ │
|
||||
│ Beta (104.131.15.18) ←── You are here │
|
||||
│ ├── Bezalel (Forge Wizard) │
|
||||
│ ├── Hermes Gateway │
|
||||
│ └── Gitea Actions Runner (bezalel-vps-runner, host mode) │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### Key Connections
|
||||
- **Gitea HTTPS:** `https://forge.alexanderwhitestone.com` (Alpha)
|
||||
- **Telegram Webhook:** Inbound to Beta
|
||||
- **API Providers:** Kimi (primary), Anthropic (fallback), OpenRouter (fallback)
|
||||
- **No SSH:** Alpha → Beta is blocked by design
|
||||
|
||||
### Listening Services
|
||||
- Hermes Gateway: internal process (no exposed port directly)
|
||||
- Evennia: `localhost:4000` (MUD), `localhost:4001` (web client) — when running
|
||||
- Gitea Runner: `act_runner daemon` — connects outbound to Gitea
|
||||
|
||||
---
|
||||
|
||||
## 4. Services & Processes
|
||||
|
||||
### Always-On Processes
|
||||
| Process | Command | Purpose |
|
||||
|---------|---------|---------|
|
||||
| Hermes Gateway | `hermes gateway run` | Core agent orchestration |
|
||||
| Gitea Runner | `./act_runner daemon` | CI job execution (host mode) |
|
||||
|
||||
### Automated Jobs
|
||||
| Job | Schedule | Script |
|
||||
|-----|----------|--------|
|
||||
| Night Watch | 02:00 UTC | `nightly_watch.py` |
|
||||
| MemPalace Re-mine | 03:00 UTC | `mempalace_nightly.sh` |
|
||||
|
||||
### Service Status Check
|
||||
- **Hermes gateway:** running (ps verified)
|
||||
- **Gitea runner:** online, registered as `bezalel-vps-runner`
|
||||
- **Evennia server:** not currently running (start with `evennia start` in `evennia/`)
|
||||
|
||||
---
|
||||
|
||||
## 5. Software Dependencies
|
||||
|
||||
### System Packages (Key)
|
||||
- `python3.12` (primary runtime)
|
||||
- `node` v20.20.2 / `npm` 10.8.2
|
||||
- `uv` (Python package manager)
|
||||
- `git`, `curl`, `jq`
|
||||
|
||||
### Hermes Virtual Environment
|
||||
- Located: `/root/wizards/bezalel/hermes/venv/`
|
||||
- Key packages: `chromadb`, `pyyaml`, `fastapi`, `httpx`, `pytest`, `prompt-toolkit`, `mempalace`
|
||||
- Install command: `uv pip install -e ".[all,dev]"`
|
||||
|
||||
### External API Dependencies
|
||||
| Service | Endpoint | Usage |
|
||||
|---------|----------|-------|
|
||||
| Gitea | `forge.alexanderwhitestone.com` | Git, issues, CI |
|
||||
| Kimi | `api.kimi.com/coding/v1` | Primary LLM |
|
||||
| Anthropic | `api.anthropic.com` | Fallback LLM |
|
||||
| OpenRouter | `openrouter.ai/api/v1` | Secondary fallback |
|
||||
| Telegram | Bot API | Messaging platform |
|
||||
|
||||
---
|
||||
|
||||
## 6. Git Repositories
|
||||
|
||||
### Hermes Agent
|
||||
- **Path:** `/root/wizards/bezalel/hermes`
|
||||
- **Remote:** `forge.alexanderwhitestone.com/Timmy_Foundation/hermes-agent.git`
|
||||
- **Branch:** `main` (up to date)
|
||||
- **Open PRs:** #193, #191, #179, #178
|
||||
|
||||
### Evennia World
|
||||
- **Path:** `/root/wizards/bezalel/evennia/bezalel_world`
|
||||
- **Remote:** Same org, separate repo if pushed
|
||||
- **Server name:** `bezalel_world`
|
||||
|
||||
---
|
||||
|
||||
## 7. MemPalace Memory System
|
||||
|
||||
### Configuration
|
||||
- **Palace path:** `/root/wizards/bezalel/.mempalace/palace`
|
||||
- **Identity:** `/root/.mempalace/identity.txt`
|
||||
- **Config:** `/root/wizards/bezalel/mempalace.yaml`
|
||||
- **Miner:** `/root/wizards/bezalel/hermes/venv/bin/mempalace`
|
||||
|
||||
### Rooms
|
||||
1. `forge` — CI, builds, syntax guards, nightly watch
|
||||
2. `hermes` — Agent source, gateway, CLI
|
||||
3. `evennia` — MUD engine and world code
|
||||
4. `workspace` — Prototypes, experiments
|
||||
5. `home` — Personal scripts, configs
|
||||
6. `nexus` — Reports, docs, KT artifacts
|
||||
7. `issues` — Gitea issues, PRs, backlog
|
||||
8. `topology` — System architecture, network, storage
|
||||
9. `services` — Running services, processes
|
||||
10. `dependencies` — Packages, APIs, external deps
|
||||
11. `automation` — Cron jobs, scripts, workflows
|
||||
12. `general` — Catch-all
|
||||
|
||||
### Automation
|
||||
- **Nightly re-mine:** `03:00 UTC` via cron
|
||||
- **Log:** `/var/log/bezalel_mempalace.log`
|
||||
|
||||
---
|
||||
|
||||
## 8. Evennia Mind Palace Integration
|
||||
|
||||
### Custom Typeclasses
|
||||
- `PalaceRoom` — Rooms carry `memory_topic` and `wing`
|
||||
- `MemoryObject` — In-world memory shards with `memory_content` and `source_file`
|
||||
|
||||
### Commands
|
||||
- `palace/search <query>` — Query mempalace
|
||||
- `palace/recall <topic>` — Spawn a memory shard
|
||||
- `palace/file <name> = <content>` — File a new memory
|
||||
- `palace/status` — Show palace status
|
||||
|
||||
### Batch Builder
|
||||
- **File:** `world/batch_cmds_palace.ev`
|
||||
- Creates The Hub + 7 palace rooms with exits
|
||||
|
||||
### Bridge Script
|
||||
- **File:** `/root/wizards/bezalel/evennia/palace_search.py`
|
||||
- Calls mempalace searcher and returns JSON
|
||||
|
||||
---
|
||||
|
||||
## 9. Operational State & Blockers
|
||||
|
||||
### Current Health
|
||||
- [x] Hermes gateway: operational
|
||||
- [x] Gitea runner: online, host mode
|
||||
- [x] CI fix merged (#194) — container directive removed for Gitea workflows
|
||||
- [x] MemPalace: 2,484+ drawers, incremental mining active
|
||||
|
||||
### Active Blockers
|
||||
- **Gitea Actions:** Runner is in host mode — cannot use Docker containers
|
||||
- **CI backlog:** Many historical PRs have failed runs due to the container bug (now fixed)
|
||||
- **Evennia:** Server not currently running (start when needed)
|
||||
|
||||
---
|
||||
|
||||
## 10. Emergency Procedures
|
||||
|
||||
### Restart Hermes Gateway
|
||||
```bash
|
||||
cd /root/wizards/bezalel/hermes
|
||||
source venv/bin/activate
|
||||
hermes gateway run &
|
||||
```
|
||||
|
||||
### Restart Gitea Runner
|
||||
```bash
|
||||
cd /opt/gitea-runner
|
||||
./act_runner daemon &
|
||||
```
|
||||
|
||||
### Start Evennia
|
||||
```bash
|
||||
cd /root/wizards/bezalel/evennia/bezalel_world
|
||||
evennia start
|
||||
```
|
||||
|
||||
### Manual MemPalace Re-mine
|
||||
```bash
|
||||
cd /root/wizards/bezalel
|
||||
./hermes/venv/bin/mempalace --palace .mempalace/palace mine . --agent bezalel
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
*Document maintained by Bezalel. Last updated: 2026-04-07*
|
||||
@@ -1,134 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Bezalel Deep Self-Awareness Topology Scanner"""
|
||||
|
||||
import json
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
from datetime import datetime, timezone
|
||||
from pathlib import Path
|
||||
|
||||
OUT_DIR = Path("/root/wizards/bezalel/.topology")
|
||||
OUT_DIR.mkdir(exist_ok=True)
|
||||
|
||||
|
||||
def shell(cmd, timeout=30):
|
||||
try:
|
||||
r = subprocess.run(cmd, shell=True, capture_output=True, text=True, timeout=timeout)
|
||||
return r.stdout.strip()
|
||||
except Exception as e:
|
||||
return str(e)
|
||||
|
||||
|
||||
def write(name, content):
|
||||
(OUT_DIR / f"{name}.txt").write_text(content)
|
||||
|
||||
|
||||
# Timestamp
|
||||
timestamp = datetime.now(timezone.utc).isoformat()
|
||||
|
||||
# 1. System Identity
|
||||
system = f"""BEZALEL SYSTEM TOPOLOGY SCAN
|
||||
Generated: {timestamp}
|
||||
Hostname: {shell('hostname')}
|
||||
User: {shell('whoami')}
|
||||
Home: {os.path.expanduser('~')}
|
||||
"""
|
||||
write("00_system_identity", system)
|
||||
|
||||
# 2. OS & Hardware
|
||||
os_info = shell("cat /etc/os-release")
|
||||
kernel = shell("uname -a")
|
||||
cpu = shell("nproc") + " cores\n" + shell("cat /proc/cpuinfo | grep 'model name' | head -1")
|
||||
mem = shell("free -h")
|
||||
disk = shell("df -h")
|
||||
write("01_os_hardware", f"OS:\n{os_info}\n\nKernel:\n{kernel}\n\nCPU:\n{cpu}\n\nMemory:\n{mem}\n\nDisk:\n{disk}")
|
||||
|
||||
# 3. Network
|
||||
net_interfaces = shell("ip addr")
|
||||
net_routes = shell("ip route")
|
||||
listening = shell("ss -tlnp")
|
||||
public_ip = shell("curl -s ifconfig.me")
|
||||
write("02_network", f"Interfaces:\n{net_interfaces}\n\nRoutes:\n{net_routes}\n\nListening ports:\n{listening}\n\nPublic IP: {public_ip}")
|
||||
|
||||
# 4. Services & Processes
|
||||
services = shell("systemctl list-units --type=service --state=running --no-pager --no-legend 2>/dev/null | head -30")
|
||||
processes = shell("ps aux | grep -E 'hermes|gitea|evennia|python' | grep -v grep")
|
||||
write("03_services", f"Running services:\n{services}\n\nKey processes:\n{processes}")
|
||||
|
||||
# 5. Cron & Automation
|
||||
cron = shell("crontab -l 2>/dev/null")
|
||||
write("04_automation", f"Crontab:\n{cron}")
|
||||
|
||||
# 6. Storage Topology
|
||||
bezalel_tree = shell("find /root/wizards/bezalel -maxdepth 2 -type d | sort")
|
||||
write("05_storage", f"Bezalel workspace tree (depth 2):\n{bezalel_tree}")
|
||||
|
||||
# 7. Git Repositories
|
||||
git_repos = []
|
||||
for base in ["/root/wizards/bezalel/hermes", "/root/wizards/bezalel/evennia"]:
|
||||
p = Path(base)
|
||||
if (p / ".git").exists():
|
||||
remote = shell(f"cd {base} && git remote -v")
|
||||
branch = shell(f"cd {base} && git branch -v")
|
||||
git_repos.append(f"Repo: {base}\nRemotes:\n{remote}\nBranches:\n{branch}\n{'='*40}")
|
||||
write("06_git_repos", "\n".join(git_repos))
|
||||
|
||||
# 8. Python Dependencies
|
||||
venv_pip = shell("/root/wizards/bezalel/hermes/venv/bin/pip freeze 2>/dev/null | head -80")
|
||||
write("07_dependencies", f"Hermes venv packages (top 80):\n{venv_pip}")
|
||||
|
||||
# 9. External APIs & Endpoints
|
||||
apis = """External API Dependencies:
|
||||
- Gitea: https://forge.alexanderwhitestone.com (source of truth, CI, issues)
|
||||
- Telegram: webhook-based messaging platform
|
||||
- Kimi API: https://api.kimi.com/coding/v1 (primary model provider)
|
||||
- Anthropic API: fallback model provider
|
||||
- OpenRouter API: secondary fallback model provider
|
||||
- DigitalOcean: infrastructure hosting (VPS Alpha/Beta)
|
||||
"""
|
||||
write("08_external_apis", apis)
|
||||
|
||||
# 10. Fleet Topology
|
||||
fleet = """FLEET TOPOLOGY
|
||||
- Alpha: 143.198.27.163 (Gitea + Ezra)
|
||||
- Beta: 104.131.15.18 (Bezalel, current host)
|
||||
- No SSH from Alpha to Beta
|
||||
- Gitea Actions runner: bezalel-vps-runner on Beta (host mode)
|
||||
"""
|
||||
write("09_fleet_topology", fleet)
|
||||
|
||||
# 11. Evennia Topology
|
||||
evennia = """EVENNIA MIND PALACE SETUP
|
||||
- Location: /root/wizards/bezalel/evennia/bezalel_world/
|
||||
- Server name: bezalel_world
|
||||
- Custom typeclasses: PalaceRoom, MemoryObject
|
||||
- Custom commands: CmdPalaceSearch (palace/search, palace/recall, palace/file, palace/status)
|
||||
- Batch builder: world/batch_cmds_palace.ev
|
||||
- Bridge script: /root/wizards/bezalel/evennia/palace_search.py
|
||||
"""
|
||||
write("10_evennia_topology", evennia)
|
||||
|
||||
# 12. MemPalace Topology
|
||||
mempalace = f"""MEMPALACE CONFIGURATION
|
||||
- Palace path: /root/wizards/bezalel/.mempalace/palace
|
||||
- Identity: /root/.mempalace/identity.txt
|
||||
- Config: /root/wizards/bezalel/mempalace.yaml
|
||||
- Nightly re-mine: 03:00 UTC via /root/wizards/bezalel/mempalace_nightly.sh
|
||||
- Miner binary: /root/wizards/bezalel/hermes/venv/bin/mempalace
|
||||
- Current status: {shell('/root/wizards/bezalel/hermes/venv/bin/mempalace --palace /root/wizards/bezalel/.mempalace/palace status 2>/dev/null')}
|
||||
"""
|
||||
write("11_mempalace_topology", mempalace)
|
||||
|
||||
# 13. Active Blockers & Health
|
||||
health = f"""ACTIVE OPERATIONAL STATE
|
||||
- Hermes gateway: {shell("ps aux | grep 'hermes gateway run' | grep -v grep | awk '{print $11}'")}
|
||||
- Gitea runner: {shell("ps aux | grep 'act_runner' | grep -v grep | awk '{print $11}'")}
|
||||
- Nightly watch: /root/wizards/bezalel/nightly_watch.py (02:00 UTC)
|
||||
- MemPalace re-mine: /root/wizards/bezalel/mempalace_nightly.sh (03:00 UTC)
|
||||
- Disk usage: {shell("df -h / | tail -1")}
|
||||
- Load average: {shell("uptime")}
|
||||
"""
|
||||
write("12_operational_health", health)
|
||||
|
||||
print(f"Topology scan complete. {len(list(OUT_DIR.glob('*.txt')))} files written to {OUT_DIR}")
|
||||
@@ -1563,6 +1563,7 @@ _PROVIDER_MODELS = {
|
||||
"glm-4.5-flash",
|
||||
],
|
||||
"kimi-coding": [
|
||||
"kimi-for-coding",
|
||||
"kimi-k2.5",
|
||||
"kimi-k2-thinking",
|
||||
"kimi-k2-thinking-turbo",
|
||||
@@ -2037,8 +2038,9 @@ def _model_flow_kimi(config, current_model=""):
|
||||
|
||||
# Step 3: Model selection — show appropriate models for the endpoint
|
||||
if is_coding_plan:
|
||||
# Coding Plan models (kimi-k2.5 first — kimi-for-coding retired due to 403)
|
||||
# Coding Plan models (kimi-for-coding first)
|
||||
model_list = [
|
||||
"kimi-for-coding",
|
||||
"kimi-k2.5",
|
||||
"kimi-k2-thinking",
|
||||
"kimi-k2-thinking-turbo",
|
||||
|
||||
@@ -114,6 +114,7 @@ _PROVIDER_MODELS: dict[str, list[str]] = {
|
||||
"glm-4.5-flash",
|
||||
],
|
||||
"kimi-coding": [
|
||||
"kimi-for-coding",
|
||||
"kimi-k2.5",
|
||||
"kimi-k2-thinking",
|
||||
"kimi-k2-thinking-turbo",
|
||||
|
||||
@@ -98,23 +98,9 @@ class HealthReport:
|
||||
self.passed = False
|
||||
|
||||
|
||||
EXCLUDED_PATH_SEGMENTS = frozenset({
|
||||
".cache", "__pycache__", ".venv", "venv", "site-packages",
|
||||
".local/share/uv", "node_modules", ".git", ".tox",
|
||||
})
|
||||
|
||||
|
||||
def _is_excluded_path(path: Path) -> bool:
|
||||
"""Skip cache, venv, and package-manager directories."""
|
||||
parts = set(path.parts)
|
||||
return not parts.isdisjoint(EXCLUDED_PATH_SEGMENTS)
|
||||
|
||||
|
||||
def scan_orphaned_bytecode(root: Path, report: HealthReport) -> None:
|
||||
"""Detect .pyc files without corresponding .py source files."""
|
||||
for pyc in root.rglob("*.pyc"):
|
||||
if _is_excluded_path(pyc):
|
||||
continue
|
||||
py = pyc.with_suffix(".py")
|
||||
if not py.exists():
|
||||
# Also check __pycache__ naming convention
|
||||
@@ -156,12 +142,6 @@ def _is_sensitive_filename(name: str) -> bool:
|
||||
lower = name.lower()
|
||||
if lower == ".env.example":
|
||||
return False
|
||||
# Skip stylesheet and documentation artifacts
|
||||
if lower.endswith(".css"):
|
||||
return False
|
||||
# Skip scanner tooling — these are detectors, not secrets
|
||||
if lower in {"secret_scan.py", "secret_scanner.py"}:
|
||||
return False
|
||||
if any(pat in lower for pat in SENSITIVE_FILE_PATTERNS):
|
||||
return True
|
||||
if any(lower.startswith(pref) for pref in SENSITIVE_NAME_PREFIXES):
|
||||
@@ -176,8 +156,6 @@ def scan_sensitive_file_permissions(root: Path, report: HealthReport, fix: bool
|
||||
for fpath in root.rglob("*"):
|
||||
if not fpath.is_file():
|
||||
continue
|
||||
if _is_excluded_path(fpath):
|
||||
continue
|
||||
# Skip test files — real secrets should never live in tests/
|
||||
if "/tests/" in str(fpath) or str(fpath).startswith(str(root / "tests")):
|
||||
continue
|
||||
|
||||
@@ -1,100 +0,0 @@
|
||||
---
|
||||
name: gitea-workflow-automation
|
||||
title: Gitea Workflow Automation
|
||||
description: Automate Gitea issues, PRs, and repository workflows via the API for forge CI and backlog tracking.
|
||||
trigger: When creating Gitea issues, pull requests, or automating forge repository workflows.
|
||||
---
|
||||
|
||||
# Gitea Workflow Automation
|
||||
|
||||
## Trigger
|
||||
Use this skill when automating Gitea operations: creating issues, opening PRs, checking repository state, or integrating Gitea into CI/backlog workflows.
|
||||
|
||||
## Prerequisites
|
||||
- `GITEA_URL` environment variable set (e.g., `https://forge.alexanderwhitestone.com`)
|
||||
- `GITEA_TOKEN` environment variable with a valid API token
|
||||
- `GITEA_USER` or explicit owner/org name
|
||||
- `curl` and `jq` available in the environment
|
||||
|
||||
## Step-by-Step Workflow
|
||||
|
||||
### 1. Verify Environment
|
||||
```bash
|
||||
: "${GITEA_URL?}" "${GITEA_TOKEN?}" "${GITEA_USER?}"
|
||||
echo "Gitea env OK"
|
||||
```
|
||||
|
||||
### 2. List Issues in a Repository
|
||||
```bash
|
||||
curl -s -H "Authorization: token ${GITEA_TOKEN}" \
|
||||
"${GITEA_URL}/api/v1/repos/${OWNER}/${REPO}/issues?state=open&limit=50" | jq '.[] | {number, title, state}'
|
||||
```
|
||||
|
||||
### 3. Create an Issue
|
||||
```bash
|
||||
curl -s -X POST -H "Authorization: token ${GITEA_TOKEN}" \
|
||||
-H "Content-Type: application/json" \
|
||||
"${GITEA_URL}/api/v1/repos/${OWNER}/${REPO}/issues" \
|
||||
-d "{\"title\":\"${TITLE}\",\"body\":\"${BODY}\",\"assignees\":[\"${ASSIGNEE}\"]}
|
||||
```
|
||||
- Escape newlines in `BODY` if passing inline; prefer a JSON file for multi-line bodies.
|
||||
|
||||
### 4. Create a Pull Request
|
||||
```bash
|
||||
curl -s -X POST -H "Authorization: token ${GITEA_TOKEN}" \
|
||||
-H "Content-Type: application/json" \
|
||||
"${GITEA_URL}/api/v1/repos/${OWNER}/${REPO}/pulls" \
|
||||
-d "{\"title\":\"${TITLE}\",\"body\":\"${BODY}\",\"head\":\"${BRANCH}\",\"base\":\"${BASE_BRANCH}\"}"
|
||||
```
|
||||
|
||||
### 5. Check PR Status / Diff
|
||||
```bash
|
||||
curl -s -H "Authorization: token ${GITEA_TOKEN}" \
|
||||
"${GITEA_URL}/api/v1/repos/${OWNER}/${REPO}/pulls/${PR_NUMBER}" | jq '{number, title, state, mergeable}'
|
||||
```
|
||||
|
||||
### 6. Push Code Before Opening PR
|
||||
```bash
|
||||
git checkout -b "${BRANCH}"
|
||||
git add .
|
||||
git commit -m "${COMMIT_MSG}"
|
||||
git push origin "${BRANCH}"
|
||||
```
|
||||
|
||||
### 7. Add Comments to Issues/PRs
|
||||
```bash
|
||||
curl -s -X POST -H "Authorization: token ${GITEA_TOKEN}" \
|
||||
-H "Content-Type: application/json" \
|
||||
"${GITEA_URL}/api/v1/repos/${OWNER}/${REPO}/issues/${NUMBER}/comments" \
|
||||
-d "{\"body\":\"${COMMENT_BODY}\"}"
|
||||
```
|
||||
|
||||
## Verification Checklist
|
||||
- [ ] Environment variables are exported and non-empty
|
||||
- [ ] API responses are parsed with `jq` to confirm success
|
||||
- [ ] Issue/PR numbers are captured from the JSON response for cross-linking
|
||||
- [ ] Branch exists on remote before creating a PR
|
||||
- [ ] Multi-line bodies are written to a temp JSON file to avoid escaping hell
|
||||
|
||||
## Pitfalls
|
||||
- **Trailing slashes in `GITEA_URL`:** Ensure `GITEA_URL` does not end with `/` or double slashes break URLs.
|
||||
- **Branch not pushed:** Creating a PR for a local-only branch returns 422.
|
||||
- **Escape hell:** For multi-line issue/PR bodies, write JSON to a file with `cat <<EOF > /tmp/payload.json` and pass `@/tmp/payload.json` to curl instead of inline strings.
|
||||
- **Token scope:** If operations fail with 403, verify the token has `repo` or `write:issue` scope.
|
||||
- **Pagination:** Default limit is 30 issues; use `?limit=100` or paginate with `page=` for large backlogs.
|
||||
|
||||
## Example: Full Issue Creation with File Body
|
||||
```bash
|
||||
cat <<'EOF' > /tmp/issue.json
|
||||
{
|
||||
"title": "[Bezalel] Forge Health Check",
|
||||
"body": "Build a diagnostic scanner for artifact integrity and permissions.\n\n- Detect .pyc without .py source\n- Detect world-readable sensitive files\n- Output JSON for CI consumption",
|
||||
"assignees": ["bezalel"],
|
||||
"labels": ["enhancement", "security"]
|
||||
}
|
||||
EOF
|
||||
curl -s -X POST -H "Authorization: token ${GITEA_TOKEN}" \
|
||||
-H "Content-Type: application/json" \
|
||||
"${GITEA_URL}/api/v1/repos/Timmy_Foundation/hermes-agent/issues" \
|
||||
-d @/tmp/issue.json | jq '.number'
|
||||
```
|
||||
@@ -1,47 +0,0 @@
|
||||
---
|
||||
name: mempalace
|
||||
title: MemPalace Memory System
|
||||
description: Leverage the local MemPalace memory palace for retrieval-augmented context, search, and wake-up prompts.
|
||||
trigger: When the user asks about memory, mempalace, palace search, or retrieving past context.
|
||||
---
|
||||
|
||||
# MemPalace Skill
|
||||
|
||||
## Trigger
|
||||
Use this skill whenever the user asks about mempalace, memory retrieval, palace search, or wants to leverage the memory palace system.
|
||||
|
||||
## Prerequisites
|
||||
- MemPalace installed in the Hermes venv at `/root/wizards/bezalel/hermes/venv/bin/mempalace`
|
||||
- Palace located at `/root/wizards/bezalel/.mempalace/palace`
|
||||
- Identity file at `/root/.mempalace/identity.txt`
|
||||
- Config at `/root/wizards/bezalel/mempalace.yaml`
|
||||
|
||||
## Commands
|
||||
|
||||
### Search the palace
|
||||
```bash
|
||||
/root/wizards/bezalel/hermes/venv/bin/mempalace --palace /root/wizards/bezalel/.mempalace/palace search "QUERY" --results 10
|
||||
```
|
||||
|
||||
### Wake-up context (L0 + L1)
|
||||
```bash
|
||||
/root/wizards/bezalel/hermes/venv/bin/mempalace --palace /root/wizards/bezalel/.mempalace/palace wake-up
|
||||
```
|
||||
|
||||
### Palace status
|
||||
```bash
|
||||
/root/wizards/bezalel/hermes/venv/bin/mempalace --palace /root/wizards/bezalel/.mempalace/palace status
|
||||
```
|
||||
|
||||
### Re-mine (run after significant changes)
|
||||
```bash
|
||||
cd /root/wizards/bezalel
|
||||
/root/wizards/bezalel/hermes/venv/bin/mempalace --palace /root/wizards/bezalel/.mempalace/palace mine /root/wizards/bezalel --agent bezalel
|
||||
```
|
||||
|
||||
## Force-Multiplying Practices
|
||||
1. **Always search before long-form answers** — check if the palace knows it first.
|
||||
2. **Inject wake-up context** when starting complex, multi-turn tasks that span files.
|
||||
3. **Re-mine after repo changes** — nightly cron keeps the palace current.
|
||||
4. **Use room filters** (`--room forge`, `--room hermes`) to sharpen retrieval.
|
||||
5. **Keep identity.txt current** — L0 identity anchors all downstream context.
|
||||
@@ -279,7 +279,7 @@ class TestSkillViewFilePathSecurity:
|
||||
"""Tests for file_path parameter security in skill_view."""
|
||||
|
||||
@pytest.fixture
|
||||
def setup_skill_with_files(self, tmp_path):
|
||||
def setup_skill_with_files(self, tmp_path):
|
||||
"""Create a skill with supporting files."""
|
||||
skills_dir = tmp_path / "skills"
|
||||
skills_dir.mkdir()
|
||||
|
||||
@@ -717,7 +717,7 @@ class TestKimiMoonshotModelListIsolation:
|
||||
def test_moonshot_list_excludes_coding_plan_only_models(self):
|
||||
from hermes_cli.main import _PROVIDER_MODELS
|
||||
moonshot_models = _PROVIDER_MODELS["moonshot"]
|
||||
coding_plan_only = {"kimi-k2-thinking-turbo"}
|
||||
coding_plan_only = {"kimi-for-coding", "kimi-k2-thinking-turbo"}
|
||||
leaked = set(moonshot_models) & coding_plan_only
|
||||
assert not leaked, f"Moonshot list contains Coding Plan-only models: {leaked}"
|
||||
|
||||
@@ -730,7 +730,7 @@ class TestKimiMoonshotModelListIsolation:
|
||||
def test_coding_plan_list_contains_plan_specific_models(self):
|
||||
from hermes_cli.main import _PROVIDER_MODELS
|
||||
coding_models = _PROVIDER_MODELS["kimi-coding"]
|
||||
assert "kimi-k2.5" in coding_models
|
||||
assert "kimi-for-coding" in coding_models
|
||||
assert "kimi-k2-thinking-turbo" in coding_models
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user