cookcli 0.33.0

A command-line interface for managing and working with Cooklang recipes
Documentation
{% extends "base.html" %}

{% block title %}Server API - Cook{% endblock %}

{% block content %}
<div class="max-w-5xl">
    <h1 class="text-2xl font-bold mb-2">Server API</h1>
    <p class="text-gray-600 dark:text-gray-400 mb-6">
        HTTP endpoints for building integrations against this CookCLI server.
    </p>

    <!-- Ground rules -->
    <div class="bg-gradient-to-r from-blue-50 to-indigo-50 dark:from-gray-800 dark:to-gray-800 p-6 rounded-2xl border-2 border-blue-200 dark:border-gray-700 mb-8">
        <h2 class="text-lg font-semibold mb-3 text-blue-900 dark:text-blue-200">Before you start</h2>
        <div class="text-sm space-y-2 text-gray-700 dark:text-gray-300">
            <div>
                <span class="font-medium">Base URL:</span>
                <code class="ml-2 px-2 py-0.5 rounded bg-white dark:bg-gray-900 font-mono">{{ base_url }}</code>
            </div>
            <div>
                <span class="font-medium">Authentication:</span>
                <span class="ml-2">None. Anyone who can reach the server can read and modify your recipes — think twice before using <code class="font-mono">--host</code> on an untrusted network.</span>
            </div>
            <div>
                <span class="font-medium">CORS:</span>
                <span class="ml-2">All origins are allowed, for the methods GET, POST, PUT and DELETE.</span>
            </div>
            <div>
                <span class="font-medium">Request size limit:</span>
                <span class="ml-2">1 MB.</span>
            </div>
            <div>
                <span class="font-medium">Content type:</span>
                <span class="ml-2">JSON in and out, except where noted — raw recipe text is <code class="font-mono">text/plain</code>.</span>
            </div>
        </div>
    </div>

    <!-- Errors -->
    <div class="bg-gray-50 dark:bg-gray-800 p-6 rounded-2xl border border-gray-200 dark:border-gray-700 mb-8">
        <h2 class="text-lg font-semibold mb-3">Errors</h2>
        <p class="text-sm text-gray-700 dark:text-gray-300 mb-3">
            Every failure returns the same shape, with the status code carrying the meaning:
        </p>
        <pre class="bg-white dark:bg-gray-900 border border-gray-200 dark:border-gray-700 rounded-lg p-3 text-xs overflow-x-auto font-mono">{ "error": "Recipe not found: Nope.cook" }</pre>
        <ul class="mt-3 text-sm space-y-1 text-gray-700 dark:text-gray-300">
            <li><span class="font-mono font-medium">400</span> — malformed input: an invalid path, a bad query parameter, or a recipe that failed to parse.</li>
            <li><span class="font-mono font-medium">404</span> — the recipe, menu, or pantry section does not exist, or no pantry file is configured.</li>
            <li><span class="font-mono font-medium">500</span> — the server could not read or write a file.</li>
        </ul>
    </div>

    <!-- Contents -->
    <div class="bg-gray-50 dark:bg-gray-800 p-6 rounded-2xl border border-gray-200 dark:border-gray-700 mb-8">
        <h2 class="text-lg font-semibold mb-3">Contents</h2>
        <div class="flex flex-wrap gap-2">
            {% for section in sections %}
            <a href="#{{ section.id }}"
               class="px-3 py-1.5 rounded-lg text-sm font-medium bg-white dark:bg-gray-900 border border-gray-300 dark:border-gray-600 text-gray-700 dark:text-gray-200 hover:border-orange-400 hover:bg-orange-50 dark:hover:bg-gray-700 transition-colors">
                {{ section.title }}
            </a>
            {% endfor %}
        </div>
    </div>

    <!-- Endpoints -->
    {% for section in sections %}
    <section id="{{ section.id }}" class="mb-10 scroll-mt-24">
        <h2 class="text-xl font-bold mb-2">{{ section.title }}</h2>
        <p class="text-sm text-gray-600 dark:text-gray-400 mb-4">{{ section.description|inline_code|safe }}</p>

        <div class="space-y-4">
            {% for endpoint in section.endpoints %}
            <article class="bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-xl p-5">
                <div class="flex flex-wrap items-center gap-2 mb-2">
                    <span class="px-2 py-0.5 rounded text-xs font-bold font-mono {{ endpoint.method_classes() }}">{{ endpoint.method }}</span>
                    <code class="font-mono text-sm break-all">{{ endpoint.path }}</code>
                    {% if let Some(feature) = endpoint.feature %}
                    <span class="px-2 py-0.5 rounded text-xs font-medium bg-purple-100 text-purple-800 dark:bg-purple-900 dark:text-purple-200">
                        requires <span class="font-mono">{{ feature }}</span> build
                    </span>
                    {% endif %}
                </div>

                <p class="text-sm font-medium mb-1">{{ endpoint.summary|inline_code|safe }}</p>
                {% if !endpoint.description.is_empty() %}
                <p class="text-sm text-gray-600 dark:text-gray-400">{{ endpoint.description|inline_code|safe }}</p>
                {% endif %}

                {% if !endpoint.params.is_empty() %}
                <div class="mt-4 overflow-x-auto">
                    <table class="w-full text-sm">
                        <thead>
                            <tr class="text-left text-xs uppercase tracking-wide text-gray-500 dark:text-gray-400 border-b border-gray-200 dark:border-gray-700">
                                <th class="py-1.5 pr-4 font-medium">Name</th>
                                <th class="py-1.5 pr-4 font-medium">In</th>
                                <th class="py-1.5 pr-4 font-medium">Type</th>
                                <th class="py-1.5 font-medium">Description</th>
                            </tr>
                        </thead>
                        <tbody>
                            {% for p in endpoint.params %}
                            <tr class="border-b border-gray-100 dark:border-gray-700 align-top">
                                <td class="py-1.5 pr-4 font-mono whitespace-nowrap">
                                    {{ p.name }}{% if p.required %}<span class="text-red-600 dark:text-red-400" title="required">*</span>{% endif %}
                                </td>
                                <td class="py-1.5 pr-4 text-gray-500 dark:text-gray-400">{{ p.kind }}</td>
                                <td class="py-1.5 pr-4 font-mono text-gray-500 dark:text-gray-400 whitespace-nowrap">{{ p.type_name }}</td>
                                <td class="py-1.5 text-gray-600 dark:text-gray-400">{{ p.description|inline_code|safe }}</td>
                            </tr>
                            {% endfor %}
                        </tbody>
                    </table>
                    <p class="mt-1 text-xs text-gray-500 dark:text-gray-400">
                        <span class="text-red-600 dark:text-red-400">*</span> required
                    </p>
                </div>
                {% endif %}

                {% if let Some(request) = endpoint.request_example %}
                <div class="mt-4">
                    <h4 class="text-xs uppercase tracking-wide text-gray-500 dark:text-gray-400 mb-1">Request body</h4>
                    <pre class="bg-gray-50 dark:bg-gray-900 border border-gray-200 dark:border-gray-700 rounded-lg p-3 text-xs overflow-x-auto font-mono">{{ request }}</pre>
                </div>
                {% endif %}

                {% if let Some(response) = endpoint.response_example %}
                <div class="mt-4">
                    <h4 class="text-xs uppercase tracking-wide text-gray-500 dark:text-gray-400 mb-1">Response</h4>
                    <pre class="bg-gray-50 dark:bg-gray-900 border border-gray-200 dark:border-gray-700 rounded-lg p-3 text-xs overflow-x-auto font-mono">{{ response }}</pre>
                </div>
                {% endif %}
            </article>
            {% endfor %}
        </div>
    </section>
    {% endfor %}
</div>
{% endblock %}