fix(rawcli): raw response crash + hex/binary opcode completion

- Crash on raw hex: reader raw() returns `data` as a hex STRING; the raw
  path fed that to bytes() -> "string argument without an encoding".
  _raw_exchange now returns the bytes (`raw` field, or converts `data`);
  render_exchange accepts bytes or a hex string.
- Completion for numeric entry, transponder-dependent: typing hex ("60")
  or binary ("01100000") now matches the identified tag's command opcodes
  and surfaces the named command (GET_VERSION) with its help. Honours a
  0x/0b prefix and the current entry mode.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
michael
2026-07-14 13:00:31 -07:00
parent 7703b638f3
commit 1370526883
4 changed files with 103 additions and 18 deletions

View File

@@ -12,13 +12,20 @@ _PROTOCOLS = {
}
def render_exchange(command: bytes, response: bytes | None, *,
protocol: str = "hf14a", is_tty: bool | None = None) -> str:
def _as_bytes(value) -> bytes:
"""Accept bytes or a hex string (which is what the reader `raw()` returns as `data`)."""
if isinstance(value, str):
return bytes.fromhex(value.replace(" ", ""))
return bytes(value)
def render_exchange(command, response, *, protocol: str = "hf14a", is_tty: bool | None = None) -> str:
"""Return the annotated trace for one exchange: a ``Reader → Tag`` line for ``command`` and,
if present, a ``Tag → Reader`` line for ``response``. ``is_tty`` forces/suppresses ANSI."""
if present, a ``Tag → Reader`` line for ``response``. Both accept bytes or a hex string.
``is_tty`` forces/suppresses ANSI."""
decoder, crc_len = _PROTOCOLS.get(protocol, (None, 0))
fmt = TraceFormatter(mode="reader", decoder=decoder, crc_len=crc_len, is_tty=is_tty)
lines = [fmt.format(0, bytes(command)).strip("\n")]
lines = [fmt.format(0, _as_bytes(command)).strip("\n")]
if response:
lines.append(fmt.format(1, bytes(response)).strip("\n"))
lines.append(fmt.format(1, _as_bytes(response)).strip("\n"))
return "\n".join(lines)