ПРОТОКОЛ ВЫВОДА НА ЭКРАНдоставка файла из среды ИИ во внешнюю ссылку — для любой LLM
v1.1 · ЯДРО VERIFIED

ПРОТОКОЛ ВЫВОДА НА ЭКРАН · СИНГУЛЯР

Версия 1.1 · Экспорт файла из внутренней среды ИИ во внешнюю ссылку · Для любой LLM / ИИ-агента · ЯДРО VERIFIEDv1.0…v1.12 этим каналом

0 · Главный вопрос и главное правило

Вопрос: файл лежит внутри твоей среды — как превратить его во внешнюю ссылку, которая открывается у пользователя в браузере?

Правило: ссылки внутренней среды (sandbox:/..., /mnt/data/...) у пользователя НЕ открываются. Это не доставка.

Доставка = внешний https://... URL, открывающийся без твоей среды.

1 · Каналы экспорта «изнутри — наружу»

К1 · GitHub PagesПостоянный URL, файл виден на экране браузера. Нужен токен (Device Flow). VERIFIED
К2 · Release assetsБольшие файлы до 2 ГБ на ассет. Нужен токен. VERIFIED — 42 МБ в v1.12
К3 · filebin.netБез регистрации и токена. Живёт ~7 дней, публично. VERIFIED — POST 201, GET 302→200
К4 · канал средыAttachment/preview-ссылка среды — только если настоящий https:// и открывается без песочницы.
ЗадачаКанал
Показать файл НА ЭКРАНЕ браузераК1
Отдать большой файл на скачиваниеК2 или К3
Нет токена / нет правК3 или К4
Максимальная надёжностьК1 + дубль в К3

2 · Гейты доставки (когда работа считается сделанной)

Хотя бы один гейт не пройден → работа НЕ сделана. Отчёт: «НЕ ГОТОВО» + причина + UNKNOWN. Никаких «должно заработать».

3 · Схема механизма

ИИ создал файл (внутри своей среды) | |-- К1: [AUTH Device Flow] → [PUSH repo main] → [WAIT Pages] → [VERIFY 200] → постоянный URL |-- К2: [AUTH Device Flow] → [Release] → [UPLOAD ассет] → [VERIFY 200] → URL ассета |-- К3: [POST filebin] → [VERIFY GET 200] → URL bin (~7 дней) |-- К4: [attachment среды] → [проверка: внешний https?] → ссылка или честный отказ | v [REPORT] CLAIM / CODE / TEST / RESULT + финальный URL пользователю

4 · Машинный контракт (JSON)

{
  "protocol": "SINGULYAR-SCREEN-DELIVERY", "version": "1.1",
  "goal": "файл из среды ИИ -> внешний https URL в браузере пользователя",
  "not_delivery": ["sandbox:/...", "/mnt/data/...", "локальный путь", "обещание без URL"],
  "channels": {
    "K1_github_pages": {
      "repo": "rtynbirf/singulyar", "branch": "main",
      "pages_base": "https://rtynbirf.github.io/singulyar/",
      "raw_base": "https://raw.githubusercontent.com/rtynbirf/singulyar/main/",
      "auth": {
        "method": "github_device_flow", "client_id": "178c6fc778ccc68e1d6a", "scope": "repo",
        "device_code": "POST https://github.com/login/device/code",
        "token": "POST https://github.com/login/oauth/access_token (grant_type=...device_code)",
        "user_entry": "https://github.com/login/device",
        "token_rules": "в памяти сессии; никогда в файлах/коммитах/логах" },
      "api": {
        "get_file": "GET /repos/.../contents/{path_urlencoded}?ref=main",
        "put_file_le1mb": "PUT /repos/.../contents/{path_urlencoded} {message, content:base64, branch, sha?}",
        "blob": "POST /repos/.../git/blobs {content:base64, encoding:base64}",
        "tree": "POST /repos/.../git/trees {base_tree, tree:[{path, mode:100644, type:blob, sha}]}",
        "commit": "POST /repos/.../git/commits {message, tree, parents}",
        "ref": "PATCH /repos/.../git/refs/heads/main {sha}",
        "pages_latest": "GET /repos/.../pages/builds/latest" } },
    "K2_release_assets": {
      "create": "POST /repos/.../releases {tag_name, name}",
      "upload": "POST https://uploads.github.com/repos/.../releases/{id}/assets?name={file}",
      "limits": "до 2 ГБ на ассет" },
    "K3_filebin": {
      "post": "POST https://filebin.net/{bin}/{filename}  body = байты файла  -> 201",
      "get": "GET https://filebin.net/{bin}/{filename} -> 302 -> 200",
      "rules": "bin создаётся сам; ~7 дней; публично; не для секретов" },
    "K4_native": "только настоящий https://, открывающийся без твоей песочницы" },
  "gates": ["G1_выложено", "G2_финальный_URL", "G3_VERIFY_200_и_маркер", "G4_REPORT"],
  "file_rules": {
    "on_screen": "самодостаточный .html без внешних зависимостей",
    "for_machine": ".md",
    "naming": "латиница/цифры/_/-; кириллица с percent-encoding; без пробелов",
    "size": "К1: <=1MB Contents API; >1MB Git Data API; >95MB — К2/К3",
    "secrets": "запрещены в файлах, коммитах и публичных bin-ах" }
}

