Perhaps I could watch the PC of the apple and see when it hits the loop to accept user input. I don't know where it actually stores the buffer position in the zero page. I was thinking that it just kept it in the X register as far as I could tell.

I could register a function that would watch the PC.

for key,value in pairs(manager:machine().devices[":maincpu"].state) do print(key,value) end
PC sol.device_state_entry*: 0x55a370aea448
CURFLAGS sol.device_state_entry*: 0x55a370aea598
IR sol.device_state_entry*: 0x55a370ae9848
CURSP sol.device_state_entry*: 0x55a370aea848
P sol.device_state_entry*: 0x55a370ae9c48
SP sol.device_state_entry*: 0x55a370ae9998
CURPC sol.device_state_entry*: 0x55a370aeac48
A sol.device_state_entry*: 0x55a370aea198
GENPC sol.device_state_entry*: 0x55a370aead98
Y sol.device_state_entry*: 0x55a370ae9d98
X sol.device_state_entry*: 0x55a370aea048


Maybe I could even stuff the keys into the $200-2FF input buffer rather than going through posting keystrokes.