admin/skills: fail loud on DB error + read skills dir from env
All checks were successful
Build & Deploy / build-and-deploy (push) Successful in 3m8s

- Raise HTTPException(503) when Paperclip DB is unreachable instead of
  silently falling through to disk-only mode and returning [].
- Honor PAPERCLIP_SKILLS_DIR env var (falls back to ~/.paperclip/...).
  In the Coolify container the host's skills dir is bind-mounted at
  /paperclip-skills; without this, Path.home() resolved to /root/ and
  the disk inventory was always empty.

Both bugs together silently turned a Paperclip DB outage into "no skills
installed" on the /skills page.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-02 15:24:39 +00:00
parent 5deb38f5cf
commit f7249b7807

View File

@@ -2656,9 +2656,15 @@ async def api_reset_methodology(category: str, key: str):
PAPERCLIP_DB_URL = os.environ.get(
"PAPERCLIP_DB_URL", "postgresql://paperclip:paperclip@127.0.0.1:54329/paperclip"
)
# Paperclip runs locally via pm2; skills are in ~/.paperclip
_local_skills = Path.home() / ".paperclip" / "instances" / "default" / "skills"
PAPERCLIP_SKILLS_DIR = _local_skills
# Paperclip skills directory. In the Coolify container this is bind-mounted from
# the host's ~/.paperclip/instances/default/skills (see PAPERCLIP_SKILLS_DIR env).
# Fallback to the host path for local/dev use.
PAPERCLIP_SKILLS_DIR = Path(
os.environ.get(
"PAPERCLIP_SKILLS_DIR",
str(Path.home() / ".paperclip" / "instances" / "default" / "skills"),
)
)
# Default company ID for skills
SKILLS_COMPANY_ID = os.environ.get("PAPERCLIP_COMPANY_ID", "42a7acd0-30c5-4cbd-ac97-7424f65df294")
@@ -2666,7 +2672,6 @@ SKILLS_COMPANY_ID = os.environ.get("PAPERCLIP_COMPANY_ID", "42a7acd0-30c5-4cbd-a
@app.get("/api/admin/skills")
async def api_list_skills():
"""List installed Paperclip skills with DB sync status."""
rows = []
try:
conn = await asyncpg.connect(PAPERCLIP_DB_URL, timeout=5)
try:
@@ -2677,9 +2682,12 @@ async def api_list_skills():
)
finally:
await conn.close()
except (OSError, asyncpg.PostgresError, asyncpg.InterfaceError, TimeoutError):
# Paperclip DB unreachable — continue with disk-only skills
pass
except (OSError, asyncpg.PostgresError, asyncpg.InterfaceError, TimeoutError) as e:
logger.exception("Paperclip DB unreachable while listing skills")
raise HTTPException(
status_code=503,
detail=f"Paperclip database unreachable: {type(e).__name__}: {e}",
) from e
skills = []
for r in rows: