feat(skills): P3 config_schema + P4 Versionierung mit Rollback
P3 — Skill-Configuration
- aria-brain/skills.py: SKILL_CONFIGS_FILE (/shared/config/skill_configs.json)
als zentrale Werte-Persistenz. _normalize_config_schema validiert die
Schema-Felder (name/type/label/secret/description/default), CFG_<UPPER_NAME>
ENV beim run_skill. create_skill + update_skill akzeptieren config_schema.
- agent.py: skill_set_config Brain-Tool fuer ARIA. skill_create/update um
config_schema-Property erweitert.
- main.py: GET/POST /skills/{name}/config — secret-Werte in Antwort gemaskt.
P4 — Versionierung mit Rollback
- aria-brain/skills.py: archive_current_version archiviert nach
versions/v_<ts>/ (ohne venv/logs). update_skill ruft das automatisch auf
bevor strukturelle Aenderungen passieren. list_skill_versions,
rollback_skill (mit Safety-Snapshot + automatischem venv-Rebuild),
delete_skill_version.
- agent.py: skill_list_versions, skill_rollback Brain-Tools.
- main.py: GET /skills/{name}/versions, POST /skills/{name}/rollback,
DELETE /skills/{name}/versions/{version_id}.
UI
- diagnostic/index.html: Skill-Detail um Config-Form (typ-spezifisch,
Secrets als password-Input mit ***SET***-Hinweis) und Versions-Liste
mit Rollback-/Delete-Button.
- android SkillBrowser: SkillDetailModal laedt config_schema + versions
on-mount. Config-Form (TextInput + Switch fuer boolean), Versionen mit
Rollback-Confirm. brainApi um SkillConfigField/SkillVersion +
getSkillConfig/setSkillConfig/listSkillVersions/rollbackSkill/
deleteSkillVersion erweitert.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -757,6 +757,7 @@ class SkillCreate(BaseModel):
|
||||
requires: dict = Field(default_factory=dict)
|
||||
pip_packages: list = Field(default_factory=list)
|
||||
author: str = "stefan"
|
||||
config_schema: list = Field(default_factory=list)
|
||||
|
||||
|
||||
class SkillRun(BaseModel):
|
||||
@@ -769,6 +770,18 @@ class SkillPatch(BaseModel):
|
||||
description: str | None = None
|
||||
active: bool | None = None
|
||||
args: list | None = None
|
||||
entry_code: str | None = None
|
||||
readme: str | None = None
|
||||
pip_packages: list | None = None
|
||||
config_schema: list | None = None
|
||||
|
||||
|
||||
class SkillConfigSet(BaseModel):
|
||||
values: dict
|
||||
|
||||
|
||||
class SkillRollback(BaseModel):
|
||||
version_id: str
|
||||
|
||||
|
||||
@app.get("/skills/list")
|
||||
@@ -798,6 +811,7 @@ def skills_create(body: SkillCreate):
|
||||
requires=body.requires,
|
||||
pip_packages=body.pip_packages,
|
||||
author=body.author,
|
||||
config_schema=body.config_schema,
|
||||
)
|
||||
except ValueError as exc:
|
||||
raise HTTPException(400, str(exc))
|
||||
@@ -834,6 +848,57 @@ def skills_logs(name: str, limit: int = 50):
|
||||
return {"logs": skills_mod.list_logs(name, limit=limit)}
|
||||
|
||||
|
||||
# ── Skill-Configs (P3): statische Werte (API-Keys etc.) je Skill ───
|
||||
|
||||
@app.get("/skills/{name}/config")
|
||||
def skills_config_get(name: str):
|
||||
"""Liefert config_schema + aktuelle Werte (secret-Felder gemaskt mit
|
||||
'***SET***')."""
|
||||
manifest = skills_mod.read_manifest(name)
|
||||
if manifest is None:
|
||||
raise HTTPException(404, f"Skill '{name}' nicht gefunden")
|
||||
return {
|
||||
"schema": manifest.get("config_schema") or [],
|
||||
"values": skills_mod.get_skill_config_masked(name),
|
||||
}
|
||||
|
||||
|
||||
@app.post("/skills/{name}/config")
|
||||
def skills_config_set(name: str, body: SkillConfigSet):
|
||||
"""Setzt Config-Werte (komplett ueberschreibend). Werte greifen ab dem
|
||||
naechsten skill_run. Secret-Felder werden in der Antwort gemaskt."""
|
||||
manifest = skills_mod.read_manifest(name)
|
||||
if manifest is None:
|
||||
raise HTTPException(404, f"Skill '{name}' nicht gefunden")
|
||||
skills_mod.set_skill_config(name, body.values)
|
||||
return {"ok": True, "values": skills_mod.get_skill_config_masked(name)}
|
||||
|
||||
|
||||
# ── Skill-Versions (P4): rollback ──────────────────────────────────
|
||||
|
||||
@app.get("/skills/{name}/versions")
|
||||
def skills_versions_list(name: str):
|
||||
if skills_mod.read_manifest(name) is None:
|
||||
raise HTTPException(404, f"Skill '{name}' nicht gefunden")
|
||||
return {"versions": skills_mod.list_skill_versions(name)}
|
||||
|
||||
|
||||
@app.post("/skills/{name}/rollback")
|
||||
def skills_rollback(name: str, body: SkillRollback):
|
||||
try:
|
||||
return skills_mod.rollback_skill(name, body.version_id)
|
||||
except ValueError as exc:
|
||||
raise HTTPException(404, str(exc))
|
||||
|
||||
|
||||
@app.delete("/skills/{name}/versions/{version_id}")
|
||||
def skills_versions_delete(name: str, version_id: str):
|
||||
try:
|
||||
return skills_mod.delete_skill_version(name, version_id)
|
||||
except ValueError as exc:
|
||||
raise HTTPException(404, str(exc))
|
||||
|
||||
|
||||
@app.get("/skills/{name}/export")
|
||||
def skills_export(name: str):
|
||||
try:
|
||||
|
||||
Reference in New Issue
Block a user