5 · Шаг 1/6 — AUTH: GitHub Device Flow (К1/К2)

# 1) запросить код устройства
curl -s -X POST https://github.com/login/device/code \
  -H "Accept: application/json" \
  -d "client_id=178c6fc778ccc68e1d6a&scope=repo"
# {"device_code":"...","user_code":"ABCD-1234","verification_uri":"https://github.com/login/device","interval":5}

# 2) показать человеку ТОЛЬКО user_code:
#    «Открой https://github.com/login/device и введи код ABCD-1234»

# 3) опрашивать токен с паузой interval:
curl -s -X POST https://github.com/login/oauth/access_token \
  -H "Accept: application/json" \
  -d "client_id=178c6fc778ccc68e1d6a&device_code=<DEVICE_CODE>&grant_type=urn:ietf:params:oauth:grant-type:device_code"
# authorization_pending — ждать; slow_down — пауза +5 c; успех — {"access_token":"ghu_..."}
TOKEN="<access_token>"   # только в памяти сессии
Если в среде уже есть рабочий токен — использовать его. Токен не включать в файлы, коммиты, отчёты, логи.

6 · Шаг 2/6 — BUILD: правила файла

  1. Чтобы пользователь увидел на экране — артефакт: самодостаточный .html (стили и скрипты внутри, без CDN, работает офлайн).
  2. Для машин рядом можно положить .md.
  3. Имена: латиница/цифры/_/-; кириллица допустима, но в URL — percent-encoding; без пробелов.
  4. Размер: ≤1 МБ → метод A; больше → метод B; >95 МБ → К2/К3, не в git.
  5. Секреты в файлы и коммиты — никогда.

7 · Шаг 3/6 — PUSH в репо (К1)

Метод A — Contents API (до 1 МБ):

ENC=$(python3 -c "import urllib.parse,sys;print(urllib.parse.quote(sys.argv[1]))" "$PATH_IN_REPO")
B64=$(base64 -w0 "$FILE")
SHA=$(curl -s -H "Authorization: Bearer $TOKEN" \
  "https://api.github.com/repos/rtynbirf/singulyar/contents/$ENC?ref=main" \
  | python3 -c "import json,sys;d=json.load(sys.stdin);print(d.get('sha','') if isinstance(d,dict) else '')")
# body.json = {"message":"ADD FAYL.html","content":B64,"branch":"main"[,"sha":SHA если заменяешь]}
curl -s -X PUT -H "Authorization: Bearer $TOKEN" -H "Accept: application/vnd.github+json" \
  -d @body.json "https://api.github.com/repos/rtynbirf/singulyar/contents/$ENC"
# успех: HTTP 200/201 + "commit":{"sha":"..."} — запомнить

Метод B — Git Data API (больше 1 МБ):

BLOB_SHA  = POST /git/blobs   {content: base64(файл), encoding:"base64"}
BASE_SHA  = GET  /git/ref/heads/main → .object.sha
TREE_SHA  = POST /git/trees   {base_tree:BASE_SHA, tree:[{path, mode:"100644", type:"blob", sha:BLOB_SHA}]}
COMMIT    = POST /git/commits {message, tree:TREE_SHA, parents:[BASE_SHA]}
          = PATCH /git/refs/heads/main {sha:COMMIT, force:false}
Не трогать чужие пути. Один смысловой файл — один коммит. Сообщение: ADD/UPDATE <имя>: <суть>.

8 · Шаг 4/6 — WAIT: сборка Pages (К1)

for i in $(seq 1 48); do
  ST=$(curl -s -H "Authorization: Bearer $TOKEN" \
    "https://api.github.com/repos/rtynbirf/singulyar/pages/builds/latest" \
    | python3 -c "import json,sys;print(json.load(sys.stdin).get('status',''))")
  [ "$ST" = "built" ] && break
  sleep 5
done   # максимум 240 c; errored — читать error в ответе

