840 lines
498 KiB
HTML
840 lines
498 KiB
HTML
<!doctype html>
|
||
<html lang="en" dir="ltr" class="docs-wrapper plugin-docs plugin-id-default docs-version-current docs-doc-page docs-doc-id-reference/registry-spec" data-has-hydrated="false">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="generator" content="Docusaurus v3.9.2">
|
||
<title data-rh="true">CmdForge Registry Design | CmdForge</title><meta data-rh="true" name="viewport" content="width=device-width,initial-scale=1"><meta data-rh="true" name="twitter:card" content="summary_large_image"><meta data-rh="true" property="og:url" content="https://pages.brrd.tech/rob/CmdForge/reference/registry-spec"><meta data-rh="true" property="og:locale" content="en"><meta data-rh="true" name="docusaurus_locale" content="en"><meta data-rh="true" name="docsearch:language" content="en"><meta data-rh="true" name="docusaurus_version" content="current"><meta data-rh="true" name="docusaurus_tag" content="docs-default-current"><meta data-rh="true" name="docsearch:version" content="current"><meta data-rh="true" name="docsearch:docusaurus_tag" content="docs-default-current"><meta data-rh="true" property="og:title" content="CmdForge Registry Design | CmdForge"><meta data-rh="true" name="description" content="Purpose"><meta data-rh="true" property="og:description" content="Purpose"><link data-rh="true" rel="icon" href="/rob/CmdForge/img/favicon.ico"><link data-rh="true" rel="canonical" href="https://pages.brrd.tech/rob/CmdForge/reference/registry-spec"><link data-rh="true" rel="alternate" href="https://pages.brrd.tech/rob/CmdForge/reference/registry-spec" hreflang="en"><link data-rh="true" rel="alternate" href="https://pages.brrd.tech/rob/CmdForge/reference/registry-spec" hreflang="x-default"><script data-rh="true" type="application/ld+json">{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"name":"Reference","item":"https://pages.brrd.tech/rob/CmdForge/category/reference"},{"@type":"ListItem","position":2,"name":"Registry API","item":"https://pages.brrd.tech/rob/CmdForge/reference/registry-spec"}]}</script><link rel="stylesheet" href="/rob/CmdForge/assets/css/styles.37cb0314.css">
|
||
<script src="/rob/CmdForge/assets/js/runtime~main.a8ea285c.js" defer="defer"></script>
|
||
<script src="/rob/CmdForge/assets/js/main.af70b7f2.js" defer="defer"></script>
|
||
</head>
|
||
<body class="navigation-with-keyboard">
|
||
<svg style="display: none;"><defs>
|
||
<symbol id="theme-svg-external-link" viewBox="0 0 24 24"><path fill="currentColor" d="M21 13v10h-21v-19h12v2h-10v15h17v-8h2zm3-12h-10.988l4.035 4-6.977 7.07 2.828 2.828 6.977-7.07 4.125 4.172v-11z"/></symbol>
|
||
</defs></svg>
|
||
<script>!function(){var t=function(){try{return new URLSearchParams(window.location.search).get("docusaurus-theme")}catch(t){}}()||function(){try{return window.localStorage.getItem("theme")}catch(t){}}();document.documentElement.setAttribute("data-theme",t||(window.matchMedia("(prefers-color-scheme: dark)").matches?"dark":"light")),document.documentElement.setAttribute("data-theme-choice",t||"system")}(),function(){try{const c=new URLSearchParams(window.location.search).entries();for(var[t,e]of c)if(t.startsWith("docusaurus-data-")){var a=t.replace("docusaurus-data-","data-");document.documentElement.setAttribute(a,e)}}catch(t){}}()</script><div id="__docusaurus"><div role="region" aria-label="Skip to main content"><a class="skipToContent_fXgn" href="#__docusaurus_skipToContent_fallback">Skip to main content</a></div><nav aria-label="Main" class="theme-layout-navbar navbar navbar--fixed-top"><div class="navbar__inner"><div class="theme-layout-navbar-left navbar__items"><button aria-label="Toggle navigation bar" aria-expanded="false" class="navbar__toggle clean-btn" type="button"><svg width="30" height="30" viewBox="0 0 30 30" aria-hidden="true"><path stroke="currentColor" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2" d="M4 7h22M4 15h22M4 23h22"></path></svg></button><a class="navbar__brand" href="/rob/CmdForge/"><b class="navbar__title text--truncate">CmdForge</b></a></div><div class="theme-layout-navbar-right navbar__items navbar__items--right"><a href="https://gitea.brrd.tech/rob/CmdForge" target="_blank" rel="noopener noreferrer" class="navbar__item navbar__link">Source Code<svg width="13.5" height="13.5" aria-label="(opens in new tab)" class="iconExternalLink_nPIU"><use href="#theme-svg-external-link"></use></svg></a><div class="toggle_vylO colorModeToggle_DEke"><button class="clean-btn toggleButton_gllP toggleButtonDisabled_aARS" type="button" disabled="" title="system mode" aria-label="Switch between dark and light mode (currently system mode)"><svg viewBox="0 0 24 24" width="24" height="24" aria-hidden="true" class="toggleIcon_g3eP lightToggleIcon_pyhR"><path fill="currentColor" d="M12,9c1.65,0,3,1.35,3,3s-1.35,3-3,3s-3-1.35-3-3S10.35,9,12,9 M12,7c-2.76,0-5,2.24-5,5s2.24,5,5,5s5-2.24,5-5 S14.76,7,12,7L12,7z M2,13l2,0c0.55,0,1-0.45,1-1s-0.45-1-1-1l-2,0c-0.55,0-1,0.45-1,1S1.45,13,2,13z M20,13l2,0c0.55,0,1-0.45,1-1 s-0.45-1-1-1l-2,0c-0.55,0-1,0.45-1,1S19.45,13,20,13z M11,2v2c0,0.55,0.45,1,1,1s1-0.45,1-1V2c0-0.55-0.45-1-1-1S11,1.45,11,2z M11,20v2c0,0.55,0.45,1,1,1s1-0.45,1-1v-2c0-0.55-0.45-1-1-1C11.45,19,11,19.45,11,20z M5.99,4.58c-0.39-0.39-1.03-0.39-1.41,0 c-0.39,0.39-0.39,1.03,0,1.41l1.06,1.06c0.39,0.39,1.03,0.39,1.41,0s0.39-1.03,0-1.41L5.99,4.58z M18.36,16.95 c-0.39-0.39-1.03-0.39-1.41,0c-0.39,0.39-0.39,1.03,0,1.41l1.06,1.06c0.39,0.39,1.03,0.39,1.41,0c0.39-0.39,0.39-1.03,0-1.41 L18.36,16.95z M19.42,5.99c0.39-0.39,0.39-1.03,0-1.41c-0.39-0.39-1.03-0.39-1.41,0l-1.06,1.06c-0.39,0.39-0.39,1.03,0,1.41 s1.03,0.39,1.41,0L19.42,5.99z M7.05,18.36c0.39-0.39,0.39-1.03,0-1.41c-0.39-0.39-1.03-0.39-1.41,0l-1.06,1.06 c-0.39,0.39-0.39,1.03,0,1.41s1.03,0.39,1.41,0L7.05,18.36z"></path></svg><svg viewBox="0 0 24 24" width="24" height="24" aria-hidden="true" class="toggleIcon_g3eP darkToggleIcon_wfgR"><path fill="currentColor" d="M9.37,5.51C9.19,6.15,9.1,6.82,9.1,7.5c0,4.08,3.32,7.4,7.4,7.4c0.68,0,1.35-0.09,1.99-0.27C17.45,17.19,14.93,19,12,19 c-3.86,0-7-3.14-7-7C5,9.07,6.81,6.55,9.37,5.51z M12,3c-4.97,0-9,4.03-9,9s4.03,9,9,9s9-4.03,9-9c0-0.46-0.04-0.92-0.1-1.36 c-0.98,1.37-2.58,2.26-4.4,2.26c-2.98,0-5.4-2.42-5.4-5.4c0-1.81,0.89-3.42,2.26-4.4C12.92,3.04,12.46,3,12,3L12,3z"></path></svg><svg viewBox="0 0 24 24" width="24" height="24" aria-hidden="true" class="toggleIcon_g3eP systemToggleIcon_QzmC"><path fill="currentColor" d="m12 21c4.971 0 9-4.029 9-9s-4.029-9-9-9-9 4.029-9 9 4.029 9 9 9zm4.95-13.95c1.313 1.313 2.05 3.093 2.05 4.95s-0.738 3.637-2.05 4.95c-1.313 1.313-3.093 2.05-4.95 2.05v-14c1.857 0 3.637 0.737 4.95 2.05z"></path></svg></button></div><div class="navbarSearchContainer_Bca1"></div></div></div><div role="presentation" class="navbar-sidebar__backdrop"></div></nav><div id="__docusaurus_skipToContent_fallback" class="theme-layout-main main-wrapper mainWrapper_z2l0"><div class="docsWrapper_hBAB"><button aria-label="Scroll back to top" class="clean-btn theme-back-to-top-button backToTopButton_sjWU" type="button"></button><div class="docRoot_UBD9"><aside class="theme-doc-sidebar-container docSidebarContainer_YfHR"><div class="sidebarViewport_aRkj"><div class="sidebar_njMd"><nav aria-label="Docs sidebar" class="menu thin-scrollbar menu_SIkG"><ul class="theme-doc-sidebar-menu menu__list"><li class="theme-doc-sidebar-item-link theme-doc-sidebar-item-link-level-1 menu__list-item"><a class="menu__link" href="/rob/CmdForge/"><span title="CmdForge Overview" class="linkLabel_WmDU">CmdForge Overview</span></a></li><li class="theme-doc-sidebar-item-link theme-doc-sidebar-item-link-level-1 menu__list-item"><a class="menu__link" href="/rob/CmdForge/architecture"><span title="CmdForge Architecture" class="linkLabel_WmDU">CmdForge Architecture</span></a></li><li class="theme-doc-sidebar-item-category theme-doc-sidebar-item-category-level-1 menu__list-item"><div class="menu__list-item-collapsible"><a class="categoryLink_byQd menu__link menu__link--sublist menu__link--active" href="/rob/CmdForge/category/reference"><span title="Reference" class="categoryLinkLabel_W154">Reference</span></a><button aria-label="Collapse sidebar category 'Reference'" aria-expanded="true" type="button" class="clean-btn menu__caret"></button></div><ul class="menu__list"><li class="theme-doc-sidebar-item-link theme-doc-sidebar-item-link-level-2 menu__list-item"><a class="menu__link" tabindex="0" href="/rob/CmdForge/reference/providers"><span title="Provider Setup" class="linkLabel_WmDU">Provider Setup</span></a></li><li class="theme-doc-sidebar-item-link theme-doc-sidebar-item-link-level-2 menu__list-item"><a class="menu__link menu__link--active" aria-current="page" tabindex="0" href="/rob/CmdForge/reference/registry-spec"><span title="Registry API" class="linkLabel_WmDU">Registry API</span></a></li><li class="theme-doc-sidebar-item-link theme-doc-sidebar-item-link-level-2 menu__list-item"><a class="menu__link" tabindex="0" href="/rob/CmdForge/reference/meta-tools"><span title="Meta-Tools" class="linkLabel_WmDU">Meta-Tools</span></a></li><li class="theme-doc-sidebar-item-link theme-doc-sidebar-item-link-level-2 menu__list-item"><a class="menu__link" tabindex="0" href="/rob/CmdForge/reference/collections"><span title="Collections" class="linkLabel_WmDU">Collections</span></a></li><li class="theme-doc-sidebar-item-link theme-doc-sidebar-item-link-level-2 menu__list-item"><a class="menu__link" tabindex="0" href="/rob/CmdForge/reference/examples"><span title="Example Tools" class="linkLabel_WmDU">Example Tools</span></a></li><li class="theme-doc-sidebar-item-link theme-doc-sidebar-item-link-level-2 menu__list-item"><a class="menu__link" tabindex="0" href="/rob/CmdForge/reference/design"><span title="Design Philosophy" class="linkLabel_WmDU">Design Philosophy</span></a></li><li class="theme-doc-sidebar-item-link theme-doc-sidebar-item-link-level-2 menu__list-item"><a class="menu__link" tabindex="0" href="/rob/CmdForge/reference/web-ui-spec"><span title="Web UI Design" class="linkLabel_WmDU">Web UI Design</span></a></li></ul></li><li class="theme-doc-sidebar-item-link theme-doc-sidebar-item-link-level-1 menu__list-item"><a class="menu__link" href="/rob/CmdForge/todos"><span title="CmdForge TODOs" class="linkLabel_WmDU">CmdForge TODOs</span></a></li><li class="theme-doc-sidebar-item-link theme-doc-sidebar-item-link-level-1 menu__list-item"><a class="menu__link" href="/rob/CmdForge/goals"><span title="Goals" class="linkLabel_WmDU">Goals</span></a></li><li class="theme-doc-sidebar-item-link theme-doc-sidebar-item-link-level-1 menu__list-item"><a class="menu__link" href="/rob/CmdForge/ideas-and-exploration"><span title="Ideas & Exploration" class="linkLabel_WmDU">Ideas & Exploration</span></a></li><li class="theme-doc-sidebar-item-link theme-doc-sidebar-item-link-level-1 menu__list-item"><a class="menu__link" href="/rob/CmdForge/milestones"><span title="Milestones" class="linkLabel_WmDU">Milestones</span></a></li></ul></nav></div></div></aside><main class="docMainContainer_TBSr"><div class="container padding-top--md padding-bottom--lg"><div class="row"><div class="col docItemCol_VOVn"><div class="docItemContainer_Djhp"><article><nav class="theme-doc-breadcrumbs breadcrumbsContainer_Z_bl" aria-label="Breadcrumbs"><ul class="breadcrumbs"><li class="breadcrumbs__item"><a aria-label="Home page" class="breadcrumbs__link" href="/rob/CmdForge/"><svg viewBox="0 0 24 24" class="breadcrumbHomeIcon_YNFT"><path d="M10 19v-5h4v5c0 .55.45 1 1 1h3c.55 0 1-.45 1-1v-7h1.7c.46 0 .68-.57.33-.87L12.67 3.6c-.38-.34-.96-.34-1.34 0l-8.36 7.53c-.34.3-.13.87.33.87H5v7c0 .55.45 1 1 1h3c.55 0 1-.45 1-1z" fill="currentColor"></path></svg></a></li><li class="breadcrumbs__item"><a class="breadcrumbs__link" href="/rob/CmdForge/category/reference"><span>Reference</span></a></li><li class="breadcrumbs__item breadcrumbs__item--active"><span class="breadcrumbs__link">Registry API</span></li></ul></nav><div class="tocCollapsible_ETCw theme-doc-toc-mobile tocMobile_ITEo"><button type="button" class="clean-btn tocCollapsibleButton_TO0P">On this page</button></div><div class="theme-doc-markdown markdown"><header><h1>CmdForge Registry Design</h1></header>
|
||
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="purpose">Purpose<a href="#purpose" class="hash-link" aria-label="Direct link to Purpose" title="Direct link to Purpose" translate="no"></a></h2>
|
||
<p>Build a centralized registry for CmdForge to enable discovery, publishing, dependency management, and future curation at scale.</p>
|
||
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="terminology">Terminology<a href="#terminology" class="hash-link" aria-label="Direct link to Terminology" title="Direct link to Terminology" translate="no"></a></h2>
|
||
<table><thead><tr><th>Term</th><th>Definition</th></tr></thead><tbody><tr><td><strong>Tool definition</strong></td><td>The full YAML file in the registry (<code>config.yaml</code>) containing name, steps, arguments, etc.</td></tr><tr><td><strong>Tool config</strong></td><td>The configuration within a tool definition (arguments, steps, provider settings)</td></tr><tr><td><strong>cmdforge.yaml</strong></td><td>Project manifest file declaring tool dependencies and overrides</td></tr><tr><td><strong>config.yaml</strong></td><td>The tool definition file, both in registry and when installed locally</td></tr><tr><td><strong>Owner</strong></td><td>Immutable namespace slug identifying the publisher (e.g., <code>rob</code>, <code>alice</code>)</td></tr><tr><td><strong>Publisher</strong></td><td>A registered user who can publish tools to the registry</td></tr><tr><td><strong>Wrapper script</strong></td><td>Auto-generated bash script in <code>~/.local/bin/</code> that invokes a tool</td></tr></tbody></table>
|
||
<p><strong>Canonical naming:</strong> Use <code>CmdForge-Registry</code> (capitalized, hyphenated) for the repository name.</p>
|
||
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="diagram-references">Diagram References<a href="#diagram-references" class="hash-link" aria-label="Direct link to Diagram References" title="Direct link to Diagram References" translate="no"></a></h2>
|
||
<ul>
|
||
<li class="">System overview: <code>discussions/diagrams/cmdforge-registry_rob_1.puml</code></li>
|
||
<li class="">Data flows: <code>discussions/diagrams/cmdforge-registry_rob_5.puml</code></li>
|
||
</ul>
|
||
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="system-overview">System Overview<a href="#system-overview" class="hash-link" aria-label="Direct link to System Overview" title="Direct link to System Overview" translate="no"></a></h2>
|
||
<p>Users interact via the CLI and a future Web UI. Both call a Registry API hosted at <code>https://cmdforge.brrd.tech/api/v1</code> (future alias: <code>cmdforge.brrd.tech/api/v1</code>). The API syncs from a Gitea-backed registry repo and maintains a SQLite cache/search index.</p>
|
||
<p><strong>Canonical API base path:</strong> <code>https://cmdforge.brrd.tech/api/v1</code></p>
|
||
<p>All API endpoints are versioned under <code>/api/v1</code>. When breaking changes are needed, a new version (<code>/api/v2</code>) will be introduced with deprecation notices.</p>
|
||
<p>Core API endpoints:</p>
|
||
<ul>
|
||
<li class=""><code>GET /api/v1/tools</code></li>
|
||
<li class=""><code>GET /api/v1/tools/search?q=...</code> (with advanced filtering)</li>
|
||
<li class=""><code>GET /api/v1/tools/{owner}/{name}</code></li>
|
||
<li class=""><code>GET /api/v1/tools/{owner}/{name}/versions</code></li>
|
||
<li class=""><code>GET /api/v1/tools/{owner}/{name}/download?version=...</code></li>
|
||
<li class=""><code>POST /api/v1/tools</code> (publish)</li>
|
||
<li class=""><code>GET /api/v1/categories</code></li>
|
||
<li class=""><code>GET /api/v1/tags</code> (list all tags with counts)</li>
|
||
<li class=""><code>GET /api/v1/collections</code></li>
|
||
<li class=""><code>GET /api/v1/collections/{name}</code></li>
|
||
<li class=""><code>GET /api/v1/stats/popular</code></li>
|
||
<li class=""><code>POST /api/v1/webhook/gitea</code></li>
|
||
</ul>
|
||
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="pagination">Pagination<a href="#pagination" class="hash-link" aria-label="Direct link to Pagination" title="Direct link to Pagination" translate="no"></a></h3>
|
||
<p>All list endpoints support pagination:</p>
|
||
<table><thead><tr><th>Parameter</th><th>Default</th><th>Max</th><th>Description</th></tr></thead><tbody><tr><td><code>page</code></td><td>1</td><td>-</td><td>Page number (1-indexed)</td></tr><tr><td><code>per_page</code></td><td>20</td><td>100</td><td>Items per page</td></tr><tr><td><code>sort</code></td><td><code>downloads</code></td><td>-</td><td>Sort field</td></tr><tr><td><code>order</code></td><td><code>desc</code></td><td>-</td><td>Sort order (asc/desc)</td></tr></tbody></table>
|
||
<p><strong>Stable ordering:</strong> To ensure deterministic results across pages, sorting includes a secondary key:</p>
|
||
<ul>
|
||
<li class="">Primary: requested field (e.g., <code>downloads</code>)</li>
|
||
<li class="">Secondary: <code>published_at</code> (desc)</li>
|
||
<li class="">Tertiary: <code>id</code> (for absolute stability)</li>
|
||
</ul>
|
||
<div class="language-sql codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-sql codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#F8F8F2"><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">ORDER</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">BY</span><span class="token plain"> downloads </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">DESC</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> published_at </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">DESC</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> id </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">DESC</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">LIMIT</span><span class="token plain"> </span><span class="token number">20</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">OFFSET</span><span class="token plain"> </span><span class="token number">0</span><br></span></code></pre></div></div>
|
||
<p><strong>Response pagination metadata:</strong></p>
|
||
<div class="language-json codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-json codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#F8F8F2"><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token property">"data"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token plain">...</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token property">"meta"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token property">"page"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token number">1</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token property">"per_page"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token number">20</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token property">"total"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token number">142</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token property">"total_pages"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token number">8</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><br></span></code></pre></div></div>
|
||
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="input-constraints">Input Constraints<a href="#input-constraints" class="hash-link" aria-label="Direct link to Input Constraints" title="Direct link to Input Constraints" translate="no"></a></h3>
|
||
<p>Size limits to prevent oversized uploads:</p>
|
||
<table><thead><tr><th>Field</th><th>Max Size</th><th>Notes</th></tr></thead><tbody><tr><td><code>config.yaml</code></td><td>64 KB</td><td>Tool definition</td></tr><tr><td><code>README.md</code></td><td>256 KB</td><td>Documentation</td></tr><tr><td>Request body</td><td>512 KB</td><td>Total POST payload</td></tr><tr><td>Tool name</td><td>64 chars</td><td>Alphanumeric + hyphen</td></tr><tr><td>Description</td><td>500 chars</td><td>Short summary</td></tr><tr><td>Tag</td><td>32 chars</td><td>Individual tag</td></tr><tr><td>Tags array</td><td>10 items</td><td>Maximum tags per tool</td></tr></tbody></table>
|
||
<p><strong>Validation errors:</strong></p>
|
||
<div class="language-json codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-json codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#F8F8F2"><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token property">"error"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token property">"code"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"PAYLOAD_TOO_LARGE"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token property">"message"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"config.yaml exceeds 64KB limit"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token property">"details"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token property">"field"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"config"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token property">"size"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token number">72000</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token property">"limit"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token number">65536</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><br></span></code></pre></div></div>
|
||
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="sort-fields-and-indexes">Sort Fields and Indexes<a href="#sort-fields-and-indexes" class="hash-link" aria-label="Direct link to Sort Fields and Indexes" title="Direct link to Sort Fields and Indexes" translate="no"></a></h3>
|
||
<p><strong>Allowed sort fields:</strong></p>
|
||
<table><thead><tr><th>Endpoint</th><th>Allowed <code>sort</code> values</th></tr></thead><tbody><tr><td><code>GET /tools</code></td><td><code>downloads</code>, <code>published_at</code>, <code>name</code></td></tr><tr><td><code>GET /tools/search</code></td><td><code>relevance</code>, <code>downloads</code>, <code>published_at</code></td></tr><tr><td><code>GET /categories</code></td><td><code>name</code>, <code>tool_count</code></td></tr></tbody></table>
|
||
<p>Invalid sort values return 400:</p>
|
||
<div class="language-json codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-json codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#F8F8F2"><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token property">"error"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token property">"code"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"INVALID_SORT"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token property">"message"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"Unknown sort field 'foo'. Allowed: downloads, published_at, name"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><br></span></code></pre></div></div>
|
||
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="tags-endpoint">Tags Endpoint<a href="#tags-endpoint" class="hash-link" aria-label="Direct link to Tags Endpoint" title="Direct link to Tags Endpoint" translate="no"></a></h3>
|
||
<p><strong><code>GET /api/v1/tags</code></strong> - List all tags with usage counts.</p>
|
||
<table><thead><tr><th>Parameter</th><th>Default</th><th>Description</th></tr></thead><tbody><tr><td><code>category</code></td><td>-</td><td>Filter tags to those used in a specific category</td></tr><tr><td><code>limit</code></td><td>100</td><td>Maximum tags to return (max 500)</td></tr></tbody></table>
|
||
<p><strong>Response:</strong></p>
|
||
<div class="language-json codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-json codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#F8F8F2"><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token property">"data"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token property">"name"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"cli"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token property">"count"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token number">45</span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token property">"name"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"ai"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token property">"count"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token number">32</span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token property">"name"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"text"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token property">"count"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token number">28</span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token property">"meta"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token property">"total"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token number">87</span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><br></span></code></pre></div></div>
|
||
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="advanced-search">Advanced Search<a href="#advanced-search" class="hash-link" aria-label="Direct link to Advanced Search" title="Direct link to Advanced Search" translate="no"></a></h3>
|
||
<p><strong><code>GET /api/v1/tools/search</code></strong> supports advanced filtering beyond basic text search.</p>
|
||
<table><thead><tr><th>Parameter</th><th>Type</th><th>Default</th><th>Description</th></tr></thead><tbody><tr><td><code>q</code></td><td>string</td><td>required</td><td>Search query (uses FTS5 full-text search)</td></tr><tr><td><code>category</code></td><td>string</td><td>-</td><td>Filter by single category</td></tr><tr><td><code>categories</code></td><td>string</td><td>-</td><td>Filter by multiple categories (comma-separated, OR logic)</td></tr><tr><td><code>tags</code></td><td>string</td><td>-</td><td>Filter by tags (comma-separated, AND logic)</td></tr><tr><td><code>owner</code></td><td>string</td><td>-</td><td>Filter by publisher/owner</td></tr><tr><td><code>min_downloads</code></td><td>int</td><td>-</td><td>Minimum download count</td></tr><tr><td><code>max_downloads</code></td><td>int</td><td>-</td><td>Maximum download count</td></tr><tr><td><code>published_after</code></td><td>date</td><td>-</td><td>Published after date (ISO 8601: YYYY-MM-DD)</td></tr><tr><td><code>published_before</code></td><td>date</td><td>-</td><td>Published before date (ISO 8601: YYYY-MM-DD)</td></tr><tr><td><code>deprecated</code></td><td>bool</td><td>false</td><td>Include deprecated tools</td></tr><tr><td><code>include_facets</code></td><td>bool</td><td>false</td><td>Include faceted counts in response</td></tr><tr><td><code>sort</code></td><td>string</td><td>relevance</td><td>Sort by: relevance, downloads, published_at, name</td></tr><tr><td><code>page</code></td><td>int</td><td>1</td><td>Page number</td></tr><tr><td><code>per_page</code></td><td>int</td><td>20</td><td>Results per page (max 100)</td></tr></tbody></table>
|
||
<p><strong>Tag filtering (AND logic):</strong> When multiple tags are specified, only tools with ALL tags are returned:</p>
|
||
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#F8F8F2"><span class="token plain">GET /api/v1/tools/search?q=summarize&tags=cli,ai</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"># Returns tools that have BOTH "cli" AND "ai" tags</span><br></span></code></pre></div></div>
|
||
<p><strong>Category filtering (OR logic):</strong> When multiple categories are specified, tools in ANY category are returned:</p>
|
||
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#F8F8F2"><span class="token plain">GET /api/v1/tools/search?q=summarize&categories=text-processing,productivity</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"># Returns tools in "text-processing" OR "productivity" category</span><br></span></code></pre></div></div>
|
||
<p><strong>Faceted response:</strong> When <code>include_facets=true</code>, the response includes counts for filtering:</p>
|
||
<div class="language-json codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-json codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#F8F8F2"><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token property">"data"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token plain">...</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token property">"meta"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token property">"page"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token number">1</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token property">"per_page"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token number">20</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token property">"total"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token number">42</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token property">"total_pages"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token number">3</span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token property">"facets"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token property">"categories"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token property">"name"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"text-processing"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token property">"count"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token number">25</span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token property">"name"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"productivity"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token property">"count"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token number">17</span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token property">"tags"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token property">"name"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"ai"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token property">"count"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token number">30</span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token property">"name"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"cli"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token property">"count"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token number">22</span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token property">"name"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"text"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token property">"count"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token number">18</span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token property">"owners"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token property">"name"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"official"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token property">"count"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token number">15</span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token property">"name"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"rob"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token property">"count"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token number">10</span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><br></span></code></pre></div></div>
|
||
<p><strong>Database indexes:</strong></p>
|
||
<div class="language-sql codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-sql codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#F8F8F2"><span class="token comment" style="color:rgb(98, 114, 164)">-- Frequent query patterns</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">CREATE</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">INDEX</span><span class="token plain"> idx_tools_owner_name </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">ON</span><span class="token plain"> tools</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">owner</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> name</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">CREATE</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">INDEX</span><span class="token plain"> idx_tools_owner </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">ON</span><span class="token plain"> tools</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">owner</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"> </span><span class="token comment" style="color:rgb(98, 114, 164)">-- For owner filtering</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">CREATE</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">INDEX</span><span class="token plain"> idx_tools_category </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">ON</span><span class="token plain"> tools</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">category</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">CREATE</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">INDEX</span><span class="token plain"> idx_tools_published_at </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">ON</span><span class="token plain"> tools</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">published_at </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">DESC</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">CREATE</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">INDEX</span><span class="token plain"> idx_tools_downloads </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">ON</span><span class="token plain"> tools</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">downloads </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">DESC</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">CREATE</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">INDEX</span><span class="token plain"> idx_tools_owner_name_version </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">ON</span><span class="token plain"> tools</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">owner</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> name</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> version</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token comment" style="color:rgb(98, 114, 164)">-- For pagination stability</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">CREATE</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">INDEX</span><span class="token plain"> idx_tools_sort_stable </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">ON</span><span class="token plain"> tools</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">downloads </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">DESC</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> published_at </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">DESC</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> id </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">DESC</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token comment" style="color:rgb(98, 114, 164)">-- Publisher lookups</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">CREATE</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">INDEX</span><span class="token plain"> idx_publishers_slug </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">ON</span><span class="token plain"> publishers</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">slug</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">CREATE</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">INDEX</span><span class="token plain"> idx_publishers_email </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">ON</span><span class="token plain"> publishers</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">email</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token comment" style="color:rgb(98, 114, 164)">-- Token lookups</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">CREATE</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">INDEX</span><span class="token plain"> idx_api_tokens_hash </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">ON</span><span class="token plain"> api_tokens</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">token_hash</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">CREATE</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">INDEX</span><span class="token plain"> idx_api_tokens_publisher </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">ON</span><span class="token plain"> api_tokens</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">publisher_id</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><br></span></code></pre></div></div>
|
||
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="api-version-compatibility">API Version Compatibility<a href="#api-version-compatibility" class="hash-link" aria-label="Direct link to API Version Compatibility" title="Direct link to API Version Compatibility" translate="no"></a></h3>
|
||
<p><strong>Forward compatibility:</strong> Clients should ignore unknown fields in API responses:</p>
|
||
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#F8F8F2"><span class="token comment" style="color:rgb(98, 114, 164)"># Good: ignore unknown fields</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">tool </span><span class="token operator">=</span><span class="token plain"> response</span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token string" style="color:rgb(255, 121, 198)">'data'</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">name </span><span class="token operator">=</span><span class="token plain"> tool</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">get</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">'name'</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token comment" style="color:rgb(98, 114, 164)"># Don't fail if 'new_field' exists but client doesn't know about it</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token comment" style="color:rgb(98, 114, 164)"># Bad: strict parsing that fails on unknown fields</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">tool </span><span class="token operator">=</span><span class="token plain"> ToolSchema</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">parse</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">response</span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token string" style="color:rgb(255, 121, 198)">'data'</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token comment" style="color:rgb(98, 114, 164)"># May fail on new fields</span><br></span></code></pre></div></div>
|
||
<p><strong>Backward compatibility:</strong> The API will:</p>
|
||
<ul>
|
||
<li class="">Never remove fields in a version (only deprecate)</li>
|
||
<li class="">Never change field types</li>
|
||
<li class="">Add new optional fields without version bump</li>
|
||
<li class="">Use new version (<code>/api/v2</code>) for breaking changes</li>
|
||
</ul>
|
||
<p><strong>Deprecation process:</strong></p>
|
||
<ol>
|
||
<li class="">Add <code>X-Deprecated-Field: old_field</code> header</li>
|
||
<li class="">Document in changelog</li>
|
||
<li class="">Remove after 6 months minimum</li>
|
||
<li class="">Major version bump if widely used</li>
|
||
</ol>
|
||
<p><strong>Client version header:</strong></p>
|
||
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#F8F8F2"><span class="token plain">X-CmdForge-Client: cli/1.2.0</span><br></span></code></pre></div></div>
|
||
<p>Helps server track client versions for deprecation decisions.</p>
|
||
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="source-of-truth">Source of Truth<a href="#source-of-truth" class="hash-link" aria-label="Direct link to Source of Truth" title="Direct link to Source of Truth" translate="no"></a></h2>
|
||
<ul>
|
||
<li class="">Gitea registry repo is the source of truth.</li>
|
||
<li class="">API syncs repo content into SQLite for fast queries, stats, and FTS5 search.</li>
|
||
<li class=""><code>index.json</code> remains useful for offline CLI search and as a fallback.</li>
|
||
</ul>
|
||
<p>If the cache is stale, the API can fall back to repo reads; a warning header may be emitted.</p>
|
||
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="namespacing-and-paths">Namespacing and Paths<a href="#namespacing-and-paths" class="hash-link" aria-label="Direct link to Namespacing and Paths" title="Direct link to Namespacing and Paths" translate="no"></a></h2>
|
||
<p>Support owner/name from day one:</p>
|
||
<ul>
|
||
<li class="">Registry path: <code>tools/{owner}/{name}/config.yaml</code></li>
|
||
<li class="">API URL: <code>/tools/{owner}/{name}</code></li>
|
||
<li class="">Install: <code>cmdforge registry install rob/summarize</code></li>
|
||
<li class="">Shorthand: <code>cmdforge registry install summarize</code> resolves to the official namespace.</li>
|
||
</ul>
|
||
<p>PR branches: <code>submit/{owner}/{name}/{version}</code>.</p>
|
||
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="namespace-identity">Namespace Identity<a href="#namespace-identity" class="hash-link" aria-label="Direct link to Namespace Identity" title="Direct link to Namespace Identity" translate="no"></a></h3>
|
||
<p>The <code>owner</code> is an <strong>immutable slug</strong>, not the display name:</p>
|
||
<div class="language-sql codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-sql codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#F8F8F2"><span class="token comment" style="color:rgb(98, 114, 164)">-- In publishers table</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">slug </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">TEXT</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">UNIQUE</span><span class="token plain"> </span><span class="token operator">NOT</span><span class="token plain"> </span><span class="token boolean">NULL</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token comment" style="color:rgb(98, 114, 164)">-- immutable: "rob", "alice-dev"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">display_name </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">TEXT</span><span class="token plain"> </span><span class="token operator">NOT</span><span class="token plain"> </span><span class="token boolean">NULL</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token comment" style="color:rgb(98, 114, 164)">-- mutable: "Rob", "Alice Developer"</span><br></span></code></pre></div></div>
|
||
<p><strong>Slug rules:</strong></p>
|
||
<ul>
|
||
<li class="">Lowercase alphanumeric + hyphens only: <code>^[a-z0-9][a-z0-9-]*[a-z0-9]$</code></li>
|
||
<li class="">2-39 characters</li>
|
||
<li class="">Cannot start/end with hyphen</li>
|
||
<li class="">Set once at registration, cannot be changed</li>
|
||
<li class="">Reserved slugs: <code>official</code>, <code>admin</code>, <code>system</code>, <code>api</code>, <code>registry</code></li>
|
||
</ul>
|
||
<p><strong>Rename policy:</strong></p>
|
||
<ul>
|
||
<li class=""><code>display_name</code> can be changed anytime via dashboard</li>
|
||
<li class=""><code>slug</code> (owner) is permanent to preserve URLs and tool references</li>
|
||
<li class="">If a publisher absolutely must change slug (legal reasons, etc.):<!-- -->
|
||
<ol>
|
||
<li class="">Create new account with new slug</li>
|
||
<li class="">Republish tools under new namespace</li>
|
||
<li class="">Mark old tools as deprecated with <code>replacement</code> pointing to new namespace</li>
|
||
<li class="">Old namespace remains reserved (cannot be reused by others)</li>
|
||
</ol>
|
||
</li>
|
||
</ul>
|
||
<p><strong>Why immutable:</strong></p>
|
||
<ul>
|
||
<li class=""><code>rob/summarize@1.0.0</code> must always resolve to the same tool</li>
|
||
<li class="">Prevents namespace hijacking after rename</li>
|
||
<li class="">Simplifies caching and CDN strategies</li>
|
||
</ul>
|
||
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="tool-format-registry--local">Tool Format (Registry == Local)<a href="#tool-format-registry--local" class="hash-link" aria-label="Direct link to Tool Format (Registry == Local)" title="Direct link to Tool Format (Registry == Local)" translate="no"></a></h2>
|
||
<p>Registry tool folders mirror local tools:</p>
|
||
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#F8F8F2"><span class="token plain">tools/</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> rob/</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> summarize/</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> config.yaml</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> README.md</span><br></span></code></pre></div></div>
|
||
<p>Tool files match the existing CmdForge format. Registry-specific metadata is kept under <code>registry:</code>. Deprecation is tool-defined and top-level:</p>
|
||
<div class="language-yaml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-yaml codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#F8F8F2"><span class="token key atrule">name</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> summarize</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token key atrule">version</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"1.2.0"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token key atrule">deprecated</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> </span><span class="token boolean important">true</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token key atrule">deprecated_message</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"Security issue. Use v1.2.1"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token key atrule">replacement</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"rob/summarize@1.2.1"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token key atrule">registry</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token key atrule">published_at</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"2025-01-15T10:30:00Z"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token key atrule">downloads</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> </span><span class="token number">142</span><br></span></code></pre></div></div>
|
||
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="attribution-and-source-fields">Attribution and Source Fields<a href="#attribution-and-source-fields" class="hash-link" aria-label="Direct link to Attribution and Source Fields" title="Direct link to Attribution and Source Fields" translate="no"></a></h3>
|
||
<p>Tools can include optional source attribution for provenance and licensing:</p>
|
||
<div class="language-yaml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-yaml codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#F8F8F2"><span class="token key atrule">name</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> summarize</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token key atrule">version</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"1.2.0"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token key atrule">description</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"Summarize text using AI"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token comment" style="color:rgb(98, 114, 164)"># Attribution fields (optional)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token key atrule">source</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token key atrule">type</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> original </span><span class="token comment" style="color:rgb(98, 114, 164)"># original, adapted, or imported</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token key atrule">license</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> MIT </span><span class="token comment" style="color:rgb(98, 114, 164)"># SPDX license identifier</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token key atrule">url</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> https</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain">//example.com/tool</span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain">repo</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token key atrule">author</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"Original Author"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token comment" style="color:rgb(98, 114, 164)"># For adapted/imported tools</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token key atrule">original_tool</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> other/original</span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain">summarize@1.0.0</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token key atrule">changes</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"Added French language support"</span><br></span></code></pre></div></div>
|
||
<p><strong>Source types:</strong></p>
|
||
<table><thead><tr><th>Type</th><th>Description</th></tr></thead><tbody><tr><td><code>original</code></td><td>Created from scratch by the publisher</td></tr><tr><td><code>adapted</code></td><td>Based on another tool with modifications</td></tr><tr><td><code>imported</code></td><td>Direct import of external tool (e.g., from npm/pip)</td></tr></tbody></table>
|
||
<p><strong>License field:</strong></p>
|
||
<ul>
|
||
<li class="">Uses SPDX identifiers: <code>MIT</code>, <code>Apache-2.0</code>, <code>GPL-3.0</code>, etc.</li>
|
||
<li class="">Required for registry publication</li>
|
||
<li class="">Validated against SPDX license list</li>
|
||
</ul>
|
||
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="collections">Collections<a href="#collections" class="hash-link" aria-label="Direct link to Collections" title="Direct link to Collections" translate="no"></a></h2>
|
||
<p>Collections are curated groups of tools that can be installed together with a single command.</p>
|
||
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="collection-structure">Collection Structure<a href="#collection-structure" class="hash-link" aria-label="Direct link to Collection Structure" title="Direct link to Collection Structure" translate="no"></a></h3>
|
||
<p>Collections are defined in <code>collections/{name}.yaml</code>:</p>
|
||
<div class="language-yaml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-yaml codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#F8F8F2"><span class="token key atrule">name</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> text</span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain">processing</span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain">essentials</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token key atrule">display_name</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"Text Processing Essentials"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token key atrule">description</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"Essential tools for text processing and manipulation"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token key atrule">icon</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"📝"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token key atrule">tools</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain"> official/summarize</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain"> official/translate</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain"> official/fix</span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain">grammar</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain"> official/simplify</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain"> official/tone</span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain">shift</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token comment" style="color:rgb(98, 114, 164)"># Optional</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token key atrule">curator</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> official</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token key atrule">tags</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token string" style="color:rgb(255, 121, 198)">"text"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"nlp"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"writing"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><br></span></code></pre></div></div>
|
||
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="collections-api">Collections API<a href="#collections-api" class="hash-link" aria-label="Direct link to Collections API" title="Direct link to Collections API" translate="no"></a></h3>
|
||
<p><strong>List all collections:</strong></p>
|
||
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#F8F8F2"><span class="token plain">GET /api/v1/collections</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">Response:</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">{</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> "data": [</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> {</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> "name": "text-processing-essentials",</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> "display_name": "Text Processing Essentials",</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> "description": "Essential tools for text processing...",</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> "icon": "📝",</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> "tool_count": 5,</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> "curator": "official"</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> }</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> ],</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> "meta": {"page": 1, "per_page": 20, "total": 8}</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">}</span><br></span></code></pre></div></div>
|
||
<p><strong>Get collection details:</strong></p>
|
||
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#F8F8F2"><span class="token plain">GET /api/v1/collections/{name}</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">Response:</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">{</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> "data": {</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> "name": "text-processing-essentials",</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> "display_name": "Text Processing Essentials",</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> "description": "Essential tools for text processing...",</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> "icon": "📝",</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> "curator": "official",</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> "tools": [</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> {"owner": "official", "name": "summarize", "version": "1.2.0", ...},</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> {"owner": "official", "name": "translate", "version": "2.1.0", ...}</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> ]</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> }</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">}</span><br></span></code></pre></div></div>
|
||
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="cli-commands">CLI Commands<a href="#cli-commands" class="hash-link" aria-label="Direct link to CLI Commands" title="Direct link to CLI Commands" translate="no"></a></h3>
|
||
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#F8F8F2"><span class="token plain"># List available collections</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">cmdforge collections list</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"># List in JSON format</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">cmdforge collections list --json</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"># View collection details</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">cmdforge collections info text-processing-essentials</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"># View in JSON format</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">cmdforge collections info text-processing-essentials --json</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"># Install all tools in a collection</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">cmdforge collections install text-processing-essentials</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"># Install with pinned versions from collection</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">cmdforge collections install text-processing-essentials --pinned</span><br></span></code></pre></div></div>
|
||
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="admin-collections-api">Admin Collections API<a href="#admin-collections-api" class="hash-link" aria-label="Direct link to Admin Collections API" title="Direct link to Admin Collections API" translate="no"></a></h3>
|
||
<p>Collections are managed via the admin dashboard at <code>/dashboard/admin/collections</code>:</p>
|
||
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#F8F8F2"><span class="token plain">GET /api/v1/admin/collections # List all collections (admin)</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">POST /api/v1/admin/collections # Create collection (admin)</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">PUT /api/v1/admin/collections/:name # Update collection (admin)</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">DELETE /api/v1/admin/collections/:name # Delete collection (admin)</span><br></span></code></pre></div></div>
|
||
<p><strong>Schema compatibility note:</strong> The current CmdForge config parser may reject unknown top-level keys like <code>deprecated</code>, <code>replacement</code>, and <code>registry</code>. Before implementing registry features:</p>
|
||
<ol>
|
||
<li class="">Update the YAML parser to ignore unknown keys (permissive mode)</li>
|
||
<li class="">Or explicitly define these fields in the Tool dataclass with defaults</li>
|
||
<li class="">Validate registry-specific fields only when publishing, not when running locally</li>
|
||
</ol>
|
||
<p>This ensures local tools continue to work even if they don't have registry fields.</p>
|
||
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="versioning-and-immutability">Versioning and Immutability<a href="#versioning-and-immutability" class="hash-link" aria-label="Direct link to Versioning and Immutability" title="Direct link to Versioning and Immutability" translate="no"></a></h2>
|
||
<ul>
|
||
<li class="">Unique key: <code>owner/name + version</code>.</li>
|
||
<li class="">Published versions are immutable.</li>
|
||
<li class="">Deprecation uses <code>deprecated</code>, <code>deprecated_message</code>, and <code>replacement</code>.</li>
|
||
<li class="">CLI warns on install if a version is deprecated.</li>
|
||
</ul>
|
||
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="yank-policy">Yank Policy<a href="#yank-policy" class="hash-link" aria-label="Direct link to Yank Policy" title="Direct link to Yank Policy" translate="no"></a></h3>
|
||
<p>Yanking allows removing a version from resolution without deleting it (for auditability):</p>
|
||
<div class="language-yaml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-yaml codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#F8F8F2"><span class="token comment" style="color:rgb(98, 114, 164)"># In tool config</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token key atrule">yanked</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> </span><span class="token boolean important">true</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token key atrule">yanked_reason</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"Critical security vulnerability CVE-2025-1234"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token key atrule">yanked_at</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"2025-01-20T15:00:00Z"</span><br></span></code></pre></div></div>
|
||
<p><strong>Yanked version behavior:</strong></p>
|
||
<table><thead><tr><th>Operation</th><th>Behavior</th></tr></thead><tbody><tr><td><code>install foo@1.0.0</code> (exact)</td><td>Warns but allows install</td></tr><tr><td><code>install foo@^1.0.0</code> (constraint)</td><td>Excludes yanked, resolves to next valid</td></tr><tr><td><code>search</code> / <code>browse</code></td><td>Hidden by default, shown with <code>--include-yanked</code></td></tr><tr><td>Direct URL access</td><td>Returns tool with <code>yanked: true</code> in response</td></tr><tr><td>Already installed</td><td>Continues to work, no forced removal</td></tr></tbody></table>
|
||
<p><strong>Database schema addition:</strong></p>
|
||
<div class="language-sql codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-sql codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#F8F8F2"><span class="token comment" style="color:rgb(98, 114, 164)">-- Add to tools table</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">yanked </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">BOOLEAN</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">DEFAULT</span><span class="token plain"> </span><span class="token boolean">FALSE</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">yanked_reason </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">TEXT</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">yanked_at </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">TIMESTAMP</span><br></span></code></pre></div></div>
|
||
<p><strong>Yank vs Delete:</strong></p>
|
||
<ul>
|
||
<li class=""><strong>Yank</strong>: Version remains in DB, excluded from resolution, auditable</li>
|
||
<li class=""><strong>Delete</strong>: Reserved for DMCA/legal, requires admin action, leaves tombstone record</li>
|
||
</ul>
|
||
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="version-format">Version Format<a href="#version-format" class="hash-link" aria-label="Direct link to Version Format" title="Direct link to Version Format" translate="no"></a></h3>
|
||
<p>Tools use semantic versioning (semver):</p>
|
||
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#F8F8F2"><span class="token plain">MAJOR.MINOR.PATCH[-PRERELEASE][+BUILD]</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">Examples:</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> 1.0.0 # stable release</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> 1.2.3 # stable release</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> 2.0.0-alpha.1 # prerelease</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> 2.0.0-beta.2 # prerelease</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> 2.0.0-rc.1 # release candidate</span><br></span></code></pre></div></div>
|
||
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="version-constraints">Version Constraints<a href="#version-constraints" class="hash-link" aria-label="Direct link to Version Constraints" title="Direct link to Version Constraints" translate="no"></a></h3>
|
||
<p>Manifest files support these constraint formats:</p>
|
||
<table><thead><tr><th>Constraint</th><th>Meaning</th><th>Example Match</th></tr></thead><tbody><tr><td><code>1.2.3</code></td><td>Exact version</td><td><code>1.2.3</code> only</td></tr><tr><td><code>>=1.2.0</code></td><td>Minimum version</td><td><code>1.2.0</code>, <code>1.3.0</code>, <code>2.0.0</code></td></tr><tr><td><code><2.0.0</code></td><td>Below version</td><td><code>1.9.9</code>, <code>1.0.0</code></td></tr><tr><td><code>>=1.0.0,<2.0.0</code></td><td>Range</td><td><code>1.0.0</code> to <code>1.9.9</code></td></tr><tr><td><code>^1.2.3</code></td><td>Compatible (same major)</td><td><code>1.2.3</code> to <code>1.9.9</code></td></tr><tr><td><code>~1.2.3</code></td><td>Approximately (same minor)</td><td><code>1.2.3</code> to <code>1.2.9</code></td></tr><tr><td><code>*</code></td><td>Any version</td><td>latest stable</td></tr></tbody></table>
|
||
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="version-resolution-rules">Version Resolution Rules<a href="#version-resolution-rules" class="hash-link" aria-label="Direct link to Version Resolution Rules" title="Direct link to Version Resolution Rules" translate="no"></a></h3>
|
||
<p>When resolving a version constraint:</p>
|
||
<ol>
|
||
<li class=""><strong>Filter</strong>: Get all versions matching the constraint</li>
|
||
<li class=""><strong>Exclude prereleases</strong>: Unless constraint explicitly includes them (e.g., <code>>=2.0.0-alpha.1</code>)</li>
|
||
<li class=""><strong>Sort</strong>: By semver precedence (descending)</li>
|
||
<li class=""><strong>Select</strong>: Highest matching version</li>
|
||
</ol>
|
||
<p><strong>Tie-breakers:</strong></p>
|
||
<ul>
|
||
<li class="">Stable versions preferred over prereleases</li>
|
||
<li class="">Later publish date wins if versions are equal (shouldn't happen with immutability)</li>
|
||
</ul>
|
||
<p><strong>Unsatisfiable constraints:</strong></p>
|
||
<div class="language-json codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-json codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#F8F8F2"><span class="token comment" style="color:rgb(98, 114, 164)">// API Response: 404</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token property">"error"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token property">"code"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"VERSION_NOT_FOUND"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token property">"message"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"No version of 'rob/summarize' satisfies constraint '>=5.0.0'"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token property">"details"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token property">"tool"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"rob/summarize"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token property">"constraint"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">">=5.0.0"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token property">"available_versions"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token string" style="color:rgb(255, 121, 198)">"1.0.0"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"1.1.0"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"1.2.0"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token property">"latest_stable"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"1.2.0"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><br></span></code></pre></div></div>
|
||
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="prerelease-handling">Prerelease Handling<a href="#prerelease-handling" class="hash-link" aria-label="Direct link to Prerelease Handling" title="Direct link to Prerelease Handling" translate="no"></a></h3>
|
||
<ul>
|
||
<li class="">Prereleases are <strong>not</strong> returned for <code>*</code> or range constraints by default</li>
|
||
<li class="">To install prerelease: <code>cmdforge registry install rob/summarize@2.0.0-beta.1</code></li>
|
||
<li class="">To allow prereleases in manifest: <code>version: ">=2.0.0-0"</code> (the <code>-0</code> suffix includes prereleases)</li>
|
||
</ul>
|
||
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="download-endpoint-version-selection">Download Endpoint Version Selection<a href="#download-endpoint-version-selection" class="hash-link" aria-label="Direct link to Download Endpoint Version Selection" title="Direct link to Download Endpoint Version Selection" translate="no"></a></h3>
|
||
<p>The <code>/api/v1/tools/{owner}/{name}/download</code> endpoint accepts version parameters:</p>
|
||
<table><thead><tr><th>Parameter</th><th>Behavior</th><th>Example</th></tr></thead><tbody><tr><td>(none)</td><td>Returns latest stable version</td><td><code>/download</code> → <code>1.2.0</code></td></tr><tr><td><code>version=1.2.0</code></td><td>Exact version (must exist)</td><td><code>/download?version=1.2.0</code></td></tr><tr><td><code>version=^1.0.0</code></td><td>Server resolves constraint</td><td><code>/download?version=^1.0.0</code> → <code>1.2.0</code></td></tr><tr><td><code>version=latest</code></td><td>Alias for latest stable</td><td><code>/download?version=latest</code></td></tr></tbody></table>
|
||
<p><strong>Server-side resolution:</strong> The API server resolves version constraints, not the client. This ensures consistent resolution and allows the server to apply policies (e.g., exclude yanked versions).</p>
|
||
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#F8F8F2"><span class="token plain">GET /api/v1/tools/rob/summarize/download?version=^1.0.0&install=true</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">Response (200):</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">{</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> "data": {</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> "owner": "rob",</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> "name": "summarize",</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> "resolved_version": "1.2.0",</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> "config": "... YAML content ..."</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> },</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> "meta": {</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> "constraint": "^1.0.0",</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> "available_versions": ["1.0.0", "1.1.0", "1.2.0"]</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> }</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">}</span><br></span></code></pre></div></div>
|
||
<p><strong>Invalid/unsatisfiable constraint:</strong></p>
|
||
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#F8F8F2"><span class="token plain">GET /api/v1/tools/rob/summarize/download?version=^5.0.0</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">Response (404):</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">{</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> "error": {</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> "code": "CONSTRAINT_UNSATISFIABLE",</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> "message": "No version matches constraint '^5.0.0'",</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> "details": {</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> "constraint": "^5.0.0",</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> "latest_stable": "1.2.0",</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> "available_versions": ["1.0.0", "1.1.0", "1.2.0"]</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> }</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> }</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">}</span><br></span></code></pre></div></div>
|
||
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="tool-resolution-order">Tool Resolution Order<a href="#tool-resolution-order" class="hash-link" aria-label="Direct link to Tool Resolution Order" title="Direct link to Tool Resolution Order" translate="no"></a></h2>
|
||
<p>When a tool is invoked, the CLI searches in this order:</p>
|
||
<ol>
|
||
<li class=""><strong>Local project</strong>: <code>./.cmdforge/<owner>/<name>/config.yaml</code> (or <code>./.cmdforge/<name>/</code> for unnamespaced)</li>
|
||
<li class=""><strong>Global user</strong>: <code>~/.cmdforge/<owner>/<name>/config.yaml</code></li>
|
||
<li class=""><strong>Registry</strong>: Fetch from API, install to global, then run</li>
|
||
<li class=""><strong>Error</strong>: <code>Tool '<toolname>' not found</code></li>
|
||
</ol>
|
||
<p>Step 3 only occurs if <code>auto_fetch_from_registry: true</code> in config (default: true).</p>
|
||
<p><strong>Path convention:</strong> Use <code>.cmdforge/</code> (with leading dot) for both local and global to maintain consistency.</p>
|
||
<p>Resolution also respects namespacing:</p>
|
||
<ul>
|
||
<li class=""><code>summarize</code> → searches for any tool named <code>summarize</code>, prefers <code>official/summarize</code> if exists</li>
|
||
<li class=""><code>rob/summarize</code> → searches for exactly <code>rob/summarize</code></li>
|
||
</ul>
|
||
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="official-namespace">Official Namespace<a href="#official-namespace" class="hash-link" aria-label="Direct link to Official Namespace" title="Direct link to Official Namespace" translate="no"></a></h3>
|
||
<p>The slug <code>official</code> is reserved for curated, high-quality tools maintained by the registry administrators.</p>
|
||
<ul>
|
||
<li class="">Shorthand <code>summarize</code> resolves to <code>official/summarize</code> if it exists</li>
|
||
<li class="">If no <code>official/summarize</code>, falls back to most-downloaded tool named <code>summarize</code></li>
|
||
<li class="">To avoid ambiguity, always use full <code>owner/name</code> in manifests</li>
|
||
</ul>
|
||
<p>Reserved slugs that cannot be registered: <code>official</code>, <code>admin</code>, <code>system</code>, <code>api</code>, <code>registry</code>, <code>cmdforge</code></p>
|
||
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="auto-fetch-behavior">Auto-Fetch Behavior<a href="#auto-fetch-behavior" class="hash-link" aria-label="Direct link to Auto-Fetch Behavior" title="Direct link to Auto-Fetch Behavior" translate="no"></a></h2>
|
||
<p>When enabled (<code>auto_fetch_from_registry: true</code>), missing tools are automatically fetched:</p>
|
||
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#F8F8F2"><span class="token plain">$ summarize < file.txt</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"># Tool 'summarize' not found locally.</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"># Fetching from registry...</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"># Installed: official/summarize@1.2.0</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"># Running...</span><br></span></code></pre></div></div>
|
||
<p>Behavior details:</p>
|
||
<ul>
|
||
<li class="">Fetches latest stable version unless pinned in <code>cmdforge.yaml</code></li>
|
||
<li class="">Installs to <code>~/.cmdforge/<owner>/<name>/</code></li>
|
||
<li class="">Generates wrapper script in <code>~/.local/bin/</code></li>
|
||
<li class="">Subsequent runs use local copy (no re-fetch)</li>
|
||
</ul>
|
||
<p>To disable (require explicit install):</p>
|
||
<div class="language-yaml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-yaml codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#F8F8F2"><span class="token comment" style="color:rgb(98, 114, 164)"># ~/.cmdforge/config.yaml</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token key atrule">auto_fetch_from_registry</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> </span><span class="token boolean important">false</span><br></span></code></pre></div></div>
|
||
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="wrapper-script-collisions">Wrapper Script Collisions<a href="#wrapper-script-collisions" class="hash-link" aria-label="Direct link to Wrapper Script Collisions" title="Direct link to Wrapper Script Collisions" translate="no"></a></h3>
|
||
<p>When two tools from different owners have the same name:</p>
|
||
<table><thead><tr><th>Scenario</th><th>Behavior</th></tr></thead><tbody><tr><td>Install <code>official/summarize</code></td><td>Creates wrapper <code>~/.local/bin/summarize</code></td></tr><tr><td>Install <code>rob/summarize</code> (collision)</td><td>Creates wrapper <code>~/.local/bin/rob-summarize</code></td></tr><tr><td>Uninstall <code>official/summarize</code></td><td>Removes <code>summarize</code> wrapper, promotes <code>rob-summarize</code> → <code>summarize</code> if desired</td></tr></tbody></table>
|
||
<p>The first-installed tool with a given name gets the short wrapper. Subsequent tools use <code>owner-name</code> format.</p>
|
||
<p>To invoke a specific owner's tool:</p>
|
||
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#F8F8F2"><span class="token plain"># Short form (whichever was installed first)</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">summarize < file.txt</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"># Explicit owner form (always works)</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">rob-summarize < file.txt</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"># Or via cmdforge run</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">cmdforge run rob/summarize < file.txt</span><br></span></code></pre></div></div>
|
||
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="project-manifest-cmdforgeyaml">Project Manifest (cmdforge.yaml)<a href="#project-manifest-cmdforgeyaml" class="hash-link" aria-label="Direct link to Project Manifest (cmdforge.yaml)" title="Direct link to Project Manifest (cmdforge.yaml)" translate="no"></a></h2>
|
||
<p>Defines tool dependencies with optional runtime overrides:</p>
|
||
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#F8F8F2"><span class="token plain">name: my-ai-project</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">version: "1.0.0"</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">dependencies:</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> - name: rob/summarize</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> version: ">=1.0.0"</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">overrides:</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> rob/summarize:</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> provider: ollama</span><br></span></code></pre></div></div>
|
||
<p>Overrides are applied at runtime and do not mutate installed tool configs.</p>
|
||
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="cli-config-and-tokens">CLI Config and Tokens<a href="#cli-config-and-tokens" class="hash-link" aria-label="Direct link to CLI Config and Tokens" title="Direct link to CLI Config and Tokens" translate="no"></a></h2>
|
||
<p>Global config lives in <code>~/.cmdforge/config.yaml</code>:</p>
|
||
<div class="language-yaml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-yaml codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#F8F8F2"><span class="token key atrule">registry</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token key atrule">url</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> https</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain">//cmdforge.brrd.tech/api/v1 </span><span class="token comment" style="color:rgb(98, 114, 164)"># Must match canonical base path</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token key atrule">token</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"reg_xxxxxxxxxxxx"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token key atrule">client_id</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"anon_abc123def456"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token key atrule">auto_fetch_from_registry</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> </span><span class="token boolean important">true</span><br></span></code></pre></div></div>
|
||
<p><code>client_id</code> is generated locally and used for anonymous install dedupe.</p>
|
||
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="publishing-and-auth">Publishing and Auth<a href="#publishing-and-auth" class="hash-link" aria-label="Direct link to Publishing and Auth" title="Direct link to Publishing and Auth" translate="no"></a></h2>
|
||
<p>Publishing uses registry accounts, not Gitea accounts:</p>
|
||
<ul>
|
||
<li class="">Public endpoints require no auth.</li>
|
||
<li class=""><code>POST /tools</code> requires a registry token.</li>
|
||
<li class="">The API server uses a private Gitea service account to open PRs.</li>
|
||
</ul>
|
||
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="publish-idempotency-and-edge-cases">Publish Idempotency and Edge Cases<a href="#publish-idempotency-and-edge-cases" class="hash-link" aria-label="Direct link to Publish Idempotency and Edge Cases" title="Direct link to Publish Idempotency and Edge Cases" translate="no"></a></h3>
|
||
<p><strong>Idempotency key:</strong> <code>owner/name@version</code></p>
|
||
<table><thead><tr><th>Scenario</th><th>API Response</th><th>HTTP Code</th></tr></thead><tbody><tr><td>New version, no PR exists</td><td>Create PR, return URL</td><td><code>201 Created</code></td></tr><tr><td>PR already exists (pending)</td><td>Return existing PR URL</td><td><code>200 OK</code></td></tr><tr><td>Version already published</td><td>Error: version exists</td><td><code>409 Conflict</code></td></tr><tr><td>PR was closed without merge</td><td>Allow new PR</td><td><code>201 Created</code></td></tr><tr><td>PR was merged, then tool deleted</td><td>Error: version exists (tombstone)</td><td><code>409 Conflict</code></td></tr></tbody></table>
|
||
<p><strong>Version immutability enforcement:</strong></p>
|
||
<div class="language-json codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-json codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#F8F8F2"><span class="token comment" style="color:rgb(98, 114, 164)">// Attempt to publish existing version</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token comment" style="color:rgb(98, 114, 164)">// Response: 409 Conflict</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token property">"error"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token property">"code"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"VERSION_EXISTS"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token property">"message"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"Version 1.2.0 of 'rob/summarize' already exists and cannot be overwritten"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token property">"details"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token property">"published_at"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"2025-01-15T10:30:00Z"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token property">"action"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"Bump version number to publish changes"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><br></span></code></pre></div></div>
|
||
<p><strong>Closed PR handling:</strong></p>
|
||
<ul>
|
||
<li class="">Track PR state in database: <code>pending</code>, <code>merged</code>, <code>closed</code></li>
|
||
<li class="">If PR was closed (rejected/abandoned), allow new submission for same version</li>
|
||
<li class="">If PR was merged, version is immutable forever</li>
|
||
</ul>
|
||
<p><strong>Update flow (new version, not overwrite):</strong></p>
|
||
<ol>
|
||
<li class="">Developer modifies tool locally</li>
|
||
<li class="">Bumps version in <code>config.yaml</code> (e.g., <code>1.2.0</code> → <code>1.3.0</code>)</li>
|
||
<li class="">Runs <code>cmdforge registry publish</code></li>
|
||
<li class="">New PR created for <code>1.3.0</code></li>
|
||
<li class="">Old version <code>1.2.0</code> remains available</li>
|
||
</ol>
|
||
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="publisher-registration">Publisher Registration<a href="#publisher-registration" class="hash-link" aria-label="Direct link to Publisher Registration" title="Direct link to Publisher Registration" translate="no"></a></h2>
|
||
<p>Publishers register on the registry website, not Gitea:</p>
|
||
<p><strong>Registration flow:</strong></p>
|
||
<ol>
|
||
<li class="">User visits <code>https://gitea.brrd.tech/registry/register</code> (or future <code>cmdforge.brrd.tech</code>)</li>
|
||
<li class="">Creates account with email + password + slug</li>
|
||
<li class="">Receives verification email (optional in v1, but track <code>verified</code> status)</li>
|
||
<li class="">Logs into dashboard at <code>/dashboard</code></li>
|
||
<li class="">Generates API token from dashboard</li>
|
||
<li class="">Uses token in CLI for publishing</li>
|
||
</ol>
|
||
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="authentication-security">Authentication Security<a href="#authentication-security" class="hash-link" aria-label="Direct link to Authentication Security" title="Direct link to Authentication Security" translate="no"></a></h3>
|
||
<p><strong>Password hashing:</strong></p>
|
||
<ul>
|
||
<li class="">Algorithm: Argon2id (memory-hard, recommended by OWASP)</li>
|
||
<li class="">Parameters: <code>memory=65536, iterations=3, parallelism=4</code></li>
|
||
<li class="">Library: <code>argon2-cffi</code> for Python</li>
|
||
</ul>
|
||
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#F8F8F2"><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">from</span><span class="token plain"> argon2 </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">import</span><span class="token plain"> PasswordHasher</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">ph </span><span class="token operator">=</span><span class="token plain"> PasswordHasher</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">memory_cost</span><span class="token operator">=</span><span class="token number">65536</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> time_cost</span><span class="token operator">=</span><span class="token number">3</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> parallelism</span><span class="token operator">=</span><span class="token number">4</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token builtin" style="color:rgb(189, 147, 249)">hash</span><span class="token plain"> </span><span class="token operator">=</span><span class="token plain"> ph</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token builtin" style="color:rgb(189, 147, 249)">hash</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">password</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">ph</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">verify</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token builtin" style="color:rgb(189, 147, 249)">hash</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> password</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token comment" style="color:rgb(98, 114, 164)"># raises on mismatch</span><br></span></code></pre></div></div>
|
||
<p><strong>API token format:</strong></p>
|
||
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#F8F8F2"><span class="token plain">reg_<random-32-bytes-base62></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">Example: reg_7kX9mPqR2sT4vW6xY8zA1bC3dE5fG7hJ</span><br></span></code></pre></div></div>
|
||
<ul>
|
||
<li class="">Prefix <code>reg_</code> for easy identification in logs/configs</li>
|
||
<li class="">32 bytes of cryptographically random data</li>
|
||
<li class="">Base62 encoded (alphanumeric, no special chars)</li>
|
||
<li class="">Total length: ~47 characters</li>
|
||
<li class="">Stored as SHA-256 hash in database (never plain text)</li>
|
||
</ul>
|
||
<p><strong>Token lifecycle:</strong></p>
|
||
<table><thead><tr><th>Action</th><th>Behavior</th></tr></thead><tbody><tr><td>Generate</td><td>Create new token, return once, store hash</td></tr><tr><td>List</td><td>Show token name, created date, last used (not the token itself)</td></tr><tr><td>Revoke</td><td>Set <code>revoked_at</code> timestamp, reject future uses</td></tr><tr><td>Rotate</td><td>Generate new token, optionally revoke old</td></tr></tbody></table>
|
||
<p><strong>Rate limits:</strong></p>
|
||
<table><thead><tr><th>Endpoint</th><th>Limit</th><th>Window</th><th>Scope</th><th>Retry-After</th></tr></thead><tbody><tr><td><code>POST /register</code></td><td>5</td><td>1 hour</td><td>IP</td><td>3600</td></tr><tr><td><code>POST /login</code></td><td>10</td><td>15 min</td><td>IP</td><td>900</td></tr><tr><td><code>POST /login</code> (failed)</td><td>5</td><td>15 min</td><td>IP + email</td><td>900</td></tr><tr><td><code>POST /tokens</code></td><td>10</td><td>1 hour</td><td>Token</td><td>3600</td></tr><tr><td><code>POST /tools</code></td><td>20</td><td>1 hour</td><td>Token</td><td>3600</td></tr><tr><td><code>GET /tools/*</code></td><td>100</td><td>1 min</td><td>IP</td><td>60</td></tr><tr><td><code>GET /download</code></td><td>60</td><td>1 min</td><td>IP</td><td>60</td></tr></tbody></table>
|
||
<p><strong>Rate limit response (429):</strong></p>
|
||
<div class="language-json codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-json codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#F8F8F2"><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token property">"error"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token property">"code"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"RATE_LIMITED"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token property">"message"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"Too many requests. Try again in 60 seconds."</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token property">"details"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token property">"limit"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token number">100</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token property">"window"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"1 minute"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token property">"retry_after"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token number">60</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><br></span></code></pre></div></div>
|
||
<p><strong>Headers on rate-limited response:</strong></p>
|
||
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#F8F8F2"><span class="token plain">HTTP/1.1 429 Too Many Requests</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">Retry-After: 60</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">X-RateLimit-Limit: 100</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">X-RateLimit-Remaining: 0</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">X-RateLimit-Reset: 1705766400</span><br></span></code></pre></div></div>
|
||
<p><strong>Scope priority:</strong> For authenticated requests, both IP and token limits apply. The more restrictive limit wins.</p>
|
||
<p><strong>Account lockout:</strong></p>
|
||
<ul>
|
||
<li class="">After 5 failed login attempts: 15-minute lockout for that email</li>
|
||
<li class="">After 10 failed attempts: 1-hour lockout</li>
|
||
<li class="">Lockout clears on successful password reset</li>
|
||
</ul>
|
||
<p><strong>Password reset flow (deferred to v1.1):</strong></p>
|
||
<ol>
|
||
<li class="">User requests reset via email</li>
|
||
<li class="">Server generates time-limited token (1 hour expiry)</li>
|
||
<li class="">Email contains reset link with token</li>
|
||
<li class="">User sets new password</li>
|
||
<li class="">All existing sessions/tokens optionally invalidated</li>
|
||
</ol>
|
||
<p><strong>Email verification flow (deferred to v1.1):</strong></p>
|
||
<ol>
|
||
<li class="">On registration, send verification email</li>
|
||
<li class="">User clicks link with verification token</li>
|
||
<li class="">Set <code>verified = true</code> in database</li>
|
||
<li class="">Unverified accounts can browse but not publish</li>
|
||
</ol>
|
||
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="token-scopes-and-authorization">Token Scopes and Authorization<a href="#token-scopes-and-authorization" class="hash-link" aria-label="Direct link to Token Scopes and Authorization" title="Direct link to Token Scopes and Authorization" translate="no"></a></h3>
|
||
<p>Tokens have scopes that limit their capabilities:</p>
|
||
<table><thead><tr><th>Scope</th><th>Permissions</th></tr></thead><tbody><tr><td><code>read</code></td><td>View own published tools, download stats</td></tr><tr><td><code>publish</code></td><td>Submit new tools, update own tool metadata</td></tr><tr><td><code>admin</code></td><td>Yank tools, manage categories (registry admins only)</td></tr></tbody></table>
|
||
<p><strong>Default scope:</strong> New tokens get <code>read,publish</code> by default.</p>
|
||
<p><strong>Ownership enforcement:</strong></p>
|
||
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#F8F8F2"><span class="token decorator annotation punctuation" style="color:rgb(248, 248, 242)">@app</span><span class="token decorator annotation punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token decorator annotation punctuation" style="color:rgb(248, 248, 242)">route</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">'/api/v1/tools'</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> methods</span><span class="token operator">=</span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token string" style="color:rgb(255, 121, 198)">'POST'</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token decorator annotation punctuation" style="color:rgb(248, 248, 242)">@require_token</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">scopes</span><span class="token operator">=</span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token string" style="color:rgb(255, 121, 198)">'publish'</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">def</span><span class="token plain"> </span><span class="token function" style="color:rgb(80, 250, 123)">publish_tool</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> token </span><span class="token operator">=</span><span class="token plain"> get_current_token</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> tool_data </span><span class="token operator">=</span><span class="token plain"> request</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">json</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token comment" style="color:rgb(98, 114, 164)"># Enforce owner == token holder's slug</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">if</span><span class="token plain"> tool_data</span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token string" style="color:rgb(255, 121, 198)">'owner'</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token plain"> </span><span class="token operator">!=</span><span class="token plain"> token</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">publisher</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">slug</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">return</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"error"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"code"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"FORBIDDEN"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"message"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> </span><span class="token string-interpolation string" style="color:rgb(255, 121, 198)">f"Cannot publish to namespace '</span><span class="token string-interpolation interpolation punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token string-interpolation interpolation">tool_data</span><span class="token string-interpolation interpolation punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token string-interpolation interpolation string" style="color:rgb(255, 121, 198)">'owner'</span><span class="token string-interpolation interpolation punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token string-interpolation interpolation punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token string-interpolation string" style="color:rgb(255, 121, 198)">'. "</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token string-interpolation string" style="color:rgb(255, 121, 198)">f"Your namespace is '</span><span class="token string-interpolation interpolation punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token string-interpolation interpolation">token</span><span class="token string-interpolation interpolation punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token string-interpolation interpolation">publisher</span><span class="token string-interpolation interpolation punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token string-interpolation interpolation">slug</span><span class="token string-interpolation interpolation punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token string-interpolation string" style="color:rgb(255, 121, 198)">'."</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token number">403</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token comment" style="color:rgb(98, 114, 164)"># Proceed with publish...</span><br></span></code></pre></div></div>
|
||
<p><strong><code>GET /api/v1/me/tools</code> authorization:</strong></p>
|
||
<ul>
|
||
<li class="">Requires valid token with <code>read</code> scope</li>
|
||
<li class="">Returns only tools where <code>owner == token.publisher.slug</code></li>
|
||
<li class="">Includes pending PRs and all versions (including yanked)</li>
|
||
</ul>
|
||
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="web-session-security">Web Session Security<a href="#web-session-security" class="hash-link" aria-label="Direct link to Web Session Security" title="Direct link to Web Session Security" translate="no"></a></h3>
|
||
<p>Dashboard login uses session cookies (not tokens) for browser auth:</p>
|
||
<p><strong>Cookie settings:</strong></p>
|
||
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#F8F8F2"><span class="token plain">SESSION_COOKIE_NAME </span><span class="token operator">=</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">'cmdforge_session'</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">SESSION_COOKIE_HTTPONLY </span><span class="token operator">=</span><span class="token plain"> </span><span class="token boolean">True</span><span class="token plain"> </span><span class="token comment" style="color:rgb(98, 114, 164)"># Prevent JS access</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">SESSION_COOKIE_SECURE </span><span class="token operator">=</span><span class="token plain"> </span><span class="token boolean">True</span><span class="token plain"> </span><span class="token comment" style="color:rgb(98, 114, 164)"># HTTPS only in production</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">SESSION_COOKIE_SAMESITE </span><span class="token operator">=</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">'Lax'</span><span class="token plain"> </span><span class="token comment" style="color:rgb(98, 114, 164)"># CSRF protection</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">SESSION_COOKIE_MAX_AGE </span><span class="token operator">=</span><span class="token plain"> </span><span class="token number">86400</span><span class="token plain"> </span><span class="token operator">*</span><span class="token plain"> </span><span class="token number">7</span><span class="token plain"> </span><span class="token comment" style="color:rgb(98, 114, 164)"># 7 days</span><br></span></code></pre></div></div>
|
||
<p><strong>CSRF protection:</strong></p>
|
||
<ul>
|
||
<li class="">All POST/PUT/DELETE forms include <code>csrf_token</code> hidden field</li>
|
||
<li class="">Token validated server-side before processing</li>
|
||
<li class="">403 Forbidden if token missing or invalid</li>
|
||
</ul>
|
||
<p><strong>Session lifecycle:</strong></p>
|
||
<table><thead><tr><th>Event</th><th>Action</th></tr></thead><tbody><tr><td>Login</td><td>Create session, set cookie</td></tr><tr><td>Logout</td><td>Delete session, clear cookie</td></tr><tr><td>Idle 24h</td><td>Session expires, re-login required</td></tr><tr><td>Password change</td><td>Invalidate all sessions</td></tr><tr><td>Token revocation</td><td>Existing sessions continue (token != session)</td></tr></tbody></table>
|
||
<p><strong>Secure session storage:</strong></p>
|
||
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#F8F8F2"><span class="token comment" style="color:rgb(98, 114, 164)"># Store sessions in DB, not filesystem</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">from</span><span class="token plain"> flask_session </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">import</span><span class="token plain"> Session</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">app</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">config</span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token string" style="color:rgb(255, 121, 198)">'SESSION_TYPE'</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token plain"> </span><span class="token operator">=</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">'sqlalchemy'</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">app</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">config</span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token string" style="color:rgb(255, 121, 198)">'SESSION_SQLALCHEMY_TABLE'</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token plain"> </span><span class="token operator">=</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">'sessions'</span><br></span></code></pre></div></div>
|
||
<p><strong>Database schema:</strong></p>
|
||
<div class="language-sql codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-sql codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#F8F8F2"><span class="token comment" style="color:rgb(98, 114, 164)">-- Publishers</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">CREATE</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">TABLE</span><span class="token plain"> publishers </span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> id </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">INTEGER</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">PRIMARY</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">KEY</span><span class="token plain"> AUTOINCREMENT</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> email </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">TEXT</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">UNIQUE</span><span class="token plain"> </span><span class="token operator">NOT</span><span class="token plain"> </span><span class="token boolean">NULL</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> password_hash </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">TEXT</span><span class="token plain"> </span><span class="token operator">NOT</span><span class="token plain"> </span><span class="token boolean">NULL</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> slug </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">TEXT</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">UNIQUE</span><span class="token plain"> </span><span class="token operator">NOT</span><span class="token plain"> </span><span class="token boolean">NULL</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token comment" style="color:rgb(98, 114, 164)">-- immutable namespace: "rob", "alice-dev"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> display_name </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">TEXT</span><span class="token plain"> </span><span class="token operator">NOT</span><span class="token plain"> </span><span class="token boolean">NULL</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token comment" style="color:rgb(98, 114, 164)">-- mutable: "Rob", "Alice Developer"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> bio </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">TEXT</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> website </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">TEXT</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> verified </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">BOOLEAN</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">DEFAULT</span><span class="token plain"> </span><span class="token boolean">FALSE</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> locked_until </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">TIMESTAMP</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token comment" style="color:rgb(98, 114, 164)">-- account lockout</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> failed_login_attempts </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">INTEGER</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">DEFAULT</span><span class="token plain"> </span><span class="token number">0</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> created_at </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">TIMESTAMP</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">DEFAULT</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">CURRENT_TIMESTAMP</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> updated_at </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">TIMESTAMP</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">DEFAULT</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">CURRENT_TIMESTAMP</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token comment" style="color:rgb(98, 114, 164)">-- API tokens (one publisher can have multiple)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">CREATE</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">TABLE</span><span class="token plain"> api_tokens </span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> id </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">INTEGER</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">PRIMARY</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">KEY</span><span class="token plain"> AUTOINCREMENT</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> publisher_id </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">INTEGER</span><span class="token plain"> </span><span class="token operator">NOT</span><span class="token plain"> </span><span class="token boolean">NULL</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">REFERENCES</span><span class="token plain"> publishers</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">id</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> token_hash </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">TEXT</span><span class="token plain"> </span><span class="token operator">NOT</span><span class="token plain"> </span><span class="token boolean">NULL</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> name </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">TEXT</span><span class="token plain"> </span><span class="token operator">NOT</span><span class="token plain"> </span><span class="token boolean">NULL</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token comment" style="color:rgb(98, 114, 164)">-- "CLI token", "CI token"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> last_used_at </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">TIMESTAMP</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> created_at </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">TIMESTAMP</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">DEFAULT</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">CURRENT_TIMESTAMP</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> revoked_at </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">TIMESTAMP</span><span class="token plain"> </span><span class="token comment" style="color:rgb(98, 114, 164)">-- NULL if active</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token comment" style="color:rgb(98, 114, 164)">-- Tools (links to publisher)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">CREATE</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">TABLE</span><span class="token plain"> tools </span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> id </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">INTEGER</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">PRIMARY</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">KEY</span><span class="token plain"> AUTOINCREMENT</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> owner </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">TEXT</span><span class="token plain"> </span><span class="token operator">NOT</span><span class="token plain"> </span><span class="token boolean">NULL</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token comment" style="color:rgb(98, 114, 164)">-- namespace slug (immutable, from publisher.slug)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> name </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">TEXT</span><span class="token plain"> </span><span class="token operator">NOT</span><span class="token plain"> </span><span class="token boolean">NULL</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> version </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">TEXT</span><span class="token plain"> </span><span class="token operator">NOT</span><span class="token plain"> </span><span class="token boolean">NULL</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> description </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">TEXT</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> category </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">TEXT</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> tags </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">TEXT</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token comment" style="color:rgb(98, 114, 164)">-- JSON array</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> config_yaml </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">TEXT</span><span class="token plain"> </span><span class="token operator">NOT</span><span class="token plain"> </span><span class="token boolean">NULL</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token comment" style="color:rgb(98, 114, 164)">-- Full tool config</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> readme </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">TEXT</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> publisher_id </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">INTEGER</span><span class="token plain"> </span><span class="token operator">NOT</span><span class="token plain"> </span><span class="token boolean">NULL</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">REFERENCES</span><span class="token plain"> publishers</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">id</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> deprecated </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">BOOLEAN</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">DEFAULT</span><span class="token plain"> </span><span class="token boolean">FALSE</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> deprecated_message </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">TEXT</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> replacement </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">TEXT</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> downloads </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">INTEGER</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">DEFAULT</span><span class="token plain"> </span><span class="token number">0</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> published_at </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">TIMESTAMP</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">DEFAULT</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">CURRENT_TIMESTAMP</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">UNIQUE</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">owner</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> name</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> version</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token comment" style="color:rgb(98, 114, 164)">-- Download stats (for deduplication)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">CREATE</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">TABLE</span><span class="token plain"> download_stats </span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> id </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">INTEGER</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">PRIMARY</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">KEY</span><span class="token plain"> AUTOINCREMENT</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> tool_id </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">INTEGER</span><span class="token plain"> </span><span class="token operator">NOT</span><span class="token plain"> </span><span class="token boolean">NULL</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">REFERENCES</span><span class="token plain"> tools</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">id</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> client_id </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">TEXT</span><span class="token plain"> </span><span class="token operator">NOT</span><span class="token plain"> </span><span class="token boolean">NULL</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> downloaded_at </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">DATE</span><span class="token plain"> </span><span class="token operator">NOT</span><span class="token plain"> </span><span class="token boolean">NULL</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">UNIQUE</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">tool_id</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> client_id</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> downloaded_at</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token comment" style="color:rgb(98, 114, 164)">-- Search index (FTS5)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">CREATE</span><span class="token plain"> VIRTUAL </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">TABLE</span><span class="token plain"> tools_fts </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">USING</span><span class="token plain"> fts5</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> name</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> description</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> tags</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> readme</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> content</span><span class="token operator">=</span><span class="token string" style="color:rgb(255, 121, 198)">'tools'</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> content_rowid</span><span class="token operator">=</span><span class="token string" style="color:rgb(255, 121, 198)">'id'</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token comment" style="color:rgb(98, 114, 164)">-- FTS5 sync triggers (required for external content tables)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">CREATE</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">TRIGGER</span><span class="token plain"> tools_ai </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">AFTER</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">INSERT</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">ON</span><span class="token plain"> tools </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">BEGIN</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">INSERT</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">INTO</span><span class="token plain"> tools_fts</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">rowid</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> name</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> description</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> tags</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> readme</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">VALUES</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">new</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">id</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> new</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">name</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> new</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">description</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> new</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">tags</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> new</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">readme</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">END</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">CREATE</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">TRIGGER</span><span class="token plain"> tools_ad </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">AFTER</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">DELETE</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">ON</span><span class="token plain"> tools </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">BEGIN</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">INSERT</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">INTO</span><span class="token plain"> tools_fts</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">tools_fts</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> rowid</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> name</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> description</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> tags</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> readme</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">VALUES</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">'delete'</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> old</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">id</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> old</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">name</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> old</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">description</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> old</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">tags</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> old</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">readme</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">END</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">CREATE</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">TRIGGER</span><span class="token plain"> tools_au </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">AFTER</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">UPDATE</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">ON</span><span class="token plain"> tools </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">BEGIN</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">INSERT</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">INTO</span><span class="token plain"> tools_fts</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">tools_fts</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> rowid</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> name</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> description</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> tags</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> readme</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">VALUES</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">'delete'</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> old</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">id</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> old</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">name</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> old</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">description</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> old</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">tags</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> old</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">readme</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">INSERT</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">INTO</span><span class="token plain"> tools_fts</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">rowid</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> name</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> description</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> tags</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> readme</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">VALUES</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">new</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">id</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> new</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">name</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> new</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">description</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> new</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">tags</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> new</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">readme</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">END</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token comment" style="color:rgb(98, 114, 164)">-- Pending PRs (track publish state)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">CREATE</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">TABLE</span><span class="token plain"> pending_prs </span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> id </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">INTEGER</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">PRIMARY</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">KEY</span><span class="token plain"> AUTOINCREMENT</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> publisher_id </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">INTEGER</span><span class="token plain"> </span><span class="token operator">NOT</span><span class="token plain"> </span><span class="token boolean">NULL</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">REFERENCES</span><span class="token plain"> publishers</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">id</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> owner </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">TEXT</span><span class="token plain"> </span><span class="token operator">NOT</span><span class="token plain"> </span><span class="token boolean">NULL</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> name </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">TEXT</span><span class="token plain"> </span><span class="token operator">NOT</span><span class="token plain"> </span><span class="token boolean">NULL</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> version </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">TEXT</span><span class="token plain"> </span><span class="token operator">NOT</span><span class="token plain"> </span><span class="token boolean">NULL</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> pr_number </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">INTEGER</span><span class="token plain"> </span><span class="token operator">NOT</span><span class="token plain"> </span><span class="token boolean">NULL</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> pr_url </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">TEXT</span><span class="token plain"> </span><span class="token operator">NOT</span><span class="token plain"> </span><span class="token boolean">NULL</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">status</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">TEXT</span><span class="token plain"> </span><span class="token operator">NOT</span><span class="token plain"> </span><span class="token boolean">NULL</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">DEFAULT</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">'pending'</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token comment" style="color:rgb(98, 114, 164)">-- pending, merged, closed</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> created_at </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">TIMESTAMP</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">DEFAULT</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">CURRENT_TIMESTAMP</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> updated_at </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">TIMESTAMP</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">DEFAULT</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">CURRENT_TIMESTAMP</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">UNIQUE</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">owner</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> name</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> version</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token comment" style="color:rgb(98, 114, 164)">-- Webhook sync log (idempotency)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">CREATE</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">TABLE</span><span class="token plain"> webhook_log </span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> id </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">INTEGER</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">PRIMARY</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">KEY</span><span class="token plain"> AUTOINCREMENT</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> delivery_id </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">TEXT</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">UNIQUE</span><span class="token plain"> </span><span class="token operator">NOT</span><span class="token plain"> </span><span class="token boolean">NULL</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token comment" style="color:rgb(98, 114, 164)">-- Gitea delivery ID</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> event_type </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">TEXT</span><span class="token plain"> </span><span class="token operator">NOT</span><span class="token plain"> </span><span class="token boolean">NULL</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> processed_at </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">TIMESTAMP</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">DEFAULT</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">CURRENT_TIMESTAMP</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><br></span></code></pre></div></div>
|
||
<p><strong>Note on tags indexing:</strong> The <code>tags</code> column stores JSON arrays as text. For v1, FTS5 will search within the JSON string. If tag filtering becomes a bottleneck, normalize to a <code>tool_tags</code> junction table:</p>
|
||
<div class="language-sql codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-sql codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#F8F8F2"><span class="token comment" style="color:rgb(98, 114, 164)">-- Future: normalized tags (if needed)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">CREATE</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">TABLE</span><span class="token plain"> tags </span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> id </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">INTEGER</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">PRIMARY</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">KEY</span><span class="token plain"> AUTOINCREMENT</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> name </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">TEXT</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">UNIQUE</span><span class="token plain"> </span><span class="token operator">NOT</span><span class="token plain"> </span><span class="token boolean">NULL</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">CREATE</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">TABLE</span><span class="token plain"> tool_tags </span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> tool_id </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">INTEGER</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">REFERENCES</span><span class="token plain"> tools</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">id</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> tag_id </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">INTEGER</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">REFERENCES</span><span class="token plain"> tags</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">id</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">PRIMARY</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">KEY</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">tool_id</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> tag_id</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><br></span></code></pre></div></div>
|
||
<p><strong>Connecting to your account:</strong></p>
|
||
<p>The recommended way to authenticate is using the app pairing flow, which eliminates manual token copying:</p>
|
||
<p><strong>CLI connection flow:</strong></p>
|
||
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#F8F8F2"><span class="token plain">$ cmdforge config connect rob</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">Connecting to CmdForge as @rob...</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">Device: my-laptop</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">Waiting for approval from the web interface...</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">Go to https://cmdforge.brrd.tech/dashboard/connections</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">and click 'Connect New App', then 'I've Run the Command'</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">Press Ctrl+C to cancel</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">Waiting...</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">Connected successfully!</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">Your device 'my-laptop' is now linked to @rob</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">You can now publish tools with: cmdforge registry publish</span><br></span></code></pre></div></div>
|
||
<p><strong>TUI connection flow:</strong></p>
|
||
<p>The TUI (<code>cmdforge ui</code>) includes a Connect button for connecting without using the command line:</p>
|
||
<ol>
|
||
<li class="">Click "Connect" button in the main menu</li>
|
||
<li class="">Enter your CmdForge username (create account at cmdforge.brrd.tech if needed)</li>
|
||
<li class="">A countdown timer shows the pairing expiration (5 minutes)</li>
|
||
<li class="">Go to cmdforge.brrd.tech/dashboard/connections in your browser</li>
|
||
<li class="">Click "Connect New App" and approve the pending connection</li>
|
||
<li class="">TUI automatically detects approval and saves the token</li>
|
||
</ol>
|
||
<p><strong>CLI first-time publish flow:</strong></p>
|
||
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#F8F8F2"><span class="token plain">$ cmdforge registry publish</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">No registry account configured.</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">Options:</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">1. Connect to account (recommended): cmdforge config connect <username></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">2. Manual token entry</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">Choose option [1]: 1</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">Enter username: rob</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">Connecting to CmdForge as @rob...</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">[Follows connection flow above]</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">Validating tool...</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">✓ config.yaml is valid</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">✓ README.md exists (2.3 KB)</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">✓ Version 1.0.0 not yet published</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">Publishing rob/my-tool@1.0.0...</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">✓ PR created: https://gitea.brrd.tech/rob/CmdForge-Registry/pulls/42</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">Your tool is pending review. You'll receive an email when it's approved.</span><br></span></code></pre></div></div>
|
||
<p><strong>TUI publish flow:</strong></p>
|
||
<p>When already connected, the TUI main menu shows "Publish" instead of "Connect":</p>
|
||
<ol>
|
||
<li class="">Select a tool in the list</li>
|
||
<li class="">Click "Publish" button</li>
|
||
<li class="">If no version in config, TUI prompts for version number</li>
|
||
<li class="">Confirm and publish</li>
|
||
</ol>
|
||
<p><strong>Private sync after save:</strong></p>
|
||
<p>When connected to an account, saving a tool in the TUI offers to sync it privately to the registry:</p>
|
||
<ol>
|
||
<li class="">Save a tool (new or edited)</li>
|
||
<li class="">TUI asks: "Sync to registry privately?"</li>
|
||
<li class="">If yes, enter/confirm version number</li>
|
||
<li class="">Tool is published with <code>visibility: private</code> (only you can see it)</li>
|
||
<li class="">Useful for backup or accessing your tools from multiple machines</li>
|
||
</ol>
|
||
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="cli-commands-reference">CLI Commands Reference<a href="#cli-commands-reference" class="hash-link" aria-label="Direct link to CLI Commands Reference" title="Direct link to CLI Commands Reference" translate="no"></a></h2>
|
||
<p>Full mapping of CLI commands to API calls:</p>
|
||
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="registry-commands">Registry Commands<a href="#registry-commands" class="hash-link" aria-label="Direct link to Registry Commands" title="Direct link to Registry Commands" translate="no"></a></h3>
|
||
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#F8F8F2"><span class="token plain"># Search for tools (basic)</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">$ cmdforge registry search <query> [--category=<cat>] [--limit=20]</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> → GET /api/v1/tools/search?q=<query>&category=<cat>&limit=20</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"># Search with advanced filtering</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">$ cmdforge registry search <query> [options]</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> Options:</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> -c, --category CAT Filter by category</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> -t, --tag TAG Filter by tag (repeatable, AND logic)</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> -o, --owner OWNER Filter by publisher/owner</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> --min-downloads N Minimum downloads</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> --popular Shortcut for --min-downloads 100</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> --new Shortcut for --max-downloads 10</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> --since DATE Published after (YYYY-MM-DD)</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> --before DATE Published before (YYYY-MM-DD)</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> -s, --sort FIELD Sort by: relevance, downloads, published_at, name</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> -l, --limit N Max results (default: 20)</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> --json Output as JSON</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> --show-facets Show category/tag counts</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> --deprecated Include deprecated tools</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"># List available tags</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">$ cmdforge registry tags [-c CATEGORY] [-l LIMIT] [--json]</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> → GET /api/v1/tags?category=<cat>&limit=<limit></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"># Browse tools (TUI)</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">$ cmdforge registry browse [--category=<cat>]</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> → GET /api/v1/tools?category=<cat>&page=1</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> → GET /api/v1/categories</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"># View tool details</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">$ cmdforge registry info <owner/name></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> → GET /api/v1/tools/<owner>/<name></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"># Install a tool</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">$ cmdforge registry install <owner/name> [--version=<ver>]</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> → GET /api/v1/tools/<owner>/<name>/download?version=<ver>&install=true</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> → Writes to ~/.cmdforge/<owner>/<name>/config.yaml</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> → Generates ~/.local/bin/<name> wrapper (or <owner>-<name> if collision)</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"># Uninstall a tool</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">$ cmdforge registry uninstall <owner/name></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> → Removes ~/.cmdforge/<owner>/<name>/</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> → Removes wrapper script</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"># Publish a tool</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">$ cmdforge registry publish [path] [--dry-run]</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> → POST /api/v1/tools (with registry token)</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> → Returns PR URL</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"># List my published tools</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">$ cmdforge registry my-tools</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> → GET /api/v1/me/tools (with registry token)</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"># Update index cache</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">$ cmdforge registry update</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> → GET /api/v1/index.json</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> → Writes to ~/.cmdforge/registry/index.json</span><br></span></code></pre></div></div>
|
||
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="project-commands">Project Commands<a href="#project-commands" class="hash-link" aria-label="Direct link to Project Commands" title="Direct link to Project Commands" translate="no"></a></h3>
|
||
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#F8F8F2"><span class="token plain"># Install project dependencies from cmdforge.yaml</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">$ cmdforge install</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> → Reads ./cmdforge.yaml</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> → For each dependency:</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> GET /api/v1/tools/<owner>/<name>/download?version=<constraint>&install=true</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> → Installs to ~/.cmdforge/<owner>/<name>/</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"># Add a dependency to cmdforge.yaml</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">$ cmdforge add <owner/name> [--version=<constraint>]</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> → Adds to ./cmdforge.yaml dependencies</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> → Runs install for that tool</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"># Show project dependencies status</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">$ cmdforge deps</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> → Reads ./cmdforge.yaml</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> → Shows installed status for each dependency</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> → Note: "cmdforge list" is reserved for listing installed tools</span><br></span></code></pre></div></div>
|
||
<p><strong>Command naming note:</strong> <code>cmdforge list</code> already exists to list locally installed tools. Use <code>cmdforge deps</code> to show project manifest dependencies.</p>
|
||
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="config-commands">Config Commands<a href="#config-commands" class="hash-link" aria-label="Direct link to Config Commands" title="Direct link to Config Commands" translate="no"></a></h3>
|
||
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#F8F8F2"><span class="token plain"># Show current configuration</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">$ cmdforge config show</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> → Displays registry URL, token status, client ID, auto-fetch setting</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"># Connect to your CmdForge account (recommended)</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">$ cmdforge config connect <username></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> → Initiates app pairing flow</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> → Polls /api/v1/pairing/check/<username>?hostname=<hostname></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> → On approval, saves token to ~/.cmdforge/config.yaml</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"># Set registry token manually (alternative to connect)</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">$ cmdforge config set-token <token></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> → Saves token to ~/.cmdforge/config.yaml</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"># Set configuration values</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">$ cmdforge config set <key> <value></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> → Available keys: auto_fetch, default_provider, registry_url</span><br></span></code></pre></div></div>
|
||
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="flags-available-on-most-commands">Flags available on most commands<a href="#flags-available-on-most-commands" class="hash-link" aria-label="Direct link to Flags available on most commands" title="Direct link to Flags available on most commands" translate="no"></a></h3>
|
||
<table><thead><tr><th>Flag</th><th>Description</th></tr></thead><tbody><tr><td><code>--offline</code></td><td>Use cached index only, don't fetch</td></tr><tr><td><code>--refresh</code></td><td>Force refresh of cached data</td></tr><tr><td><code>--json</code></td><td>Output in JSON format</td></tr><tr><td><code>--verbose</code></td><td>Show detailed output</td></tr></tbody></table>
|
||
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="publish-state-tracking">Publish State Tracking<a href="#publish-state-tracking" class="hash-link" aria-label="Direct link to Publish State Tracking" title="Direct link to Publish State Tracking" translate="no"></a></h2>
|
||
<p>The GUI tracks the publish state of local tools to show whether they've been published, are pending review, or have been modified since publishing.</p>
|
||
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="local-state-storage">Local State Storage<a href="#local-state-storage" class="hash-link" aria-label="Direct link to Local State Storage" title="Direct link to Local State Storage" translate="no"></a></h3>
|
||
<p>When a tool is published, two fields are saved to the local <code>config.yaml</code>:</p>
|
||
<div class="language-yaml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-yaml codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#F8F8F2"><span class="token key atrule">name</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> my</span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain">tool</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token key atrule">description</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> My awesome tool</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token comment" style="color:rgb(98, 114, 164)"># ... tool config ...</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token key atrule">registry_hash</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> sha256</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain">abc123</span><span class="token punctuation" style="color:rgb(248, 248, 242)">...</span><span class="token plain"> </span><span class="token comment" style="color:rgb(98, 114, 164)"># Hash of published config</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token key atrule">registry_status</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> pending </span><span class="token comment" style="color:rgb(98, 114, 164)"># Moderation status: pending, approved, rejected</span><br></span></code></pre></div></div>
|
||
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="visual-indicators">Visual Indicators<a href="#visual-indicators" class="hash-link" aria-label="Direct link to Visual Indicators" title="Direct link to Visual Indicators" translate="no"></a></h3>
|
||
<p>The Tools page shows different indicators based on publish state:</p>
|
||
<table><thead><tr><th>State</th><th>Indicator</th><th>Meaning</th></tr></thead><tbody><tr><td>Published</td><td>✓ (green)</td><td>Approved in registry, local matches published</td></tr><tr><td>Pending</td><td>◐ (yellow)</td><td>Submitted, awaiting moderator review</td></tr><tr><td>Modified</td><td>● (orange)</td><td>Published but local config has changes</td></tr><tr><td>Local</td><td>(none)</td><td>Never published to registry</td></tr></tbody></table>
|
||
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="automatic-status-sync">Automatic Status Sync<a href="#automatic-status-sync" class="hash-link" aria-label="Direct link to Automatic Status Sync" title="Direct link to Automatic Status Sync" translate="no"></a></h3>
|
||
<p>When the Tools page loads, a background sync automatically checks the registry for status updates on all published tools. This means:</p>
|
||
<ul>
|
||
<li class="">When a moderator approves your tool, the indicator updates automatically on next visit</li>
|
||
<li class="">No manual refresh needed - just navigate to the Tools page</li>
|
||
<li class="">Status messages appear when tool statuses change</li>
|
||
</ul>
|
||
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="manual-sync">Manual Sync<a href="#manual-sync" class="hash-link" aria-label="Direct link to Manual Sync" title="Direct link to Manual Sync" translate="no"></a></h3>
|
||
<p>A "Sync Status" button is available to force an immediate status check for the selected tool. This is useful if you want to check status without leaving the page.</p>
|
||
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="hash-computation">Hash Computation<a href="#hash-computation" class="hash-link" aria-label="Direct link to Hash Computation" title="Direct link to Hash Computation" translate="no"></a></h3>
|
||
<p>The publish state hash is computed from the tool's core content, excluding:</p>
|
||
<ul>
|
||
<li class=""><code>registry_hash</code> - The stored hash itself</li>
|
||
<li class=""><code>registry_status</code> - The moderation status</li>
|
||
<li class=""><code>version</code> - Publication version (added during publish)</li>
|
||
<li class=""><code>tags</code> - Publication tags (added during publish)</li>
|
||
</ul>
|
||
<p>This ensures that only actual tool content changes (steps, arguments, prompts) trigger the "modified" indicator.</p>
|
||
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="api-endpoint">API Endpoint<a href="#api-endpoint" class="hash-link" aria-label="Direct link to API Endpoint" title="Direct link to API Endpoint" translate="no"></a></h3>
|
||
<p>The status sync uses:</p>
|
||
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#F8F8F2"><span class="token plain">GET /api/v1/me/tools/<name>/status</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">Response:</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">{</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> "data": {</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> "name": "my-tool",</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> "version": "1.0.0",</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> "status": "approved",</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> "config_hash": "sha256:abc123...",</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> "published_at": "2025-01-15T10:30:00Z"</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> }</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">}</span><br></span></code></pre></div></div>
|
||
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="webhooks-and-security">Webhooks and Security<a href="#webhooks-and-security" class="hash-link" aria-label="Direct link to Webhooks and Security" title="Direct link to Webhooks and Security" translate="no"></a></h2>
|
||
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="hmac-verification">HMAC Verification<a href="#hmac-verification" class="hash-link" aria-label="Direct link to HMAC Verification" title="Direct link to HMAC Verification" translate="no"></a></h3>
|
||
<p>All Gitea webhooks are verified using HMAC-SHA256:</p>
|
||
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#F8F8F2"><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">import</span><span class="token plain"> hmac</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">import</span><span class="token plain"> hashlib</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">def</span><span class="token plain"> </span><span class="token function" style="color:rgb(80, 250, 123)">verify_webhook</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">request</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> secret</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> signature </span><span class="token operator">=</span><span class="token plain"> request</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">headers</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">get</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">'X-Gitea-Signature'</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">if</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">not</span><span class="token plain"> signature</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">return</span><span class="token plain"> </span><span class="token boolean">False</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> expected </span><span class="token operator">=</span><span class="token plain"> hmac</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">new</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> secret</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">encode</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> request</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">body</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> hashlib</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">sha256</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">hexdigest</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">return</span><span class="token plain"> hmac</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">compare_digest</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">signature</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> expected</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><br></span></code></pre></div></div>
|
||
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="replay-protection">Replay Protection<a href="#replay-protection" class="hash-link" aria-label="Direct link to Replay Protection" title="Direct link to Replay Protection" translate="no"></a></h3>
|
||
<p>While sync is idempotent, implement basic replay protection:</p>
|
||
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#F8F8F2"><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">def</span><span class="token plain"> </span><span class="token function" style="color:rgb(80, 250, 123)">process_webhook</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">request</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> delivery_id </span><span class="token operator">=</span><span class="token plain"> request</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">headers</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">get</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">'X-Gitea-Delivery'</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token comment" style="color:rgb(98, 114, 164)"># Check if already processed</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">if</span><span class="token plain"> db</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">webhook_log</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">exists</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">delivery_id</span><span class="token operator">=</span><span class="token plain">delivery_id</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">return</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token string" style="color:rgb(255, 121, 198)">"status"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"already_processed"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token number">200</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token comment" style="color:rgb(98, 114, 164)"># Verify signature</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">if</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">not</span><span class="token plain"> verify_webhook</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">request</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> WEBHOOK_SECRET</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">return</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token string" style="color:rgb(255, 121, 198)">"error"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"invalid_signature"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token number">401</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token comment" style="color:rgb(98, 114, 164)"># Process with lock to prevent concurrent processing</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">with</span><span class="token plain"> db</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">lock</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string-interpolation string" style="color:rgb(255, 121, 198)">f"webhook:</span><span class="token string-interpolation interpolation punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token string-interpolation interpolation">delivery_id</span><span class="token string-interpolation interpolation punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token string-interpolation string" style="color:rgb(255, 121, 198)">"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token comment" style="color:rgb(98, 114, 164)"># Double-check after acquiring lock</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">if</span><span class="token plain"> db</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">webhook_log</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">exists</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">delivery_id</span><span class="token operator">=</span><span class="token plain">delivery_id</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">return</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token string" style="color:rgb(255, 121, 198)">"status"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"already_processed"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token number">200</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token comment" style="color:rgb(98, 114, 164)"># Process the webhook</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> result </span><span class="token operator">=</span><span class="token plain"> sync_from_repo</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token comment" style="color:rgb(98, 114, 164)"># Log successful processing</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> db</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">webhook_log</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">insert</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> delivery_id</span><span class="token operator">=</span><span class="token plain">delivery_id</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> event_type</span><span class="token operator">=</span><span class="token plain">request</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">json</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">get</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">'action'</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> processed_at</span><span class="token operator">=</span><span class="token plain">datetime</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">utcnow</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">return</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token string" style="color:rgb(255, 121, 198)">"status"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"processed"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token number">200</span><br></span></code></pre></div></div>
|
||
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="sync-job-locking">Sync Job Locking<a href="#sync-job-locking" class="hash-link" aria-label="Direct link to Sync Job Locking" title="Direct link to Sync Job Locking" translate="no"></a></h3>
|
||
<p>Prevent concurrent sync operations:</p>
|
||
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#F8F8F2"><span class="token comment" style="color:rgb(98, 114, 164)"># Using file lock or database advisory lock</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">SYNC_LOCK_TIMEOUT </span><span class="token operator">=</span><span class="token plain"> </span><span class="token number">300</span><span class="token plain"> </span><span class="token comment" style="color:rgb(98, 114, 164)"># 5 minutes max</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">def</span><span class="token plain"> </span><span class="token function" style="color:rgb(80, 250, 123)">sync_from_repo</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">try</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">with</span><span class="token plain"> acquire_lock</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">"registry_sync"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> timeout</span><span class="token operator">=</span><span class="token plain">SYNC_LOCK_TIMEOUT</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token comment" style="color:rgb(98, 114, 164)"># Pull latest from Gitea</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> repo</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">fetch</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> repo</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">reset</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">'origin/main'</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> hard</span><span class="token operator">=</span><span class="token boolean">True</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token comment" style="color:rgb(98, 114, 164)"># Parse and update database</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">for</span><span class="token plain"> tool_path </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">in</span><span class="token plain"> glob</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">'tools/*/*/config.yaml'</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> update_tool_in_db</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">tool_path</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token comment" style="color:rgb(98, 114, 164)"># Rebuild FTS index if needed</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> rebuild_fts_index</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">except</span><span class="token plain"> LockTimeout</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> logger</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">warning</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">"Sync already in progress, skipping"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">return</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token string" style="color:rgb(255, 121, 198)">"status"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"skipped"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"reason"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"sync_in_progress"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><br></span></code></pre></div></div>
|
||
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="atomic-sync-strategy">Atomic Sync Strategy<a href="#atomic-sync-strategy" class="hash-link" aria-label="Direct link to Atomic Sync Strategy" title="Direct link to Atomic Sync Strategy" translate="no"></a></h3>
|
||
<p>To avoid partially updated DB during webhook sync, use transactional table swap:</p>
|
||
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#F8F8F2"><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">def</span><span class="token plain"> </span><span class="token function" style="color:rgb(80, 250, 123)">sync_from_repo_atomic</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">with</span><span class="token plain"> acquire_lock</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">"registry_sync"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> timeout</span><span class="token operator">=</span><span class="token plain">SYNC_LOCK_TIMEOUT</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token comment" style="color:rgb(98, 114, 164)"># 1. Pull latest from Gitea</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> repo</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">fetch</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> repo</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">reset</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">'origin/main'</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> hard</span><span class="token operator">=</span><span class="token boolean">True</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token comment" style="color:rgb(98, 114, 164)"># 2. Parse all tools into memory</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> new_tools </span><span class="token operator">=</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">for</span><span class="token plain"> tool_path </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">in</span><span class="token plain"> glob</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">'tools/*/*/config.yaml'</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> tool_data </span><span class="token operator">=</span><span class="token plain"> parse_tool</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">tool_path</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">if</span><span class="token plain"> tool_data</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> new_tools</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">append</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">tool_data</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token comment" style="color:rgb(98, 114, 164)"># 3. Atomic swap using transaction</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">with</span><span class="token plain"> db</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">transaction</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token comment" style="color:rgb(98, 114, 164)"># Create temp table</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> db</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">execute</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">"CREATE TABLE tools_new AS SELECT * FROM tools WHERE 0"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token comment" style="color:rgb(98, 114, 164)"># Bulk insert into temp table</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">for</span><span class="token plain"> tool </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">in</span><span class="token plain"> new_tools</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> db</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">execute</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">"INSERT INTO tools_new ..."</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> tool</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token comment" style="color:rgb(98, 114, 164)"># Swap tables atomically</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> db</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">execute</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">"ALTER TABLE tools RENAME TO tools_old"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> db</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">execute</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">"ALTER TABLE tools_new RENAME TO tools"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> db</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">execute</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">"DROP TABLE tools_old"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token comment" style="color:rgb(98, 114, 164)"># Rebuild FTS index</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> db</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">execute</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">"INSERT INTO tools_fts(tools_fts) VALUES('rebuild')"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token comment" style="color:rgb(98, 114, 164)"># Update sync timestamp</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> db</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">execute</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">"UPDATE sync_status SET last_sync = ?"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token plain">datetime</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">utcnow</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><br></span></code></pre></div></div>
|
||
<p><strong>Why atomic:</strong> Per-row updates with FTS triggers can yield inconsistent reads under load. Readers may see partial state mid-sync. Table swap ensures all-or-nothing visibility.</p>
|
||
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="error-handling">Error Handling<a href="#error-handling" class="hash-link" aria-label="Direct link to Error Handling" title="Direct link to Error Handling" translate="no"></a></h3>
|
||
<table><thead><tr><th>Error Scenario</th><th>Behavior</th></tr></thead><tbody><tr><td>Repo fetch fails</td><td>Log error, retry in 5 min, alert if 3 failures</td></tr><tr><td>YAML parse error</td><td>Skip tool, log error, continue with others</td></tr><tr><td>Database write fails</td><td>Rollback transaction, retry once, then alert</td></tr><tr><td>Lock timeout</td><td>Skip this sync, next webhook will retry</td></tr></tbody></table>
|
||
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="automated-ci-validation">Automated CI Validation<a href="#automated-ci-validation" class="hash-link" aria-label="Direct link to Automated CI Validation" title="Direct link to Automated CI Validation" translate="no"></a></h2>
|
||
<p>PRs are validated automatically using CmdForge (dogfooding):</p>
|
||
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#F8F8F2"><span class="token plain">PR Submitted</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> │</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> ▼</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">┌─────────────────────────────────────┐</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">│ Gitea CI runs validation tools: │</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">│ • schema-validator │</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">│ • security-scanner │</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">│ • duplicate-detector │</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">└───────────────┬─────────────────────┘</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> │</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> ┌───────┴───────┐</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> │ │</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> All pass Any fail</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> │ │</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> ▼ ▼</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> Auto-merge or Add comment,</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> flag for review request changes</span><br></span></code></pre></div></div>
|
||
<p>Validation checks:</p>
|
||
<ol>
|
||
<li class=""><strong>Schema validation</strong>: config.yaml matches expected format</li>
|
||
<li class=""><strong>Security scan</strong>: No dangerous shell commands, no secrets in prompts</li>
|
||
<li class=""><strong>Duplicate detection</strong>: AI-powered similarity check against existing tools</li>
|
||
<li class=""><strong>README check</strong>: README.md exists and is non-empty</li>
|
||
</ol>
|
||
<p>CI workflow (<code>.gitea/workflows/validate.yaml</code>):</p>
|
||
<div class="language-yaml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-yaml codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#F8F8F2"><span class="token key atrule">name</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> Validate Tool Submission</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token key atrule">on</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token plain">pull_request</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token key atrule">jobs</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token key atrule">validate</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token key atrule">runs-on</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> ubuntu</span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain">latest</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token key atrule">steps</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain"> </span><span class="token key atrule">uses</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> actions/checkout@v3</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain"> </span><span class="token key atrule">name</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> Validate schema</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token key atrule">run</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> python scripts/validate_tool.py $</span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"> github.event.pull_request.head.sha </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain"> </span><span class="token key atrule">name</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> Security scan</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token key atrule">run</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> cmdforge run security</span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain">scanner < changed_files.txt</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain"> </span><span class="token key atrule">name</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> Check duplicates</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token key atrule">run</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> cmdforge run duplicate</span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain">detector < changed_files.txt</span><br></span></code></pre></div></div>
|
||
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="registry-repository-structure">Registry Repository Structure<a href="#registry-repository-structure" class="hash-link" aria-label="Direct link to Registry Repository Structure" title="Direct link to Registry Repository Structure" translate="no"></a></h2>
|
||
<p>Full structure of the CmdForge-Registry repo:</p>
|
||
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#F8F8F2"><span class="token plain">CmdForge-Registry/</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">├── README.md # Registry overview</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">├── CONTRIBUTING.md # How to submit tools</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">├── LICENSE</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">│</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">├── tools/ # All published tools</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">│ ├── rob/</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">│ │ ├── summarize/</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">│ │ │ ├── config.yaml</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">│ │ │ └── README.md</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">│ │ └── translate/</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">│ │ ├── config.yaml</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">│ │ └── README.md</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">│ └── alice/</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">│ └── code-review/</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">│ ├── config.yaml</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">│ └── README.md</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">│</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">├── categories/</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">│ └── categories.yaml # Category definitions</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">│</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">├── collections/ # Curated tool collections</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">│ ├── text-processing-essentials.yaml</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">│ ├── developer-toolkit.yaml</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">│ └── data-pipeline-basics.yaml</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">│</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">├── index.json # Auto-generated search index</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">│</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">├── .gitea/</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">│ └── workflows/</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">│ ├── validate.yaml # PR validation</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">│ ├── build-index.yaml # Rebuild index on merge</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">│ └── notify-api.yaml # Webhook to API server</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">│</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">└── scripts/</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> ├── validate_tool.py # Schema validation</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> ├── build_index.py # Generate index.json</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> ├── check_duplicates.py # Similarity detection</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> └── security_scan.py # Security checks</span><br></span></code></pre></div></div>
|
||
<p><code>categories.yaml</code> format:</p>
|
||
<div class="language-yaml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-yaml codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#F8F8F2"><span class="token key atrule">categories</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain"> </span><span class="token key atrule">name</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> text</span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain">processing</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token key atrule">description</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> Tools for manipulating and analyzing text</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token key atrule">icon</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> 📝</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain"> </span><span class="token key atrule">name</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> code</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token key atrule">description</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> Tools for code review</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> generation</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> and analysis</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token key atrule">icon</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> 💻</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain"> </span><span class="token key atrule">name</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> data</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token key atrule">description</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> Tools for data transformation and analysis</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token key atrule">icon</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> 📊</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain"> </span><span class="token key atrule">name</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> media</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token key atrule">description</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> Tools for image</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> audio</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> and video processing</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token key atrule">icon</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> 🎨</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain"> </span><span class="token key atrule">name</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> productivity</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token key atrule">description</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> General productivity and automation tools</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token key atrule">icon</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> ⚡</span><br></span></code></pre></div></div>
|
||
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="download-stats">Download Stats<a href="#download-stats" class="hash-link" aria-label="Direct link to Download Stats" title="Direct link to Download Stats" translate="no"></a></h2>
|
||
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="counting-methodology">Counting Methodology<a href="#counting-methodology" class="hash-link" aria-label="Direct link to Counting Methodology" title="Direct link to Counting Methodology" translate="no"></a></h3>
|
||
<ul>
|
||
<li class="">Count installs only, not views or searches</li>
|
||
<li class="">Increment <strong>after</strong> successful download (response sent)</li>
|
||
<li class="">Dedupe by <code>client_id + tool_id + date</code></li>
|
||
</ul>
|
||
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#F8F8F2"><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">def</span><span class="token plain"> </span><span class="token function" style="color:rgb(80, 250, 123)">download_tool</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">owner</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> name</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> version</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> install</span><span class="token operator">=</span><span class="token boolean">False</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> client_id</span><span class="token operator">=</span><span class="token boolean">None</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> tool </span><span class="token operator">=</span><span class="token plain"> get_tool</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">owner</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> name</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> version</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">if</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">not</span><span class="token plain"> tool</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">return</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token string" style="color:rgb(255, 121, 198)">"error"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"not_found"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token number">404</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> config_yaml </span><span class="token operator">=</span><span class="token plain"> tool</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">config_yaml</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token comment" style="color:rgb(98, 114, 164)"># Only count if this is an install (not just viewing)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">if</span><span class="token plain"> install</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> record_download</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">tool</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token builtin" style="color:rgb(189, 147, 249)">id</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> client_id</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">return</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token string" style="color:rgb(255, 121, 198)">"config"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> config_yaml</span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token number">200</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">def</span><span class="token plain"> </span><span class="token function" style="color:rgb(80, 250, 123)">record_download</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">tool_id</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> client_id</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> today </span><span class="token operator">=</span><span class="token plain"> date</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">today</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token comment" style="color:rgb(98, 114, 164)"># Use client_id if provided, otherwise generate anonymous fallback</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> effective_client_id </span><span class="token operator">=</span><span class="token plain"> client_id </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">or</span><span class="token plain"> </span><span class="token string-interpolation string" style="color:rgb(255, 121, 198)">f"anon_</span><span class="token string-interpolation interpolation punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token string-interpolation interpolation builtin" style="color:rgb(189, 147, 249)">hash</span><span class="token string-interpolation interpolation punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string-interpolation interpolation">request</span><span class="token string-interpolation interpolation punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token string-interpolation interpolation">remote_addr</span><span class="token string-interpolation interpolation punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token string-interpolation interpolation punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token string-interpolation string" style="color:rgb(255, 121, 198)">"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token comment" style="color:rgb(98, 114, 164)"># Dedupe: only count once per client per tool per day</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">try</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> db</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">download_stats</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">insert</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> tool_id</span><span class="token operator">=</span><span class="token plain">tool_id</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> client_id</span><span class="token operator">=</span><span class="token plain">effective_client_id</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> downloaded_at</span><span class="token operator">=</span><span class="token plain">today</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token comment" style="color:rgb(98, 114, 164)"># Increment counter (can be async/batch updated)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> db</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">execute</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">"UPDATE tools SET downloads = downloads + 1 WHERE id = ?"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token plain">tool_id</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">except</span><span class="token plain"> IntegrityError</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">pass</span><span class="token plain"> </span><span class="token comment" style="color:rgb(98, 114, 164)"># Already counted today, ignore</span><br></span></code></pre></div></div>
|
||
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="client-id-generation">Client ID Generation<a href="#client-id-generation" class="hash-link" aria-label="Direct link to Client ID Generation" title="Direct link to Client ID Generation" translate="no"></a></h3>
|
||
<p>CLI generates a persistent anonymous ID on first run:</p>
|
||
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#F8F8F2"><span class="token comment" style="color:rgb(98, 114, 164)"># In CLI, on first run</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">import</span><span class="token plain"> uuid</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">import</span><span class="token plain"> os</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">CONFIG_PATH </span><span class="token operator">=</span><span class="token plain"> os</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">path</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">expanduser</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">"~/.cmdforge/config.yaml"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">def</span><span class="token plain"> </span><span class="token function" style="color:rgb(80, 250, 123)">get_or_create_client_id</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> config </span><span class="token operator">=</span><span class="token plain"> load_config</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">if</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">'client_id'</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">not</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">in</span><span class="token plain"> config</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> config</span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token string" style="color:rgb(255, 121, 198)">'client_id'</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token plain"> </span><span class="token operator">=</span><span class="token plain"> </span><span class="token string-interpolation string" style="color:rgb(255, 121, 198)">f"anon_</span><span class="token string-interpolation interpolation punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token string-interpolation interpolation">uuid</span><span class="token string-interpolation interpolation punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token string-interpolation interpolation">uuid4</span><span class="token string-interpolation interpolation punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string-interpolation interpolation punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token string-interpolation interpolation punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token string-interpolation interpolation builtin" style="color:rgb(189, 147, 249)">hex</span><span class="token string-interpolation interpolation punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token string-interpolation interpolation punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token string-interpolation interpolation format-spec">16]</span><span class="token string-interpolation interpolation punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token string-interpolation string" style="color:rgb(255, 121, 198)">"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> save_config</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">config</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">return</span><span class="token plain"> config</span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token string" style="color:rgb(255, 121, 198)">'client_id'</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><br></span></code></pre></div></div>
|
||
<p><strong>Fallback when client_id missing:</strong></p>
|
||
<ul>
|
||
<li class="">If header <code>X-Client-ID</code> not sent, use IP hash as fallback</li>
|
||
<li class="">This still provides some dedupe for anonymous users</li>
|
||
<li class="">Logged users' downloads are attributed to their account instead</li>
|
||
</ul>
|
||
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="privacy-considerations">Privacy Considerations<a href="#privacy-considerations" class="hash-link" aria-label="Direct link to Privacy Considerations" title="Direct link to Privacy Considerations" translate="no"></a></h3>
|
||
<ul>
|
||
<li class="">No IP addresses stored in database</li>
|
||
<li class=""><code>client_id</code> is client-controlled and can be regenerated</li>
|
||
<li class="">Stats are aggregated (total count), not individual tracking</li>
|
||
</ul>
|
||
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="async-stats-strategy">Async Stats Strategy<a href="#async-stats-strategy" class="hash-link" aria-label="Direct link to Async Stats Strategy" title="Direct link to Async Stats Strategy" translate="no"></a></h3>
|
||
<p>To avoid DB contention on the hot download path:</p>
|
||
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#F8F8F2"><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">from</span><span class="token plain"> queue </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">import</span><span class="token plain"> Queue</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">from</span><span class="token plain"> threading </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">import</span><span class="token plain"> Thread</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token comment" style="color:rgb(98, 114, 164)"># In-memory queue for stats</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">stats_queue </span><span class="token operator">=</span><span class="token plain"> Queue</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">def</span><span class="token plain"> </span><span class="token function" style="color:rgb(80, 250, 123)">record_download_async</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">tool_id</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> client_id</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token triple-quoted-string string" style="color:rgb(255, 121, 198)">"""Non-blocking: enqueue for background processing"""</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> stats_queue</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">put</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">'tool_id'</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> tool_id</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">'client_id'</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> client_id</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">'date'</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> date</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">today</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">def</span><span class="token plain"> </span><span class="token function" style="color:rgb(80, 250, 123)">stats_worker</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token triple-quoted-string string" style="color:rgb(255, 121, 198)">"""Background thread: batch process stats every 5 seconds"""</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> batch </span><span class="token operator">=</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">while</span><span class="token plain"> </span><span class="token boolean">True</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">try</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> item </span><span class="token operator">=</span><span class="token plain"> stats_queue</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">get</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">timeout</span><span class="token operator">=</span><span class="token number">5</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> batch</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">append</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">item</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">except</span><span class="token plain"> Empty</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">if</span><span class="token plain"> batch</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> flush_batch</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">batch</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> batch </span><span class="token operator">=</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">def</span><span class="token plain"> </span><span class="token function" style="color:rgb(80, 250, 123)">flush_batch</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">batch</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token triple-quoted-string string" style="color:rgb(255, 121, 198)">"""Bulk insert with conflict ignore"""</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">with</span><span class="token plain"> db</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">transaction</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">for</span><span class="token plain"> item </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">in</span><span class="token plain"> batch</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">try</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> db</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">execute</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token triple-quoted-string string" style="color:rgb(255, 121, 198)">"""</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token triple-quoted-string string" style="color:rgb(255, 121, 198)"> INSERT INTO download_stats (tool_id, client_id, downloaded_at)</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token triple-quoted-string string" style="color:rgb(255, 121, 198)"> VALUES (?, ?, ?)</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token triple-quoted-string string" style="color:rgb(255, 121, 198)"> ON CONFLICT DO NOTHING</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token triple-quoted-string string" style="color:rgb(255, 121, 198)"> """</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token plain">item</span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token string" style="color:rgb(255, 121, 198)">'tool_id'</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> item</span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token string" style="color:rgb(255, 121, 198)">'client_id'</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> item</span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token string" style="color:rgb(255, 121, 198)">'date'</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">except</span><span class="token plain"> Exception </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">as</span><span class="token plain"> e</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> logger</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">warning</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string-interpolation string" style="color:rgb(255, 121, 198)">f"Stats insert failed: </span><span class="token string-interpolation interpolation punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token string-interpolation interpolation">e</span><span class="token string-interpolation interpolation punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token string-interpolation string" style="color:rgb(255, 121, 198)">"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token comment" style="color:rgb(98, 114, 164)"># Don't fail downloads for stats errors</span><br></span></code></pre></div></div>
|
||
<p><strong>Failure behavior:</strong> If stats DB write fails, log the error but don't fail the download. Stats are "best effort" - the download must succeed.</p>
|
||
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="search">Search<a href="#search" class="hash-link" aria-label="Direct link to Search" title="Direct link to Search" translate="no"></a></h2>
|
||
<ul>
|
||
<li class="">Primary search: SQLite FTS5 inside the API.</li>
|
||
<li class=""><code>index.json</code> provides offline CLI search and backup.</li>
|
||
<li class="">If FTS5 is stale, return results with <code>X-Search-Index-Stale: true</code>.</li>
|
||
</ul>
|
||
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="api-caching-strategy">API Caching Strategy<a href="#api-caching-strategy" class="hash-link" aria-label="Direct link to API Caching Strategy" title="Direct link to API Caching Strategy" translate="no"></a></h2>
|
||
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="cache-headers">Cache Headers<a href="#cache-headers" class="hash-link" aria-label="Direct link to Cache Headers" title="Direct link to Cache Headers" translate="no"></a></h3>
|
||
<table><thead><tr><th>Endpoint</th><th>Cache-Control</th><th>ETag</th><th>Notes</th></tr></thead><tbody><tr><td><code>GET /index.json</code></td><td><code>max-age=300, stale-while-revalidate=60</code></td><td>Yes</td><td>5 min cache, background refresh</td></tr><tr><td><code>GET /tools/{owner}/{name}</code></td><td><code>max-age=60</code></td><td>Yes</td><td>1 min cache</td></tr><tr><td><code>GET /tools/{owner}/{name}/download</code></td><td><code>max-age=3600, immutable</code></td><td>Yes</td><td>Immutable versions, 1 hour</td></tr><tr><td><code>GET /tools/search</code></td><td><code>no-cache</code></td><td>No</td><td>Always fresh</td></tr><tr><td><code>GET /categories</code></td><td><code>max-age=3600</code></td><td>Yes</td><td>Categories change rarely</td></tr></tbody></table>
|
||
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="etag-implementation">ETag Implementation<a href="#etag-implementation" class="hash-link" aria-label="Direct link to ETag Implementation" title="Direct link to ETag Implementation" translate="no"></a></h3>
|
||
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#F8F8F2"><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">import</span><span class="token plain"> hashlib</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">from</span><span class="token plain"> datetime </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">import</span><span class="token plain"> datetime</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">def</span><span class="token plain"> </span><span class="token function" style="color:rgb(80, 250, 123)">get_tool_etag</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">tool</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token triple-quoted-string string" style="color:rgb(255, 121, 198)">"""Generate ETag from tool identity (immutable versions don't change)"""</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token comment" style="color:rgb(98, 114, 164)"># Since versions are immutable, owner/name@version is stable</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token comment" style="color:rgb(98, 114, 164)"># Use published_at for extra safety (not updated_at, which doesn't exist)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> content </span><span class="token operator">=</span><span class="token plain"> </span><span class="token string-interpolation string" style="color:rgb(255, 121, 198)">f"</span><span class="token string-interpolation interpolation punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token string-interpolation interpolation">tool</span><span class="token string-interpolation interpolation punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token string-interpolation interpolation">owner</span><span class="token string-interpolation interpolation punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token string-interpolation string" style="color:rgb(255, 121, 198)">/</span><span class="token string-interpolation interpolation punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token string-interpolation interpolation">tool</span><span class="token string-interpolation interpolation punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token string-interpolation interpolation">name</span><span class="token string-interpolation interpolation punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token string-interpolation string" style="color:rgb(255, 121, 198)">@</span><span class="token string-interpolation interpolation punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token string-interpolation interpolation">tool</span><span class="token string-interpolation interpolation punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token string-interpolation interpolation">version</span><span class="token string-interpolation interpolation punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token string-interpolation string" style="color:rgb(255, 121, 198)">:</span><span class="token string-interpolation interpolation punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token string-interpolation interpolation">tool</span><span class="token string-interpolation interpolation punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token string-interpolation interpolation">published_at</span><span class="token string-interpolation interpolation punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token string-interpolation interpolation">isoformat</span><span class="token string-interpolation interpolation punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string-interpolation interpolation punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token string-interpolation interpolation punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token string-interpolation string" style="color:rgb(255, 121, 198)">"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">return</span><span class="token plain"> hashlib</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">md5</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">content</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">encode</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">hexdigest</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">def</span><span class="token plain"> </span><span class="token function" style="color:rgb(80, 250, 123)">get_index_etag</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token triple-quoted-string string" style="color:rgb(255, 121, 198)">"""Generate ETag from last sync timestamp"""</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> last_sync </span><span class="token operator">=</span><span class="token plain"> db</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">get_last_sync_time</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">return</span><span class="token plain"> hashlib</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">md5</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">last_sync</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">isoformat</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">encode</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">hexdigest</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token decorator annotation punctuation" style="color:rgb(248, 248, 242)">@app</span><span class="token decorator annotation punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token decorator annotation punctuation" style="color:rgb(248, 248, 242)">route</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">'/api/v1/tools/<owner>/<name>/download'</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">def</span><span class="token plain"> </span><span class="token function" style="color:rgb(80, 250, 123)">download_tool</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">owner</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> name</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> version </span><span class="token operator">=</span><span class="token plain"> request</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">args</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">get</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">'version'</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">'latest'</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> tool </span><span class="token operator">=</span><span class="token plain"> resolve_and_get_tool</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">owner</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> name</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> version</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> etag </span><span class="token operator">=</span><span class="token plain"> get_tool_etag</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">tool</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token comment" style="color:rgb(98, 114, 164)"># Check If-None-Match header</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">if</span><span class="token plain"> request</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">headers</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">get</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">'If-None-Match'</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token operator">==</span><span class="token plain"> etag</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">return</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">''</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token number">304</span><span class="token plain"> </span><span class="token comment" style="color:rgb(98, 114, 164)"># Not Modified</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> response </span><span class="token operator">=</span><span class="token plain"> jsonify</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"data"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"owner"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> tool</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">owner</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"name"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> tool</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">name</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"resolved_version"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> tool</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">version</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"config"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> tool</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">config_yaml</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> response</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">headers</span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token string" style="color:rgb(255, 121, 198)">'ETag'</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token plain"> </span><span class="token operator">=</span><span class="token plain"> etag</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> response</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">headers</span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token string" style="color:rgb(255, 121, 198)">'Cache-Control'</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token plain"> </span><span class="token operator">=</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">'max-age=3600, immutable'</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">return</span><span class="token plain"> response</span><br></span></code></pre></div></div>
|
||
<p><strong>Note:</strong> Since tool versions are immutable, the ETag based on <code>owner/name@version</code> is permanently stable. The <code>published_at</code> timestamp is included for defense-in-depth but won't change.</p>
|
||
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="db-vs-repo-read-strategy">DB vs Repo Read Strategy<a href="#db-vs-repo-read-strategy" class="hash-link" aria-label="Direct link to DB vs Repo Read Strategy" title="Direct link to DB vs Repo Read Strategy" translate="no"></a></h3>
|
||
<table><thead><tr><th>Scenario</th><th>Read From</th><th>Reason</th></tr></thead><tbody><tr><td>Normal operation</td><td>SQLite DB</td><td>Fast, indexed, FTS</td></tr><tr><td>DB empty/corrupted</td><td>Gitea repo</td><td>Fallback/recovery</td></tr><tr><td>Webhook sync in progress</td><td>DB (stale OK)</td><td>Avoid blocking reads</td></tr><tr><td>Search query</td><td>SQLite FTS5</td><td>Full-text search</td></tr><tr><td>Download specific version</td><td>DB, fallback to repo</td><td>DB is cache, repo is truth</td></tr></tbody></table>
|
||
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="staleness-detection">Staleness Detection<a href="#staleness-detection" class="hash-link" aria-label="Direct link to Staleness Detection" title="Direct link to Staleness Detection" translate="no"></a></h3>
|
||
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#F8F8F2"><span class="token plain">STALE_THRESHOLD </span><span class="token operator">=</span><span class="token plain"> timedelta</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">minutes</span><span class="token operator">=</span><span class="token number">10</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">def</span><span class="token plain"> </span><span class="token function" style="color:rgb(80, 250, 123)">is_db_stale</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> last_sync </span><span class="token operator">=</span><span class="token plain"> db</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">get_last_sync_time</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">return</span><span class="token plain"> datetime</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">utcnow</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token operator">-</span><span class="token plain"> last_sync </span><span class="token operator">></span><span class="token plain"> STALE_THRESHOLD</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token decorator annotation punctuation" style="color:rgb(248, 248, 242)">@app</span><span class="token decorator annotation punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token decorator annotation punctuation" style="color:rgb(248, 248, 242)">route</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">'/tools/search'</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">def</span><span class="token plain"> </span><span class="token function" style="color:rgb(80, 250, 123)">search_tools</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">q</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> results </span><span class="token operator">=</span><span class="token plain"> db</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">search_fts</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">q</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> response </span><span class="token operator">=</span><span class="token plain"> jsonify</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token string" style="color:rgb(255, 121, 198)">"results"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> results</span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">if</span><span class="token plain"> is_db_stale</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> response</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">headers</span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token string" style="color:rgb(255, 121, 198)">'X-Search-Index-Stale'</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token plain"> </span><span class="token operator">=</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">'true'</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> response</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">headers</span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token string" style="color:rgb(255, 121, 198)">'X-Last-Sync'</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token plain"> </span><span class="token operator">=</span><span class="token plain"> db</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">get_last_sync_time</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">isoformat</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">return</span><span class="token plain"> response</span><br></span></code></pre></div></div>
|
||
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="error-model">Error Model<a href="#error-model" class="hash-link" aria-label="Direct link to Error Model" title="Direct link to Error Model" translate="no"></a></h2>
|
||
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="response-envelopes">Response Envelopes<a href="#response-envelopes" class="hash-link" aria-label="Direct link to Response Envelopes" title="Direct link to Response Envelopes" translate="no"></a></h3>
|
||
<p><strong>Success response:</strong></p>
|
||
<div class="language-json codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-json codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#F8F8F2"><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token property">"data"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"> ... </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token property">"meta"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token property">"page"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token number">1</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token property">"per_page"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token number">20</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token property">"total"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token number">42</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token property">"total_pages"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token number">3</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><br></span></code></pre></div></div>
|
||
<p><strong>Error response:</strong></p>
|
||
<div class="language-json codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-json codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#F8F8F2"><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token property">"error"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token property">"code"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"TOOL_NOT_FOUND"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token property">"message"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"Tool 'foo/bar' does not exist"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token property">"details"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token property">"owner"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"foo"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token property">"name"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"bar"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token property">"suggestion"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"Did you mean 'rob/bar'?"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token property">"docs_url"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"https://cmdforge.brrd.tech/docs/errors#TOOL_NOT_FOUND"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><br></span></code></pre></div></div>
|
||
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="error-codes">Error Codes<a href="#error-codes" class="hash-link" aria-label="Direct link to Error Codes" title="Direct link to Error Codes" translate="no"></a></h3>
|
||
<table><thead><tr><th>Code</th><th>HTTP</th><th>Description</th></tr></thead><tbody><tr><td><code>TOOL_NOT_FOUND</code></td><td>404</td><td>Tool does not exist</td></tr><tr><td><code>VERSION_NOT_FOUND</code></td><td>404</td><td>Requested version doesn't exist</td></tr><tr><td><code>VERSION_EXISTS</code></td><td>409</td><td>Cannot overwrite published version</td></tr><tr><td><code>INVALID_VERSION</code></td><td>400</td><td>Version string is not valid semver</td></tr><tr><td><code>INVALID_CONSTRAINT</code></td><td>400</td><td>Version constraint syntax error</td></tr><tr><td><code>CONSTRAINT_UNSATISFIABLE</code></td><td>404</td><td>No version matches constraint</td></tr><tr><td><code>VALIDATION_ERROR</code></td><td>400</td><td>Tool config validation failed</td></tr><tr><td><code>UNAUTHORIZED</code></td><td>401</td><td>Missing or invalid auth token</td></tr><tr><td><code>FORBIDDEN</code></td><td>403</td><td>Token valid but lacks permission</td></tr><tr><td><code>RATE_LIMITED</code></td><td>429</td><td>Too many requests</td></tr><tr><td><code>SLUG_TAKEN</code></td><td>409</td><td>Namespace slug already registered</td></tr><tr><td><code>ACCOUNT_LOCKED</code></td><td>403</td><td>Too many failed login attempts</td></tr><tr><td><code>SERVER_ERROR</code></td><td>500</td><td>Internal error (logged for debugging)</td></tr></tbody></table>
|
||
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="error-scenarios-and-fallbacks">Error Scenarios and Fallbacks<a href="#error-scenarios-and-fallbacks" class="hash-link" aria-label="Direct link to Error Scenarios and Fallbacks" title="Direct link to Error Scenarios and Fallbacks" translate="no"></a></h2>
|
||
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="cli-error-handling">CLI Error Handling<a href="#cli-error-handling" class="hash-link" aria-label="Direct link to CLI Error Handling" title="Direct link to CLI Error Handling" translate="no"></a></h3>
|
||
<table><thead><tr><th>Scenario</th><th>CLI Behavior</th><th>User Message</th></tr></thead><tbody><tr><td>Registry offline</td><td>Use cached tools if available</td><td>"Registry unavailable. Using cached version."</td></tr><tr><td>Tool not found</td><td>Check cache, then fail</td><td>"Tool 'foo/bar' not found in registry or cache."</td></tr><tr><td>Version constraint unsatisfiable</td><td>Show available versions</td><td>"No version matches '>=5.0.0'. Available: 1.0.0, 1.1.0, 1.2.0"</td></tr><tr><td>Auth token expired</td><td>Prompt for new token</td><td>"Token expired. Please re-authenticate."</td></tr><tr><td>Rate limited</td><td>Wait and retry (backoff)</td><td>"Rate limited. Retrying in 30 seconds..."</td></tr><tr><td>Network timeout</td><td>Retry with backoff, then fail</td><td>"Connection timed out. Check your network."</td></tr></tbody></table>
|
||
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="validation-failure-details">Validation Failure Details<a href="#validation-failure-details" class="hash-link" aria-label="Direct link to Validation Failure Details" title="Direct link to Validation Failure Details" translate="no"></a></h3>
|
||
<p>When <code>VALIDATION_ERROR</code> occurs, provide specific field errors:</p>
|
||
<div class="language-json codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-json codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#F8F8F2"><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token property">"error"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token property">"code"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"VALIDATION_ERROR"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token property">"message"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"Tool configuration is invalid"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token property">"details"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token property">"errors"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token property">"path"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"steps[0].provider"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token property">"message"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"Provider 'gpt5' is not recognized"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token property">"allowed"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token string" style="color:rgb(255, 121, 198)">"claude"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"openai"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"ollama"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"mock"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token property">"path"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"version"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token property">"message"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"Version '1.0' is not valid semver (use '1.0.0')"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token property">"docs_url"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"https://cmdforge.brrd.tech/docs/tool-format"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><br></span></code></pre></div></div>
|
||
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="dependency-resolution-failures">Dependency Resolution Failures<a href="#dependency-resolution-failures" class="hash-link" aria-label="Direct link to Dependency Resolution Failures" title="Direct link to Dependency Resolution Failures" translate="no"></a></h3>
|
||
<p>When <code>cmdforge install</code> fails on a manifest:</p>
|
||
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#F8F8F2"><span class="token plain">$ cmdforge install</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">Error: Could not resolve all dependencies</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> rob/summarize@^2.0.0</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> ✗ No matching version (latest: 1.2.0)</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> alice/translate@>=1.0.0</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> ✓ Found 1.3.0</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">Suggestions:</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> - Update rob/summarize constraint to "^1.0.0"</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> - Contact the tool author for a v2 release</span><br></span></code></pre></div></div>
|
||
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="graceful-degradation">Graceful Degradation<a href="#graceful-degradation" class="hash-link" aria-label="Direct link to Graceful Degradation" title="Direct link to Graceful Degradation" translate="no"></a></h3>
|
||
<table><thead><tr><th>Component Down</th><th>Fallback Behavior</th></tr></thead><tbody><tr><td>API server</td><td>CLI uses <code>~/.cmdforge/registry/index.json</code> for search</td></tr><tr><td>Gitea repo</td><td>API serves from DB cache (may be stale)</td></tr><tr><td>FTS5 index</td><td>Fall back to LIKE queries (slower but works)</td></tr><tr><td>Network</td><td>Use locally installed tools, skip registry features</td></tr></tbody></table>
|
||
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="ux-requirements-clitui">UX Requirements (CLI/TUI)<a href="#ux-requirements-clitui" class="hash-link" aria-label="Direct link to UX Requirements (CLI/TUI)" title="Direct link to UX Requirements (CLI/TUI)" translate="no"></a></h2>
|
||
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="publishing-ux">Publishing UX<a href="#publishing-ux" class="hash-link" aria-label="Direct link to Publishing UX" title="Direct link to Publishing UX" translate="no"></a></h3>
|
||
<ul>
|
||
<li class="">
|
||
<p><code>cmdforge registry publish --dry-run</code> validates locally and shows what would be submitted:</p>
|
||
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#F8F8F2"><span class="token plain">$ cmdforge registry publish --dry-run</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">Validating tool...</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">✓ config.yaml is valid</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">✓ README.md exists (2.3 KB)</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">✓ Version 1.1.0 not yet published</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">Would submit:</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> Owner: rob</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> Name: summarize</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> Version: 1.1.0</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> Category: text-processing</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> Tags: summarization, ai, text</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">Config preview:</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">─────────────────────────────</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">name: summarize</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">version: "1.1.0"</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">description: Summarize text using AI</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">...</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">─────────────────────────────</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">Run without --dry-run to submit for review.</span><br></span></code></pre></div></div>
|
||
</li>
|
||
<li class="">
|
||
<p><strong>Version bump reminder:</strong> CLI warns if version hasn't changed from published:</p>
|
||
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#F8F8F2"><span class="token plain">⚠ Version 1.0.0 is already published. Bump version in config.yaml to publish changes.</span><br></span></code></pre></div></div>
|
||
</li>
|
||
<li class="">
|
||
<p>First-time publishing flow prompts for token and saves it to config.</p>
|
||
</li>
|
||
</ul>
|
||
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="progress-indicators">Progress Indicators<a href="#progress-indicators" class="hash-link" aria-label="Direct link to Progress Indicators" title="Direct link to Progress Indicators" translate="no"></a></h3>
|
||
<p>Long-running operations show progress:</p>
|
||
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#F8F8F2"><span class="token plain">$ cmdforge install</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">Installing project dependencies...</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> [1/3] rob/summarize@^1.0.0</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> Resolving version... 1.2.0</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> Downloading... done</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> Installing... done ✓</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> [2/3] alice/translate@>=2.0.0</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> Resolving version... 2.1.0</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> Downloading... done</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> Installing... done ✓</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> [3/3] official/code-review@*</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> Resolving version... 1.0.0</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> Downloading... done</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> Installing... done ✓</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">✓ Installed 3 tools</span><br></span></code></pre></div></div>
|
||
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#F8F8F2"><span class="token plain">$ cmdforge registry publish</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">Submitting rob/summarize@1.1.0...</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> Validating... done ✓</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> Uploading... done ✓</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> Creating PR... done ✓</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">✓ PR created: https://gitea.brrd.tech/rob/CmdForge-Registry/pulls/42</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">Your tool is pending review. You'll receive an email when it's approved.</span><br></span></code></pre></div></div>
|
||
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="tui-browse">TUI Browse<a href="#tui-browse" class="hash-link" aria-label="Direct link to TUI Browse" title="Direct link to TUI Browse" translate="no"></a></h3>
|
||
<p><code>cmdforge registry browse</code> opens a full-screen terminal UI:</p>
|
||
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#F8F8F2"><span class="token plain">┌─ CmdForge Registry ───────────────────────────────────────┐</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">│ Search: [________________] [All Categories ▼] [Sort: Popular ▼] │</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">├─────────────────────────────────────────────────────────────┤</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">│ │</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">│ ▶ rob/summarize v1.2.0 ⬇ 142 │</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">│ Summarize text using AI │</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">│ [text-processing] [ai] [summarization] │</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">│ │</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">│ alice/translate v2.1.0 ⬇ 98 │</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">│ Translate text between languages │</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">│ [text-processing] [translation] │</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">│ │</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">│ official/code-review v1.0.0 ⬇ 87 │</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">│ AI-powered code review │</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">│ [code] [review] [ai] │</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">│ │</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">├─────────────────────────────────────────────────────────────┤</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">│ ↑↓ Navigate Enter: Details i: Install /: Search q: Quit │</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">└─────────────────────────────────────────────────────────────┘</span><br></span></code></pre></div></div>
|
||
<p><strong>Keyboard shortcuts:</strong></p>
|
||
<table><thead><tr><th>Key</th><th>Action</th></tr></thead><tbody><tr><td><code>↑/↓</code> or <code>j/k</code></td><td>Navigate list</td></tr><tr><td><code>Enter</code></td><td>View tool details</td></tr><tr><td><code>i</code></td><td>Install selected tool</td></tr><tr><td><code>/</code></td><td>Focus search box</td></tr><tr><td><code>c</code></td><td>Change category filter</td></tr><tr><td><code>s</code></td><td>Change sort order</td></tr><tr><td><code>?</code></td><td>Show help</td></tr><tr><td><code>q</code></td><td>Quit</td></tr></tbody></table>
|
||
<p><strong>Virtual scrolling:</strong> For large tool lists (>100), use virtual scrolling to maintain performance.</p>
|
||
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="project-initialization">Project Initialization<a href="#project-initialization" class="hash-link" aria-label="Direct link to Project Initialization" title="Direct link to Project Initialization" translate="no"></a></h3>
|
||
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#F8F8F2"><span class="token plain">$ cmdforge init</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">Creating cmdforge.yaml...</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">Project name [my-project]: my-ai-project</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">Version [1.0.0]:</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">Would you like to add any tools? (search with 's', skip with Enter)</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">> s</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">Search: summ</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> 1. rob/summarize v1.2.0 - Summarize text using AI</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> 2. alice/summary v1.0.0 - Generate summaries</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">Add tool (number, or Enter to finish): 1</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">Added rob/summarize@^1.2.0</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">Add tool (number, or Enter to finish):</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">✓ Created cmdforge.yaml</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">name: my-ai-project</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">version: "1.0.0"</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">dependencies:</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> - name: rob/summarize</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> version: "^1.2.0"</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">Run 'cmdforge install' to install dependencies.</span><br></span></code></pre></div></div>
|
||
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="accessibility">Accessibility<a href="#accessibility" class="hash-link" aria-label="Direct link to Accessibility" title="Direct link to Accessibility" translate="no"></a></h3>
|
||
<ul>
|
||
<li class=""><strong>CLI:</strong> All output works with screen readers, no color-only information</li>
|
||
<li class=""><strong>TUI:</strong> Full keyboard navigation, high-contrast mode support</li>
|
||
<li class=""><strong>Web UI:</strong> WCAG 2.1 AA compliance target<!-- -->
|
||
<ul>
|
||
<li class="">Semantic HTML</li>
|
||
<li class="">ARIA labels for interactive elements</li>
|
||
<li class="">Focus management in modals</li>
|
||
<li class="">Skip links for navigation</li>
|
||
</ul>
|
||
</li>
|
||
</ul>
|
||
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="offline-cache">Offline Cache<a href="#offline-cache" class="hash-link" aria-label="Direct link to Offline Cache" title="Direct link to Offline Cache" translate="no"></a></h2>
|
||
<p>Cache registry index locally:</p>
|
||
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#F8F8F2"><span class="token plain">~/.cmdforge/registry/index.json</span><br></span></code></pre></div></div>
|
||
<p>Refresh when older than 24 hours; support <code>--offline</code> and <code>--refresh</code> flags.</p>
|
||
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="index-integrity">Index Integrity<a href="#index-integrity" class="hash-link" aria-label="Direct link to Index Integrity" title="Direct link to Index Integrity" translate="no"></a></h3>
|
||
<p>The cached <code>index.json</code> includes integrity metadata:</p>
|
||
<div class="language-json codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-json codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#F8F8F2"><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token property">"version"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"1.0"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token property">"generated_at"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"2025-01-20T12:00:00Z"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token property">"checksum"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"sha256:abc123..."</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token property">"tool_count"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token number">142</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token property">"tools"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token plain">...</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><br></span></code></pre></div></div>
|
||
<p><strong>API response headers:</strong></p>
|
||
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#F8F8F2"><span class="token plain">ETag: "abc123def456"</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">X-Index-Checksum: sha256:abc123...</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">X-Index-Generated: 2025-01-20T12:00:00Z</span><br></span></code></pre></div></div>
|
||
<p><strong>CLI verification:</strong></p>
|
||
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#F8F8F2"><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">def</span><span class="token plain"> </span><span class="token function" style="color:rgb(80, 250, 123)">verify_cached_index</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token triple-quoted-string string" style="color:rgb(255, 121, 198)">"""Verify cached index integrity on load"""</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> cached </span><span class="token operator">=</span><span class="token plain"> load_cached_index</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">if</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">not</span><span class="token plain"> cached</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">return</span><span class="token plain"> </span><span class="token boolean">None</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token comment" style="color:rgb(98, 114, 164)"># Verify checksum</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> content </span><span class="token operator">=</span><span class="token plain"> json</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">dumps</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">cached</span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token string" style="color:rgb(255, 121, 198)">'tools'</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> sort_keys</span><span class="token operator">=</span><span class="token boolean">True</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> computed </span><span class="token operator">=</span><span class="token plain"> hashlib</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">sha256</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">content</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">encode</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">hexdigest</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">if</span><span class="token plain"> computed </span><span class="token operator">!=</span><span class="token plain"> cached</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">get</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">'checksum'</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">''</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">replace</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">'sha256:'</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">''</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> logger</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">warning</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">"Cached index checksum mismatch, will refresh"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">return</span><span class="token plain"> </span><span class="token boolean">None</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">return</span><span class="token plain"> cached</span><br></span></code></pre></div></div>
|
||
<p><strong>Corruption handling:</strong></p>
|
||
<ul>
|
||
<li class="">If checksum fails, discard cache and fetch fresh</li>
|
||
<li class="">If partial write detected (missing fields), discard and refresh</li>
|
||
<li class="">CLI shows warning: "Cached index corrupted, fetching fresh copy..."</li>
|
||
</ul>
|
||
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="web-ui-vision">Web UI Vision<a href="#web-ui-vision" class="hash-link" aria-label="Direct link to Web UI Vision" title="Direct link to Web UI Vision" translate="no"></a></h2>
|
||
<p>The registry includes a full website, not just an API:</p>
|
||
<p><strong>Site structure:</strong></p>
|
||
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#F8F8F2"><span class="token plain">cmdforge.brrd.tech (or gitea.brrd.tech/registry)</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">├── / # Landing page</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">├── /tools # Browse all tools</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">├── /tools/{owner}/{name} # Tool detail page</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">├── /categories # Browse by category</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">├── /categories/{name} # Tools in category</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">├── /collections # Browse curated collections</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">├── /collections/{name} # Collection detail page</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">├── /search?q=... # Search results</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">├── /docs # Documentation</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">│ ├── /docs/getting-started</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">│ ├── /docs/creating-tools</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">│ ├── /docs/publishing</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">│ └── /docs/best-practices</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">├── /tutorials # Step-by-step guides</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">│ ├── /tutorials/first-tool</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">│ ├── /tutorials/chaining-steps</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">│ └── /tutorials/code-steps</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">├── /examples # Example projects</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">├── /blog # Updates, announcements (optional)</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">├── /register # Publisher registration</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">├── /login # Publisher login</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">├── /dashboard # Publisher dashboard</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">│ ├── /dashboard/tools # My published tools</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">│ ├── /dashboard/connections # Connected apps</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">│ └── /dashboard/settings # Account settings</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">└── /api/v1/... # API endpoints</span><br></span></code></pre></div></div>
|
||
<p><strong>Landing page content:</strong></p>
|
||
<ul>
|
||
<li class="">Hero: "Share and discover AI-powered CLI tools"</li>
|
||
<li class="">Quick install example</li>
|
||
<li class="">Featured/popular tools</li>
|
||
<li class="">Category highlights</li>
|
||
<li class="">"Get Started" CTA</li>
|
||
</ul>
|
||
<p><strong>Tool detail page:</strong></p>
|
||
<ul>
|
||
<li class="">Name, description, version, author</li>
|
||
<li class="">README rendered as markdown (sanitized)</li>
|
||
<li class="">Install command (copy-to-clipboard)</li>
|
||
<li class="">Version history</li>
|
||
<li class="">Download stats</li>
|
||
<li class="">Category/tags</li>
|
||
<li class="">"Report" button for abuse</li>
|
||
</ul>
|
||
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="readme-security">README Security<a href="#readme-security" class="hash-link" aria-label="Direct link to README Security" title="Direct link to README Security" translate="no"></a></h3>
|
||
<p>When rendering README markdown, apply XSS sanitization:</p>
|
||
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#F8F8F2"><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">import</span><span class="token plain"> bleach</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">from</span><span class="token plain"> markdown </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">import</span><span class="token plain"> markdown</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">ALLOWED_TAGS </span><span class="token operator">=</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">'h1'</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">'h2'</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">'h3'</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">'h4'</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">'h5'</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">'h6'</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">'p'</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">'br'</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">'hr'</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">'ul'</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">'ol'</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">'li'</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">'strong'</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">'em'</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">'code'</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">'pre'</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">'blockquote'</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">'a'</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">'img'</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">'table'</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">'thead'</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">'tbody'</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">'tr'</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">'th'</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">'td'</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">ALLOWED_ATTRS </span><span class="token operator">=</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">'a'</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token string" style="color:rgb(255, 121, 198)">'href'</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">'title'</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">'img'</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token string" style="color:rgb(255, 121, 198)">'src'</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">'alt'</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">'title'</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">'code'</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token string" style="color:rgb(255, 121, 198)">'class'</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token comment" style="color:rgb(98, 114, 164)"># for syntax highlighting</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">def</span><span class="token plain"> </span><span class="token function" style="color:rgb(80, 250, 123)">render_readme_safe</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">readme_raw</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> </span><span class="token builtin" style="color:rgb(189, 147, 249)">str</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token operator">-</span><span class="token operator">></span><span class="token plain"> </span><span class="token builtin" style="color:rgb(189, 147, 249)">str</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token triple-quoted-string string" style="color:rgb(255, 121, 198)">"""Convert markdown to sanitized HTML"""</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token comment" style="color:rgb(98, 114, 164)"># Convert markdown to HTML</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> html </span><span class="token operator">=</span><span class="token plain"> markdown</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">readme_raw</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> extensions</span><span class="token operator">=</span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token string" style="color:rgb(255, 121, 198)">'fenced_code'</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">'tables'</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token comment" style="color:rgb(98, 114, 164)"># Sanitize to prevent XSS</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> safe_html </span><span class="token operator">=</span><span class="token plain"> bleach</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">clean</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> html</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> tags</span><span class="token operator">=</span><span class="token plain">ALLOWED_TAGS</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> attributes</span><span class="token operator">=</span><span class="token plain">ALLOWED_ATTRS</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> strip</span><span class="token operator">=</span><span class="token boolean">True</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token comment" style="color:rgb(98, 114, 164)"># Linkify URLs</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> safe_html </span><span class="token operator">=</span><span class="token plain"> bleach</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">linkify</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">safe_html</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">return</span><span class="token plain"> safe_html</span><br></span></code></pre></div></div>
|
||
<p><strong>Storage strategy:</strong></p>
|
||
<ul>
|
||
<li class="">Store raw README in <code>tools.readme</code></li>
|
||
<li class="">Render and sanitize on request (or cache rendered HTML)</li>
|
||
<li class="">Never trust client-submitted HTML directly</li>
|
||
</ul>
|
||
<p><strong>Tech stack options:</strong></p>
|
||
<table><thead><tr><th>Option</th><th>Pros</th><th>Cons</th></tr></thead><tbody><tr><td>Flask + Jinja + Tailwind</td><td>Simple, Python-only, fast to build</td><td>Less interactive</td></tr><tr><td>FastAPI + Vue/React SPA</td><td>Modern, interactive</td><td>More complex, separate build</td></tr><tr><td>Astro/Next.js</td><td>Great SEO, static-first</td><td>Different stack (Node.js)</td></tr></tbody></table>
|
||
<p><strong>Recommendation:</strong> Flask + Jinja + Tailwind for v1</p>
|
||
<ul>
|
||
<li class="">Keeps everything in Python</li>
|
||
<li class="">Server-rendered is fine for a registry</li>
|
||
<li class="">Good SEO out of the box</li>
|
||
<li class="">Can add interactivity with Alpine.js or htmx if needed</li>
|
||
</ul>
|
||
<p><strong>Monetization considerations:</strong></p>
|
||
<ul>
|
||
<li class="">AdSense-compatible (server-rendered pages)</li>
|
||
<li class="">Analytics tracking for traffic insights</li>
|
||
<li class="">Future: sponsored tools, featured placements</li>
|
||
<li class="">Future: premium publisher tiers (more tools, priority review)</li>
|
||
</ul>
|
||
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="registry-curation-system">Registry Curation System<a href="#registry-curation-system" class="hash-link" aria-label="Direct link to Registry Curation System" title="Direct link to Registry Curation System" translate="no"></a></h2>
|
||
<p>The registry includes a moderation system for content curation, abuse prevention, and quality control.</p>
|
||
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="roles-and-permissions">Roles and Permissions<a href="#roles-and-permissions" class="hash-link" aria-label="Direct link to Roles and Permissions" title="Direct link to Roles and Permissions" translate="no"></a></h3>
|
||
<table><thead><tr><th>Role</th><th>Permissions</th></tr></thead><tbody><tr><td><code>user</code></td><td>Publish tools, manage own tools, view public content</td></tr><tr><td><code>moderator</code></td><td>All user permissions + approve/reject tools, resolve reports, view all publishers</td></tr><tr><td><code>admin</code></td><td>All moderator permissions + ban/unban publishers, change roles, delete tools, view audit log</td></tr></tbody></table>
|
||
<p><strong>Database columns:</strong></p>
|
||
<div class="language-sql codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-sql codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#F8F8F2"><span class="token comment" style="color:rgb(98, 114, 164)">-- In publishers table</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">role </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">TEXT</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">DEFAULT</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">'user'</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token comment" style="color:rgb(98, 114, 164)">-- 'user', 'moderator', 'admin'</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">banned </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">INTEGER</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">DEFAULT</span><span class="token plain"> </span><span class="token number">0</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">banned_at </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">TIMESTAMP</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">banned_by </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">TEXT</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">ban_reason </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">TEXT</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token comment" style="color:rgb(98, 114, 164)">-- In tools table</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">visibility </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">TEXT</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">DEFAULT</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">'public'</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token comment" style="color:rgb(98, 114, 164)">-- 'public', 'private', 'unlisted'</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">moderation_status </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">TEXT</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">DEFAULT</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">'pending'</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token comment" style="color:rgb(98, 114, 164)">-- 'pending', 'approved', 'rejected', 'removed'</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">moderation_note </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">TEXT</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">moderated_by </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">TEXT</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">moderated_at </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">TIMESTAMP</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><br></span></code></pre></div></div>
|
||
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="tool-visibility">Tool Visibility<a href="#tool-visibility" class="hash-link" aria-label="Direct link to Tool Visibility" title="Direct link to Tool Visibility" translate="no"></a></h3>
|
||
<table><thead><tr><th>Visibility</th><th>In Search/List</th><th>Direct Link</th><th>Who Can See</th></tr></thead><tbody><tr><td><code>public</code></td><td>Yes (if approved)</td><td>Yes (if approved)</td><td>Everyone</td></tr><tr><td><code>private</code></td><td>No</td><td>No</td><td>Owner only</td></tr><tr><td><code>unlisted</code></td><td>No</td><td>Yes (if approved)</td><td>Anyone with link</td></tr></tbody></table>
|
||
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="moderation-workflow">Moderation Workflow<a href="#moderation-workflow" class="hash-link" aria-label="Direct link to Moderation Workflow" title="Direct link to Moderation Workflow" translate="no"></a></h3>
|
||
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#F8F8F2"><span class="token plain">Tool Published</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> │</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> ├── visibility = 'private' or 'unlisted'</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> │ └── Auto-approved (moderation_status = 'approved')</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> │</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> └── visibility = 'public'</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> └── moderation_status = 'pending'</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> │</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> ├── Moderator approves → 'approved' → Visible in search</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> ├── Moderator rejects → 'rejected' → Not visible</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> └── Moderator removes → 'removed' → Removed from view</span><br></span></code></pre></div></div>
|
||
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="admin-api-endpoints">Admin API Endpoints<a href="#admin-api-endpoints" class="hash-link" aria-label="Direct link to Admin API Endpoints" title="Direct link to Admin API Endpoints" translate="no"></a></h3>
|
||
<p><strong>Tool Moderation (moderator+):</strong></p>
|
||
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#F8F8F2"><span class="token plain">GET /api/v1/admin/tools/pending # List pending tools</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">GET /api/v1/admin/tools/<id> # Get full tool details for review</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">POST /api/v1/admin/tools/<id>/approve # Approve a tool</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">POST /api/v1/admin/tools/<id>/reject # Reject with reason (required)</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">POST /api/v1/admin/tools/<id>/remove # Soft-delete approved tool</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">DELETE /api/v1/admin/tools/<id> # Hard delete (admin only)</span><br></span></code></pre></div></div>
|
||
<p><strong>Tool Detail Response (<code>GET /api/v1/admin/tools/<id></code>):</strong></p>
|
||
<div class="language-json codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-json codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#F8F8F2"><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token property">"data"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token property">"id"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token number">123</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token property">"owner"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"alice"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token property">"name"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"my-tool"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token property">"version"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"1.0.0"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token property">"description"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"Tool description"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token property">"category"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"Text Processing"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token property">"tags"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"ai,text"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token property">"published_at"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"2025-01-15T10:30:00Z"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token property">"publisher_name"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"Alice Smith"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token property">"visibility"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"public"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token property">"moderation_status"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"pending"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token property">"scrutiny_status"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"pending_review"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token property">"scrutiny_report"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token property">"findings"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token property">"check"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"shell_commands"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token property">"result"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"warning"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token property">"message"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"..."</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token property">"suggestion"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"..."</span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token property">"config"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token property">"name"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"my-tool"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token property">"description"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"..."</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token property">"arguments"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token plain">...</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token property">"steps"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token plain">...</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token property">"readme"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"# My Tool\n\nDocumentation here..."</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><br></span></code></pre></div></div>
|
||
<p><strong>Publisher Management (moderator+ to view, admin to modify):</strong></p>
|
||
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#F8F8F2"><span class="token plain">GET /api/v1/admin/publishers # List all publishers</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">GET /api/v1/admin/publishers/<id> # Publisher details</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">POST /api/v1/admin/publishers/<id>/ban # Ban with reason (admin)</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">POST /api/v1/admin/publishers/<id>/unban # Unban (admin)</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">POST /api/v1/admin/publishers/<id>/role # Change role (admin)</span><br></span></code></pre></div></div>
|
||
<p><strong>Reports (moderator+):</strong></p>
|
||
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#F8F8F2"><span class="token plain">GET /api/v1/admin/reports # List reports</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">POST /api/v1/admin/reports/<id>/resolve # Resolve with action</span><br></span></code></pre></div></div>
|
||
<p><strong>Audit Log (admin only):</strong></p>
|
||
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#F8F8F2"><span class="token plain">GET /api/v1/admin/audit-log # View moderation history</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> ?target_type=tool|publisher</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> ?target_id=<id></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> ?actor_id=<id></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> ?since=<date></span><br></span></code></pre></div></div>
|
||
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="ban-behavior">Ban Behavior<a href="#ban-behavior" class="hash-link" aria-label="Direct link to Ban Behavior" title="Direct link to Ban Behavior" translate="no"></a></h3>
|
||
<p>When a publisher is banned:</p>
|
||
<ol>
|
||
<li class=""><code>banned</code> set to 1, <code>banned_at</code>, <code>banned_by</code>, <code>ban_reason</code> recorded</li>
|
||
<li class="">All active API tokens revoked</li>
|
||
<li class="">All their tools set to <code>moderation_status = 'removed'</code></li>
|
||
<li class="">Action logged to audit trail</li>
|
||
</ol>
|
||
<p>Banned publishers see error on any authenticated API call:</p>
|
||
<div class="language-json codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-json codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#F8F8F2"><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token property">"error"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token property">"code"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"ACCOUNT_BANNED"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token property">"message"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"Your account has been banned: <reason>"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><br></span></code></pre></div></div>
|
||
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="report-resolution-actions">Report Resolution Actions<a href="#report-resolution-actions" class="hash-link" aria-label="Direct link to Report Resolution Actions" title="Direct link to Report Resolution Actions" translate="no"></a></h3>
|
||
<table><thead><tr><th>Action</th><th>Effect</th></tr></thead><tbody><tr><td><code>dismiss</code></td><td>Close report, no action taken</td></tr><tr><td><code>warn</code></td><td>Close report, no automated action (manual warning)</td></tr><tr><td><code>remove_tool</code></td><td>Remove the reported tool</td></tr><tr><td><code>ban_publisher</code></td><td>Ban the tool's publisher</td></tr></tbody></table>
|
||
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="audit-log">Audit Log<a href="#audit-log" class="hash-link" aria-label="Direct link to Audit Log" title="Direct link to Audit Log" translate="no"></a></h3>
|
||
<p>All moderation actions are logged:</p>
|
||
<div class="language-sql codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-sql codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#F8F8F2"><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">CREATE</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">TABLE</span><span class="token plain"> audit_log </span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> id </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">INTEGER</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">PRIMARY</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">KEY</span><span class="token plain"> AUTOINCREMENT</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">action</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">TEXT</span><span class="token plain"> </span><span class="token operator">NOT</span><span class="token plain"> </span><span class="token boolean">NULL</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token comment" style="color:rgb(98, 114, 164)">-- 'approve_tool', 'reject_tool', 'ban_publisher', etc.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> target_type </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">TEXT</span><span class="token plain"> </span><span class="token operator">NOT</span><span class="token plain"> </span><span class="token boolean">NULL</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token comment" style="color:rgb(98, 114, 164)">-- 'tool', 'publisher', 'report'</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> target_id </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">TEXT</span><span class="token plain"> </span><span class="token operator">NOT</span><span class="token plain"> </span><span class="token boolean">NULL</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> actor_id </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">TEXT</span><span class="token plain"> </span><span class="token operator">NOT</span><span class="token plain"> </span><span class="token boolean">NULL</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token comment" style="color:rgb(98, 114, 164)">-- Who performed the action</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> details </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">TEXT</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token comment" style="color:rgb(98, 114, 164)">-- JSON with additional context</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> created_at </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">TIMESTAMP</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">DEFAULT</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">CURRENT_TIMESTAMP</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><br></span></code></pre></div></div>
|
||
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="web-ui-admin-dashboard">Web UI Admin Dashboard<a href="#web-ui-admin-dashboard" class="hash-link" aria-label="Direct link to Web UI Admin Dashboard" title="Direct link to Web UI Admin Dashboard" translate="no"></a></h3>
|
||
<p>Moderators and admins see an "Admin Panel" link in their dashboard sidebar leading to:</p>
|
||
<ul>
|
||
<li class=""><code>/dashboard/admin</code> - Overview with pending counts</li>
|
||
<li class=""><code>/dashboard/admin/pending</code> - Pending tools queue</li>
|
||
<li class=""><code>/dashboard/admin/publishers</code> - Publisher management</li>
|
||
<li class=""><code>/dashboard/admin/reports</code> - Report queue</li>
|
||
</ul>
|
||
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="pending-tools-review-page">Pending Tools Review Page<a href="#pending-tools-review-page" class="hash-link" aria-label="Direct link to Pending Tools Review Page" title="Direct link to Pending Tools Review Page" translate="no"></a></h4>
|
||
<p>The pending tools page (<code>/dashboard/admin/pending</code>) provides a comprehensive interface for reviewing submitted tools:</p>
|
||
<p><strong>Table View:</strong></p>
|
||
<ul>
|
||
<li class="">Tool name (clickable to open detail modal)</li>
|
||
<li class="">Publisher name and category</li>
|
||
<li class="">Scrutiny status with expandable warnings</li>
|
||
<li class="">Submission date</li>
|
||
<li class="">Approve/Reject action buttons</li>
|
||
</ul>
|
||
<p><strong>Pagination:</strong></p>
|
||
<ul>
|
||
<li class="">Page number links with ellipsis for large ranges</li>
|
||
<li class="">First (<code>«</code>) and Last (<code>»</code>) page buttons</li>
|
||
<li class="">Previous/Next navigation</li>
|
||
<li class="">"Page X of Y (total)" indicator</li>
|
||
</ul>
|
||
<p><strong>Tool Detail Modal:</strong></p>
|
||
<p>Clicking a tool name opens a draggable modal showing:</p>
|
||
<ol>
|
||
<li class="">
|
||
<p><strong>Scrutiny Warnings</strong> - Yellow warning boxes at the top showing any security or quality concerns from automated analysis, including:</p>
|
||
<ul>
|
||
<li class="">Check name (e.g., "shell_commands", "network_access")</li>
|
||
<li class="">Warning message</li>
|
||
<li class="">Suggestion for resolution</li>
|
||
</ul>
|
||
</li>
|
||
<li class="">
|
||
<p><strong>Description</strong> - Tool's description text</p>
|
||
</li>
|
||
<li class="">
|
||
<p><strong>Arguments</strong> - List of tool arguments with flags, variables, and descriptions</p>
|
||
</li>
|
||
<li class="">
|
||
<p><strong>Steps</strong> - Full display of all tool steps:</p>
|
||
<ul>
|
||
<li class=""><strong>Prompt steps</strong>: Shows provider, output variable, and full prompt content</li>
|
||
<li class=""><strong>Code steps</strong>: Shows output variable and code with syntax highlighting (dark theme)</li>
|
||
<li class=""><strong>Tool steps</strong>: Shows tool name and arguments</li>
|
||
</ul>
|
||
</li>
|
||
<li class="">
|
||
<p><strong>README</strong> - Full README content if provided</p>
|
||
</li>
|
||
</ol>
|
||
<p><strong>Modal Features:</strong></p>
|
||
<ul>
|
||
<li class="">Draggable by header bar</li>
|
||
<li class="">Scrollable content area (header and buttons stay fixed)</li>
|
||
<li class="">Approve/Reject buttons in modal footer</li>
|
||
<li class="">Dark overlay prevents interaction with page behind</li>
|
||
<li class="">Background scroll locked while modal is open</li>
|
||
<li class="">Closes with X button or after action</li>
|
||
</ul>
|
||
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="creating-the-first-admin">Creating the First Admin<a href="#creating-the-first-admin" class="hash-link" aria-label="Direct link to Creating the First Admin" title="Direct link to Creating the First Admin" translate="no"></a></h3>
|
||
<p>After deployment, create the first admin via direct SQL:</p>
|
||
<div class="language-sql codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-sql codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#F8F8F2"><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">UPDATE</span><span class="token plain"> publishers </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">SET</span><span class="token plain"> role </span><span class="token operator">=</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">'admin'</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">WHERE</span><span class="token plain"> slug </span><span class="token operator">=</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">'rob'</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><br></span></code></pre></div></div>
|
||
<p>Subsequent admins can be promoted via the web UI or API.</p>
|
||
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="implementation-phases">Implementation Phases<a href="#implementation-phases" class="hash-link" aria-label="Direct link to Implementation Phases" title="Direct link to Implementation Phases" translate="no"></a></h2>
|
||
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="phase-1-foundation">Phase 1: Foundation<a href="#phase-1-foundation" class="hash-link" aria-label="Direct link to Phase 1: Foundation" title="Direct link to Phase 1: Foundation" translate="no"></a></h3>
|
||
<ul>
|
||
<li class="">Define <code>cmdforge.yaml</code> manifest format</li>
|
||
<li class="">Implement tool resolution order (local → global → registry)</li>
|
||
<li class="">Create CmdForge-Registry repo on Gitea (bootstrap)</li>
|
||
<li class="">Add 3-5 example tools to seed the registry</li>
|
||
</ul>
|
||
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="phase-2-core-backend">Phase 2: Core Backend<a href="#phase-2-core-backend" class="hash-link" aria-label="Direct link to Phase 2: Core Backend" title="Direct link to Phase 2: Core Backend" translate="no"></a></h3>
|
||
<ul>
|
||
<li class="">Set up Flask/FastAPI project structure</li>
|
||
<li class="">Implement SQLite database schema</li>
|
||
<li class="">Build core API endpoints (list, search, get, download)</li>
|
||
<li class="">Implement webhook receiver for Gitea sync</li>
|
||
<li class="">Set up HMAC verification</li>
|
||
</ul>
|
||
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="phase-3-cli-commands">Phase 3: CLI Commands<a href="#phase-3-cli-commands" class="hash-link" aria-label="Direct link to Phase 3: CLI Commands" title="Direct link to Phase 3: CLI Commands" translate="no"></a></h3>
|
||
<ul>
|
||
<li class=""><code>cmdforge registry search</code></li>
|
||
<li class=""><code>cmdforge registry install</code></li>
|
||
<li class=""><code>cmdforge registry info</code></li>
|
||
<li class=""><code>cmdforge registry browse</code> (TUI)</li>
|
||
<li class="">Local index caching</li>
|
||
</ul>
|
||
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="phase-4-publishing">Phase 4: Publishing<a href="#phase-4-publishing" class="hash-link" aria-label="Direct link to Phase 4: Publishing" title="Direct link to Phase 4: Publishing" translate="no"></a></h3>
|
||
<ul>
|
||
<li class="">Publisher registration (web UI)</li>
|
||
<li class="">Token management</li>
|
||
<li class=""><code>cmdforge registry publish</code> command</li>
|
||
<li class="">PR creation via Gitea API</li>
|
||
<li class="">CI validation workflows</li>
|
||
</ul>
|
||
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="phase-5-project-dependencies">Phase 5: Project Dependencies<a href="#phase-5-project-dependencies" class="hash-link" aria-label="Direct link to Phase 5: Project Dependencies" title="Direct link to Phase 5: Project Dependencies" translate="no"></a></h3>
|
||
<ul>
|
||
<li class=""><code>cmdforge install</code> (from manifest)</li>
|
||
<li class=""><code>cmdforge add</code> command</li>
|
||
<li class="">Runtime override application</li>
|
||
<li class="">Dependency resolution</li>
|
||
</ul>
|
||
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="phase-6-smart-features">Phase 6: Smart Features<a href="#phase-6-smart-features" class="hash-link" aria-label="Direct link to Phase 6: Smart Features" title="Direct link to Phase 6: Smart Features" translate="no"></a></h3>
|
||
<ul>
|
||
<li class="">SQLite FTS5 search index</li>
|
||
<li class="">AI-powered auto-categorization</li>
|
||
<li class="">Duplicate/similarity detection</li>
|
||
<li class="">Security scanning</li>
|
||
</ul>
|
||
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="phase-7-full-web-ui">Phase 7: Full Web UI<a href="#phase-7-full-web-ui" class="hash-link" aria-label="Direct link to Phase 7: Full Web UI" title="Direct link to Phase 7: Full Web UI" translate="no"></a></h3>
|
||
<ul>
|
||
<li class="">Landing page</li>
|
||
<li class="">Tool browsing/search pages</li>
|
||
<li class="">Tool detail pages with README rendering</li>
|
||
<li class="">Publisher dashboard</li>
|
||
<li class="">Documentation/tutorials section</li>
|
||
</ul>
|
||
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="phase-8-polish--scale">Phase 8: Polish & Scale<a href="#phase-8-polish--scale" class="hash-link" aria-label="Direct link to Phase 8: Polish & Scale" title="Direct link to Phase 8: Polish & Scale" translate="no"></a></h3>
|
||
<ul>
|
||
<li class="">Rate limiting</li>
|
||
<li class="">Abuse reporting</li>
|
||
<li class="">Analytics integration</li>
|
||
<li class="">Performance optimization</li>
|
||
<li class="">Monitoring/alerting</li>
|
||
</ul></div></article><nav class="docusaurus-mt-lg pagination-nav" aria-label="Docs pages"><a class="pagination-nav__link pagination-nav__link--prev" href="/rob/CmdForge/reference/providers"><div class="pagination-nav__sublabel">Previous</div><div class="pagination-nav__label">Provider Setup</div></a><a class="pagination-nav__link pagination-nav__link--next" href="/rob/CmdForge/reference/meta-tools"><div class="pagination-nav__sublabel">Next</div><div class="pagination-nav__label">Meta-Tools</div></a></nav></div></div><div class="col col--3"><div class="tableOfContents_bqdL thin-scrollbar theme-doc-toc-desktop"><ul class="table-of-contents table-of-contents__left-border"><li><a href="#purpose" class="table-of-contents__link toc-highlight">Purpose</a></li><li><a href="#terminology" class="table-of-contents__link toc-highlight">Terminology</a></li><li><a href="#diagram-references" class="table-of-contents__link toc-highlight">Diagram References</a></li><li><a href="#system-overview" class="table-of-contents__link toc-highlight">System Overview</a><ul><li><a href="#pagination" class="table-of-contents__link toc-highlight">Pagination</a></li><li><a href="#input-constraints" class="table-of-contents__link toc-highlight">Input Constraints</a></li><li><a href="#sort-fields-and-indexes" class="table-of-contents__link toc-highlight">Sort Fields and Indexes</a></li><li><a href="#tags-endpoint" class="table-of-contents__link toc-highlight">Tags Endpoint</a></li><li><a href="#advanced-search" class="table-of-contents__link toc-highlight">Advanced Search</a></li><li><a href="#api-version-compatibility" class="table-of-contents__link toc-highlight">API Version Compatibility</a></li></ul></li><li><a href="#source-of-truth" class="table-of-contents__link toc-highlight">Source of Truth</a></li><li><a href="#namespacing-and-paths" class="table-of-contents__link toc-highlight">Namespacing and Paths</a><ul><li><a href="#namespace-identity" class="table-of-contents__link toc-highlight">Namespace Identity</a></li></ul></li><li><a href="#tool-format-registry--local" class="table-of-contents__link toc-highlight">Tool Format (Registry == Local)</a><ul><li><a href="#attribution-and-source-fields" class="table-of-contents__link toc-highlight">Attribution and Source Fields</a></li></ul></li><li><a href="#collections" class="table-of-contents__link toc-highlight">Collections</a><ul><li><a href="#collection-structure" class="table-of-contents__link toc-highlight">Collection Structure</a></li><li><a href="#collections-api" class="table-of-contents__link toc-highlight">Collections API</a></li><li><a href="#cli-commands" class="table-of-contents__link toc-highlight">CLI Commands</a></li><li><a href="#admin-collections-api" class="table-of-contents__link toc-highlight">Admin Collections API</a></li></ul></li><li><a href="#versioning-and-immutability" class="table-of-contents__link toc-highlight">Versioning and Immutability</a><ul><li><a href="#yank-policy" class="table-of-contents__link toc-highlight">Yank Policy</a></li><li><a href="#version-format" class="table-of-contents__link toc-highlight">Version Format</a></li><li><a href="#version-constraints" class="table-of-contents__link toc-highlight">Version Constraints</a></li><li><a href="#version-resolution-rules" class="table-of-contents__link toc-highlight">Version Resolution Rules</a></li><li><a href="#prerelease-handling" class="table-of-contents__link toc-highlight">Prerelease Handling</a></li><li><a href="#download-endpoint-version-selection" class="table-of-contents__link toc-highlight">Download Endpoint Version Selection</a></li></ul></li><li><a href="#tool-resolution-order" class="table-of-contents__link toc-highlight">Tool Resolution Order</a><ul><li><a href="#official-namespace" class="table-of-contents__link toc-highlight">Official Namespace</a></li></ul></li><li><a href="#auto-fetch-behavior" class="table-of-contents__link toc-highlight">Auto-Fetch Behavior</a><ul><li><a href="#wrapper-script-collisions" class="table-of-contents__link toc-highlight">Wrapper Script Collisions</a></li></ul></li><li><a href="#project-manifest-cmdforgeyaml" class="table-of-contents__link toc-highlight">Project Manifest (cmdforge.yaml)</a></li><li><a href="#cli-config-and-tokens" class="table-of-contents__link toc-highlight">CLI Config and Tokens</a></li><li><a href="#publishing-and-auth" class="table-of-contents__link toc-highlight">Publishing and Auth</a><ul><li><a href="#publish-idempotency-and-edge-cases" class="table-of-contents__link toc-highlight">Publish Idempotency and Edge Cases</a></li></ul></li><li><a href="#publisher-registration" class="table-of-contents__link toc-highlight">Publisher Registration</a><ul><li><a href="#authentication-security" class="table-of-contents__link toc-highlight">Authentication Security</a></li><li><a href="#token-scopes-and-authorization" class="table-of-contents__link toc-highlight">Token Scopes and Authorization</a></li><li><a href="#web-session-security" class="table-of-contents__link toc-highlight">Web Session Security</a></li></ul></li><li><a href="#cli-commands-reference" class="table-of-contents__link toc-highlight">CLI Commands Reference</a><ul><li><a href="#registry-commands" class="table-of-contents__link toc-highlight">Registry Commands</a></li><li><a href="#project-commands" class="table-of-contents__link toc-highlight">Project Commands</a></li><li><a href="#config-commands" class="table-of-contents__link toc-highlight">Config Commands</a></li><li><a href="#flags-available-on-most-commands" class="table-of-contents__link toc-highlight">Flags available on most commands</a></li></ul></li><li><a href="#publish-state-tracking" class="table-of-contents__link toc-highlight">Publish State Tracking</a><ul><li><a href="#local-state-storage" class="table-of-contents__link toc-highlight">Local State Storage</a></li><li><a href="#visual-indicators" class="table-of-contents__link toc-highlight">Visual Indicators</a></li><li><a href="#automatic-status-sync" class="table-of-contents__link toc-highlight">Automatic Status Sync</a></li><li><a href="#manual-sync" class="table-of-contents__link toc-highlight">Manual Sync</a></li><li><a href="#hash-computation" class="table-of-contents__link toc-highlight">Hash Computation</a></li><li><a href="#api-endpoint" class="table-of-contents__link toc-highlight">API Endpoint</a></li></ul></li><li><a href="#webhooks-and-security" class="table-of-contents__link toc-highlight">Webhooks and Security</a><ul><li><a href="#hmac-verification" class="table-of-contents__link toc-highlight">HMAC Verification</a></li><li><a href="#replay-protection" class="table-of-contents__link toc-highlight">Replay Protection</a></li><li><a href="#sync-job-locking" class="table-of-contents__link toc-highlight">Sync Job Locking</a></li><li><a href="#atomic-sync-strategy" class="table-of-contents__link toc-highlight">Atomic Sync Strategy</a></li><li><a href="#error-handling" class="table-of-contents__link toc-highlight">Error Handling</a></li></ul></li><li><a href="#automated-ci-validation" class="table-of-contents__link toc-highlight">Automated CI Validation</a></li><li><a href="#registry-repository-structure" class="table-of-contents__link toc-highlight">Registry Repository Structure</a></li><li><a href="#download-stats" class="table-of-contents__link toc-highlight">Download Stats</a><ul><li><a href="#counting-methodology" class="table-of-contents__link toc-highlight">Counting Methodology</a></li><li><a href="#client-id-generation" class="table-of-contents__link toc-highlight">Client ID Generation</a></li><li><a href="#privacy-considerations" class="table-of-contents__link toc-highlight">Privacy Considerations</a></li><li><a href="#async-stats-strategy" class="table-of-contents__link toc-highlight">Async Stats Strategy</a></li></ul></li><li><a href="#search" class="table-of-contents__link toc-highlight">Search</a></li><li><a href="#api-caching-strategy" class="table-of-contents__link toc-highlight">API Caching Strategy</a><ul><li><a href="#cache-headers" class="table-of-contents__link toc-highlight">Cache Headers</a></li><li><a href="#etag-implementation" class="table-of-contents__link toc-highlight">ETag Implementation</a></li><li><a href="#db-vs-repo-read-strategy" class="table-of-contents__link toc-highlight">DB vs Repo Read Strategy</a></li><li><a href="#staleness-detection" class="table-of-contents__link toc-highlight">Staleness Detection</a></li></ul></li><li><a href="#error-model" class="table-of-contents__link toc-highlight">Error Model</a><ul><li><a href="#response-envelopes" class="table-of-contents__link toc-highlight">Response Envelopes</a></li><li><a href="#error-codes" class="table-of-contents__link toc-highlight">Error Codes</a></li></ul></li><li><a href="#error-scenarios-and-fallbacks" class="table-of-contents__link toc-highlight">Error Scenarios and Fallbacks</a><ul><li><a href="#cli-error-handling" class="table-of-contents__link toc-highlight">CLI Error Handling</a></li><li><a href="#validation-failure-details" class="table-of-contents__link toc-highlight">Validation Failure Details</a></li><li><a href="#dependency-resolution-failures" class="table-of-contents__link toc-highlight">Dependency Resolution Failures</a></li><li><a href="#graceful-degradation" class="table-of-contents__link toc-highlight">Graceful Degradation</a></li></ul></li><li><a href="#ux-requirements-clitui" class="table-of-contents__link toc-highlight">UX Requirements (CLI/TUI)</a><ul><li><a href="#publishing-ux" class="table-of-contents__link toc-highlight">Publishing UX</a></li><li><a href="#progress-indicators" class="table-of-contents__link toc-highlight">Progress Indicators</a></li><li><a href="#tui-browse" class="table-of-contents__link toc-highlight">TUI Browse</a></li><li><a href="#project-initialization" class="table-of-contents__link toc-highlight">Project Initialization</a></li><li><a href="#accessibility" class="table-of-contents__link toc-highlight">Accessibility</a></li></ul></li><li><a href="#offline-cache" class="table-of-contents__link toc-highlight">Offline Cache</a><ul><li><a href="#index-integrity" class="table-of-contents__link toc-highlight">Index Integrity</a></li></ul></li><li><a href="#web-ui-vision" class="table-of-contents__link toc-highlight">Web UI Vision</a><ul><li><a href="#readme-security" class="table-of-contents__link toc-highlight">README Security</a></li></ul></li><li><a href="#registry-curation-system" class="table-of-contents__link toc-highlight">Registry Curation System</a><ul><li><a href="#roles-and-permissions" class="table-of-contents__link toc-highlight">Roles and Permissions</a></li><li><a href="#tool-visibility" class="table-of-contents__link toc-highlight">Tool Visibility</a></li><li><a href="#moderation-workflow" class="table-of-contents__link toc-highlight">Moderation Workflow</a></li><li><a href="#admin-api-endpoints" class="table-of-contents__link toc-highlight">Admin API Endpoints</a></li><li><a href="#ban-behavior" class="table-of-contents__link toc-highlight">Ban Behavior</a></li><li><a href="#report-resolution-actions" class="table-of-contents__link toc-highlight">Report Resolution Actions</a></li><li><a href="#audit-log" class="table-of-contents__link toc-highlight">Audit Log</a></li><li><a href="#web-ui-admin-dashboard" class="table-of-contents__link toc-highlight">Web UI Admin Dashboard</a></li><li><a href="#creating-the-first-admin" class="table-of-contents__link toc-highlight">Creating the First Admin</a></li></ul></li><li><a href="#implementation-phases" class="table-of-contents__link toc-highlight">Implementation Phases</a><ul><li><a href="#phase-1-foundation" class="table-of-contents__link toc-highlight">Phase 1: Foundation</a></li><li><a href="#phase-2-core-backend" class="table-of-contents__link toc-highlight">Phase 2: Core Backend</a></li><li><a href="#phase-3-cli-commands" class="table-of-contents__link toc-highlight">Phase 3: CLI Commands</a></li><li><a href="#phase-4-publishing" class="table-of-contents__link toc-highlight">Phase 4: Publishing</a></li><li><a href="#phase-5-project-dependencies" class="table-of-contents__link toc-highlight">Phase 5: Project Dependencies</a></li><li><a href="#phase-6-smart-features" class="table-of-contents__link toc-highlight">Phase 6: Smart Features</a></li><li><a href="#phase-7-full-web-ui" class="table-of-contents__link toc-highlight">Phase 7: Full Web UI</a></li><li><a href="#phase-8-polish--scale" class="table-of-contents__link toc-highlight">Phase 8: Polish & Scale</a></li></ul></li></ul></div></div></div></div></main></div></div></div><footer class="theme-layout-footer footer footer--dark"><div class="container container-fluid"><div class="row footer__links"><div class="theme-layout-footer-column col footer__col"><div class="footer__title">Docs</div><ul class="footer__items clean-list"><li class="footer__item"><a class="footer__link-item" href="/rob/CmdForge/">Overview</a></li></ul></div><div class="theme-layout-footer-column col footer__col"><div class="footer__title">More</div><ul class="footer__items clean-list"><li class="footer__item"><a href="https://gitea.brrd.tech/rob/CmdForge" target="_blank" rel="noopener noreferrer" class="footer__link-item">Gitea<svg width="13.5" height="13.5" aria-label="(opens in new tab)" class="iconExternalLink_nPIU"><use href="#theme-svg-external-link"></use></svg></a></li></ul></div></div><div class="footer__bottom text--center"><div class="footer__copyright">CmdForge Documentation</div></div></div></footer></div>
|
||
</body>
|
||
</html> |