Skip to content

Web dashboard for the control API, graceful Stop, and reliable CONFIG_* overrides - #607

Closed
Zer02811 wants to merge 4 commits into
TheNetsky:v4from
Zer02811:claude/elated-bhabha-c15f2b
Closed

Zer02811 wants to merge 4 commits into
TheNetsky:v4from
Zer02811:claude/elated-bhabha-c15f2b

Conversation

@Zer02811

Copy link
Copy Markdown

Summary

Adds a browser-based control dashboard for the script and fixes the run-control
gaps it exposed: toggles that were sent but never applied, and a Stop button that
killed the process without closing Chromium.

Four commits, each independently reviewable:

Commit What
b860db0 Web dashboard (public/) + scheduling & account endpoints in the control API
fa93375 Graceful abort: Stop now closes browsers instead of orphaning them
760b2f3 Fix: CONFIG_* env overrides are applied on every bot start
9938d4e 30-minute Edge browsing task now drives a real browsing session

Why

1. Web dashboard and scheduling (b860db0)

The control API (scripts/api/server.js) had no UI. public/ is a
dependency-free static dashboard (vanilla JS, no build step) served by the
existing server: start/stop a run, watch logs live over SSE, see per-account
login status, and schedule runs for later.

Supporting pieces:

  • scripts/api/taskScheduler.js — persists scheduled tasks to
    scheduled_tasks.json, splits due vs. missed tasks (1h grace window).
  • scripts/api/taskRunner.js — 30s tick, one run at a time, maps task toggles
    to CONFIG_* env vars via buildEnvForTask.
  • scripts/api/envAccounts.js — reads account emails from .env so the UI can
    list accounts without a separate config.
  • scripts/api/sessionStore.js — reports real login status per account from the
    session DB (live / expired / never logged in) instead of guessing.
  • logParser.jsExperimentalWarning was being classified as ERROR, which
    made every run look like it failed in the log console. Now a warning.

2. Graceful stop (fa93375)

Stop sent SIGTERM and, on Windows, that is TerminateProcess — the bot never
got a chance to run its handler, so Chromium instances survived until the OS
reaped the tree (and sometimes not even then).

Now:

  • src/util/Abort.ts — a process-wide abort signal plus a registry of live
    browsers. Browser.ts registers on launch and unregisters on disconnected.
  • processManager.js writes an __ABORT__ sentinel to the child's stdin (the
    only cross-platform way to say "stop" to a Node child on Windows) and still
    sends SIGTERM on POSIX. The existing kill timer escalates to a tree kill if
    the bot does not exit in time, so a hung run is still stoppable.
  • src/index.ts aborts on stop: closes every tracked browser, cancels the
    Edge-browsing background task, breaks out of the account loop before starting
    a fresh login, and wakes up from the multi-minute inter-account delay (which
    previously made Stop look frozen).

Force-stop still hard-kills the tree immediately — unchanged.

3. CONFIG_* overrides never reached the bot (760b2f3)

This is the actual bug behind "Visual Search and 30-Minute Edge Browsing are
skipped even when toggled on".

The chain was intact right up to the last link:

  1. The UI sent CONFIG_WORKER_VISUAL_SEARCH / CONFIG_EXPERIMENTAL_EDGE_BROWSING correctly.
  2. buildEnvForTask and ProcessManager._resolveEnv forwarded them into the child's env correctly.
  3. Nothing in the child ever read them. applyEnvOverrides — the only thing
    that translates CONFIG_* into config — was called solely from the Docker
    entrypoint and the CLI. A locally spawned node dist/index.js read
    config.json verbatim, where both flags are false, so the if guards in
    src/index.ts never fired. Restarting the server changed nothing.

Fix, in-memory and per-run:

  • mergeEnvOverrides(config, env) applies overrides to an already-parsed config
    object. loadConfig() calls it before validateConfig, so overridden values
    go through the same zod schema as file values.
  • config.json is never rewritten — it is a real gitignored user file.
    Overrides live and die with the run.
  • Boolean parsing widened from strict 'true'/'false' to shell-style words
    (1/0, yes/no, on/off, case-insensitive, trimmed), matching what
    Load.ts already accepts for its own env flags.
  • Partial-failure tolerant: one bad value no longer discards the valid
    overrides; each rejected var logs a warning instead of failing silently.
  • Every applied override is logged ([Config] override: CONFIG_… -> .path = …)
    so a toggle that did not take effect is visible in the log console. Webhook
    URLs and bot tokens are marked secret and masked as ***.
  • applyEnvOverrides (Docker) now preflights then delegates to the same merge,
    so both paths share one implementation. Docker behaviour is unchanged, and
    FORCE_HEADLESS still wins over CONFIG_HEADLESS.

4. Real browsing during the Edge task (9938d4e)

The 30-minute Edge browsing activity only posted progress reports — no browser
activity accompanied them. EdgeLiveBrowsing.ts opens a tab in the
already-authenticated context and reads like a person: scrolls with variable
pauses, dwells at the end of an article, occasionally follows a link, then moves
to another feed. Runs alongside the report loop and is fully abort-aware.

