|
# retoor <retoor@molodetz.nl>
|
|
|
|
from .._shared import endpoint, field
|
|
|
|
GROUP = {
|
|
"slug": "tools",
|
|
"title": "Tools (SEO, DeepSearch & AI Usage Analyzer)",
|
|
"intro": """
|
|
# Tools: SEO Diagnostics & DeepSearch
|
|
|
|
Two public developer tools that run as background jobs.
|
|
|
|
**SEO Diagnostics** audits a URL or sitemap with a headless browser and runs a broad battery of
|
|
technical, on-page, structured-data, Core Web Vitals, accessibility and AI-readiness checks.
|
|
|
|
**DeepSearch** is a multi-agent deep web researcher that crawls and indexes sources, then
|
|
synthesises a cited report with confidence scoring and gap analysis, plus a grounded chat over
|
|
the results.
|
|
|
|
**AI Usage Analyzer** classifies a git repository or website as AI slop, sophisticated AI-assisted
|
|
work or genuine human work, and publishes a persistent report with an embeddable authenticity
|
|
badge.
|
|
|
|
Every endpoint follows the shared [Conventions & Errors](/docs/conventions.html). These are
|
|
**capability URLs**: the job `uid` is an unguessable identifier, so anyone holding it can read the
|
|
status and report.
|
|
""",
|
|
"endpoints": [
|
|
endpoint(
|
|
id="tools-seo-run",
|
|
method="POST",
|
|
path="/tools/seo/run",
|
|
title="Queue an SEO audit",
|
|
summary="Start a background SEO audit of a URL or sitemap. Returns the job uid plus status and websocket URLs.",
|
|
auth="public",
|
|
encoding="form",
|
|
params=[
|
|
field("url", "form", "string", True, "https://example.com", "Page URL or sitemap.xml URL to audit."),
|
|
field("mode", "form", "enum", False, "url", "'url' (single page) or 'sitemap' (crawl).", ["url", "sitemap"]),
|
|
field("max_pages", "form", "integer", False, "10", "Max pages to crawl in sitemap mode (1-50)."),
|
|
],
|
|
sample_response={
|
|
"uid": "SEO_JOB_UID",
|
|
"status_url": "/tools/seo/SEO_JOB_UID",
|
|
"ws_url": "/tools/seo/SEO_JOB_UID/ws",
|
|
},
|
|
),
|
|
endpoint(
|
|
id="tools-seo-status",
|
|
method="GET",
|
|
path="/tools/seo/{uid}",
|
|
title="SEO audit status",
|
|
summary="Poll an SEO audit. Once done, score, grade and report_url are populated.",
|
|
auth="public",
|
|
params=[
|
|
field("uid", "path", "string", True, "SEO_JOB_UID", "SEO job uid returned when the audit was queued."),
|
|
],
|
|
sample_response={
|
|
"uid": "SEO_JOB_UID",
|
|
"kind": "seo",
|
|
"status": "done",
|
|
"target": "https://example.com",
|
|
"mode": "url",
|
|
"ws_url": "/tools/seo/SEO_JOB_UID/ws",
|
|
"report_url": "/tools/seo/SEO_JOB_UID/report",
|
|
"score": 82,
|
|
"grade": "B",
|
|
"page_count": 1,
|
|
"error": None,
|
|
"created_at": "2026-06-14T10:00:00+00:00",
|
|
"completed_at": "2026-06-14T10:00:18+00:00",
|
|
},
|
|
),
|
|
endpoint(
|
|
id="tools-seo-report",
|
|
method="GET",
|
|
path="/tools/seo/{uid}/report",
|
|
title="SEO audit report",
|
|
summary="Full categorised report: overall score, per-category subscores, and every check with its recommendation. Negotiates HTML or JSON.",
|
|
auth="public",
|
|
params=[
|
|
field("uid", "path", "string", True, "SEO_JOB_UID", "SEO job uid of a finished audit."),
|
|
],
|
|
sample_response={
|
|
"uid": "SEO_JOB_UID",
|
|
"status": "done",
|
|
"target": "https://example.com",
|
|
"score": 82,
|
|
"grade": "B",
|
|
"page_count": 1,
|
|
"counts": {"pass": 40, "warn": 8, "fail": 3, "info": 5, "skip": 0},
|
|
"categories": {"crawlability": {"score": 90, "pass": 9, "warn": 1, "fail": 0}},
|
|
"pages": [{"url": "https://example.com", "status": 200, "score": 82, "grade": "B"}],
|
|
"checks": [
|
|
{
|
|
"id": "meta.title_present",
|
|
"category": "meta",
|
|
"title": "Title tag",
|
|
"status": "pass",
|
|
"severity": "high",
|
|
"value": "Example Domain",
|
|
"recommendation": "",
|
|
"url": "https://example.com",
|
|
}
|
|
],
|
|
"site": {"robots": {"status": 200}, "sitemap": {"status": 200, "url_count": 12}},
|
|
"generated_at": "2026-06-14T10:00:18+00:00",
|
|
},
|
|
),
|
|
endpoint(
|
|
id="tools-seo-screenshot",
|
|
method="GET",
|
|
path="/tools/seo/{uid}/screenshot/{index}",
|
|
title="SEO audit page screenshot",
|
|
summary="Stream the rendered screenshot (image/png) captured for the audited page at the given zero-based index.",
|
|
auth="public",
|
|
interactive=True,
|
|
params=[
|
|
field("uid", "path", "string", True, "SEO_JOB_UID", "SEO job uid of a finished audit."),
|
|
field("index", "path", "integer", True, "0", "Zero-based index of the audited page."),
|
|
],
|
|
),
|
|
endpoint(
|
|
id="tools-seo-meta-status",
|
|
method="GET",
|
|
path="/tools/seo-meta/{target_type}/{target_uid}",
|
|
title="Generated SEO metadata for a content item",
|
|
summary="Read the clean, AI-generated SEO title, description and keywords for a published post, project, gist, news article or issue. Returns a plain-content default with status 'pending' until the AI value is ready.",
|
|
auth="public",
|
|
params=[
|
|
field("target_type", "path", "enum", True, "post", "Content type.", ["post", "project", "gist", "news", "issue"]),
|
|
field("target_uid", "path", "string", True, "CONTENT_UID", "The content uid (or issue number)."),
|
|
],
|
|
sample_response={
|
|
"uid": "SEO_META_UID",
|
|
"target_type": "post",
|
|
"target_uid": "CONTENT_UID",
|
|
"seo_title": "Building a fast SQLite social network",
|
|
"seo_description": "How DevPlace keeps SQLite synchronous and still serves a developer social network fast, with WAL, mmap and batch query helpers.",
|
|
"seo_keywords": "sqlite, fastapi, social network, performance, wal",
|
|
"status": "ready",
|
|
"source": "ai",
|
|
"generated_at": "2026-06-14T10:00:18+00:00",
|
|
},
|
|
),
|
|
endpoint(
|
|
id="tools-deepsearch-run",
|
|
method="POST",
|
|
path="/tools/deepsearch/run",
|
|
title="Queue a DeepSearch research job",
|
|
summary="Start a multi-agent deep web research job. Returns the job uid plus status and websocket URLs. Connect ws_url for live progress frames.",
|
|
auth="public",
|
|
encoding="form",
|
|
params=[
|
|
field("query", "form", "string", True, "history of the transistor", "The research question to investigate."),
|
|
field("depth", "form", "integer", False, "2", "Research depth (1-4)."),
|
|
field("max_pages", "form", "integer", False, "12", "Maximum sources to crawl (1-30)."),
|
|
],
|
|
sample_response={
|
|
"uid": "DEEPSEARCH_JOB_UID",
|
|
"status_url": "/tools/deepsearch/DEEPSEARCH_JOB_UID",
|
|
"ws_url": "/tools/deepsearch/DEEPSEARCH_JOB_UID/ws",
|
|
"progress_frames_note": (
|
|
"The ws_url stream emits newline-delimited JSON frames; the set is append-only "
|
|
"and the first frame carries version:1. Each frame has a type: phase "
|
|
"(phase, index, total, label), stage, substep (planning angles), queries, "
|
|
"candidates, rsearch, progress (done, total, url), page_loaded (source, render, "
|
|
"elapsed_ms, done, total), page_cached, page_skipped (reason), page_duplicate, "
|
|
"embed_batch (batch, total_batches, backend, done, total), embed_done (backend, "
|
|
"chunk_count), agent (agent, status start|done, elapsed_ms, tokens_in, tokens_out), "
|
|
"report_ready, done (session_url), failed (message)."
|
|
),
|
|
},
|
|
),
|
|
endpoint(
|
|
id="tools-deepsearch-status",
|
|
method="GET",
|
|
path="/tools/deepsearch/{uid}",
|
|
title="DeepSearch status",
|
|
summary="Poll a DeepSearch job. Once done, score, confidence and session_url are populated.",
|
|
auth="public",
|
|
params=[
|
|
field("uid", "path", "string", True, "DEEPSEARCH_JOB_UID", "DeepSearch job uid returned when the run was queued."),
|
|
],
|
|
sample_response={
|
|
"uid": "DEEPSEARCH_JOB_UID",
|
|
"kind": "deepsearch",
|
|
"status": "done",
|
|
"query": "history of the transistor",
|
|
"depth": 2,
|
|
"max_pages": 12,
|
|
"ws_url": "/tools/deepsearch/DEEPSEARCH_JOB_UID/ws",
|
|
"chat_ws_url": "/tools/deepsearch/DEEPSEARCH_JOB_UID/chat",
|
|
"session_url": "/tools/deepsearch/DEEPSEARCH_JOB_UID/session",
|
|
"score": 78,
|
|
"confidence": 0.72,
|
|
"source_diversity": 0.64,
|
|
"page_count": 11,
|
|
"chunk_count": 240,
|
|
"error": None,
|
|
"created_at": "2026-06-14T10:00:00+00:00",
|
|
"completed_at": "2026-06-14T10:01:40+00:00",
|
|
},
|
|
),
|
|
endpoint(
|
|
id="tools-deepsearch-session",
|
|
method="GET",
|
|
path="/tools/deepsearch/{uid}/session",
|
|
title="DeepSearch report",
|
|
summary="Full cited research report: summary, findings, sources and metrics. Negotiates HTML or JSON.",
|
|
auth="public",
|
|
params=[
|
|
field("uid", "path", "string", True, "DEEPSEARCH_JOB_UID", "DeepSearch job uid of a finished run."),
|
|
],
|
|
sample_response={
|
|
"uid": "DEEPSEARCH_JOB_UID",
|
|
"status": "done",
|
|
"query": "history of the transistor",
|
|
"score": 78,
|
|
"confidence": 0.72,
|
|
"source_diversity": 0.64,
|
|
"page_count": 11,
|
|
"chunk_count": 240,
|
|
"summary": "The transistor was invented at Bell Labs in 1947...",
|
|
"findings": [
|
|
{"title": "Invention", "detail": "...", "confidence": 0.8, "citations": [1]}
|
|
],
|
|
"sources": [{"url": "https://example.com", "title": "Example", "source": "httpx"}],
|
|
"chat_ws_url": "/tools/deepsearch/DEEPSEARCH_JOB_UID/chat",
|
|
"export_md_url": "/tools/deepsearch/DEEPSEARCH_JOB_UID/export.md",
|
|
"export_json_url": "/tools/deepsearch/DEEPSEARCH_JOB_UID/export.json",
|
|
"export_pdf_url": "/tools/deepsearch/DEEPSEARCH_JOB_UID/export.pdf",
|
|
},
|
|
),
|
|
endpoint(
|
|
id="tools-isslop-run",
|
|
method="POST",
|
|
path="/tools/isslop/run",
|
|
title="Queue a AI usage analysis",
|
|
summary="Start a background authenticity analysis of a git repository or website. Returns the job uid plus status, events and report URLs.",
|
|
auth="public",
|
|
encoding="form",
|
|
params=[
|
|
field("url", "form", "string", True, "https://github.com/owner/repository", "Repository (http/git/ssh) or website URL to classify."),
|
|
],
|
|
sample_response={
|
|
"uid": "ISSLOP_UID",
|
|
"status_url": "/tools/isslop/ISSLOP_UID",
|
|
"events_url": "/tools/isslop/ISSLOP_UID/events",
|
|
"report_url": "/tools/isslop/ISSLOP_UID/report",
|
|
"topic": "public.isslop.ISSLOP_UID",
|
|
},
|
|
),
|
|
endpoint(
|
|
id="tools-isslop-list",
|
|
method="GET",
|
|
path="/tools/isslop/list",
|
|
title="My AI usage analyses",
|
|
summary="List the caller's analyses, newest first. Member history is account-bound; guest history is session-bound and claimed by the account on first signed-in call.",
|
|
auth="public",
|
|
params=[
|
|
field("limit", "query", "integer", False, "50", "Maximum analyses to return (1-200)."),
|
|
],
|
|
sample_response={
|
|
"analyses": [
|
|
{
|
|
"uid": "ISSLOP_UID",
|
|
"status": "completed",
|
|
"source_url": "https://github.com/owner/repository",
|
|
"source_kind": "git",
|
|
"grade": "B",
|
|
"human_percent": 71.4,
|
|
"ai_percent": 28.6,
|
|
"category": "human-clean",
|
|
"report_url": "/tools/isslop/ISSLOP_UID/report",
|
|
"badge_url": "/tools/isslop/ISSLOP_UID/badge.svg",
|
|
}
|
|
]
|
|
},
|
|
),
|
|
endpoint(
|
|
id="tools-isslop-status",
|
|
method="GET",
|
|
path="/tools/isslop/{uid}",
|
|
title="AI usage analysis status",
|
|
summary="Poll an analysis. Once completed, grade, category and the human/AI split are populated.",
|
|
auth="public",
|
|
params=[
|
|
field("uid", "path", "string", True, "ISSLOP_UID", "Analysis uid returned when the run was queued."),
|
|
],
|
|
sample_response={
|
|
"uid": "ISSLOP_UID",
|
|
"status": "completed",
|
|
"source_url": "https://github.com/owner/repository",
|
|
"source_kind": "git",
|
|
"grade": "B",
|
|
"slop_score": 31.2,
|
|
"origin_score": 28.0,
|
|
"quality_deficit_score": 22.5,
|
|
"human_percent": 71.4,
|
|
"ai_percent": 28.6,
|
|
"category": "human-clean",
|
|
"confidence": "medium",
|
|
"files_total": 120,
|
|
"files_analyzed": 96,
|
|
"report_url": "/tools/isslop/ISSLOP_UID/report",
|
|
"badge_url": "/tools/isslop/ISSLOP_UID/badge.svg",
|
|
"events_url": "/tools/isslop/ISSLOP_UID/events",
|
|
"topic": "public.isslop.ISSLOP_UID",
|
|
},
|
|
),
|
|
endpoint(
|
|
id="tools-isslop-events",
|
|
method="GET",
|
|
path="/tools/isslop/{uid}/events",
|
|
title="AI usage analysis event trail",
|
|
summary="The persisted, ordered event trail of an analysis. Use ?after=SEQ to poll incrementally; live frames also stream on the pub/sub topic.",
|
|
auth="public",
|
|
params=[
|
|
field("uid", "path", "string", True, "ISSLOP_UID", "Analysis uid."),
|
|
field("after", "query", "integer", False, "0", "Return only events with a sequence number greater than this."),
|
|
field("limit", "query", "integer", False, "2000", "Maximum events to return (1-5000)."),
|
|
],
|
|
sample_response={
|
|
"uid": "ISSLOP_UID",
|
|
"status": "running",
|
|
"events": [
|
|
{"seq": 1, "kind": "stage", "message": "Resolving source type", "data": {"stage": "resolve"}, "created_at": "2026-06-14T10:00:00+00:00"}
|
|
],
|
|
},
|
|
),
|
|
endpoint(
|
|
id="tools-isslop-report",
|
|
method="GET",
|
|
path="/tools/isslop/{uid}/report",
|
|
title="AI usage analysis report",
|
|
summary="Full report: verdict, markdown body, per-file results, image review and badge embeds. Negotiates HTML or JSON.",
|
|
auth="public",
|
|
params=[
|
|
field("uid", "path", "string", True, "ISSLOP_UID", "Analysis uid of a finished run."),
|
|
],
|
|
sample_response={
|
|
"uid": "ISSLOP_UID",
|
|
"status": "completed",
|
|
"source_url": "https://github.com/owner/repository",
|
|
"grade": "B",
|
|
"human_percent": 71.4,
|
|
"ai_percent": 28.6,
|
|
"category": "human-clean",
|
|
"markdown": "# Verdict...",
|
|
"generator_model": "molodetz",
|
|
"badge": {
|
|
"badge_url": "https://devplace.example/tools/isslop/ISSLOP_UID/badge.svg",
|
|
"report_url": "https://devplace.example/tools/isslop/ISSLOP_UID/report",
|
|
"markdown": "[](...)",
|
|
"html": "<a href=...><img src=.../></a>",
|
|
},
|
|
"files": [{"path": "src/main.py", "language": "python", "lines": 120, "origin_score": 35.0, "quality_deficit_score": 18.0, "category": "human-clean", "signals": []}],
|
|
"images": [{"path": "assets/hero.png", "ai_probability": 84.0, "grade": "F", "verdict": "ai-generated", "image_kind": "illustration", "tells": ["waxy skin"], "description": "...", "thumb_url": "/tools/isslop/ISSLOP_UID/media/0f3a9c2d1b4e5a67.webp"}],
|
|
},
|
|
),
|
|
endpoint(
|
|
id="tools-isslop-report-md",
|
|
method="GET",
|
|
path="/tools/isslop/{uid}/report.md",
|
|
title="Download report markdown",
|
|
summary="Download the full report as a markdown file.",
|
|
auth="public",
|
|
params=[
|
|
field("uid", "path", "string", True, "ISSLOP_UID", "Analysis uid of a finished run."),
|
|
],
|
|
),
|
|
endpoint(
|
|
id="tools-isslop-source",
|
|
method="GET",
|
|
path="/tools/isslop/{uid}/source",
|
|
title="Annotated source of a flagged file",
|
|
summary="The persisted source of a signal-bearing file with its signals, rendered with line numbers and highlighted findings (HTML) or as JSON. Linked from the report's file table, signal chips and prose.",
|
|
auth="public",
|
|
params=[
|
|
field("uid", "path", "string", True, "ISSLOP_UID", "Analysis uid."),
|
|
field("path", "query", "string", True, "src/libs/Env.ts", "Workspace-relative file path from the report."),
|
|
field("line", "query", "integer", False, "12", "Line to focus and highlight."),
|
|
],
|
|
sample_response={
|
|
"uid": "ISSLOP_UID",
|
|
"path": "src/libs/Env.ts",
|
|
"language": "typescript",
|
|
"category": "human-clean",
|
|
"origin_score": 24.0,
|
|
"quality_deficit_score": 34.9,
|
|
"source": "import { createEnv } from '@t3-oss/env-nextjs';...",
|
|
"truncated": False,
|
|
"signals": [{"code": "PUBLIC_ENV_SECRET", "title": "Secret exposed via public env variable", "severity": "strong", "line": 12}],
|
|
},
|
|
),
|
|
endpoint(
|
|
id="tools-isslop-media",
|
|
method="GET",
|
|
path="/tools/isslop/{uid}/media/{name}",
|
|
title="Reviewed image thumbnail",
|
|
summary="Aspect-preserving WebP thumbnail of a reviewed image, persisted as evidence. The name comes from the report's images[].thumb_url.",
|
|
auth="public",
|
|
params=[
|
|
field("uid", "path", "string", True, "ISSLOP_UID", "Analysis uid."),
|
|
field("name", "path", "string", True, "0f3a9c2d1b4e5a67.webp", "Thumbnail file name from the report."),
|
|
],
|
|
),
|
|
endpoint(
|
|
id="tools-isslop-badge",
|
|
method="GET",
|
|
path="/tools/isslop/{uid}/badge.svg",
|
|
title="Authenticity badge",
|
|
summary="Embeddable SVG badge showing the human score and authenticity grade, linking to the report.",
|
|
auth="public",
|
|
params=[
|
|
field("uid", "path", "string", True, "ISSLOP_UID", "Analysis uid."),
|
|
],
|
|
),
|
|
],
|
|
}
|