用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
直接命令不会经过审查 Prompt;运行前请先检查来源。
npx skills add https://github.com/arm2arm/AstroAgentAssistant --skill drphub-anon-access命令会保持在同一行。复制前请横向滚动并检查完整内容。
想先保存到本地?可下载 SkillsMP 当前能够提供的文件。
正在显示 SKILL.md
| name | drphub-anon-access |
| description | Query DRP Hub public cards without auth. |
| version | 1.0.0 |
| author | Hermes Agent |
| license | MIT |
| platforms | ["linux"] |
| metadata | {"drphub":{"tags":["drp","hub","cards","rest-api","supabase","anon-key","punch4nfdi"],"homepage":"https://drphub-p4n.aip.de","api_base":"https://drp-term.kube.aip.de/api/v1","related_skills":["drphub-cards","drp-hub"]}} |
The DRP Hub REST API (drp-term.kube.aip.de/api/v1) requires authentication even for public products — GET /products?visibility=public returns 401 without a Bearer token. However, the web frontend embeds a Supabase anonymous key that enables read-only queries against the drp_cards table.
Additionally, maturity_level in the raw database is always 0. The actual maturity is computed server-side via computed_maturity_level (REST API only) or client-side by the frontend JS using gate-checking logic.
The frontend bundles change filename on each deploy. Find the current one:
curl -s https://drphub-p4n.aip.de/ | grep -oP 'href="/([^"]*index[^"]*\.js)"' | head -1
curl -s "https://drphub-p4n.aip.de/assets/index-*.js" | grep -oP '_o="\K[^"]+'
import re, json, urllib.request, subprocess
from urllib.parse import urlencode
js = subprocess.check_output(["curl", "-s", "https://drphub-p4n.aip.de/assets/index-*.js"]).decode()
anon_key = re.search(r'_o="([^"]+)"', js).group(1)
SUPABASE_URL = "https://rrgnjinkabvqavwwzyfs.supabase.co"
headers = {"apikey": anon_key, "Authorization": f"Bearer {anon_key}"}
params = {"select": "*", "visibility": "eq.public", "deleted_at": "is.null", "order": "last_modified.desc"}
url = f"{SUPABASE_URL}/rest/v1/drp_cards?{urlencode(params)}"
req = urllib.request.Request(url, headers=headers)
with urllib.request.urlopen(req) as resp:
cards = json.loads(resp.read().decode())
The frontend's be() function in DRPCard-B26H0C_j.js defines these gates. The fe() function computes: auto-level = highest level where all gates pass, then applies maturity_override if set.
def t(s):
return bool(s) and len(str(s)) > 0
def S(s):
return bool(s) and len(str(s)) > 0
def has_reana(card):
if card.get("has_reana"):
return True
yaml = card.get("reana_yaml") or card.get("reanaYaml")
return bool(yaml)
def compute_maturity(card):
r = has_reana(card)
# L0: title + description
l0 = [t(card.get("title")), t(card.get("description"))]
# L1: git_url + (entry_command OR reana.yaml)
l1 = [t(card.get("git_url")), t(card.get("entry_command")) or r]
# L2: license + citation_cff_url + release_tag
l2 = [t(card.get("license")), t(card.get("citation_cff_url")), t(card.get("release_tag"))]
# L3: workflow_file + last_run_id + validation_scope
l3 = [t(card.get("workflow_file")) or r, t(card.get("last_run_id")),
bool(card.get()) (card.get(, {})) > ]
l4 = [S(card.get()), t(card.get()),
(card.get()), card.get() == ,
(card.get())]
levels = [l0, l1, l2, l3, l4]
m =
i, reqs (levels):
(reqs):
m = i
:
override = card.get()
(override, (, )) <= override <= :
level = (override)
:
level = m
level, m
The web UI shows "L4" on cards whose title/description text mentions "L4" — NOT because the card actually achieved L4. Always compute gates or query /products/{id}/maturity to verify.
Example: "Maturity walkthrough" displays as L4 on the web UI but its API maturity_level is 0, computed_maturity_level is 1. It fails L2 (missing env_image) and L4 (human_reviewed=false).
drp_cards table, not drp_productscomputed_maturity_level, maturity_gates, or maturity_missingdrp_cards (legacy): no maturity_level column, no computed_maturity_leveldrp_products (new): has maturity_level (always 0 in practice); computed_maturity_level is a REST API-only computed fieldid UUID