Metadata-Version: 2.4
Name: uriloop
Version: 0.3.2
Summary: Wspólna płaszczyzna URI/URN do autonomicznego sterowania przeglądarką (CDP) i pulpitem (noVNC) z pętlą zwrotną CQRS+ES i serwerami MCP
License-Expression: Apache-2.0
Requires-Python: >=3.11
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: websockets>=12
Requires-Dist: jsonschema>=4.18
Requires-Dist: pillow>=10
Provides-Extra: pg
Requires-Dist: psycopg[binary]>=3.1; extra == "pg"
Provides-Extra: vnc
Requires-Dist: pycryptodome>=3.19; extra == "vnc"
Provides-Extra: onnx
Requires-Dist: onnxruntime>=1.18; extra == "onnx"
Requires-Dist: numpy>=1.26; extra == "onnx"
Provides-Extra: yolo
Requires-Dist: ultralytics>=8.3; extra == "yolo"
Provides-Extra: test
Requires-Dist: pyyaml>=6; extra == "test"
Requires-Dist: onnx>=1.15; extra == "test"
Dynamic: license-file

# uriloop


## AI Cost Tracking

![PyPI](https://img.shields.io/badge/pypi-costs-blue) ![Version](https://img.shields.io/badge/version-0.3.2-blue) ![Python](https://img.shields.io/badge/python-3.9+-blue) ![License](https://img.shields.io/badge/license-Apache--2.0-green)
![AI Cost](https://img.shields.io/badge/AI%20Cost-$0.58-orange) ![Human Time](https://img.shields.io/badge/Human%20Time-6.9h-blue) ![Model](https://img.shields.io/badge/Model-openrouter%2Fqwen%2Fqwen3--coder--next-lightgrey)

- 🤖 **LLM usage:** $0.5831 (12 commits)
- 👤 **Human dev:** ~$691 (6.9h @ $100/h, 30min dedup)

Generated on 2026-10-30 using [openrouter/qwen/qwen3-coder-next](https://openrouter.ai/qwen/qwen3-coder-next)

---

Wspólna płaszczyzna **URI/URN** do autonomicznego sterowania **przeglądarką (CDP)** i **pulpitem (noVNC/VNC)**
przez **MCP**, z warstwową analizą ekranu, pętlą zwrotną nad `TODO.md` i wzorcem **CQRS + Event Sourcing**
(`logs.jsonl` → `chat.jsonl`, PostgreSQL).

Zgodność z wytycznymi wellmanifest: `nl-uri-dsl-llm` (NUL-001…009, 015), `logs` (hash-chain JSONL, runbooki),
`poa` (typowane procesy, kwity, fail-closed), `ssot` (`ssot.json`), `reuse` (rejestr deklaratywny, fasady), `dsl` (`dsl-manifest.json`).

## AI: decyzje, generowanie procesów, wizja

| Rola | Model (domyślnie) | Co robi |
|---|---|---|
| Decyzje | **Liquid d1** `d1:3b` przez Ollama `/v1/systemone` | wybiera Action URI z katalogu (2..255 kandydatów, także elementy ekranu po URN), ocenia ryzyko 1..3 i zgodę na wykonanie; widzi zrzut ekranu |
| Generator | `qwen3:8b` (GPU 8 GB) · `qwen3.8:27b-q4_K_M` (≥ 20 GB) · DeepSeek `deepseek-v4-flash/pro` · proxy subllm | tworzy proces złożony `proc://uriloop/<nazwa>` z kroków rejestru, z pętlą poprawek po walidacji |
| Wizja | YOLO (`ultralytics` lub `onnxruntime`) | detekcje z URN i akcjami kliknięcia na pulpicie i stronie |

```bash
make ollama-pull                                   # natywnie: modele dobrane do VRAM (np. d1:3b + qwen3:8b)
make models                                        # telemetria GPU i wybrane profile
make decide Q='kliknij Powitanie' X=1              # decyzja d1 (+ wykonanie, jeśli polityka pozwala)
make generate G='wyszukaj uriloop, a następnie otwórz drugą stronę' R=1
make generate G='…' P=deepseek                     # z DEEPSEEK_API_KEY
make detect SRC=desktop                            # YOLO
```

W pętli: zadanie bez kroków → NL → decyzja d1 → (gdy wieloetapowe lub niepewne) generator → `proc://` → weryfikacja.
Zablokowane przez politykę (`URILOOP_AUTONOMY`, negacja „nie klikaj…”) dostaje `[?]` i czeka na człowieka.
Szczegóły: [docs/AI.md](docs/AI.md).

## Szybki start

**Granica wdrożenia:** Ollama (d1, qwen), YOLO i ciężkie pakiety (onnxruntime, ultralytics/torch) działają
**natywnie na hoście** — z bezpośrednim dostępem do GPU i bez wielogigabajtowych obrazów. Docker uruchamia tylko
lekką infrastrukturę: Postgres, Chrome (CDP), pulpit (VNC/noVNC), stronę demo i projektor CQRS.

```bash
make host-setup    # .venv + uriloop[pg,vnc,onnx] + sprawdzenie Ollamy + `ollama pull` modeli dobranych do VRAM
                   #   INSTALL_OLLAMA=1 make host-setup — instalacja Ollamy oficjalnym skryptem
                   #   WITH_YOLO=1 make host-setup       — ultralytics (torch zainstaluj wcześniej z właściwym CUDA)
source .venv/bin/activate  # udostępnia uriloop w bieżącej powłoce, także po pracy w conda (base)
export URILOOP_ROOT="$PWD/workspace"
make up            # docker: postgres, chrome (CDP 127.0.0.1:9222), desktop (VNC :5901, noVNC :6901), demo (:8088), projektor
make doctor        # host: Ollama, modele, GPU, pakiety · docker: CDP, VNC, Postgres
make run-dry       # plan: TODO.md → Action URI (bez wykonania)
make run           # pętla autonomiczna na hoście; obserwuj workspace/TODO.md i workspace/chat.jsonl
make tail          # zdarzenia
make verify        # łańcuch hashy
make db-shell      # select * from chat_view; select * from elements_view;
```

Ollama działa jak zwykle na hoście (`ollama serve` lub usługa systemowa) pod `http://127.0.0.1:11434`.
Usługi docker są wystawione tylko na `127.0.0.1`. Opcjonalny kontener `agent` (`make agent-docker A='todo'`)
nie zawiera AI; jeśli ma korzystać z Ollamy hosta, ustaw `URILOOP_AI_IN_DOCKER=on` i uruchom Ollamę z `OLLAMA_HOST=0.0.0.0`.

Katalog `workspace/` jest współdzielony: projektor w kontenerze działa z UID/GID użytkownika (eksportuje je `make`),
więc pliki projekcji pozostają zapisywalne dla procesów hosta. Na macOS (Docker Desktop) blokady plików między
hostem a kontenerem bywają zawodne — tam uruchom projektor natywnie (`make follow`) zamiast usługi `projector`.

Podgląd pulpitu: `http://localhost:6901/vnc.html?password=vncpassword`.

## Interaktywny shell i przykłady

`make shell` uruchamia terminal ze stałym `Runtime` oraz bramą narzędzi MCP. Analiza i kliknięcie po URN zachowują wspólną sesję. `make mcp` nadal udostępnia serwer stdio dla zewnętrznego klienta MCP.

```text
uriloop> help
uriloop> tools
uriloop> scenarios
uriloop> scenario-plan demo-complex
uriloop> scenario demo-complex http://127.0.0.1:18089/
uriloop> do browser://page/analyze
uriloop> call chat_message_post {"text":"Sprawdzenie zakończone"}
uriloop> quit
```

Dostępne przykłady: `demo-search`, `demo-data`, `demo-complex`, `demo-form`, `demo-table`, `demo-files`, `demo-tabs`, `demo-auth`. Wykonują analizę przed kliknięciem, wybierają bieżący URN, sprawdzają wyniki pośrednie i zapisują raport jako artefakt. Błąd zatrzymuje scenariusz; nie ma automatycznych ponowień efektów. URL wskazuje serwer demo widoczny dla sterowanej przeglądarki. Można też użyć `make scenarios` oraz `make scenario NAME=demo-complex URL=http://127.0.0.1:18089/`.

Terminal przyjmuje Action URI, `nl TEKST`, `plan CEL` i `call NAZWA JSON`. `plan` zapisuje plan do osobnego późniejszego wykonania przez `proc://uriloop/run?name=...`. `help`, `tools` i `quit` sterują terminalem; działania systemu przechodzą przez rejestr i MCP. Shell obsługuje lokalny katalog uriloop; nie jest klientem dowolnego zewnętrznego serwera MCP.

## Seria scenariuszy i CI

`make suite` wykonuje wszystkie osiem gotowych scenariuszy, każdy w oddzielnej sesji i katalogu. Całość wymaga grantów i fikcyjnych danych do logowania:

```bash
URILOOP_GRANTS=browser.upload,browser.tabs.close,browser.secrets \
URILOOP_SECRET_DEMO_PASSWORD=uriloop-demo-only \
URILOOP_SECRET_DEMO_BAD_PASSWORD=wrong-demo-password \
make suite URL=http://127.0.0.1:18089/
```

W shellu uruchomionym z tymi ustawieniami odpowiednikiem jest `suite http://127.0.0.1:18089/`. Akcja `scenario://suite/run` zapisuje raport JSON, JUnit XML, kwity i logi; błąd dowolnego scenariusza daje exit 1. Można wybrać podzbiór parametrem `names` i limit czasu jednego przypadku parametrem `timeout`.

`make suite-local OUT=/tmp/nowe-wyniki CHROME_BIN=/sciezka/do/chrome` uruchamia własne Chromium i serwer demo na wolnych portach. Wymaga pustego katalogu wyników. Workflow GitHub Actions sprawdza Python 3.11 i 3.13 i zachowuje raporty nawet po niepowodzeniu. Szczegóły: [docs/CI.md](docs/CI.md).

Rzeczywisty VNC i PostgreSQL są sprawdzane osobnym jobem `real-infrastructure`: tekst jest wpisywany do aplikacji xterm i weryfikowany w pliku, a lustro bazy porównywane z logiem także po awarii połączenia i replay. Lokalne uruchomienie opisuje [docs/CI.md](docs/CI.md). Synchronizacja bazy ma teraz Action URI `log://mirror/sync`.

Nowe strony demo: formularz kontaktu (`forms.html`) i lista zamówień (`table.html`). Scenariusze otwierają je względem bazowego URL demo. Dane pozostają w pamięci strony do jej przeładowania.

```text
uriloop> scenario demo-form http://demo/
uriloop> scenario demo-table http://demo/
```

`demo-form` sprawdza pola wymagane, niepoprawny e-mail, zastąpienie tekstu i checkbox. `demo-table` sprawdza filtr, sortowanie, anulowanie i potwierdzenie modala oraz odmowę kliknięcia zasłoniętej kontrolki. Polecenie `browser://page/type` przyjmuje `clear=true`, aby zastąpić zawartość wskazanego pola; wymaga wtedy URN lub selektora.

Scenariusz plikowy wymaga grantu operatora:

```bash
URILOOP_GRANTS=browser.upload ./uriloop shell
# W terminalu uriloop:
# scenario demo-files http://demo/
```

`demo-files` pobiera raport UTF-8, sprawdza nazwę, MIME, bajty i SHA-256, wykrywa HTTP 404, załącza niewłaściwy typ, czyści wybór i zapisuje poprawny załącznik dokładnie raz w atrapie. Plik jest odczytywany w formularzu lokalnie; atrapa nie wysyła go do serwera. Runner `make suite-local` sam przyznaje potrzebne granty i ustawia jawne dane fikcyjnego konta dla własnego Chromium i lokalnych stron demo.

`browser://file/fetch?urn=…` pobiera zawartość linku przez Fetch w przeglądarce do artefaktu w katalogu roboczym. Obsługuje HTTP(S) tej samej domeny, bez przekierowań, maksymalnie 5 MiB i z ograniczonym czasem. Kwit zwraca metadane pliku i artefakt; błąd, timeout lub niekompletny transfer nie tworzy artefaktu plikowego. To ścieżka pobierania przez Fetch; natywny menedżer pobierania i dialog zapisu wymagają osobnych testów.

`browser://page/attach?urn=…&artifact=artifacts/…&name=raport.txt&mime=text/plain` wybiera jeden artefakt we wskazanym polu plikowym. `clear=true` czyści wybór. Wymaga grantu `browser.upload`, aktualnego celu i zgodnego hasha pliku; nie odczytuje dowolnych ścieżek hosta. Transfer bajtów przez CDP działa także wtedy, gdy Chromium jest w innym kontenerze. Używa `File`/`DataTransfer`; formularze wymagające zaufanych zdarzeń systemowych lub natywnego dialogu nie są jeszcze obsługiwane.

Scenariusz kart otwiera dwie kopie tej samej strony i sprawdza oddzielny stan notatek, odrzucenie URN z innej karty oraz zachowanie po zamknięciu wybranej karty:

```bash
URILOOP_GRANTS=browser.tabs.close ./uriloop shell
# W terminalu uriloop:
# scenario demo-tabs http://demo/
```

`browser://tabs/list` zwraca URN, URL, tytuł i informację o karcie wybranej w Runtime. `browser://tabs/open` przyjmuje albo `url`, albo URN linku z analizy; otwiera i wybiera nową kartę. `browser://tabs/switch?urn=…` wybiera wskazaną kartę. `browser://tabs/close?urn=…` wymaga grantu `browser.tabs.close`. Po zamknięciu wybranej karty trzeba jawnie wskazać kolejną przez `switch`; zwykła akcja strony nie wybiera automatycznie sąsiedniej karty. Zamknięcie karty w tle zachowuje wybraną kartę i jej indeks.

URN elementów i formularzy uwzględnia kartę, także przy identycznym URL. Przełączenie czyści indeks, poprzedni obraz i obserwację; nowa sesja CDP ma osobną historię żądań. `open` otwiera adres linku bez wykonywania jego handlera JavaScript. Okna popup, iframe, pobieranie danych między formularzami i odzyskiwanie niezapisanej pracy wymagają dalszych scenariuszy.

Logowanie demonstracyjne:

```bash
URILOOP_GRANTS=browser.secrets \
URILOOP_SECRET_DEMO_PASSWORD=uriloop-demo-only \
URILOOP_SECRET_DEMO_BAD_PASSWORD=wrong-demo-password \
./uriloop shell
# W terminalu uriloop:
# scenario demo-auth http://demo/
```

To jawne dane fikcyjnego konta. `demo-auth` sprawdza niepoprawne dane, zalogowanie, odświeżenie, odmowę dostępu, wygaśnięcie i wylogowanie. Kody 200/401/403 są komunikatami lokalnej symulacji w `sessionStorage`; nie są odpowiedziami serwera uwierzytelniania. Backend, ciasteczka, SSO i MFA wymagają kolejnych testów.

`browser://page/secret?urn=…&env=URILOOP_SECRET_PASSWORD` odczytuje wartość z otoczenia procesu operatora i wpisuje ją wyłącznie do wskazanego pola hasła. Wymaga grantu `browser.secrets`, świeżego URN, poprawnego fokusu i nazwy zmiennej zaczynającej się od `URILOOP_SECRET_`. Nie zatwierdza formularza. W URI, kwicie i błędzie sterownika pozostaje referencja, bez wartości ani jej długości. Zwykłe `browser://page/type` odrzuca pole hasła; tekst tej komendy jest maskowany w publicznym URI także przy błędzie kontraktu. URI z maską jest dowodem audytu, a nie sposobem odtworzenia wpisanej treści.

Sekrety przekazuj przez referencję środowiskową, również w procesach złożonych. Nie umieszczaj ich w NL, TODO, kodzie procesu, `eval` ani innych parametrach URI. Ochrona dotyczy tej ścieżki wprowadzania i standardowych pól hasła; nie zapewnia maskowania dowolnych danych widocznych na ekranie ani treści, którą strona sama ujawni poza polem hasła.

## Dostęp do polecenia uriloop

Instalacja znajduje się w `.venv`. Skrypt `make host-setup` aktywuje ją tylko wewnątrz swojego procesu. W nowym terminalu aktywuj środowisko przez `source .venv/bin/activate`, aby używać polecenia `uriloop`. W katalogu projektu można od razu używać `./uriloop` albo `.venv/bin/uriloop` bez aktywacji. Launcher `./uriloop` wybiera Python projektu i domyślnie zapisuje zdarzenia do `workspace/`; respektuje jawne `URILOOP_ROOT` i `--root`.

```bash
./uriloop --help
./uriloop nl 'wpisz „uriloop” ' --dry
./uriloop shell
# Alternatywnie, w bieżącym terminalu:
source .venv/bin/activate
export URILOOP_ROOT="$PWD/workspace"
uriloop --help
```

`nl 'wpisz …'` wpisuje do pola, które ma fokus. Do przykładu z wyszukiwaniem i weryfikacją wyniku użyj w shellu `scenario demo-search http://demo/`. Samo uruchomienie CLI nie wymaga `goal -a`.

## Goal: dostarczenie zmian do prywatnego repozytorium

Do commitu i push bez wydania pakietu użyj:

```bash
make deliver
# To samo bez Make:
goal -a --no-publish push --no-version-sync --no-tag --no-changelog
# Podgląd bez wykonywania zmian:
goal -a --no-publish --dry-run push --no-version-sync --no-tag --no-changelog
```

W Goal 2.2.4 samo `-a` włącza również `--force-publish`. Przy czystym drzewie i braku bazowej wersji z tagów, rejestru lub historii Goal proponuje `normal-bump`, po czym odmawia publikacji: `clean --force-publish requires an already synchronized, pre-bumped release version`. To konflikt w przebiegu wydania Goal; ponowienie tego samego polecenia nie przygotuje wydania. Linie `VERSION ... -> ...` w tym komunikacie opisują plan, a nie wykonany zapis.

Samo `--no-publish` pomija publikację pakietu; pozostałe flagi w `make deliver` wyłączają także synchronizację wersji, tagowanie i generowanie changeloga. Podgląd Goal 2.2.4 może nadal wyświetlić hipotetyczne podbicie wersji mimo tych flag; wykonanie korzysta ze ścieżki commitu bez wydania. Goal nadal może przygotować środowisko oraz zmienić pliki konfiguracyjne, utworzyć commit i wykonać push bieżącej gałęzi. Do uruchomienia aplikacji wystarczy `./uriloop shell`, a do lokalnej kontroli `make test` i `make verify`.

Ostrzeżenie `AI badge generation failed` pochodzi z dodatkowego mechanizmu szacowania kosztów i jest oddzielne od błędu wydania. Nie wymaga dodawania klucza OpenRouter do uruchamiania CLI ani deterministycznych scenariuszy. Wydanie do rejestru pakietów jest osobnym procesem: wymaga świadomego wyboru wersji, jej synchronizacji i konfiguracji docelowego rejestru.

## Pojedyncze polecenia

```bash
uriloop do 'browser://page/navigate?url=http://demo/'
uriloop do 'browser://page/analyze?format=webp'          # zwraca URN elementów + gotowe akcje
uriloop do 'browser://page/click?urn=urn:uriloop:el:3f9c0a1b2c4d'
uriloop nl 'wpisz „uriloop” '                             # NL → browser://page/type?text=uriloop
uriloop do 'desktop://keyboard/key?key=ctrl+l'
uriloop do 'desktop://screen/analyze?grid=12'
uriloop snapshot                                          # NUL-009: stan rozmowy, procesów, zadań
```

## MCP

Narzędzia są generowane z `registry/processes.json` (nazwa = URI: `browser://page/click` → `browser_page_click`),
plus `uri_execute` (dowolne URI) i `nl_execute` (polecenie w języku naturalnym). Zrzuty wracają jako treść `image`.
Zasoby: `TODO.md`, `chat.jsonl`, `state.json`, `logs.jsonl`, `uriloop://registry/gbnf`.

```bash
make mcp-browser   # profil CDP (natywnie, stdio)
make mcp-desktop   # profil noVNC (natywnie, stdio)
```

Konfiguracja klienta: `.mcp.json` uruchamia serwery z `.venv` na hoście (ustaw `URILOOP_HOME` na katalog projektu).

## TODO.md — kolejka zadań

```markdown
- [ ] Wyszukaj frazę formularzem
  - `browser://page/type?selector=%23q&text=uriloop&submit=true`
  - expect: url~search.html?q=uriloop
  - expect: text~Fraza: uriloop
- [ ] przejdź do http://demo/page2.html         ← bez kroków: NL → URI
  - expect: text~Nawigacja zakończona sukcesem
```

Statusy `[~]` w toku, `[x]` wykonane, `[!]` nieudane ustawia projektor ze zdarzeń; dopisuje też `<!-- urn:uriloop:task:… -->`.

## Pliki robocze (`workspace/`)

| Plik | Rola |
|---|---|
| `logs.jsonl` | Event store: append-only, `prev`/`hash` SHA-256, jedyne źródło prawdy |
| `chat.jsonl` | Projekcja rozmowy: wiadomości + zwięzłe powiadomienia procesów z URI/URN |
| `state.json` | Projekcja zasobów po URN (elementy, żądania, artefakty, procesy, zadania) |
| `TODO.md` | Zadania (pisze człowiek) + statusy (pisze projektor) |
| `artifacts/` | PNG/WebP, Set-of-Mark, JSON warstw — adresowane treścią |

## Struktura

```
registry/processes.json   SSOT akcji (URI, schematy, aliasy NL, efekty)
src/uriloop/uri.py        Action URI / URN, stable_urn
src/uriloop/runtime.py    szyna komend + handlery (CQRS write)
src/uriloop/eventstore.py logs.jsonl + lustro PostgreSQL + artefakty
src/uriloop/projector.py  projekcje (CQRS read), follow/rebuild
src/uriloop/loop.py       pętla autonomiczna z expect i ponowieniami
src/uriloop/analysis/     warstwy L0–L4, sonda DOM, Set-of-Mark
src/uriloop/drivers/      cdp.py, vnc.py (RFB 3.8, TCP lub WebSocket noVNC)
src/uriloop/mcp/server.py serwer MCP stdio
src/uriloop/ai/           router (NUL-022), decision (d1/System One), generator (qwen/DeepSeek), vision (YOLO)
src/uriloop/composites.py procesy złożone proc://uriloop/<nazwa>, katalog DockURI
registry/models.json      SSOT modeli i profili
db/init.sql               tabela events (append-only) + widoki read-modeli
```

Szczegóły: [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md), zasady dla agentów: [AGENTS.md](AGENTS.md), runbooki: [errors/](errors/README.md).

## Testy

`make test` — 110 testów, w tym kontrola granicy wdrożenia (brak Ollamy/GPU/torch w compose i obrazie, MCP natywnie, `doctor`), warstwa AI na atrapie Ollama/DeepSeek (router GPU 8 GB, kontrakt d1, polityki, pętla poprawek generatora, YOLO ONNX) i E2E zadania złożonego (d1 → generator → proc:// → weryfikacja na żywym Chromium). Pozostałe: URI/rejestr/NL, event store i projekcje, schematy, klient RFB vs atrapa serwera (TCP i WebSocket),
warstwy analizy, kontrakt MCP oraz E2E z prawdziwym Chromium (pętla nad TODO.md, MCP po stdio), uruchamiane gdy przeglądarka jest dostępna.

## Ograniczenia i dalsze kroki

- Analiza pulpitu jest pikselowa (siatka, zmiany); rozpoznawanie tekstu/ikon (OCR, VLM na Set-of-Mark) to następny krok.
- Klient VNC obsługuje kodowania Raw/CopyRect; dla wolnych łączy warto dodać Tight/ZRLE.
- Klienci d1, qwen i DeepSeek są przetestowani na atrapach zgodnych z opublikowanymi kontraktami API, nie na prawdziwych modelach.
- Format katalogu DockURI jest własnym profilem — repozytorium dockuri/dockuri było niedostępne.


## License

Licensed under Apache-2.0.
