3 Commits
Author SHA1 Message Date
juxtapoandClaude Opus 5 01fae3ca74 v0.7: one-click install — pyproject, console scripts, user units, hermes wiring
A clone + ./install.sh + ./launch-brave.sh + sign in once. Nothing else.

pyproject.toml declares the one real dependency (websockets) and maps the
repo-root-is-the-package layout, which retires the PYTHONPATH=<parent> line
every unit used to carry.

cli.py backs three console scripts. They exist because `python3 -m socialmag`
run from INSIDE a clone dies at `import asyncio`: the repo root is the package
dir, so it ships selectors.py and queue.py, and python puts the current
directory first on sys.path. `cd socialmag` is the first thing anyone does
after a clone, so the documented workaround failed at exactly the moment it
would be tried. A generated bin/ script never puts cwd on sys.path.

The same trap bit `python3 -m venv` inside install.sh — the installer was
broken by the thing it exists to fix. Every python3 call now runs from /.

deploy/user/*.in replaces root system units with systemd --user templates:
no User=, WantedBy=default.target, venv paths, @REPO@/@VENV@/@CDP_ENDPOINT@
substituted at install time. deploy/home/ is untouched and still live here.

launch-brave.sh carries --disable-background-timer-throttling. That is
load-bearing, not tidiness: five of six platforms are read in a hidden tab, and
a throttled background tab stops running the timers those pages use to fetch
their own data — the reads come back empty with no error.

install.sh checks the mcp package in hermes's own interpreter and refuses to
continue without it. Hermes treats mcp as optional and silently no-ops the
whole subsystem when it is missing, so a server that never loaded looks exactly
like one that loaded and did nothing.

It will not merge into an existing mcp_servers: key — it backs up config.yaml
and appends only when the key is absent, otherwise it prints the block. Blind
YAML merging is how someone's other servers disappear.

Verified end-to-end in a sandbox with a fake HOME and stubbed systemctl:
real MCP initialize handshake from inside the repo dir returns all 7 tools;
hermes's own yaml parser re-reads the written config with every pre-existing
key intact; a second run leaves the mcp_servers key count at 1; launch-brave.sh
exits 1 against a busy port; no unsubstituted @PLACEHOLDER@ in any rendered unit.

Not fixed, pre-existing: server.py reports SERVER_VERSION 0.1.0 while the repo
is at v0.7. The handshake shows 0.1.0.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-13 02:10:58 +08:00
juxtapoandClaude Opus 5 22e63ebf35 v0.5: capture lane, sqlite store, refresher wired to both
capture.py — read the platform's own JSON instead of the pixels it painted.
Network capture on X (/i/api/graphql HomeLatestTimeline) and Threads
(/graphql/query feedData); in-document read on Instagram, whose feed is
prefetched into a RelayPrefetchedStreamCache script and never crosses the
wire. 190 X rows in 0.9s against the DOM lane's 10 in 11.1s, with exact
integer counts and view numbers the DOM never exposes. Waits for
Network.loadingFinished, not responseReceived — the body is not there yet at
responseReceived. Nothing captured raises; an empty list would say the
platform posted nothing when the truth is our listener missed it.

store.py — SQLite, stdlib only. platform_checks / items / metrics.
id = sha1(platform|kind|url or text), first_seen written once and ts under
COALESCE so a derived Threads marker cannot decay across passes.
FEED_PER_PLATFORM = 10 caps any one platform before the merge, or X's ~185
rows a pass sort Instagram's 3 off the page entirely. dashboard.json is now a
derived view of the database; dashboard.html did not have to change for it.

refresher.py — check_all runs the background-safe platforms in parallel
(19.0s sequential to 6.1s), tiktok still sequential and foreground.
--skip-foreground leaves it out of a timed pass and store.latest_checks keeps
returning its last real result with its own older checkedAt; stale is honest,
a fabricated row is not. Browser down stays the third state and exits 0.

.gitignore: socialmag.db (regenerable), deploy/kdmirah/ (carries the box's
VNC password and addresses — stays in the lab, out of git).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-13 01:58:36 +08:00
juxtapo 1ac06c1bbe v0.3: dashboard local mode, openbridge tab reuse, tab activation fix
- dashboard.html: local dashboard.json mode (10s poll) alongside artifact db mode;
  clickable cards behind an http(s)-only safeUrl filter; meta charset; mode-aware
  freshness line
- openbridge.py: localhost shim exposing open_link so dashboard clicks reuse the
  platform's warm tab instead of spawning one per click
- cdp.py: Tab.activate(); get_or_open_tab activates reused tabs — a backgrounded
  tab never finishes rendering, which made YouTube's login check read a signed-in
  account as signed out
- README/MANIFEST: corrected CDP port (9336, was documented as 9222), removed the
  false claim that the server writes dashboard.json, documented the reboot/VPS
  requirements
- .gitignore: __pycache__/, *.pyc, state.json, Archived/; removed stale tracked
  pycache and state.json from index
2026-09-11 10:18:38 +08:00