Add acquisitionctl for safe local inventory inspection and refresh control - #192
Add acquisitionctl for safe local inventory inspection and refresh control#192aurokin wants to merge 49 commits into
Conversation
Reuses the stored OAuth token (refreshed at startup by OAuthManager), runs the same session/update path as the GUI login flow with no windows shown, prints a parseable HEADLESS_SYNC_RESULT line, and exits. Exit codes: 0 ok, 2 needs interactive login (no token/league/account), 3 token refresh failed, 4 another instance running, 5 sync error, 6 watchdog timeout (10 min). Every run now holds a QLockFile in the data dir; headless refuses to start alongside any other instance, the GUI only warns. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WtLF9xL3JK3wez4d7ffE57
# Conflicts: # src/itemsmanagerworker.cpp
|
Nice work. I'll spend time evaluating the design this week, but at first glance I agree with your approach. I'm guessing you're using this already. If so, would you mind incrementing the version and adding an alpha or beta postfix in CMakeLists.txt? That number makes it into the GGG-visible User-Agent header, which they monitor, but don't change the application name, because I think GGG white-lists apps by name. (If you're not using this yet, let me know and I'll prioritize releasing this as an alpha). |
|
Yes, I'm using it lightly against my real account. I'm a new PoE player, and my main use so far is letting agents inspect my gear and inventory so they can help me learn the game; I also use it to manually queue refreshes. I've bumped the branch to |
|
Welcome to a very complicated game, and thank you--GGG has contacted me based off the User-Agent header, so I know they are looking at it when things go wrong. For background, most of the users I've heard from since I forked acquisition in 2023 are long-term players with thousands of tab in standard, or people who use forum shops to list items on the trade site instead of merchant tabs. I have more internal rework planned for 0.18.x, but it shouldn't impact the CLI design and I love the idea of making acquisition AI-friendly for the next generation of players. |
|
Thank you! My friends are long term players like you are describing so I have some help but I like offloading some of the simple questions because, theres a lot of them. I'm building a CLI that works similar to my Warcraft CLI to connect an agent to multiple PoE surfaces! I'll make it public when I can. I'll stop using it until the User-Agent stuff is all handled I don't want you to get in trouble with GGG! Edit: Also wanted to note I have access to linux + windows hosts along with OSX so if you need me to test / dogfood on a platform I can! |
|
No, please keep using this! Your feedback and thinking on what to build here is valuable. The User-Agent is just a way to make sure GGG has some visibility. |
|
Actually, if you're interested, are you ok talking about this sometime? |
|
Sure I sent a friend request on discord! |
Patch release staging for the fixes on master since v0.18.0: the buyout price formatting fix (#150), the F30 pacing-message downgrade, the PR #193 cleanups, and the PR #194 mechanical credential-safety findings (F68-F73, F76). Cut ahead of merging PR #192 so the 0.19.0-alpha.1 feature release stays separate. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Here's a proper review. as we get through the redesign I'm guessing the control surface will change, but for the application as it stands now, this looks like a good candidate for 0.19.0-alpha.1. --@gerwaric Review: recommend mergeThis is unusually careful work, and I'd like to land it. I did a full review with Claude's help — spec first, then the diff, then building the branch and driving both binaries hard on Windows, which is the platform the spec itself flags as its validation gap. Everything below is either resolved, a genuine question, or a nit; nothing is a merge condition. What was verified locally (Windows 11, MSVC 2022, Qt 6.11.1)
Two questions I can already resolveData-dir default ( The committed Open questions (your call on each)Q3 — The spec's Verification contract claims a bit more than the automated tests deliver. Four deltas: a Q4 — Busy-recovery with a fresh request id is untested. The busy-retry test reuses request id Q5 — Should Q6 — After a GUI restart, a stale cursor reports Q7 — The install command targets two agents ( Nits (batch at will; none urgent)
Two small doc asks, at merge timeBoth come from the architecture side (a fuller contract-focused review of the protocol as the long-term UI/core seam is coming separately on the
That second item is also the graduation gate for dropping the |
…nd 1) The A0 seam review migration-order names under step zero. Verdict: good seed, cheap to walk back. The generation token the windowed protocol needs already exists as (instance_id, revision); pages are bounded three independent ways (write it down as a standing constraint, R1-2); the refresh terminal outcome answers shop-write-path §5's open question with yes (R1-3); refresh.start establishes the idempotent command semantics the A0 write addendum can reuse (R1-5); an internals-leakage audit found nothing a Rust core could not reproduce, including the endpoint identity re-derived from a non-Qt client (R1-6). The one structural gap is the missing notification path — v1 is strictly poll-based, right for a CLI, insufficient for a live UI (R1-7); that plus a windowed-read command is what A0 still owes. Verified by build-and-drive on Windows against scrubbed real data (39/39 tests; mid-refresh revision_changed observed live). Answers the open questions in shop-write-path (refresh outcome) and migration-order (versioning story; superseded by the sharper R1-7). No new correctness findings in untouched code this round. Deletes the working brief per docs/redesign conventions (precedent: 2d27655, 8a3f751). The merge-side review is a separate untracked deliverable for Tom (pr192-merge-review.md). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
@gerwaric do you think the redesign branch is a better target for this? |
|
@aurokin I don't have a strong opinion, but redesign makes sense |
Author Note: I was going to do smaller PRs but this is a pretty big shift, feel free to reference what you like, or decline all of it! The goal here is to add a surface for agents to drive acquisition so they can read data themselves, and manually queue refreshes. Instead of more hacky solutions this should work long term. I haven't tested AT ALL on linux or windows.
Summary
This adds
acquisitionctl, a versioned command-line interface to an already-running Acquisition GUI.The CLI lets users, scripts, and coding agents:
The GUI remains the sole owner of OAuth credentials, POESESSID, persistence, rate limiting, refresh execution, and existing automatic-shop behavior.
Why
Automating Acquisition previously required one of several undesirable approaches:
This change instead exposes a narrow local interface over the state Acquisition has already published to its UI. It avoids creating a second source of truth, another network client, or an agent-specific protocol inside the application.
User-facing commands
acquisitionctl statusacquisitionctl tabsacquisitionctl itemsacquisitionctl item <id>acquisitionctl refresh startacquisitionctl refresh status <id>acquisitionctl refresh wait <id>Successful commands emit a versioned JSON envelope. Human diagnostics go to stderr.
--data-dirselects the same application instance as the GUI.Example:
Inventory contract
Inventory responses come from
ItemsManager, not repository tables. Effective prices come fromBuyoutManager, matching the data consumed by the GUI.Items expose normalized fields including:
The implementation adds maintained item-ID and per-location count indexes to
ItemsManager. This avoids flattening or scanning the entire inventory for single-item lookups and tab counts.Consistent, bounded pagination
Large inventories are traversed using opaque authenticated cursors.
The first page captures an
instance_idandinventory_revision. Later pages returnrevision_changedif the published inventory changes, preventing clients from silently combining pages from different snapshots.Bounds include:
Cursors contain the original filters and pagination state and are authenticated with a process-private HMAC. Clients cannot modify a cursor to change its query.
A sparse filtered page may be empty while still returning a continuation cursor. This keeps individual requests bounded without incorrectly declaring the traversal complete.
Application-owned refreshes
Refreshes use the existing
ItemsManager::Update(TabSelection::All)path rather than introducing another synchronization implementation.A refresh-start request ID also serves as:
The application retains the 32 most recent operations. Disconnecting the CLI or timing out while waiting does not cancel an accepted refresh.
Terminal results distinguish:
If a start response is lost after transmission, the CLI retries once with the same ID. If the result remains ambiguous, it reports that operation ID instead of risking a duplicate refresh.
Existing automatic-shop settings remain in effect. Refresh completion describes inventory refresh only; it does not claim that asynchronous forum posting has completed.
Local transport and security
The protocol uses
QLocalServerandQLocalSocket. It does not open a TCP port or provide remote control.Messages are length-prefixed, versioned JSON with strict request validation. The server limits concurrent connections, request time, frame size, buffered input, and response size.
Endpoint identity is derived from the canonical data-directory path, so separate Acquisition data directories remain independently addressable.
On Unix:
sockaddr_un::sun_path;On Windows:
A secondary GUI retries ownership periodically, allowing it to take over if the original endpoint owner exits without changing Acquisition's existing multi-instance policy.
Packaging
Release packaging adds the CLI alongside the GUI:
acquisitionctl.exebesideacquisition.exe;acquisitionctlAppImage artifact.Local builds produce
build/acquisitionctl.The CLI is intentionally release-matched with the GUI rather than installed as an unrelated system-wide command.
Consumer agent skill
The PR includes an optional consumer-facing agent skill at:
This is similar to the reusable skill shipped by Diffwarden. It teaches coding agents how to operate an installed Acquisition application safely.
Consumers can install it with the Skills CLI:
The skill covers:
acquisitionctl;--data-dirconsistently;The skill is for consumers using Acquisition, not contributors developing the repository. It is plain Markdown plus agent interface metadata and introduces no runtime dependency or embedded agent protocol.
Installing the skill does not install Acquisition or
acquisitionctl; users install an Acquisition release separately.Deliberate non-goals
This does not add:
Existing-code impact
Most of the change is isolated in new control, CLI, test, documentation, and skill files:
The main integration changes are:
Applicationowns and wires the control server and service;ItemsManagermaintains item-ID and per-location indexes;ItemsManagerWorkerexposes its application-thread readiness state;ItemLocationexposes already-stored projection fields;No persistence schema or credential-handling code is exposed through the control service.
Suggested review order
1. Contract and scope
docs/design/local-control.mdREADME.mdskills/acquisition-cli/SKILL.mdThese establish the intended behavior, security boundary, non-goals, and consumer workflow.
2. Protocol and endpoint ownership
src/control/controlprotocol.*src/control/controlendpoint.*src/control/localcontrolserver.*src/control/localcontrolclient.*Review framing, bounds, endpoint discovery, same-user ownership, stale recovery, and deadlines here.
3. Published data and refresh behavior
src/control/controlservice.*src/control/viewprojection.*Review the JSON projection, pagination contract, cursor authentication, revision checks, and refresh-operation lifecycle here.
4. Existing application integration
src/application.*src/itemsmanager.*src/itemsmanagerworker.*src/itemlocation.hThis is the relatively small portion that changes existing application behavior.
5. CLI and packaging
src/acquisitionctl.cppCMakeLists.txt.github/workflows/build-*.ymlacquisitionctl.desktopReview command parsing, exit statuses, release locations, and artifacts here.
6. Tests and scale coverage
tests/tst_controlprotocol.cpptests/tst_localcontrolserver.cpptests/tst_controlservice.cpptests/tst_acquisitionctl.cpptests/control_benchmark.cppReviewing the final diff by these layers is easier than following the development commit sequence, where later commits harden behavior introduced by earlier commits.
Validation
The branch includes current upstream
masterand is zero commits behind it.Completed locally:
Release benchmark on an Apple M4 Max:
Remaining validation gaps
The following require native CI or platform testing:
A live authenticated refresh and forum update were deliberately not run against user data during local verification.