truetype 388b0a87c3
ci / lint (ruff) (push) Has been cancelled
ci / pytest (py3.10) (push) Has been cancelled
ci / pytest (py3.11) (push) Has been cancelled
ci / pytest (py3.12) (push) Has been cancelled
ci / pytest (py3.13) (push) Has been cancelled
fix(ci): switch runner label to self-hosted, extend matrix to 3.13
The build steps now declare 'runs-on: self-hosted' (default label
advertised by an act_runner registered with Docker mode). Previously
they used 'ubuntu-22.04' which would not match any default runner and
would leave every job in 'no runner available'.

ci.yml matrix extended to ['3.10','3.11','3.12','3.13'] for forward
coverage on Python 3.13 (current stable as of 2026).

docs/development.md 'CI/CD - Gitea Actions' section also corrected:
- the egress workaround now uses the actual Gitea 1.26 key
  [actions].DEFAULT_ACTIONS_URL (not ENABLE_GITHUB_ACTIONS_MIRROR)
- the act_runner docker command uses --rm -it for the first run to
  surface registration errors in the terminal before daemonization
- runner-scoping note clarifies global vs repo-scoped token URLs

Verified by ruff_exit=0 and pytest 66/66 still green locally.
2026-07-07 17:19:32 +00:00
2026-07-07 15:38:28 +00:00
2026-07-07 15:47:25 +00:00

viandante

ci version

viandante ospita un client MUD CLI pensato per collegarsi a un MUD LP‑MUD basato sul driver FluffOS (per esempio Dead Souls, libreria 3.8.6). Il client parla telnet con estensioni standard: ECHO, NAWS, TTYPE, e rifiuta (WONT) tutte le altre (GMCP, MSDP, MCCP, MXP per ora). Tiene traccia delle stanze visitate e le renderizza come mappa ASCII on-demand.


viandante — Italian for "wanderer". A telnet client + ASCII mapper for LP-MUDs (FluffOS, Dead Souls), with GMCP vitals/room decoder and append-only session logging.

        [ Town ]  -- n --  [ Bank ]
            |                |
            s                e
            |                |
        [ Gate ] ---------- [ Inn ]

Mapping the world, one step at a time.

Changelog

Le novità di viandante rispetto alla release 0.1 (bootstrap del client, prima del rename a viandante).

v0.2.0 — GMCP, mapper BFS, EventLogger, rename

Aggiunto

  • GMCP decoder (src/viandante/gmcp.py): validazione del module name via regex ^[A-Za-z][A-Za-z0-9_.]*$, decoder per Char.Vitals con sinonimi (hp/health/life, sp/mana/magic, mv/move/stam), Room.Info con vnum del server come id stabile (vnum{int(num)}).
  • Mapper multi-source BFS (src/viandante/mapper.py): coordinate calcolate solo al render (lazy layout), phase 1 BFS dal current, phase 2 orphan placement iterato (gestisce n→s), phase 3 stack nell'angolo in basso a destra per stanze isolate. Snapshot di coords.items() prima di mutare per evitare collisioni.
  • EventLogger (src/viandante/logger.py): due sink opt‑in paralleli — --log-json JSONL append‑only (un evento per riga) e --log-md Markdown streamed con heading ## HH:MM:SS.mmm — kind. Lock difensivo + idempotenza di close() + redazione password in tre punti (line_sent / local_cmd / login).
  • Comandi slash locali in cli._handle_local_command: /map /where /look /vitals /stats /password PWD /clear /help /quit. Le /... non riconosciute vengono inoltrate al MUD; /password PWD logga local_cmd + line_sent + login tutti esplicitamente redatti.
  • Auto‑login regex ancorate a riga (_LOGIN_RE / _PASSWORD_RE) con varianti > / * / : / ? e invio one‑shot del nome; suggerimento /password PWD se la password è assente.
  • Status bar prompt_toolkit con FormattedText rinfrescato 2×/s (refresh_interval=0.5) che mostra stanza corrente, GMCP on/off, hp/sp/mv.
  • Documentazione completa in docs/ (8 file: architecture, modules, protocol, usage, events, development, gitea + README index) con analisi architetturale, data flow, schema eventi del logger, flag CLI, workflow dev e flusso Gitea con warning sicurezza token.

Modificato

  • Hardening asyncio in cli._run: asyncio.wait(FIRST_COMPLETED) + conn.close() prima del drain delle pending task. Sblocco via EOF invece che CancelledError.
  • Strip delle credenziali in .git/config dopo ogni push: URL remoto resta pulito, il token viaggia solo inline nella URL del singolo git push.

