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

@@ -66,6 +66,11 @@ class TestTraceView:
out = _plain(render_exchange(bytes([0x30, 0x04]), None, protocol="hf14a", is_tty=False))
assert "READ" in out.upper()
def test_accepts_hex_string_response(self):
# reader raw() returns `data` as a hex STRING — must not crash on bytes()
out = _plain(render_exchange(b"\x60", "0004040201000f03", protocol="hf14a", is_tty=False))
assert "60" in out and "00 04 04" in out
class TestCliDispatch:
def test_rawcli_subcommand_parsed(self):
@@ -380,6 +385,19 @@ class TestCompleter:
cs = self._complete(s, "help GET")
assert any(c.text == "GET_VERSION" for c in cs)
def test_opcode_completion_hex(self):
s = RawSession()
s.protocol, s.transponder = "hf14a", "NTAG213"
cs = self._complete(s, "60") # GET_VERSION opcode
assert any(c.text == "GET_VERSION(" for c in cs)
def test_opcode_completion_binary(self):
s = RawSession()
s.protocol, s.transponder = "hf14a", "NTAG213"
s.entry_mode = "bin"
cs = self._complete(s, "01100000") # 0x60 in binary
assert any(c.text == "GET_VERSION(" for c in cs)
class TestKeyBindings:
def test_install_does_not_raise(self):