feat(doctor): add private API helper check; refactor checks into named functions#8
Conversation
…d functions
Adds a 'private API helper' doctor check that queries
GET /api/v1/server/info and reports:
- PASS when private_api && helper_connected
- WARN when private_api but helper not connected (unsend/edit/tapback
will fail; tells user where to install the Messages Helper)
- WARN when private_api disabled in server settings
- WARN when api ping didn't succeed (skip rationale)
This surfaces a common silent failure mode where the server reports
Private API enabled but the in-Messages helper dylib never connected,
so all private-API-dependent endpoints (unsend, edit, tapback,
typing-indicator) 500 with a generic error.
Refactor: each check is now its own named function returning a
DoctorCheck. runDoctor just composes them. Easier to add new checks,
easier to test individually.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: fec92173d3
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| const privateApi = info.private_api === true; | ||
| const helperConnected = info.helper_connected === true; |
There was a problem hiding this comment.
Treat unknown helper flags as indeterminate, not disabled
/api/v1/server/info fields are optional in practice, but this logic coerces missing private_api/helper_connected keys into false and then reports "Private API is disabled". In environments where the server returns partial metadata (e.g., older server builds or stripped responses), users will get a misleading diagnostic even when the actual Private API state is unknown; this should instead emit an "unable to determine" warning unless both flags are explicitly present.
Useful? React with 👍 / 👎.
## Summary Merges to `main` no longer sit unreleased. Each merge triggers an automated workflow that: 1. Scans commits since the last release for `feat:` / `fix:` / etc prefixes 2. Opens (or updates) a "release PR" that bumps `package.json`, regenerates `CHANGELOG.md`, and proposes the next semver version 3. When that release PR is merged, the matching `vX.Y.Z` tag is created 4. The existing `release.yml` (already wired to `v*` tag push) handles the npm publish This fixes the visible bug: PR #8 (private-API doctor check) and PR #9 (`bluebubbles update` command) merged into `main` but never reached npm, because `release.yml` only fires on tag push and nothing was automating the tag. ## Files added - `.github/workflows/release-please.yml` — runs on every push to `main` - `release-please-config.json` — release-type=node, v-in-tag=true - `.release-please-manifest.json` — current version (`0.1.7`) ## What happens after this merges 1. release-please run on `main` scans commits since `v0.1.7` 2. Sees two `feat:` commits → opens "chore(release): 0.2.0" PR with CHANGELOG diff 3. You merge that PR → `v0.2.0` tag + GitHub Release created 4. `release.yml` fires on tag push → `npm publish` ## Implementation note The bot is [`googleapis/release-please-action`](https://github.com/googleapis/release-please) — Google's standard tool for conventional-commit-driven releases. Internally named "release please" (the polite-ask meme stuck when they open-sourced it in 2020). All the logic lives in their action; this PR is just the config to point it at this repo. ## Requires - Commit messages already follow conventional commits (`feat:`, `fix:`, `chore:`, etc.) — verified against current log - No npm secrets/config changes — publish still goes through the existing trusted-publishing setup 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Summary
Adds a new
doctorcheck that surfaces a common silent failure: the BlueBubbles server reports Private API as enabled, but the in-Messages helper dylib never actually connected — so every Private-API endpoint (unsend, edit, tapback, typing-indicator) 500s with a genericUnsend Message Error/iMessage Private API Helper is not connected!.Hit this in production with
bluebubbles messages unsend— wasted 20 min before catching the helper status in/api/v1/server/info. Doctor should flag it.New check
Reads
info.private_api+info.helper_connectedfrom the existinggetServerInfo()helper. No new dependencies.Refactor
The original
runDoctor()was a single 100-line function with inline check logic. Pulled each check out into its own named function returning aDoctorCheck.runDoctor()now just composes them. Easier to extend, easier to test individually, no behavior change for existing checks.Diff: +140 / -67 lines in
src/lib/doctor.ts.Test plan
bun run buildpassesbun run test(16 tests) passesbluebubbles doctoron a server with helper disconnected → shows WARN line abovebluebubbles doctoron a server with helper connected → shows PASS🤖 Generated with Claude Code