forked from retoor/devplacepy
Add community node protocol spec, Agent Skills, and make the failing tests hermetic
Community nodes (Phase 1 of plan.md): the protocol specification in services/nodes/spec.py as the single source of truth, GET /nodes/spec.json, a generated protocol docs page, the Community nodes API docs group, and the implementation plan plus the residential proxy research. The no-open-proxy rule is enforced in the spec (open_proxy cannot be set). Agent Skills: register the Skills API docs group, cascade skills on account deletion, add the nested CLAUDE.md, README and routes docs, and cover the parser, the fetcher (end to end against a mock origin), the data layer and the routes with 73 unit and 16 api tests. Tests made hermetic with the repo's existing patterns instead of ambient environment: embeddings tests set OPENROUTER_API_KEY via monkeypatch, the workspace tests install FakeBackend via runtime.set_backend, and the skills install tests use run_async. DockerCliBackend now returns 127 (command not found) when the CLI is missing instead of raising FileNotFoundError, so a host without docker answers with a clean error rather than a 500. The Providers tab empty state no longer names the Model routes tab. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -1849,6 +1849,7 @@ def init_db():
|
||||
)
|
||||
|
||||
_ensure_moderation_tables()
|
||||
_ensure_skills_table()
|
||||
|
||||
for table in db.tables:
|
||||
_uid_index(db, table)
|
||||
@@ -2151,6 +2152,45 @@ MODERATION_INDEXES: tuple[tuple[str, str, list[str]], ...] = (
|
||||
)
|
||||
|
||||
|
||||
SKILLS_COLUMNS: tuple[tuple[str, object], ...] = (
|
||||
("uid", ""),
|
||||
("user_uid", ""),
|
||||
("name", ""),
|
||||
("description", ""),
|
||||
("body", ""),
|
||||
("files", ""),
|
||||
("source_url", ""),
|
||||
("license", ""),
|
||||
("compatibility", ""),
|
||||
("metadata", ""),
|
||||
("allowed_tools", ""),
|
||||
("enabled", 1),
|
||||
("sha256", ""),
|
||||
("skipped", ""),
|
||||
("warnings", ""),
|
||||
("fetched_at", ""),
|
||||
("created_at", ""),
|
||||
("updated_at", ""),
|
||||
("deleted_at", ""),
|
||||
("deleted_by", ""),
|
||||
)
|
||||
|
||||
|
||||
def _ensure_skills_table() -> None:
|
||||
table = get_table("skills")
|
||||
for column, example in SKILLS_COLUMNS:
|
||||
if not table.has_column(column):
|
||||
table.create_column_by_example(column, example)
|
||||
_index(db, "skills", "idx_skills_user_name", ["user_uid", "name"], unique=True)
|
||||
_index(
|
||||
db,
|
||||
"skills",
|
||||
"idx_skills_user_live",
|
||||
["user_uid", "enabled"],
|
||||
where="deleted_at IS NULL",
|
||||
)
|
||||
|
||||
|
||||
def _ensure_moderation_tables() -> None:
|
||||
for table_name, columns in MODERATION_COLUMNS.items():
|
||||
table = get_table(table_name)
|
||||
|
||||
Reference in New Issue
Block a user