Rinominato

  • progetto2 → viandante (Italian for wanderer) ovunque: package Python (src/progetto2/ → src/viandante/), pyproject name + description + keywords, repo Gitea (truetype/progetto2 → truetype/viandante), documentazione. L'entry‑point script resta mudclient (un solo nome da ricordare).

Tests

  • 66 test (precedenti 46 + 20 nuovi per EventLogger).
  • Nuovo tests/test_logger.py per roundtrip JSONL + rendering MD, redazione password, idempotenza di close(), robustness su byte ESC non validi.

v0.1.0

  • Bootstrap iniziale: client telnet minimale (IAC state machine + GMCP marker), mapper ASCII naive, supporto Dead Souls via localhost:6666. Package e repo chiamati progetto2.

Requisiti

Setup

cd viandante
python3 -m venv .venv
source .venv/bin/activate
pip install -e ".[dev]"

Verrà installato il comando mudclient.

Uso

# Default: si collega a localhost:6666 con un finestra iniziale 120×40
mudclient

# Espliciti
mudclient --host localhost --port 6666 --cols 120 --rows 40

# Auto-login: passa il nome; il client lo invia quando il server chiede
# "What name do you wish?". Se serve la password, il client chiede
# /password PWD oppure la puoi passare con --password (o env MUDCLIENT_PASSWORD).
mudclient --user zork --password hunter2

# Log di sessione: ogni evento (testo, GMCP, stanze, comandi locali, linee inviate)
# viene serializzato in append‑only, flushed dopo ogni write — adatto a `tail -f`.
mudclient --log-json ~/mud-session.jsonl --log-md ~/mud-session.md

I file --log-json / --log-md vengono truncati all'apertura: ogni nuova sessione ricomincia da zero. Per ruotare manualmente prima della sessione:

mv ~/mud-session.jsonl{,.prev} && mudclient --log-json ~/mud-session.jsonl

Schema eventi (un oggetto JSON per riga):

kind Scopo Note
session_start apertura sessione host, port, user
session_end chiusura normale reason
disconnect chiusura socket reason
text chunk ANSI ricevuto dal server raw preservato
room_enter mapper ha registrato una stanza id, name, exits
gmcp frame GMCP accettato module, payload
vitals snapshot hp/sp/mv solo i campi presenti
line_sent riga inoltrata al MUD password redatte (***)
local_cmd comando /... intercettato password/segreti redatti
login prompt/risposta auto-login value redatto di default; passare redact=False per i nomi account

Una volta connesso:

Tasto Effetto
Testo qualsiasi inviato al MUD terminato da \r\n
/map disegna la mappa ASCII delle stanze visitate (multi-source BFS)
/where mostra la stanza corrente e le uscite
/look invia look al MUD
/vitals mostra hp/sp/mv (via GMCP Char.Vitals)
/stats locali scoperti, GMCP stato, vitali
/password PWD invia una password al MUD
/clear pulisce lo schermo
/help (/?) mostra l'aiuto
/quit (/q, Ctrl‑D) esce

Il prompt è sempre del tipo:

[NomeStanza]> _

con il nome della stanza corrente in cyan brillante. In basso c'è una status bar che mostra:

[stanza: Outer Courtyard]   [GMCP: on]   HP 100/100  SP 50/50  MV 80/80

Quando il server invia Char.Vitals via GMCP, hp/sp/mv compaiono automaticamente.

Layout dei sorgenti

viandante/
├── pyproject.toml         # metadata + dipendenze + entry point `mudclient`
├── README.md              # questo file
├── .gitignore
├── docs/                  # documentazione completa (vedi sotto)
│   ├── README.md          # indice della cartella docs
│   ├── architecture.md    # analisi architetturale (data flow + boundary)
│   ├── modules.md         # descrizione modulo‑per‑modulo
│   ├── protocol.md        # telnet, GMCP, ANSI, euristiche Dead Souls
│   ├── usage.md           # flag CLI, esempi, auto‑login, comandi slash
│   ├── events.md          # schema eventi del logger
│   ├── development.md     # setup dev, pytest, ruff, come estendere
│   └── gitea.md           # flusso di pubblicazione sul Gitea + sicurezza token
├── src/viandante/         # package
│   ├── __init__.py
│   ├── __main__.py        # supporta `python -m viandante`
│   ├── cli.py             # entry point + UI a riga di comando
│   ├── telnet.py          # state machine IAC + asyncio
│   ├── deadsouls.py       # parser di stanze Dead Souls / LP‑MUD
│   ├── gmcp.py            # parser GMCP (Char.Vitals / Room.Info)
│   ├── mapper.py          # grafo delle stanze + render ASCII
│   ├── logger.py          # EventLogger (JSONL + Markdown sinks)
│   └── ansi.py            # stripper ANSI per il parser
└── tests/                 # unit test (pytest)
    ├── test_telnet.py
    ├── test_ansi.py
    ├── test_deadsouls.py
    ├── test_mapper.py
    └── test_logger.py

📚 Documentazione completa

La documentazione vive nella cartella docs/ e copre:

  • docs/architecture.md — analisi architetturale completa: dipendenze tra moduli, data flow di un frame dal server, boundary concerns, refactor raccomandati.
  • docs/modules.md — cosa fa ogni modulo, API pubblica, responsabilità, tolleranze.
  • docs/protocol.md — IAC state machine, GMCP wire format, euristiche Dead Souls, ANSI handling.
  • docs/usage.md — riferimento flag CLI, esempi pratici, flusso auto‑login, comandi slash, rotazione log.
  • docs/events.md — schema completo degli eventi del logger (JSONL + Markdown), regole di redazione, esempi jq.
  • docs/development.md — workflow sviluppatore: setup, test, lint, come aggiungere moduli, debugging.
  • docs/gitea.md — flusso di pubblicazione sul server Gitea 192.168.0.183:8085 (utente truetype) + regole di sicurezza del token.

Note sul protocollo

Il client negozia:

  • ECHO: invia WILL ECHO in risposta a DO ECHO. Il server non eseguirà più echo remoto, mentre l'eco locale è gestita da prompt_toolkit.
  • NAWS: se attivo, invia la dimensione della finestra iniziale.
  • TTYPE: risponde con XTERM-256COLOR quando il server lo richiede.
  • GMCP: viene offerto proattivamente (WILL al connect). Dead Souls lo accetta e invia pacchetti Char.Vitals, Room.Info, Char.Login, ecc. Quando una stanza arriva via Room.Info, si usa il num (vnum) del server come id stabile della stanza.

Le righe di stato del parser considerano IAC GA (249) e IAC EOR (239) come delimitatori di "record/prompt", così da poter chiudere un blocco di stanza anche quando il prompt testuale non è immediatamente riconoscibile.

La rilevazione automatica della geometria del mondo si basa sul movimento inviato dall'utente + descrizione della stanza osservata subito dopo: quando digiti n, il client informa il mapper; quando la risposta del MUD arriva, Mapper.visit() collega automaticamente corrente → nuova lungo la direzione digitata.

Il render della mappa è un multi-source BFS dal current room, con fallback di posizionamento orfano per le stanze visitate in sequenza indietro (es. n→s) e stack nell'angolo in basso a destra per stanze completamente isolate.

Limitazioni note (v0.2)

  • MSDP / MCCP / MCP / MXP non sono ancora abilitati: una sessione che li richiede ottiene WONT dal nostro lato. Dead Souls usa GMCP, che è ben coperto; altri MUD basati su MSDP possono essere aggiunti seguendo lo schema di gmcp.py.
  • La detection delle stanze senza GMCP resta euristica sul testo.
  • Login automatico: il client riconosce il prompt What name do you wish? / Password: e invia automaticamente solo la prima volta. Per la password senza --password viene mostrato un suggerimento usa /password <pwd> (niente prompt annidato, troppo fragile).
  • Gli u/d (verticali) sono rappresentati nella mappa ma non influenzano le coordinate 2D (offset (0,0)).
  • La status bar si aggiorna ad ogni rilettura della riga del prompt; per refresh continui in idle si può aggiungere refresh_interval al PromptSession come estensione futura.

Sviluppo

pip install -e ".[dev]"
pytest          # gira tutta la suite
ruff check src tests   # lint

Licenza

MIT per il codice del client. Il server Dead Souls non è GPL — assicurati di rispettarne la licenza prima di redistribuirlo.

S
Description
viandante - CLI MUD client (Dead Souls / LP-MUD over telnet) with ASCII mapper, GMCP vitals+room decoder and append-only session logging
Readme
165 KiB
0 Stars 1 Watchers 0 Forks
Latest
2026-07-07 15:59:00 +00:00
Languages
Python 100%