• v1.3.0 5c96f176b8

    v1.3.0
    All checks were successful
    CI / docker-build (push) Successful in 20s
    CI / build-and-test (push) Successful in 1m41s
    Stable

    joeyr released this 2026-07-13 09:11:37 -05:00 | 28 commits to main since this release

    Adds Docker/Swarm secret-file support so credentials can be delivered as mounted files instead of plain environment variables. Local docker-compose deployment is unchanged; the Swarm migration itself remains future work (docs/DOCKER_SWARM_SETUP.md).

    Added

    • *_FILE secret convention. Every secret the bot reads now also accepts a <NAME>_FILE variant pointing at a file whose contents are the secret (the Docker/Swarm /run/secrets/* convention), read through a new readSecret() helper (src/utils/readSecret.ts). <NAME>_FILE takes precedence over an inline <NAME> (a warning is logged if both are set); an unreadable file is logged and treated as unset so the normal "<NAME> is required" validation still fires. Covers DISCORD_TOKEN, CLAUDE_API_KEY, DATABASE_URL, BRAVE_SEARCH_API_KEY, RIOT_API_KEY, LOL_PREDICTION_API_KEY, D2EMU_API_USERNAME, and D2EMU_API_TOKEN. Plain env vars (and .env-based local runs) are byte-for-byte unchanged.
    Downloads
  • v1.2.6 b6c373d3fa

    v1.2.6
    Some checks failed
    CI / build-and-test (push) Failing after 14s
    CI / docker-build (push) Successful in 22s
    Stable

    joeyr released this 2026-07-12 22:42:38 -05:00 | 32 commits to main since this release

    Bug-fix release: unbreaks LoL live-game predictions and the /aufhocker chat command against Sonnet 5, records prediction outcomes again, and paces Riot API calls more conservatively.

    Fixed

    • Stop sending temperature to Claude. The current Claude families (Sonnet 5 and newer) reject temperature with a 400 (temperature is deprecated for this model), which was failing live-game predictions, lane detection, and — whenever invoked — the /aufhocker chat command itself. Dropped the parameter from all three call sites and removed the now-dead CLAUDE_TEMPERATURE config field, its validation, and the env templates.
    • Record prediction outcomes again. The recordResult UPDATE bound $3 to both a varchar assignment (actual_winner) and a varchar comparison (predicted_winner); with node-pg's untyped parameter, Postgres deduced two conflicting types and rejected the query (42P08: inconsistent types deduced for parameter $3 — text versus character varying). Every result write failed, so accuracy stats stayed empty. Cast $3::varchar in both spots.

    Changed

    • More conservative Riot API pacing. A game detection fans out ~80 Riot calls (rank + mastery + up to 60 match-v5); the shared client throttle sat at 90 req/2min — barely under a personal key's ~100/2min ceiling, which is also shared with the lol-companion project — so the match-v5 burst tripped 429s (absorbed by retry, logged as warnings). Lowered the throttle and made it env-tunable via LOL_RIOT_MAX_REQUESTS_PER_2MIN (75), LOL_RIOT_MAX_REQUESTS_PER_SEC (15), and LOL_RIOT_MIN_SPACING_MS (350).
    Downloads
  • v1.2.5 95cb8db9e8

    v1.2.5
    All checks were successful
    CI / docker-build (push) Successful in 20s
    CI / build-and-test (push) Successful in 1m45s
    Stable

    joeyr released this 2026-07-12 22:30:10 -05:00 | 33 commits to main since this release

    Docs only — no change to the bot's runtime, image, or command surface.

    Changed

    • Consolidated docs under docs/. Moved DOCKER_SETUP.md, DOCKER_SWARM_SETUP.md, and WHATS_LEFT.md out of the repo root into docs/ (alongside DEPLOYMENT.md, the admin guides, etc.); fixed the now-sibling relative links. README.md, CHANGELOG.md, and CLAUDE.md stay at root by convention.
    • WHATS_LEFT.md is now open-work-only. Dropped the "what landed" and completed-item history (that lives in CHANGELOG.md + git); it now lists just the remaining items.
    • Refreshed docs/DOCKER_SWARM_SETUP.md. Its "runner needs Docker access" step is marked done (shipped in v1.2.3 via the repo-scoped aufhocker-docker runner), and the example publish job now targets runs-on: aufhocker-docker — targeting the shared ubuntu-latest runner would fail now that it no longer exposes the Docker socket to jobs.
    Downloads
  • v1.2.4 51b1e5e726

    v1.2.4
    All checks were successful
    CI / docker-build (push) Successful in 20s
    CI / build-and-test (push) Successful in 2m1s
    Stable

    joeyr released this 2026-07-12 22:19:22 -05:00 | 34 commits to main since this release

    Docs only — no change to the bot's runtime, image, or command surface.

    Added

    • docs/PLUGIN_DECOMPOSITION_PLAN.md — a design + staged-migration plan for P4-1 (fuller plugin decomposition). Adopts making the core query feature the third plugin as the "rule of three" trigger: define a real Plugin contract so query/lol/d2 all implement it and the bot core becomes a thin host. Sequenced D2 → LoL → query (riskiest last, behind a proven interface). WHATS_LEFT.md's P4-1 item now points at it.
    Downloads
  • v1.2.3 2a5fe3a802

    v1.2.3
    All checks were successful
    CI / docker-build (push) Successful in 2s
    CI / build-and-test (push) Successful in 1m51s
    Stable

    joeyr released this 2026-07-12 21:56:07 -05:00 | 35 commits to main since this release

    CI infrastructure only — no change to the bot's runtime, image, or command surface.

    Changed

    • docker-build CI moves to a repo-scoped runner. The job now runs on a dedicated aufhocker-docker runner (registered scoped to just this repo) that mounts the host Docker socket into its job containers, instead of the shared ubuntu-latest runner. The shared runner no longer passes the socket into any job, so other repos' workflows can no longer reach the host Docker daemon — only this repo's (trusted) docker-build job gets host-daemon access. Closes the socket-isolation item; the second runner service lives in the forgejo-runner.yaml Swarm stack.
    Downloads
  • v1.2.2 e3401f9f84

    v1.2.2
    All checks were successful
    CI / build-and-test (push) Successful in 1m46s
    CI / docker-build (push) Successful in 21s
    Stable

    joeyr released this 2026-07-12 21:03:01 -05:00 | 37 commits to main since this release

    Changed

    • LOL_PREDICTION_MODEL: .env.config moves from claude-opus-4-8 (priciest model, polled per active game) to claude-sonnet-5 — a deliberate quality/cost tradeoff, still above the code default (claude-haiku-4-5-20251001) that CLAUDE.md calls for.
    Downloads
  • v1.2.1 1016fa6374

    v1.2.1
    All checks were successful
    CI / build-and-test (push) Successful in 1m55s
    CI / docker-build (push) Successful in 20s
    Stable

    joeyrendar released this 2026-07-12 20:45:06 -05:00 | 39 commits to main since this release

    CI and test hygiene that restores a fully green pipeline. No change to the bot's runtime, built image, or command surface.

    Fixed

    • build-and-test CI is green again. The config-validation tests built their "valid base config" without BRAVE_SEARCH_API_KEY, then depended on it being present in the ambient environment — config.ts loads dotenv over ['.env', '.env.config'], and the committed .env.config sets ENABLE_ADMIN_TOOLS=true, so validate() requires the key for web_search. It was present locally (from .env) but absent in CI, so two "should validate successfully" cases threw only there. The base config now pins the key and is self-contained. Separately, files that were not Prettier-clean (CHANGELOG.md, four __tests__ files, WHATS_LEFT.md) were formatted so format:check — the job's first step — stops aborting the run.
    • docker-build CI is green. The Forgejo Actions runner now mounts the host Docker socket into job containers, so the image build reaches the daemon (previously it failed with "cannot connect to the Docker daemon"). This needs both container.options (with -v /var/run/docker.sock:/var/run/docker.sock) and container.valid_volumes: [/var/run/docker.sock] — the runner validates -v mounts against valid_volumes, which defaults to deny-all. Set in the forgejo-runner.yaml Swarm stack; the ci.yml comment is corrected to match.

    Changed

    • WHATS_LEFT.md: removed the closed SECURITY.md (A3) item and dropped P5-1 (CI docker-build) now that it is done.
    Downloads
  • v1.2.0 316c3b881d

    v1.2.0
    Some checks failed
    CI / build-and-test (push) Failing after 14s
    CI / docker-build (push) Failing after 2s
    Stable

    joeyr released this 2026-07-12 11:55:23 -05:00 | 44 commits to main since this release

    Open-sourcing, a Riot rate-limit fix, and a secrets/config split, plus a sweep of documentation and compose drift. No change to the bot's command surface.

    Changed

    • Relicensed from proprietary to MIT. LICENSE is now the MIT text, package.json license is MIT (was UNLICENSED), and the README — which already claimed MIT — is now consistent with both. The "Contributing" (fork + PR) section is valid again.
    • Secrets are now split from non-secret config. Credentials and deployment identity live in .env (git-ignored); non-secret tuning (models, intervals, feature flags, pool sizes) lives in .env.config, which is committed. Both load together — docker-compose via env_file: [.env.config, .env], local runs via dotenv.config({ path: ['.env', '.env.config'] }) — with disjoint keys so order is immaterial (and .env wins on any overlap).
    • Corrected drifted docker-compose.yml (dev) defaults. Model ids that matched the retired-family guard and an aggressive LoL poll interval (30000) / rate-limit threshold (10) are gone, replaced by the committed .env.config values (claude-sonnet-5, 300000, 50). Dead env vars LOL_EMBED_LAYOUT and DEPLOYMENT_ENVIRONMENT were removed. The dev healthcheck now hits the real /health endpoint (parity with production), and user-mappings.json is mounted so its absence no longer warns on every boot.

    Added

    • Client-side Riot API pacing (RiotRateLimiter). A single limiter shared across every Riot client spreads calls to stay under a personal key's budget (~20 req/s, ~100 req/2 min) so bursty game-detection enrichment avoids 429s rather than merely backing off after hitting them. Combined with the saner 5-minute poll interval, this ends the recurring spectator-v5 rate-limit warnings. Unit-tested (spacing, rolling windows, concurrent serialization) and wired as an optional collaborator so existing client tests are unaffected.

    Fixed

    • Documentation drift: the README "Project Structure" now matches the actual tree and CLAUDE.md, and the stale Dockerfile "health endpoint requires implementation" comment (it is implemented) is gone.
    • Minor code smells: removed the dead stripHtml() in ToolExecutor, switched its numeric HTML-entity decode to String.fromCodePoint (astral-safe), and replaced the deprecated String.prototype.substr in AufhockerBot with slice. npm ci --only=productionnpm ci --omit=dev in the Dockerfile.
    • Tooling: an ESLint no-console override for the migration CLI keeps the warning meaningful elsewhere, and Prettier now sets endOfLine: auto so format:check stops false-flagging a Windows (CRLF) checkout.
    Downloads
  • v1.1.0 2e43155865

    v1.1.0 Stable

    joeyr released this 2026-07-08 23:04:02 -05:00 | 108 commits to main since this release

    Toolchain and dependency modernization — every dependency to latest stable, zero deprecation warnings and zero npm audit findings on a fresh install (down from 17 vulnerabilities including 1 critical). No user-facing behavior change, but the supported Node floor moves from 18 to 22.

    Changed

    • ESLint 8 (end-of-life) → 10.6 with typescript-eslint 8.63, migrating .eslintrc.js to flat eslint.config.js. This alone removed the deprecated rimraf@3/glob@7/inflight/@humanwhocodes/* install warnings. Two rules no longer exist upstream: interface-name-prefix (dropped) and no-var-requires (now no-require-imports in the test overrides).
    • Jest 29 → 30 (with @types/jest 30, ts-jest 29.4.11); ts-jest's deprecated transform-level isolatedModules option moved into tsconfig.test.json.
    • fast-check 3 → 4: removed APIs migrated (fc.stringOf(unit, opts)fc.string({ unit, ...opts }), fc.char()-based strings → default fc.string()), and all fc.date() arbitraries now pass noInvalidDate: true since v4 generates Invalid Date by default — Discord/DB timestamps are always valid dates.
    • @anthropic-ai/sdk 0.78 → 0.110 (its improved types made two manual casts in ClaudeClient redundant), dotenv 16 → 17, discord.js 14.26.4, pg 8.22, winston 3.19.
    • TypeScript 5.9 → 6.0.3, deliberately capped ~6.0.x: typescript-eslint's peer range is <6.1.0, so TS 7 has to wait for upstream support. tsconfig moved off the TS7-removed node10 module resolution to module/moduleResolution: nodenext (output is still CommonJS).
    • Node floor 18 → 22 (engines, .nvmrc, Dockerfile node:22-alpine, CI container node:22-bookworm) — Node 20 went end-of-life in April 2026. @types/node tracks the runtime major (^22), not npm's latest.
    • Added a package.json overrides block for upstream pins: undici@^6.27 (discord.js pins a 6.24.1 with several CVEs — this cleared most of the 17 audit findings), and babel-plugin-istanbul@8/test-exclude@8/glob@13 to lift Jest's own deprecated-glob chain.

    Fixed

    • Four re-thrown errors (ToolExecutor fetch timeout/network wrappers, ShutdownHandler cancellation, UserMapper database-sync failure) now attach the original error as cause — previously the root cause was discarded when the wrapper error was thrown (surfaced by ESLint 10's new preserve-caught-error rule).
    Downloads
  • v1.0.1 e510839a1e

    v1.0.1 Stable

    joeyr released this 2026-07-08 21:44:54 -05:00 | 110 commits to main since this release

    Full lint cleanup following the config fix in 1.0.0 -- no user-facing behavior change, but a few real bugs turned up along the way and got fixed.

    Changed

    • Fixed all ~1270 lint violations the corrected .eslintrc.js surfaced. Production code is now fully lint-clean (typed DB rows, narrowed unknown error handling, missing return types, floating/misused promises). A targeted ESLint override relaxes the unsafe-any family for test files, since Jest mocks are inherently loosely typed.
    • npm run lint now runs in CI, after format check and before type-check.

    Fixed

    • MessageStore and ServerConfigManager cleanup methods logged deletedCount: 0 on every call -- read .rowCount off a value that never had it. Added RETURNING clauses and count result.length instead.
    • DiscordClient's rate-limit handler logged fields (timeout/url) that don't exist on the real payload type -- always undefined. Now logs the real fields.
    • DiscordClient.disconnect() never awaited client.destroy(), so shutdown could complete before the gateway connection actually closed.
    Downloads