feat(windows): add HyperHQ launcher and bridge plugin#806
feat(windows): add HyperHQ launcher and bridge plugin#806wizzomafizzo wants to merge 13 commits into
Conversation
Adds a HyperHQ launcher to the Windows platform. Zaparoo Core hosts a named pipe at \\.\pipe\zaparoo-hyperhq-ipc and exchanges JSON-line messages with a bridge plugin (separate Go module under scripts/windows/hyperhq-plugin/) that HyperHQ launches as a Socket.IO plugin per the API at https://docs.hyperai.io/docs/plugins/. Two wire formats: pipe (Core <-> bridge) is internal PascalCase JSON; HyperHQ Socket.IO (bridge <-> HyperHQ) is camelCase per the API. The bridge handles challenge-response auth, plugin:register, subscribeEvents, requestData/dataResponse routing by requestId, and the hyperHqEvent envelope (gameLaunched / gameClosed). Bridge module is exempt from forbidigo and depguard so it can use stdlib sync and log without pulling Zaparoo Core internals into its dep tree. Linted via task cross-lint:windows since winio is Windows-only.
📝 WalkthroughWalkthroughAdds Windows HyperHQ support: a named-pipe server in core, a Windows bridge plugin (Socket.IO client) with build artifacts, hyperhq URI scheme, a HyperHQ launcher, mapping/assets, tests, and lint/build task updates. ChangesHyperHQ Windows Integration
Estimated code review effort🎯 4 (Complex) | ⏱️ ~75 minutes
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
There was a problem hiding this comment.
Actionable comments posted: 3
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
scripts/windows/hyperhq-plugin/main.go (1)
224-857: 🛠️ Refactor suggestion | 🟠 Major | 🏗️ Heavy liftPlease add tests for the new bridge module.
I don't see companion coverage in this diff for the auth handshake, pipe request routing, or reconnect/error paths, and this file carries most of the new protocol risk.
As per coding guidelines, "Write tests for all new code — follow TESTING.md and pkg/testing/README.md patterns".
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@scripts/windows/hyperhq-plugin/main.go` around lines 224 - 857, Add unit/integration tests for the new bridge behavior: create tests exercising the authentication handshake and reconnect path (exercise connectSocket by driving "connect" and "authenticated" events and assert sessionToken set and authDone behavior), request/response routing (test requestData/requestDataCtx together with handleDataResponse to ensure pendingData mapping, cleanup, and error paths), lifecycle request handling (call handleLifecycleRequest with various hqLifecycleRequest payloads including missing ID and hqMethodShutdown to verify plugin:response emit and cancel), pipe interaction (exercise writePipeEvent, servePipeOnce/runPipeLoop by faking a pipe connection or using an in-memory net.Pipe to verify pushed Systems/Games and command routing via handlePipeCommand), and edge cases (decodeFirst, unmarshalIfPresent, newRequestID uniqueness/fallback). Follow TESTING.md and pkg/testing/README.md patterns: add a _test.go beside main.go, use test doubles/mocked socketio server and a fake pipe connection, control contexts/timeouts to exercise reconnects and error returns, and assert logs/returned errors where appropriate.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@scripts/windows/hyperhq-plugin/go.mod`:
- Line 3: The linter version in .golangci.yml is pinned to go: "1.25" which
conflicts with the root module's go 1.26.3 and the plugin's go 1.26.2; update
the go setting in .golangci.yml from "1.25" to "1.26.3" so the linter target
matches the root go.mod, and optionally also bump the plugin's go directive in
scripts/windows/hyperhq-plugin/go.mod from 1.26.2 to 1.26.3 to keep all go
versions consistent across the repo.
In `@scripts/windows/hyperhq-plugin/main.go`:
- Around line 584-586: The goroutines started by b.pushSystems and pushGames are
using the shared b.pipeWriter and can outlive servePipeOnce, causing late writes
to hit a new session; fix by making the work session-scoped: in servePipeOnce
create a per-connection context or generation ID and a session-scoped writer
(e.g. capture the current b.pipeWriter into a local variable or pass a
sessionWriter interface into pushSystems/pushGames), pass that session writer
and context/generation into the goroutines, have the goroutines check the
context/generation (or return early if writer is nil/closed) before writing, and
cancel or bump the generation on reconnect so late completions are dropped
instead of writing to the new pipe.
- Around line 384-386: The disconnect handler registered via onSocket should
clear the bridge's authentication state so requestDataCtx doesn't think the
bridge is still authenticated; inside the "disconnect" callback clear
b.sessionToken (and optionally b.authenticated or any flag used to indicate an
authenticated state) so subsequent transient reconnects don't use a stale token
until a new "authenticated" event repopulates it.
---
Outside diff comments:
In `@scripts/windows/hyperhq-plugin/main.go`:
- Around line 224-857: Add unit/integration tests for the new bridge behavior:
create tests exercising the authentication handshake and reconnect path
(exercise connectSocket by driving "connect" and "authenticated" events and
assert sessionToken set and authDone behavior), request/response routing (test
requestData/requestDataCtx together with handleDataResponse to ensure
pendingData mapping, cleanup, and error paths), lifecycle request handling (call
handleLifecycleRequest with various hqLifecycleRequest payloads including
missing ID and hqMethodShutdown to verify plugin:response emit and cancel), pipe
interaction (exercise writePipeEvent, servePipeOnce/runPipeLoop by faking a pipe
connection or using an in-memory net.Pipe to verify pushed Systems/Games and
command routing via handlePipeCommand), and edge cases (decodeFirst,
unmarshalIfPresent, newRequestID uniqueness/fallback). Follow TESTING.md and
pkg/testing/README.md patterns: add a _test.go beside main.go, use test
doubles/mocked socketio server and a fake pipe connection, control
contexts/timeouts to exercise reconnects and error returns, and assert
logs/returned errors where appropriate.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 207c0fcd-bec9-4c97-98a6-7d6d7c3f1593
⛔ Files ignored due to path filters (1)
scripts/windows/hyperhq-plugin/go.sumis excluded by!**/*.sum
📒 Files selected for processing (13)
.golangci.ymlTaskfile.dist.ymlpkg/platforms/shared/schemes.gopkg/platforms/windows/hyperhq.gopkg/platforms/windows/hyperhq_test.gopkg/platforms/windows/launchbox.gopkg/platforms/windows/platform.goscripts/tasks/cross-lint.ymlscripts/tasks/windows.ymlscripts/windows/hyperhq-plugin/Dockerfilescripts/windows/hyperhq-plugin/go.modscripts/windows/hyperhq-plugin/main.goscripts/windows/hyperhq-plugin/plugin.json
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
pkg/platforms/windows/hyperhq.go (1)
1-986:⚠️ Potential issue | 🟠 Major | ⚡ Quick winAdd missing unit coverage for HyperHQ request/response correlation
pkg/platforms/windows/hyperhq_test.goalready covers the HyperHQ wire structs (JSON serialization/deserialization), the mapping helpers (buildHqMappings,shouldIgnoreEmptyHqSystemsRefresh,buildHqSystemLookupviaTestHyperHqPlatformMapping), and scanner buffer sizing, plus “not connected” error paths forIsConnected/LaunchGame/RequestSystems.Add unit tests for
hqSystemQueryKeyand for the"Games"event handling logic that matchespendingGamesReq.queryKeyand sends onpendingGamesReq.response(there are currently no tests exercisingRequestGamesForSystemSyncor this pending-response correlation path).🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@pkg/platforms/windows/hyperhq.go` around lines 1 - 986, Add unit tests to cover hqSystemQueryKey and the pending-games response correlation: write a test that verifies hqSystemQueryKey produces the expected null-separated key for a hqSystemQueryTarget, and write a test that simulates receiving a "Games" event string by calling handleEvent (or by invoking the scanner path) with a JSON payload whose SystemId/SystemName/SystemReferenceId matches a pendingGamesReq.queryKey; set up the HyperHqPipeServer with a pendingGamesReq.response channel (protected by pendingGamesReqMu) and assert the response channel receives the expected hqGamesResponse (including Error and Games fields), and also a negative case where queryKey mismatches so no send occurs. Use the server methods RequestGamesForSystemSync and/or directly manipulate pendingGamesReq/pendingGamesReqMu to emulate an in-flight request and ensure proper locking/unlocking around pendingGamesReq in the test.
🧹 Nitpick comments (1)
pkg/platforms/windows/hyperhq.go (1)
277-318: 💤 Low valueConsider using afero for filesystem detection to improve testability.
findHyperHqDirusesos.Statdirectly, making it difficult to unit test without real filesystem access. Injecting anafero.Fsparameter would allow mocking the filesystem in tests.As per coding guidelines: "Use afero for filesystem operations in testable code".
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@pkg/platforms/windows/hyperhq.go` around lines 277 - 318, The findHyperHqDir function currently calls os.Stat directly which prevents unit tests from mocking the filesystem; change its signature to accept an afero.Fs (e.g. func findHyperHqDir(fs afero.Fs, cfg *config.Instance) (string, error)), keep using os.UserHomeDir() for home resolution, replace any os.Stat(dir) checks with fs.Stat(dir) (or afero.Exists equivalents) and update all callers to pass an appropriate Fs (real afero.NewOsFs() in production, afero.NewMemMapFs() in tests); update tests to inject a mock in-memory FS and assert directory discovery without touching the real filesystem.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Outside diff comments:
In `@pkg/platforms/windows/hyperhq.go`:
- Around line 1-986: Add unit tests to cover hqSystemQueryKey and the
pending-games response correlation: write a test that verifies hqSystemQueryKey
produces the expected null-separated key for a hqSystemQueryTarget, and write a
test that simulates receiving a "Games" event string by calling handleEvent (or
by invoking the scanner path) with a JSON payload whose
SystemId/SystemName/SystemReferenceId matches a pendingGamesReq.queryKey; set up
the HyperHqPipeServer with a pendingGamesReq.response channel (protected by
pendingGamesReqMu) and assert the response channel receives the expected
hqGamesResponse (including Error and Games fields), and also a negative case
where queryKey mismatches so no send occurs. Use the server methods
RequestGamesForSystemSync and/or directly manipulate
pendingGamesReq/pendingGamesReqMu to emulate an in-flight request and ensure
proper locking/unlocking around pendingGamesReq in the test.
---
Nitpick comments:
In `@pkg/platforms/windows/hyperhq.go`:
- Around line 277-318: The findHyperHqDir function currently calls os.Stat
directly which prevents unit tests from mocking the filesystem; change its
signature to accept an afero.Fs (e.g. func findHyperHqDir(fs afero.Fs, cfg
*config.Instance) (string, error)), keep using os.UserHomeDir() for home
resolution, replace any os.Stat(dir) checks with fs.Stat(dir) (or afero.Exists
equivalents) and update all callers to pass an appropriate Fs (real
afero.NewOsFs() in production, afero.NewMemMapFs() in tests); update tests to
inject a mock in-memory FS and assert directory discovery without touching the
real filesystem.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: ac8b2721-0204-436f-a7f5-d90ee18e46d3
⛔ Files ignored due to path filters (1)
scripts/windows/hyperhq-plugin/go.sumis excluded by!**/*.sum
📒 Files selected for processing (15)
pkg/assets/systems/Custom.jsonpkg/database/systemdefs/systemdefs.gopkg/platforms/windows/hyperhq.gopkg/platforms/windows/hyperhq_test.gopkg/platforms/windows/launchbox.gopkg/platforms/windows/launchbox_test.gopkg/platforms/windows/platform.gopkg/service/clock_monitor.gopkg/service/clock_monitor_test.goscripts/tasks/windows.ymlscripts/windows/hyperhq-plugin/go.modscripts/windows/hyperhq-plugin/main.goscripts/windows/hyperhq-plugin/main_test.goscripts/windows/hyperhq-plugin/pipe_windows.goscripts/windows/hyperhq-plugin/plugin.json
💤 Files with no reviewable changes (1)
- scripts/tasks/windows.yml
✅ Files skipped from review due to trivial changes (1)
- pkg/assets/systems/Custom.json
🚧 Files skipped from review as they are similar to previous changes (4)
- scripts/windows/hyperhq-plugin/pipe_windows.go
- pkg/platforms/windows/platform.go
- pkg/platforms/windows/hyperhq_test.go
- scripts/windows/hyperhq-plugin/main.go
\\.\pipe\zaparoo-hyperhq-ipcand exchanges JSON-line messages with a separate bridge plugin underscripts/windows/hyperhq-plugin/that HyperHQ runs as a Socket.IO plugin per the HyperAI plugin API.plugin:register,subscribeEvents,requestData/dataResponserouting byrequestId, and thehyperHqEventenvelope (gameLaunched/gameClosed).syncandlogwithout pulling Zaparoo Core internals into its dep tree. Linted viatask cross-lint:windowssince winio is Windows-only.Summary by CodeRabbit
New Features
Bug Fixes
Chores
Tests