| Commit message (Collapse) | Author | Age | Files | Lines |
| |
|
|
|
|
|
|
|
| |
The multiplexer is auto-detected ($ZELLIJ then $TMUX) and every pane
command works the same under both. Pane ids are self-identifying, so a
mixed set of tmux and zellij panes can be tracked at once. zellij has no
-l, so --size is ignored there, it always focuses a new pane (--detached
is emulated), and it leaves an EXITED husk behind that stop/reap now
clear. The pane tests skip on neither multiplexer rather than on no TMUX.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Opening a headerless blob was the one workflow that fell out of the driving
surface entirely, and each gap hid the next:
- `pane spawn` couldn't pass --processor/--base/--ida-args, so the pane came up
"ready" with zero functions (x86 at 0) and the only way through was to
hand-write a project file. It now forwards them to idatui.launch.
- c/p/t/T (code, function, ARM<->Thumb, vector scan) existed as listing
bindings with no verb, so a driver had to guess raw keys -- and raw keys are
swallowed by whatever modal happens to be up. `define {kind,target?}` goes
through the app's own edit worker and reports what IDA actually did.
- every name went through the typed rename prompt: a navigation (listing page +
decompile) plus two prompt round-trips each. A 427-symbol map took tens of
minutes of driving. `rename_many {items|file}` hands IDA's rename tool the
whole list in one call (371 symbols in 3s) and refreshes the caches and the
function table once.
drive gains `define <kind> [target...]` and `syms <file.json>`.
Verified live against a real pane (tests/test_rawimage_rpc.py, 13 checks:
spawn load options, define thumb/func + unknown-kind rejection, rename_many
from a file and inline, with resolve/functions readback).
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Five defects found while an agent drove a long RE session over the socket.
Each one was reproduced on a live spawned pane first (an in-process pilot
would not have shown any of them), then fixed:
pane stop truncated the save. `stop` asked the app to quit, slept 400 ms, then
unconditionally killed the pane. Quitting runs App.on_unmount, which writes
every dirty database; a 90 MB .i64 takes tens of seconds, so the kill landed
mid-write and a whole session's annotations went to /dev/null with a cheerful
{"stopped": [...]} on stdout. Now it waits for the pane to actually exit
(--timeout, default 600 s) and only force-kills on timeout, saying so.
The quit verb bypassed the dirty check. It called app.exit() directly rather
than the path a human gets, so the "unsaved changes" logic never ran. It now
routes through _on_quit_choice and reports {saving, dirty}.
Naming a function start from the listing never reached the function index.
`goto <addr>` puts the cursor on the address token, so `n` takes the
name-an-address path, which called bump_items() but left FunctionIndex holding
the old name. Result: the rename response snapshot showed the section label,
and functions()/names()/the palette all reported the rename had not happened —
so a driver that trusts its readbacks redoes work it already did. Twice, in
the session that prompted this. _do_name_addr now updates the index, the nav
stack and the table cell when the address is a function start.
A stripped binary with no entry function started up *inside a modal*.
_auto_land pushed the symbol palette when main() was missing, while ping still
answered ready:true. Every keystroke an RPC driver injected went into the
palette's search box and was silently swallowed. It now lands on the first
function instead and hints at Ctrl+N.
Verbs that inject keystrokes now refuse when a modal is on top, naming it,
instead of failing with "'goto' prompt did not open (word under cursor?)" —
a message that blamed the cursor for what was always a focus problem.
Also: `drive raw` passes k=v values through as strings, so `view lines=8` died
with "'<' not supported between instances of 'int' and 'str'". Numeric params
are now coerced centrally rather than at each call site.
tests/test_scenarios.py: 212 passed, 0 failed.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Driving the trace viewer needed the same treatment as everything else: seeking by
hand through a few hundred keypresses to reach an interesting timestamp is not a
way to test it.
trace {seek: 120} absolute timestamp
trace {seek: "!50"} halfway through, like Tenet's timestamp shell
trace {goto: "main"} first execution of a name or 0xADDR
trace {step: 20} relative, negative goes back
trace {step: 5, over: true}
Returns the usual state snapshot plus {idx, length, pc, changed} so a driver can
see where it landed and what that instruction wrote.
pane spawn --trace FILE passes it through to the launcher, so a trace pane is one
command.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Three things, all following from phase 3 making cross-binary jumps ordinary.
**A cross-binary jump was a one-way door.** Nav history is per-binary, so
arriving in another binary — a project search hit, or now following an import
into the library that implements it — landed you in an empty history with nothing
to take you back. _switch_then_goto records the binary it came FROM, and
action_back falls through to that hop once local history is spent: Esc walks back
through the function you were in, then the binary you were in. Manual Ctrl+O
switching records nothing, because that isn't navigation.
**Pre-warm follows the linkage graph, not list order.** _prewarm_provider warms
the binary providing the most of this one's imports — where a follow is most
likely to go, so its startup is paid before you ask for it. "Next in the list"
would have been arbitrary; phase 3 gave us something better to ask.
pool.prewarm() refuses rather than making room. Evicting a binary the user
visited to speculatively load one they haven't is a straight downgrade, and it
throws away that binary's caches as well; at a tight budget pre-warm just does
nothing. The cost of a worker that doesn't exist yet can only be estimated, so it
uses the largest resident one (same program, different database) — and if that
estimate proves wrong, the speculative worker is the one evicted, never a chosen
one.
**Driving a project.** pane spawn --project FILE [--open BIN]; `binaries` lists
the inventory (active / resident / indexed / where Esc returns to) and `switch
{binary,addr?}` makes another active — with an address it takes the search-hit
path, so it records a hop. state gains `binary` and `hops`, which it should have
had the moment project mode existed.
Verified on real sessions: drive binaries/switch against an echo+cat project
pane; Esc crossing back from a switch; and prewarm on echo+libc picking libc
(provider of echo's imports) and warming it after an evict.
tests: +5 pool (prewarm warms, no-ops when resident, refuses at budget, evicts
nothing when refusing, ignores unknown labels) and +4 project UI (jump records
the hop, Esc crosses back, hop consumed). Confirmed the Esc-back checks fail with
the branch removed. 195/0 scenarios, 27/0 project UI, 36/0 index, 27/0 pool,
33/0 project.
Left open: project-level persistence across sessions.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
The idalib worker is the only backend now, so remove the dead HTTP/supervisor
surface entirely (~2200 lines):
* deleted idatui/client.py (the IDAClient HTTP/JSON-RPC transport + session
manager), idatui/tui.py (the old mcp TUI entry, superseded by launch.py),
spawn.sh, and systemd/ (the supervisor unit).
* deleted the mcp-only tests (stress_client, smoke_client, test_keepalive,
stress_paging, rpc_smoke, serverctl.sh, pane_smoke, test_domain) -- the worker
pilot (tests/test_scenarios.py) supersedes them.
* migrated the tmux RPC harness (idatui/pane.py) to the worker: it spawns
`idatui.launch <binary> --rpc <sock>` instead of the mcp `idatui.tui`, drops
the supervisor auto-start/ensure machinery, and reaps our own worker
(idatui/worker.py) instead of ida_pro_mcp.idalib_server. --db/--url/--no-
ensure-server are gone; --open is required.
* __init__ / __main__ / domain no longer import client (exceptions come from
errors.py, the domain client hint is WorkerClient); pyproject points both
console scripts at idatui.launch; README + ida-tui header describe the
worker-only flow.
What stays (by design): the ida_pro_mcp *package* (the worker reuses its @tool
functions in-process) and server/patch_server.py (the worker injects its custom
tools on startup). Verified: whole package imports + IdaTui constructs + pilot
lists 31 scenarios. The worker pilot (134 pass / 2 known flakes) is the E2E gate.
|
| |
|
|
| |
spawn-hang + bare KeyError)
|
| |
|
|
|
|
|
|
|
|
|
|
| |
Before creating the TUI pane, spawn probes 127.0.0.1:8745 and, if nothing
is listening, launches ./spawn.sh in its own detached tmux pane and waits
for the port (server_started/server_pane are reported in the JSON). Only
for a local server, never a duplicate (spawn.sh binds the port), and it's
not killed on 'stop' (shared across TUIs). --no-ensure-server opts out;
IDATUI_SERVER_CMD overrides the launch command (used by the test).
tests/pane_smoke.py exercises the machinery with a dummy port-binder in a
pane (start / detect / idempotent / remote-guard), 5 green.
|
|
|
python -m idatui.pane spawn --open <bin> (or --db <session>) splits a
tmux pane running the TUI on a fresh RPC socket, blocks until it reports
ready (ping.ready; --open runs full auto-analysis so the default wait is
generous), and prints {sock,pane,ready,functions,module} as JSON. The
agent then drives it via idatui.rpcclient and tears it down with
'stop --sock <s>' (graceful quit -> kill-pane -> unlink). A small
registry in $XDG_RUNTIME_DIR tracks panes for 'list'/'stop'.
Verified end-to-end: spawn -> goto/pseudocode -> list -> stop.
|