Link following is restricted to an allowlist of hosts
(bing.com, microsoft.com, msn.com, microsoftedge.com) plus the current
host, so a hostile feed link cannot navigate the authenticated session off-site.

Testing

  • npm test32/32 pass (22 existing + 10 new). New suites:
    tests/configEnvOverrides.test.mjs, tests/abort.test.mjs,
    tests/sessionStatus.test.mjs.
  • npx tsc --noEmit clean, eslint clean, prettier --check clean on all
    touched files.
  • Verified live against a real API server (not just unit tests), since the bug
    was an integration gap:
    • POST /start with the exact UI payload, toggles ON → the spawned child
      resolved doVisualSearch=true edgeBrowsing=true. Toggles OFF → both false.
    • POST /schedule/tasks with both flags → task fired on the 30s tick → child
      resolved both true, task finished done.
    • config.json confirmed untouched on disk after every run.

Notes for reviewers

  • Auth: the control API still has no token by default and logs a warning
    saying it is open to anything on the machine. That is pre-existing, but this PR
    widens what is reachable (account list, scheduling, run control), so it is
    worth a decision. Schedule writes are gated behind API_ALLOW_SCHEDULE_WRITE
    (default off) for that reason.
  • scheduled_tasks.json is gitignored — it is user runtime state.
  • public/ is intentionally build-free: no bundler, no npm deps, served
    straight by the existing node:http server.
  • .claude/launch.json is included so the dev-server preview points at the
    dashboard on port 3010.
  • EdgeLiveBrowsing is under experimental/ and only runs when
    experimental.edgeBrowsing is on.

🤖 Generated with Claude Code

Zer02811 and others added 4 commits September 13, 2026 18:56
Adds a dependency-free dashboard (public/) served by the existing
node:http control API, plus the backend it needs:

- account list sourced from .env (scripts/api/envAccounts.js) instead of
  a hand-maintained copy, so the UI cannot drift from the real accounts
- per-account login status derived from the session store's stored
  cookies (scripts/api/sessionStore.js), so the UI can show which
  accounts still hold a live Microsoft auth cookie
- task scheduling: taskScheduler.js persists tasks to
  scheduled_tasks.json, taskRunner.js polls and starts them one at a
  time, and both are wired into server.js behind API_ALLOW_SCHEDULE_WRITE
- live log streaming over SSE so the dashboard console mirrors the run

logParser: treat ExperimentalWarning as a warning rather than an error.
Node prints it on stderr for the SQLite session store, and the old
/\bWARN/ pattern missed it, so every run opened with a red herring.

scheduled_tasks.json is gitignored: it is per-machine run state, not
project config. eslint gains a browser-globals block for public/**.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
A Stop from the dashboard previously just killed the process tree, which left
Chromium instances to be reaped by the OS and could orphan them.

- src/util/Abort.ts: run-wide AbortController plus a registry of live browsers,
  so an abort can force-close every Chromium it knows about.
- Browser.ts registers each launched browser and unregisters on disconnect.
- index.ts: SIGINT/SIGTERM now abort first (closing browsers) before flushing
  webhooks and exiting; the between-accounts delay and the 30-minute Edge
  browsing task are both interruptible, and the account loop stops rather than
  starting a fresh login.
- processManager.js: writes an __ABORT__ sentinel to the child's stdin because
  Windows cannot deliver a real SIGTERM to a child process; the existing kill
  timer still escalates to a tree kill if the bot does not exit in time.

Covered by tests/abort.test.mjs.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Toggling Visual Search or 30-Minute Edge Browsing in the Web UI had no
effect. The env vars reached the spawned bot correctly, but nothing in
the child ever consumed them: applyEnvOverrides() was only called by the
Docker entrypoint and the CLI, so loadConfig() read config.json verbatim
and both flags stayed false.

Add mergeEnvOverrides(), which applies the overrides to an already-parsed
config object in memory, and call it from loadConfig() before
validateConfig() - so overridden values pass the same schema checks as
file values, and config.json is never rewritten. applyEnvOverrides() now
preflights and delegates to the same merge, keeping Docker behaviour.

Also widen boolean parsing to the shell-style words Load.ts already
accepts (1/0, yes/no, on/off, case-insensitive) instead of only
true/false, tolerate partial failure so one bad value no longer discards
valid overrides, and mask secret values (webhook URLs, bot tokens) in the
applied-override log.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…te task

The background Edge browsing activity only posted progress reports to the
rewards API; no browser tab was actually browsing. EdgeLiveBrowsing opens a
tab in the already-authenticated context and reads for the full session
window: scrolls with human-shaped pauses, dwells at the end of an article,
and occasionally follows a link instead of jumping back to a feed.

Links are restricted to the current host or a bing/microsoft/msn/microsoftedge
suffix so a session cannot wander onto arbitrary sites. The session honours the
run's AbortSignal at every wait, so a Stop unwinds it instead of hanging, and
failures are logged as a warning rather than failing the whole activity.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@Zer02811

Copy link
Copy Markdown
Author

Opened against the wrong repository by mistake - this targets my own fork, not upstream. Closing; no review needed here.

@Zer02811 Zer02811 closed this Sep 13, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant