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.
viandante
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 perChar.Vitalscon sinonimi (hp/health/life,sp/mana/magic,mv/move/stam),Room.Infocon 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 dalcurrent, phase 2 orphan placement iterato (gestiscen→s), phase 3 stack nell'angolo in basso a destra per stanze isolate. Snapshot dicoords.items()prima di mutare per evitare collisioni. - EventLogger (
src/viandante/logger.py): due sink opt‑in paralleli —--log-jsonJSONL append‑only (un evento per riga) e--log-mdMarkdown streamed con heading## HH:MM:SS.mmm — kind. Lock difensivo + idempotenza diclose()+ 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 PWDloggalocal_cmd+line_sent+logintutti esplicitamente redatti. - Auto‑login regex ancorate a riga (
_LOGIN_RE/_PASSWORD_RE) con varianti>/*/:/?e invio one‑shot del nome; suggerimento/password PWDse la password è assente. - Status bar
prompt_toolkitconFormattedTextrinfrescato 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 cheCancelledError. - Strip delle credenziali in
.git/configdopo ogni push: URL remoto resta pulito, il token viaggia solo inline nella URL del singologit push.
Rinominato
progetto2→viandante(Italian for wanderer) ovunque: package Python (src/progetto2/→src/viandante/), pyprojectname+description+keywords, repo Gitea (truetype/progetto2→truetype/viandante), documentazione. L'entry‑point script restamudclient(un solo nome da ricordare).
Tests
- 66 test (precedenti 46 + 20 nuovi per
EventLogger). - Nuovo
tests/test_logger.pyper roundtrip JSONL + rendering MD, redazione password, idempotenza diclose(), 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 chiamatiprogetto2.
Requisiti
- Python ≥ 3.10
- Una libreria Python:
prompt_toolkit≥ 3.0
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-mdvengono 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, esempijq.docs/development.md— workflow sviluppatore: setup, test, lint, come aggiungere moduli, debugging.docs/gitea.md— flusso di pubblicazione sul server Gitea192.168.0.183:8085(utentetruetype) + regole di sicurezza del token.
Note sul protocollo
Il client negozia:
ECHO: inviaWILL ECHOin risposta aDO ECHO. Il server non eseguirà più echo remoto, mentre l'eco locale è gestita daprompt_toolkit.NAWS: se attivo, invia la dimensione della finestra iniziale.TTYPE: risponde conXTERM-256COLORquando il server lo richiede.GMCP: viene offerto proattivamente (WILLal connect). Dead Souls lo accetta e invia pacchettiChar.Vitals,Room.Info,Char.Login, ecc. Quando una stanza arriva viaRoom.Info, si usa ilnum(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
WONTdal nostro lato. Dead Souls usa GMCP, che è ben coperto; altri MUD basati su MSDP possono essere aggiunti seguendo lo schema digmcp.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--passwordviene mostrato un suggerimentousa /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_intervalalPromptSessioncome 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.