gammonview-helper 0.1.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (29) hide show
  1. gammonview_helper-0.1.1/.github/workflows/ci.yml +99 -0
  2. gammonview_helper-0.1.1/.github/workflows/publish.yml +182 -0
  3. gammonview_helper-0.1.1/.gitignore +21 -0
  4. gammonview_helper-0.1.1/.python-version +1 -0
  5. gammonview_helper-0.1.1/LICENSE +21 -0
  6. gammonview_helper-0.1.1/PKG-INFO +197 -0
  7. gammonview_helper-0.1.1/README.md +168 -0
  8. gammonview_helper-0.1.1/deploy/com.gammonview.helper.plist +88 -0
  9. gammonview_helper-0.1.1/deploy/install-login-item.sh +95 -0
  10. gammonview_helper-0.1.1/docs/Relay.md +103 -0
  11. gammonview_helper-0.1.1/gvhelper/__init__.py +42 -0
  12. gammonview_helper-0.1.1/gvhelper/__main__.py +14 -0
  13. gammonview_helper-0.1.1/gvhelper/cli.py +291 -0
  14. gammonview_helper-0.1.1/gvhelper/client.py +319 -0
  15. gammonview_helper-0.1.1/gvhelper/config.py +261 -0
  16. gammonview_helper-0.1.1/gvhelper/daemon.py +250 -0
  17. gammonview_helper-0.1.1/gvhelper/runner.py +207 -0
  18. gammonview_helper-0.1.1/gvhelper/store.py +164 -0
  19. gammonview_helper-0.1.1/pyproject.toml +93 -0
  20. gammonview_helper-0.1.1/tests/conftest.py +46 -0
  21. gammonview_helper-0.1.1/tests/golden/B4_SrGcsKAQmoTyHlgJCbM.fast.gvab +0 -0
  22. gammonview_helper-0.1.1/tests/test_cli.py +194 -0
  23. gammonview_helper-0.1.1/tests/test_client.py +262 -0
  24. gammonview_helper-0.1.1/tests/test_config.py +137 -0
  25. gammonview_helper-0.1.1/tests/test_contract.py +119 -0
  26. gammonview_helper-0.1.1/tests/test_daemon.py +242 -0
  27. gammonview_helper-0.1.1/tests/test_runner.py +113 -0
  28. gammonview_helper-0.1.1/tests/test_store.py +156 -0
  29. gammonview_helper-0.1.1/uv.lock +843 -0