9 · Шаг 5/6 — VERIFY (обязателен для всех каналов)

# К1:
URL="https://rtynbirf.github.io/singulyar/$ENC"
CODE=$(curl -sL -o out.bin -w "%{http_code}" "$URL?v=$(date +%s)")   # ?v= против CDN-кэша
MARK=$(grep -c "КОНТРОЛЬНАЯ_СТРОКА" out.bin)
# гейт: CODE=200 и MARK>=1

# К3:
curl -sL "https://filebin.net/$BIN/файл"   # 302 → 200 — нормально, браузер проходит сам
Для К1 проверить оба адреса: экран — rtynbirf.github.io/singulyar/<путь>; машины — raw.githubusercontent.com/rtynbirf/singulyar/main/<путь>.

10 · Шаг 6/6 — REPORT

CLAIM:  <что должно открыться и по какому URL>
CODE:   <файл, канал, путь/commit sha, размер>
TEST:   HTTP <код>; маркер: да/нет; сборка: <время>
RESULT: ГОТОВО → <финальный URL>   |   НЕ ГОТОВО → причина
UNKNOWN: <что не проверено>

11 · К2: большие файлы через Release assets

REL=$(curl -s -X POST -H "Authorization: Bearer $TOKEN" -H "Accept: application/vnd.github+json" \
  -d '{"tag_name":"files-20261002","name":"Файлы"}' \
  "https://api.github.com/repos/rtynbirf/singulyar/releases" | python3 -c "import json,sys;print(json.load(sys.stdin)['id'])")
curl -s -X POST -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/octet-stream" \
  --data-binary @"БОЛЬШОЙ_ФАЙЛ" \
  "https://uploads.github.com/repos/rtynbirf/singulyar/releases/$REL/assets?name=%D0%A4%D0%90%D0%99%D0%9B"
# VERIFY browser_download_url → 200. Проверено: ассет 42 483 085 Б в v1.12 отдаётся анонимно 200.

12 · К3: filebin.net — без токена и прав

BIN="sv$(date +%s)"     # bin создаётся автоматически
curl -s -o /dev/null -w "%{http_code}" -X POST --data-binary @"ФАЙЛ" \
  -H "Content-Type: application/octet-stream" "https://filebin.net/$BIN/ФАЙЛ"   # ждать 201
curl -sL "https://filebin.net/$BIN/ФАЙЛ"                                        # 302 → 200
Факты теста: POST → 201; GET → 302 → подписанное хранилище → 200; содержимое байт-в-байт. Bin публичен, живёт ~7 дней, не для секретов.

13 · Псевдокод (нет shell, есть HTTP)

function deliver(file, path):
    if no_token_or_size_gt_95MB:            # канал К3
        bin = "sv" + now
        assert POST filebin.net/{bin}/{name} == 201
        body = GET filebin.net/{bin}/{name} (follow redirect)
        assert status == 200 and MARKER in body
        return report(RESULT="ГОТОВО", url)
    token = device_flow("178c6fc778ccc68e1d6a", scope="repo")
    sha   = GET /contents/{quote(path)}?ref=main → .sha | None
    r     = PUT /contents/{quote(path)} {message, base64(file), branch, sha?}
    assert r in (200,201); commit = r.commit.sha
    wait ≤240s: GET /pages/builds/latest → status == "built"
    body  = GET pages_url + "?v=" + now
    assert 200 and MARKER in body
    return report(CLAIM, CODE(commit), TEST, RESULT="ГОТОВО", url)

14 · Запреты

  1. Выдавать sandbox/локальные ссылки за доставку.
  2. Сообщать URL до прохождения VERIFY.
  3. Включать секреты в файлы, коммиты, публичные bin-ы, отчёты, логи.
  4. Переписывать/удалять чужие файлы репозитория — только свой путь.
  5. Говорить «должно работать» вместо проверенного факта.
  6. Собирать токены через собственные формы — только официальный Device Flow.

15 · Fallback

16 · Проверенность

VERIFIED (проект СИНГУЛЯР): Device Flow · Contents API · Git Data API · Pages · Release-ассеты (42 МБ) · filebin.net (POST 201 / GET 302→200). Состояние: Pages отвечает 200; в корне репо СИНГУЛЯР_01…22.

Выполняй TEST сам в своей среде. Непроверенное помечай UNKNOWN.

СИНГУЛЯР · ПРОТОКОЛ ВЫВОДА НА ЭКРАН · v1.1 · машина-исполнителю: читай JSON-контракт из §4 первым.

Вся документация дома: ДОКУМЕНТАЦИЯ.html