| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152 |
- <?php
- declare(strict_types=1);
- /**
- * The handbook as plain Markdown, one page per request.
- *
- * llms.php index: every page with a summary and its size
- * llms.php?doc=KEY one chapter, as written
- * llms.php?code=PATH one source file
- *
- * Apache also answers llms.txt for the index, see .htaccess. That alias is a
- * convenience, not the canonical address: it needs mod_rewrite, and this file
- * has to work without it.
- *
- * This is the rendering meant for agents. It exists separately from index.php
- * so that a fetch costs only the page that was asked for: the index says what
- * each page contains and how large it is, and names the pages a given job
- * needs, so nothing has to be pulled in on the chance it might be relevant.
- */
- require_once __DIR__ . "/inc/handbook.php";
- $requestedDoc = isset($_GET["doc"]) ? (string) $_GET["doc"] : "";
- $requestedCode = isset($_GET["code"]) ? (string) $_GET["code"] : "";
- if ($requestedDoc === "" && $requestedCode === "") {
- handbookSendText(handbookRenderIndex());
- }
- $page = $requestedDoc !== ""
- ? handbookPage("doc", $requestedDoc)
- : handbookPage("code", $requestedCode);
- if ($page === null) {
- http_response_code(404);
- handbookSendText(
- "# Not Found\n\n"
- . "This page doesn't exist. The index of every page is at\n"
- . handbookSelfUrl() . "/llms.php\n",
- );
- }
- // Chapters link each other by file name; inside this rendering the neighbour
- // is another llms.php page, so a following request stays in plain Markdown.
- handbookSendText(
- rtrim(handbookPageMarkdown($page, "llms.php")) . "\n\n"
- . "---\n\n"
- . "Index of every page: " . handbookSelfUrl() . "/llms.php\n"
- . "This page for humans: " . handbookPageUrl($page) . "\n",
- );
- /** Emits a Markdown document and ends the request. */
- function handbookSendText(string $text): void
- {
- header("Content-Type: text/plain; charset=utf-8");
- header("Content-Length: " . (string) strlen($text));
- header("X-Content-Type-Options: nosniff");
- // Cheap to rebuild, but an agent walking several pages should not pay for
- // a revalidation on each one.
- header("Cache-Control: public, max-age=300");
- echo $text;
- exit;
- }
- /**
- * The index: what exists, how big it is, and which pages a given job needs.
- */
- function handbookRenderIndex(): string
- {
- $self = handbookSelfUrl();
- $docs = handbookPagesOfKind("doc");
- $code = handbookPagesOfKind("code");
- $total = 0;
- foreach (handbookPages() as $page) {
- $total += $page["bytes"];
- }
- $out = [];
- $out[] = strtr(handbookRead(HANDBOOK_CONTENT_DIR . "/llms-intro.md"), [
- "{{SELF_URL}}" => $self,
- "{{BASE_URL}}" => handbookBaseUrl(),
- "{{PAGE_COUNT}}" => (string) count(handbookPages()),
- "{{TOTAL_SIZE}}" => handbookFormatBytes($total),
- ]);
- // ---- Recipes: the shortlist per job ------------------------------------
- $out[] = "## What Each Page Is For\n";
- foreach (handbookRecipes() as $recipe) {
- $lines = ["### " . $recipe["title"], "", $recipe["note"], ""];
- foreach ($recipe["pages"] as [$kind, $key]) {
- $page = handbookPage($kind, $key);
- if ($page === null) {
- continue;
- }
- $lines[] = "- " . handbookRawUrl($page) . " – " . $page["title"];
- }
- $out[] = implode("\n", $lines) . "\n";
- }
- // ---- Every page --------------------------------------------------------
- $out[] = handbookRenderList(
- "## Documentation",
- "Chapters in the recommended reading order.",
- $docs,
- );
- $out[] = "## Interface\n\n"
- . "- " . $self . "/openapi.php – OpenAPI 3.1 of the Manage server's four endpoints, "
- . "as JSON. Read together with the *Protocol v1* chapter for a custom client.\n"
- . "- " . $self . "/api.php – the same description in Swagger UI. A page for "
- . "humans, no use to a program.\n";
- $out[] = handbookRenderList(
- "## Source Code",
- "The complete client package, every file individually retrievable. Integrating it "
- . "needs the folder `manage-client/`; `examples/`, `docs/` and `scripts/` "
- . "are not part of that.",
- $code,
- );
- $vendored = handbookVendoredFiles();
- if ($vendored !== []) {
- $lines = ["## Not Reproduced Here", "", "Third-party libraries that are part of the "
- . "package but don't have a page here:", ""];
- foreach ($vendored as $relative => $bytes) {
- $lines[] = "- `client-package/" . $relative . "` (" . handbookFormatBytes($bytes) . ")";
- }
- $out[] = implode("\n", $lines) . "\n";
- }
- return implode("\n", $out);
- }
- /** @param array<string, array> $pages */
- function handbookRenderList(string $heading, string $intro, array $pages): string
- {
- $lines = [$heading, "", $intro, ""];
- foreach ($pages as $page) {
- $lines[] = "- " . handbookRawUrl($page)
- . " (" . handbookFormatBytes($page["bytes"]) . ")"
- . " – **" . $page["title"] . "**"
- . ($page["summary"] !== "" ? ". " . $page["summary"] : "");
- }
- return implode("\n", $lines) . "\n";
- }
|