@@ -0,0 +1,99 @@
1
+ # The helper's suite, on every push and pull request.
2
+ #
3
+ # This package runs on other people's computers, which is what the matrix is
4
+ # shaped around: the platforms, not the Python versions. What breaks here is a
5
+ # credential store that is not a Keychain and an `os.nice` that does not exist
6
+ # on Windows -- neither of which a second interpreter on Linux would ever find.
7
+ name: CI
8
+
9
+ on:
10
+ push:
11
+ branches: [main]
12
+ pull_request:
13
+ workflow_dispatch:
14
+
15
+ permissions:
16
+ contents: read
17
+
18
+ # A newer push to the same ref makes the older run's verdict worthless.
19
+ concurrency:
20
+ group: ci-${{ github.ref }}
21
+ cancel-in-progress: true
22
+
23
+ jobs:
24
+ tests:
25
+ name: ${{ matrix.os }} / py${{ matrix.python }}
26
+ runs-on: ${{ matrix.os }}
27
+ strategy:
28
+ # One red row must not hide the others: the usual reason to read this
29
+ # matrix is to find out *which* platform broke.
30
+ fail-fast: false
31
+ matrix:
32
+ os: [ubuntu-latest, macos-latest, windows-latest]
33
+ python: ["3.13"]
34
+ include:
35
+ # The floor and the ceiling of the supported range, on the cheap
36
+ # runner. `keyring` and `httpx` are where a version difference would
37
+ # actually show up; the middle versions are not worth three minutes.
38
+ - os: ubuntu-latest
39
+ python: "3.10"
40
+ - os: ubuntu-latest
41
+ python: "3.14"
42
+ steps:
43
+ - uses: actions/checkout@v4
44
+
45
+ - uses: astral-sh/setup-uv@v6
46
+ with:
47
+ enable-cache: true
48
+
49
+ # Two markers deselected, for different reasons.
50
+ #
51
+ # `slow` runs a real analysis: it needs a minute of CPU and proves the
52
+ # engine works, which is bgsage's suite's job rather than this one's.
53
+ #
54
+ # `contract` talks to a live relay. The server half of that protocol is
55
+ # in a private repository, so there is nothing a public workflow could
56
+ # stand up, and pointing this at beta would make every push a request to
57
+ # a running service. `docs/Relay.md` says when to run it by hand.
58
+ - name: helper suite
59
+ run: uv run --python ${{ matrix.python }} pytest -q -m "not slow and not contract"
60
+
61
+ build:
62
+ name: build (sdist + wheel)
63
+ runs-on: ubuntu-latest
64
+ steps:
65
+ - uses: actions/checkout@v4
66
+ with:
67
+ # hatch-vcs derives the version from git tags, and a shallow clone has
68
+ # none. It does not fail -- it quietly stamps 0.1.dev1+g<sha> on the
69
+ # artifacts, which is the kind of thing you discover on PyPI.
70
+ fetch-depth: 0
71
+
72
+ - uses: astral-sh/setup-uv@v6
73
+ with:
74
+ enable-cache: true
75
+
76
+ - run: uv build
77
+
78
+ - name: report the derived version
79
+ run: ls -l dist/
80
+
81
+ # The wheel's real test is that the console script it installs actually
82
+ # runs. It is the first thing a user types after `uv tool install`, and an
83
+ # entry point naming a module that moved fails right there -- with a
84
+ # traceback, on their machine, before they have any reason to trust this.
85
+ #
86
+ # `--version` rather than `status`: it exits 0 with no credentials (status
87
+ # exits 1, correctly, because an unlinked helper is not a working one) and
88
+ # it reads the version back out of the *installed metadata*, so it fails
89
+ # if hatch-vcs stamped nothing. Resolving the dependencies at all is the
90
+ # other half of the check -- this is the only place `gammonview[engine]`
91
+ # is installed from PyPI the way a user gets it.
92
+ #
93
+ # From outside the checkout on purpose: with the repo as cwd, `gvhelper`
94
+ # is importable from the source tree and the wheel is never involved.
95
+ - name: the wheel's entry point runs
96
+ working-directory: ${{ runner.temp }}
97
+ run: |
98
+ uv run --isolated --no-project --with "$GITHUB_WORKSPACE"/dist/*.whl \
99
+ gammonview-helper --version
@@ -0,0 +1,182 @@
1
+ # Publish to PyPI via trusted publishing (OIDC). No API token exists anywhere:
2
+ # PyPI is configured to trust *this workflow in this repository*, and mints a
3
+ # short-lived credential for the job at run time. Nothing to store, leak, or
4
+ # rotate, and a stolen repo secret cannot publish because there is no secret.
5
+ #
6
+ # This is the reason the helper is a public repository on GitHub at all. It is
7
+ # installed by strangers with `uv tool install gammonview-helper`, so PyPI is
8
+ # not optional -- and the alternative to OIDC is a long-lived upload token with
9
+ # write access to the namespace, sitting on a laptop.
10
+ #
11
+ # One-time setup on PyPI (https://pypi.org/manage/account/publishing/), which
12
+ # must be done before the first run, because the project does not exist yet:
13
+ #
14
+ # PyPI project name : gammonview-helper
15
+ # Owner : ngvlamis
16
+ # Repository name : gammonview-helper
17
+ # Workflow name : publish.yml
18
+ # Environment name : pypi
19
+ #
20
+ # That registers a "pending publisher"; the project is created by the first
21
+ # successful upload.
22
+ name: publish
23
+
24
+ on:
25
+ # A tag is the release signal, matching how the version is derived: hatch-vcs
26
+ # reads it from git, so the tag *is* the version. Pushing v0.2.0 publishes
27
+ # 0.2.0 -- there is no number to bump anywhere else, and no way for the tag
28
+ # and the artifact to disagree.
29
+ #
30
+ # These tags are this repository's own. The helper used to share the analysis
31
+ # monorepo's tags, which meant a codec release renumbered a helper nobody had
32
+ # touched; `pyproject.toml` records why that was the wrong coupling.
33
+ push:
34
+ tags: ["v*"]
35
+ # Manual trigger, to retry a failed upload against a tag that already exists.
36
+ workflow_dispatch:
37
+
38
+ permissions:
39
+ contents: read
40
+
41
+ jobs:
42
+ # The suite is a gate on the release, not merely a thing that also runs.
43
+ # Publishing is irreversible -- a version number on PyPI cannot be reused
44
+ # even after a yank -- and this package's failures land on other people's
45
+ # computers, where nobody can roll them back for them.
46
+ test:
47
+ name: ${{ matrix.os }}
48
+ runs-on: ${{ matrix.os }}
49
+ strategy:
50
+ fail-fast: false
51
+ matrix:
52
+ os: [ubuntu-latest, macos-latest, windows-latest]
53
+ steps:
54
+ - uses: actions/checkout@v4
55
+
56
+ - uses: astral-sh/setup-uv@v6
57
+ with:
58
+ enable-cache: true
59
+
60
+ - run: uv run pytest -q -m "not slow and not contract"
61
+
62
+ build:
63
+ name: build sdist + wheel
64
+ needs: test
65
+ runs-on: ubuntu-latest
66
+ steps:
67
+ - uses: actions/checkout@v4
68
+ with:
69
+ # hatch-vcs derives the version from git tags, and a shallow clone has
70
+ # none. It does not fail -- it quietly stamps 0.1.dev1+g<sha> on the
71
+ # artifacts, which is the sort of thing you discover on PyPI.
72
+ fetch-depth: 0
73
+
74
+ - uses: astral-sh/setup-uv@v6
75
+ with:
76
+ enable-cache: true
77
+
78
+ - run: uv build
79
+
80
+ # Refuse to publish a version that git did not name. Without this, a run
81
+ # from an untagged commit uploads a dev version to PyPI, where it cannot
82
+ # be taken back.
83
+ - name: the artifacts carry a real release version
84
+ run: |
85
+ ls -l dist/
86
+ version=$(ls dist/*.whl | sed -E 's|.*/gammonview_helper-([^-]+)-py3.*|\1|')
87
+ echo "derived version: $version"
88
+ case "$version" in
89
+ *dev*|*+*)
90
+ echo "::error::refusing to publish $version -- hatch-vcs found no tag on this commit"
91
+ exit 1 ;;
92
+ esac
93
+
94
+ - name: metadata renders on PyPI
95
+ run: uv run --isolated --no-project --with twine python -m twine check dist/*
96
+
97
+ - uses: actions/upload-artifact@v4
98
+ with:
99
+ name: dist
100
+ path: dist/
101
+
102
+ pypi:
103
+ name: upload to PyPI
104
+ needs: build
105
+ runs-on: ubuntu-latest
106
+ # The environment is the approval gate: GitHub can require a manual review
107
+ # before the job runs, and PyPI's trusted publisher is bound to this name,
108
+ # so a workflow that does not declare it cannot publish.
109
+ environment:
110
+ name: pypi
111
+ url: https://pypi.org/p/gammonview-helper
112
+ permissions:
113
+ # The OIDC token this mints is what PyPI trades for an upload credential.
114
+ # Without it the action fails with a confusing 403.
115
+ id-token: write
116
+ steps:
117
+ - uses: actions/download-artifact@v4
118
+ with:
119
+ name: dist
120
+ path: dist/
121
+
122
+ - uses: pypa/gh-action-pypi-publish@release/v1
123
+
124
+ # ---------------------------------------------------------------------------
125
+ # The same wheel, attached to the GitHub Release.
126
+ # ---------------------------------------------------------------------------
127
+ # `uv tool install <url-to-wheel>` is a complete install path that needs no
128
+ # index at all, which is what makes shipping ahead of PyPI possible. It is
129
+ # deliberately the *wheel* and not `git+https://…`: a git install makes every
130
+ # user's machine clone the repo and run the build backend, so it needs `git`
131
+ # present (a fresh Mac prompts for the Xcode tools) and it needs hatch-vcs to
132
+ # find a tag in whatever clone uv made -- the exact failure `fetch-depth: 0`
133
+ # above exists to prevent, reproduced on a stranger's laptop where nothing
134
+ # checks it. The artifact is pure Python, so one file covers every platform;
135
+ # `bgsage`'s per-platform wheels still come from PyPI either way.
136
+ #
137
+ # It is also the artifact the launcher installs (docs/DesktopHelper.md, step
138
+ # 7), which "asks the server which version to install rather than taking
139
+ # whatever is newest" -- a version string and a predictable asset name is all
140
+ # that takes.
141
+ #
142
+ # `needs: build`, NOT `needs: pypi`, and that ordering is the point: a PyPI
143
+ # upload that fails for reasons of its own must not withhold an install path
144
+ # that does not depend on it, and deleting the `pypi` job later must leave
145
+ # this one working unchanged.
146
+ release:
147
+ name: attach to the GitHub Release
148
+ needs: build
149
+ runs-on: ubuntu-latest
150
+ # A dispatch run has no tag to attach to. The tag is the release signal
151
+ # everywhere else in this file; here it is also the destination.
152
+ if: startsWith(github.ref, 'refs/tags/')
153
+ permissions:
154
+ # Overrides the read-only default at the top of the file. Scoped to this
155
+ # job so the test and build jobs stay unable to write to the repository.
156
+ contents: write
157
+ steps:
158
+ - uses: actions/download-artifact@v4
159
+ with:
160
+ name: dist
161
+ path: dist/
162
+
163
+ # `gh` is preinstalled on the runner. Used in preference to a third-party
164
+ # release action because this is a supply-chain path: the fewer things
165
+ # that get to touch the artifact between `uv build` and the user, the
166
+ # better, and that argument is the same one that put trusted publishing
167
+ # above an upload token.
168
+ #
169
+ # Written to be re-runnable. `workflow_dispatch` exists to retry a tag
170
+ # that already exists, and by then the Release usually does too.
171
+ - name: upload
172
+ env:
173
+ GH_TOKEN: ${{ github.token }}
174
+ TAG: ${{ github.ref_name }}
175
+ run: |
176
+ if gh release view "$TAG" >/dev/null 2>&1; then
177
+ gh release upload "$TAG" dist/* --clobber
178
+ else
179
+ gh release create "$TAG" dist/* \
180
+ --title "$TAG" \
181
+ --generate-notes
182
+ fi
@@ -0,0 +1,21 @@
1
+ # Python-generated files
2
+ __pycache__/
3
+ *.py[oc]
4
+ build/
5
+ dist/
6
+ wheels/
7
+ *.egg-info
8
+
9
+ .pytest_cache/
10
+
11
+ # Virtual environments
12
+ .venv
13
+
14
+ # Agent instruction data -- covered by a global gitignore on the author's
15
+ # machine, which a public clone obviously cannot rely on.
16
+ .claude/
17
+
18
+ # The helper's own state, if anyone points GAMMONVIEW_CONFIG_DIR in here while
19
+ # poking at it. It holds a worker token.
20
+ config.json
21
+ token
@@ -0,0 +1 @@
1
+ 3.13
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Nicholas Vlamis
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,197 @@
1
+ Metadata-Version: 2.5
2
+ Name: gammonview-helper
3
+ Version: 0.1.1
4
+ Summary: Analyze GammonView matches on your own computer.
5
+ Project-URL: Homepage, https://gammonview.com
6
+ Project-URL: Repository, https://github.com/ngvlamis/gammonview-helper
7
+ Project-URL: Issues, https://github.com/ngvlamis/gammonview-helper/issues
8
+ Author: Nicholas Vlamis
9
+ License-Expression: MIT
10
+ License-File: LICENSE
11
+ Keywords: analysis,backgammon,bgsage,gammonview
12
+ Classifier: Development Status :: 4 - Beta
13
+ Classifier: Environment :: Console
14
+ Classifier: Intended Audience :: End Users/Desktop
15
+ Classifier: Operating System :: MacOS
16
+ Classifier: Operating System :: Microsoft :: Windows
17
+ Classifier: Operating System :: POSIX :: Linux
18
+ Classifier: Programming Language :: Python :: 3.10
19
+ Classifier: Programming Language :: Python :: 3.11
20
+ Classifier: Programming Language :: Python :: 3.12
21
+ Classifier: Programming Language :: Python :: 3.13
22
+ Classifier: Programming Language :: Python :: 3.14
23
+ Classifier: Topic :: Games/Entertainment :: Board Games
24
+ Requires-Python: >=3.10
25
+ Requires-Dist: gammonview[engine]>=1.0.0
26
+ Requires-Dist: httpx>=0.27
27
+ Requires-Dist: keyring>=25.0
28
+ Description-Content-Type: text/markdown
29
+
30
+ # gammonview-helper
31
+
32
+ Analyze your backgammon matches on your own computer, and see the results on
33
+ [gammonview.com](https://gammonview.com).
34
+
35
+ GammonView analyses matches on a shared server. That server is shared, so the
36
+ deepest settings are not on its menu — a *World Class* run that takes a minute
37
+ on your laptop would take an hour of a queue everybody else is waiting in. The
38
+ helper moves that work to your machine: you keep using the website exactly as
39
+ before, and the analysis happens at home.
40
+
41
+ Nothing listens on a port. The helper dials out to gammonview.com, waits for
42
+ work **you** queued from your own browser, runs it, and sends the result back.
43
+ There is no inbound connection, no router setting, no firewall rule.
44
+
45
+ ## Install
46
+
47
+ Needs [uv](https://docs.astral.sh/uv/) (or pipx, or a plain `pip install` into
48
+ a virtualenv).
49
+
50
+ ```bash
51
+ uv tool install https://github.com/ngvlamis/gammonview-helper/releases/download/v0.1.1/gammonview_helper-0.1.1-py3-none-any.whl
52
+ ```
53
+
54
+ That pulls in the analysis engine and its neural networks — about 80 MB, and it
55
+ takes a minute or two the first time.
56
+
57
+ The URL names a version on purpose. Point it at a different release to install
58
+ that one, and re-run the same line with a newer version to upgrade — there is
59
+ no `uv tool upgrade` for a package installed from a URL, because there is no
60
+ index for it to ask.
61
+
62
+ Two other forms, for whoever prefers them:
63
+
64
+ ```bash
65
+ # once this is on PyPI, the short name and `uv tool upgrade` both work
66
+ uv tool install gammonview-helper
67
+
68
+ # straight from a tag — needs `git`, and builds from source rather than
69
+ # installing the wheel that was tested
70
+ uv tool install git+https://github.com/ngvlamis/gammonview-helper@v0.1.1
71
+ ```
72
+
73
+ ## Link this computer
74
+
75
+ ```bash
76
+ gammonview-helper link
77
+ ```
78
+
79
+ It prints a word and opens your browser. Sign in if you are asked, and pick the
80
+ word it printed out of the three you are shown. You will get an email saying
81
+ the computer was linked.
82
+
83
+ If the three words do not include the one on your screen, **something is
84
+ wrong** — close the page and start again. That is the check working.
85
+
86
+ ## Run it
87
+
88
+ ```bash
89
+ gammonview-helper run
90
+ ```
91
+
92
+ Leave it running. Analyses you start on gammonview.com will now go to this
93
+ computer, and the deeper presets appear in the menu. If the helper is not
94
+ running, the website falls back to the shared server on its own — you never get
95
+ an error for having closed it.
96
+
97
+ You can start it before linking. An unlinked helper waits for a credential
98
+ rather than giving up, so the usual order — install, let it start, then link in
99
+ the browser — needs no third step telling it to look again; it picks the link
100
+ up within a few seconds. The same is true if you unlink and re-link later.
101
+
102
+ ### Keeping it running (macOS)
103
+
104
+ `run` stops when you close the terminal. To have it start at login and stay up:
105
+
106
+ ```bash
107
+ deploy/install-login-item.sh
108
+ ```
109
+
110
+ That registers a launchd agent pointing at the installed `gammonview-helper`,
111
+ logging to `~/Library/Logs/GammonView/helper.log`. Re-run it after an upgrade —
112
+ it boots the old job out first, so you never end up with two pollers sharing one
113
+ worker id, and it keeps whatever site the installed agent already pointed at.
114
+ To move a machine between sites, say so explicitly:
115
+
116
+ ```bash
117
+ GAMMONVIEW_SITE=https://gammonview.com deploy/install-login-item.sh
118
+ ```
119
+
120
+ It says when it moves one, because a credential is per site: the machine has to
121
+ be linked to the new one separately.
122
+
123
+ ```bash
124
+ deploy/install-login-item.sh stop # stop it, and leave it stopped
125
+ tail -f ~/Library/Logs/GammonView/helper.log
126
+ ```
127
+
128
+ Installing always leaves the helper running, so a `stop` does not survive the
129
+ next install. After stopping, the website keeps saying **Connected** for up to
130
+ two minutes — a helper stops by going quiet, and the relay waits that long
131
+ before believing a silence.
132
+
133
+ The eventual installer does this for you; the script exists because the machine
134
+ this is developed on needed it first, and `deploy/com.gammonview.helper.plist`
135
+ records the four choices in it that are not boilerplate.
136
+
137
+ ## The other two commands
138
+
139
+ ```bash
140
+ gammonview-helper status # what is configured, and is the link still alive
141
+ gammonview-helper unlink # forget this computer's credentials
142
+ ```
143
+
144
+ `unlink` is local. To remove the computer from your account, use **Unlink** in
145
+ your account settings on the website — the helper's credentials deliberately
146
+ cannot reach your account, so they cannot revoke themselves.
147
+
148
+ ## Settings
149
+
150
+ Everything lives in one directory, which is the whole of the uninstall:
151
+
152
+ | | |
153
+ |---|---|
154
+ | macOS | `~/Library/Application Support/GammonView/` |
155
+ | Windows | `%LOCALAPPDATA%\GammonView\` |
156
+ | Linux | `~/.config/gammonview/` |
157
+
158
+ The credential is kept in your platform's credential store (Keychain, Credential
159
+ Manager, Secret Service), not in that directory. `status` says which store it
160
+ actually got — on a machine with no credential store it falls back to a
161
+ `0600` file, and tells you so.
162
+
163
+ `config.json` takes a few optional keys:
164
+
165
+ | key | default | what it does |
166
+ |---|---|---|
167
+ | `jobs` | `0` — the engine decides | how many of a match's decisions run in parallel |
168
+ | `threads` | `0` — the engine decides | engine threads inside each of those |
169
+ | `nice` | 10 | how hard the helper tries to stay out of your way |
170
+
171
+ The parallelism is sized by the engine, from measurements it keeps for the
172
+ purpose; set either to a number if you want a hard cap, and it will be kept.
173
+ `nice` is what makes the helper something you forget is running — it yields to
174
+ whatever you are actually doing, and every engine worker inherits it.
175
+
176
+ ## Licence
177
+
178
+ MIT. The engine ([bgsage](https://pypi.org/project/bgsage/)) is MPL-2.0 and is
179
+ installed as an ordinary dependency.
180
+
181
+ ## Development
182
+
183
+ ```bash
184
+ uv run pytest -q -m "not slow and not contract"
185
+ ```
186
+
187
+ Two markers are deselected there. `slow` runs a real analysis and needs a
188
+ minute of CPU. `contract` talks to a **live** relay and is opt-in:
189
+
190
+ ```bash
191
+ GAMMONVIEW_CONTRACT_BASE=https://beta.gammonview.com uv run pytest -m contract
192
+ ```
193
+
194
+ The other end of that protocol — the relay itself — is part of the
195
+ gammonview.com service and is not open source. `docs/Relay.md` documents the
196
+ seam from this side, says which revision this client implements, and explains
197
+ why the contract carries a number of its own.
@@ -0,0 +1,168 @@
1
+ # gammonview-helper
2
+
3
+ Analyze your backgammon matches on your own computer, and see the results on
4
+ [gammonview.com](https://gammonview.com).
5
+
6
+ GammonView analyses matches on a shared server. That server is shared, so the
7
+ deepest settings are not on its menu — a *World Class* run that takes a minute
8
+ on your laptop would take an hour of a queue everybody else is waiting in. The
9
+ helper moves that work to your machine: you keep using the website exactly as
10
+ before, and the analysis happens at home.
11
+
12
+ Nothing listens on a port. The helper dials out to gammonview.com, waits for
13
+ work **you** queued from your own browser, runs it, and sends the result back.
14
+ There is no inbound connection, no router setting, no firewall rule.
15
+
16
+ ## Install
17
+
18
+ Needs [uv](https://docs.astral.sh/uv/) (or pipx, or a plain `pip install` into
19
+ a virtualenv).
20
+
21
+ ```bash
22
+ uv tool install https://github.com/ngvlamis/gammonview-helper/releases/download/v0.1.1/gammonview_helper-0.1.1-py3-none-any.whl
23
+ ```
24
+
25
+ That pulls in the analysis engine and its neural networks — about 80 MB, and it
26
+ takes a minute or two the first time.
27
+
28
+ The URL names a version on purpose. Point it at a different release to install
29
+ that one, and re-run the same line with a newer version to upgrade — there is
30
+ no `uv tool upgrade` for a package installed from a URL, because there is no
31
+ index for it to ask.
32
+
33
+ Two other forms, for whoever prefers them:
34
+
35
+ ```bash
36
+ # once this is on PyPI, the short name and `uv tool upgrade` both work
37
+ uv tool install gammonview-helper
38
+
39
+ # straight from a tag — needs `git`, and builds from source rather than
40
+ # installing the wheel that was tested
41
+ uv tool install git+https://github.com/ngvlamis/gammonview-helper@v0.1.1
42
+ ```
43
+
44
+ ## Link this computer
45
+
46
+ ```bash
47
+ gammonview-helper link
48
+ ```
49
+
50
+ It prints a word and opens your browser. Sign in if you are asked, and pick the
51
+ word it printed out of the three you are shown. You will get an email saying
52
+ the computer was linked.
53
+
54
+ If the three words do not include the one on your screen, **something is
55
+ wrong** — close the page and start again. That is the check working.
56
+
57
+ ## Run it
58
+
59
+ ```bash
60
+ gammonview-helper run
61
+ ```
62
+
63
+ Leave it running. Analyses you start on gammonview.com will now go to this
64
+ computer, and the deeper presets appear in the menu. If the helper is not
65
+ running, the website falls back to the shared server on its own — you never get
66
+ an error for having closed it.
67
+
68
+ You can start it before linking. An unlinked helper waits for a credential
69
+ rather than giving up, so the usual order — install, let it start, then link in
70
+ the browser — needs no third step telling it to look again; it picks the link
71
+ up within a few seconds. The same is true if you unlink and re-link later.
72
+
73
+ ### Keeping it running (macOS)
74
+
75
+ `run` stops when you close the terminal. To have it start at login and stay up:
76
+
77
+ ```bash
78
+ deploy/install-login-item.sh
79
+ ```
80
+
81
+ That registers a launchd agent pointing at the installed `gammonview-helper`,
82
+ logging to `~/Library/Logs/GammonView/helper.log`. Re-run it after an upgrade —
83
+ it boots the old job out first, so you never end up with two pollers sharing one
84
+ worker id, and it keeps whatever site the installed agent already pointed at.
85
+ To move a machine between sites, say so explicitly:
86
+
87
+ ```bash
88
+ GAMMONVIEW_SITE=https://gammonview.com deploy/install-login-item.sh
89
+ ```
90
+
91
+ It says when it moves one, because a credential is per site: the machine has to
92
+ be linked to the new one separately.
93
+
94
+ ```bash
95
+ deploy/install-login-item.sh stop # stop it, and leave it stopped
96
+ tail -f ~/Library/Logs/GammonView/helper.log
97
+ ```
98
+
99
+ Installing always leaves the helper running, so a `stop` does not survive the
100
+ next install. After stopping, the website keeps saying **Connected** for up to
101
+ two minutes — a helper stops by going quiet, and the relay waits that long
102
+ before believing a silence.
103
+
104
+ The eventual installer does this for you; the script exists because the machine
105
+ this is developed on needed it first, and `deploy/com.gammonview.helper.plist`
106
+ records the four choices in it that are not boilerplate.
107
+
108
+ ## The other two commands
109
+
110
+ ```bash
111
+ gammonview-helper status # what is configured, and is the link still alive
112
+ gammonview-helper unlink # forget this computer's credentials
113
+ ```
114
+
115
+ `unlink` is local. To remove the computer from your account, use **Unlink** in
116
+ your account settings on the website — the helper's credentials deliberately
117
+ cannot reach your account, so they cannot revoke themselves.
118
+
119
+ ## Settings
120
+
121
+ Everything lives in one directory, which is the whole of the uninstall:
122
+
123
+ | | |
124
+ |---|---|
125
+ | macOS | `~/Library/Application Support/GammonView/` |
126
+ | Windows | `%LOCALAPPDATA%\GammonView\` |
127
+ | Linux | `~/.config/gammonview/` |
128
+
129
+ The credential is kept in your platform's credential store (Keychain, Credential
130
+ Manager, Secret Service), not in that directory. `status` says which store it
131
+ actually got — on a machine with no credential store it falls back to a
132
+ `0600` file, and tells you so.
133
+
134
+ `config.json` takes a few optional keys:
135
+
136
+ | key | default | what it does |
137
+ |---|---|---|
138
+ | `jobs` | `0` — the engine decides | how many of a match's decisions run in parallel |
139
+ | `threads` | `0` — the engine decides | engine threads inside each of those |
140
+ | `nice` | 10 | how hard the helper tries to stay out of your way |
141
+
142
+ The parallelism is sized by the engine, from measurements it keeps for the
143
+ purpose; set either to a number if you want a hard cap, and it will be kept.
144
+ `nice` is what makes the helper something you forget is running — it yields to
145
+ whatever you are actually doing, and every engine worker inherits it.
146
+
147
+ ## Licence
148
+
149
+ MIT. The engine ([bgsage](https://pypi.org/project/bgsage/)) is MPL-2.0 and is
150
+ installed as an ordinary dependency.
151
+
152
+ ## Development
153
+
154
+ ```bash
155
+ uv run pytest -q -m "not slow and not contract"
156
+ ```
157
+
158
+ Two markers are deselected there. `slow` runs a real analysis and needs a
159
+ minute of CPU. `contract` talks to a **live** relay and is opt-in:
160
+
161
+ ```bash
162
+ GAMMONVIEW_CONTRACT_BASE=https://beta.gammonview.com uv run pytest -m contract
163
+ ```
164
+
165
+ The other end of that protocol — the relay itself — is part of the
166
+ gammonview.com service and is not open source. `docs/Relay.md` documents the
167
+ seam from this side, says which revision this client implements, and explains
168
+ why the contract carries a number of its own.