tahuti 0.4.0__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.
- tahuti-0.4.0/.gitignore +181 -0
- tahuti-0.4.0/CHANGELOG.md +299 -0
- tahuti-0.4.0/LICENSE +21 -0
- tahuti-0.4.0/PKG-INFO +509 -0
- tahuti-0.4.0/README.md +477 -0
- tahuti-0.4.0/SECURITY.md +190 -0
- tahuti-0.4.0/docs/downstream-notifier-guide.md +362 -0
- tahuti-0.4.0/docs/events.md +912 -0
- tahuti-0.4.0/docs/library.md +548 -0
- tahuti-0.4.0/docs/realtime-transport-findings.md +310 -0
- tahuti-0.4.0/extras/mb-notifier/README.md +18 -0
- tahuti-0.4.0/extras/mb-notifier/bark_webhook_receiver.py +907 -0
- tahuti-0.4.0/extras/mb-notifier/test_bark_webhook.py +702 -0
- tahuti-0.4.0/extras/probe_actioncable.py +221 -0
- tahuti-0.4.0/extras/probe_frontend.py +134 -0
- tahuti-0.4.0/extras/probe_latency.py +87 -0
- tahuti-0.4.0/extras/probe_push.py +178 -0
- tahuti-0.4.0/extras/probe_ws.py +103 -0
- tahuti-0.4.0/pyproject.toml +107 -0
- tahuti-0.4.0/src/mb_cli/__init__.py +10 -0
- tahuti-0.4.0/src/mb_cli/__main__.py +2489 -0
- tahuti-0.4.0/src/mb_cli/auth.py +273 -0
- tahuti-0.4.0/src/mb_cli/cache.py +176 -0
- tahuti-0.4.0/src/mb_cli/client.py +2441 -0
- tahuti-0.4.0/src/mb_cli/config.py +367 -0
- tahuti-0.4.0/src/mb_cli/daemon/__init__.py +566 -0
- tahuti-0.4.0/src/mb_cli/daemon/events.py +404 -0
- tahuti-0.4.0/src/mb_cli/daemon/provider.py +350 -0
- tahuti-0.4.0/src/mb_cli/daemon/scheduler.py +160 -0
- tahuti-0.4.0/src/mb_cli/daemon/service.py +477 -0
- tahuti-0.4.0/src/mb_cli/daemon/state.py +205 -0
- tahuti-0.4.0/src/mb_cli/daemon/stealth.py +171 -0
- tahuti-0.4.0/src/mb_cli/daemon/stream.py +116 -0
- tahuti-0.4.0/src/mb_cli/daemon/system.py +331 -0
- tahuti-0.4.0/src/mb_cli/daemon/webhook.py +647 -0
- tahuti-0.4.0/src/mb_cli/exceptions.py +15 -0
- tahuti-0.4.0/src/mb_cli/filters.py +288 -0
- tahuti-0.4.0/src/mb_cli/formatters.py +532 -0
- tahuti-0.4.0/src/mb_cli/keychain.py +374 -0
- tahuti-0.4.0/src/mb_cli/mcp_server.py +1088 -0
- tahuti-0.4.0/src/mb_cli/notifications.py +182 -0
- tahuti-0.4.0/src/mb_cli/task_status.py +278 -0
- tahuti-0.4.0/tests/__init__.py +0 -0
- tahuti-0.4.0/tests/conftest.py +410 -0
- tahuti-0.4.0/tests/test_auth.py +631 -0
- tahuti-0.4.0/tests/test_cache.py +935 -0
- tahuti-0.4.0/tests/test_cli_fixes.py +367 -0
- tahuti-0.4.0/tests/test_cli_handler_defects.py +930 -0
- tahuti-0.4.0/tests/test_client.py +1069 -0
- tahuti-0.4.0/tests/test_client_scrape_fixes.py +882 -0
- tahuti-0.4.0/tests/test_config.py +271 -0
- tahuti-0.4.0/tests/test_credential_security.py +1386 -0
- tahuti-0.4.0/tests/test_daemon.py +534 -0
- tahuti-0.4.0/tests/test_daemon_cli.py +90 -0
- tahuti-0.4.0/tests/test_daemon_cli_flags.py +501 -0
- tahuti-0.4.0/tests/test_daemon_e2e_integration.py +402 -0
- tahuti-0.4.0/tests/test_daemon_events.py +232 -0
- tahuti-0.4.0/tests/test_daemon_provider.py +81 -0
- tahuti-0.4.0/tests/test_daemon_provider_normalization.py +537 -0
- tahuti-0.4.0/tests/test_daemon_scheduler.py +183 -0
- tahuti-0.4.0/tests/test_daemon_service.py +496 -0
- tahuti-0.4.0/tests/test_daemon_state.py +28 -0
- tahuti-0.4.0/tests/test_daemon_stealth.py +165 -0
- tahuti-0.4.0/tests/test_daemon_stream.py +273 -0
- tahuti-0.4.0/tests/test_daemon_system.py +81 -0
- tahuti-0.4.0/tests/test_daemon_webhook.py +796 -0
- tahuti-0.4.0/tests/test_download.py +383 -0
- tahuti-0.4.0/tests/test_env_isolation.py +206 -0
- tahuti-0.4.0/tests/test_exceptions.py +24 -0
- tahuti-0.4.0/tests/test_exit_codes.py +399 -0
- tahuti-0.4.0/tests/test_feedback.py +350 -0
- tahuti-0.4.0/tests/test_filters.py +498 -0
- tahuti-0.4.0/tests/test_formatters.py +745 -0
- tahuti-0.4.0/tests/test_main.py +643 -0
- tahuti-0.4.0/tests/test_mcp_server.py +711 -0
- tahuti-0.4.0/tests/test_new_features.py +198 -0
- tahuti-0.4.0/tests/test_notification_suppression.py +334 -0
- tahuti-0.4.0/tests/test_notifications.py +189 -0
- tahuti-0.4.0/tests/test_notifications_transport.py +251 -0
- tahuti-0.4.0/tests/test_security_fixes.py +263 -0
- tahuti-0.4.0/tests/test_submissions.py +302 -0
- tahuti-0.4.0/tests/test_submit_refresh.py +322 -0
- tahuti-0.4.0/tests/test_task_status.py +317 -0
tahuti-0.4.0/.gitignore
ADDED
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
# Byte-compiled / optimized / DLL files
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*$py.class
|
|
5
|
+
|
|
6
|
+
# C extensions
|
|
7
|
+
*.so
|
|
8
|
+
|
|
9
|
+
# Distribution / packaging
|
|
10
|
+
.Python
|
|
11
|
+
build/
|
|
12
|
+
develop-eggs/
|
|
13
|
+
dist/
|
|
14
|
+
downloads/
|
|
15
|
+
eggs/
|
|
16
|
+
.eggs/
|
|
17
|
+
lib/
|
|
18
|
+
lib64/
|
|
19
|
+
parts/
|
|
20
|
+
sdist/
|
|
21
|
+
var/
|
|
22
|
+
wheels/
|
|
23
|
+
share/python-wheels/
|
|
24
|
+
*.egg-info/
|
|
25
|
+
.installed.cfg
|
|
26
|
+
*.egg
|
|
27
|
+
MANIFEST
|
|
28
|
+
|
|
29
|
+
# PyInstaller
|
|
30
|
+
*.manifest
|
|
31
|
+
*.spec
|
|
32
|
+
|
|
33
|
+
# Installer logs
|
|
34
|
+
pip-log.txt
|
|
35
|
+
pip-delete-this-directory.txt
|
|
36
|
+
|
|
37
|
+
# Unit test / coverage reports
|
|
38
|
+
htmlcov/
|
|
39
|
+
.tox/
|
|
40
|
+
.nox/
|
|
41
|
+
.coverage
|
|
42
|
+
.coverage.*
|
|
43
|
+
.cache
|
|
44
|
+
nosetests.xml
|
|
45
|
+
coverage.xml
|
|
46
|
+
*.cover
|
|
47
|
+
*.py,cover
|
|
48
|
+
.hypothesis/
|
|
49
|
+
.pytest_cache/
|
|
50
|
+
cover/
|
|
51
|
+
|
|
52
|
+
# Translations
|
|
53
|
+
*.mo
|
|
54
|
+
*.pot
|
|
55
|
+
|
|
56
|
+
# Django stuff:
|
|
57
|
+
*.log
|
|
58
|
+
local_settings.py
|
|
59
|
+
db.sqlite3
|
|
60
|
+
db.sqlite3-journal
|
|
61
|
+
|
|
62
|
+
# Flask stuff:
|
|
63
|
+
instance/
|
|
64
|
+
.webassets-cache
|
|
65
|
+
|
|
66
|
+
# Scrapy stuff:
|
|
67
|
+
.scrapy
|
|
68
|
+
|
|
69
|
+
# Sphinx documentation
|
|
70
|
+
docs/_build/
|
|
71
|
+
|
|
72
|
+
# PyBuilder
|
|
73
|
+
.pybuilder/
|
|
74
|
+
target/
|
|
75
|
+
|
|
76
|
+
# Jupyter Notebook
|
|
77
|
+
.ipynb_checkpoints
|
|
78
|
+
|
|
79
|
+
# IPython
|
|
80
|
+
profile_default/
|
|
81
|
+
ipython_config.py
|
|
82
|
+
|
|
83
|
+
# pyenv
|
|
84
|
+
.python-version
|
|
85
|
+
|
|
86
|
+
# pipenv
|
|
87
|
+
Pipfile.lock
|
|
88
|
+
|
|
89
|
+
# poetry
|
|
90
|
+
poetry.lock
|
|
91
|
+
|
|
92
|
+
# pdm
|
|
93
|
+
.pdm.toml
|
|
94
|
+
.pdm-python
|
|
95
|
+
.pdm-build/
|
|
96
|
+
|
|
97
|
+
# PEP 582
|
|
98
|
+
__pypackages__/
|
|
99
|
+
|
|
100
|
+
# Celery stuff
|
|
101
|
+
celerybeat-schedule
|
|
102
|
+
celerybeat.pid
|
|
103
|
+
|
|
104
|
+
# SageMath parsed files
|
|
105
|
+
*.sage.py
|
|
106
|
+
|
|
107
|
+
# Environments
|
|
108
|
+
.env
|
|
109
|
+
.venv
|
|
110
|
+
env/
|
|
111
|
+
venv/
|
|
112
|
+
ENV/
|
|
113
|
+
env.bak/
|
|
114
|
+
venv.bak/
|
|
115
|
+
|
|
116
|
+
# Spyder project settings
|
|
117
|
+
.spyderproject
|
|
118
|
+
.spyproject
|
|
119
|
+
|
|
120
|
+
# Rope project settings
|
|
121
|
+
.ropeproject
|
|
122
|
+
|
|
123
|
+
# mkdocs documentation
|
|
124
|
+
/site
|
|
125
|
+
|
|
126
|
+
# mypy
|
|
127
|
+
.mypy_cache/
|
|
128
|
+
.dmypy.json
|
|
129
|
+
dmypy.json
|
|
130
|
+
|
|
131
|
+
# Pyre type checker
|
|
132
|
+
.pyre/
|
|
133
|
+
|
|
134
|
+
# pytype static type analyzer
|
|
135
|
+
.pytype/
|
|
136
|
+
|
|
137
|
+
# Cython debug symbols
|
|
138
|
+
cython_debug/
|
|
139
|
+
|
|
140
|
+
# Ruff
|
|
141
|
+
.ruff_cache/
|
|
142
|
+
|
|
143
|
+
# macOS
|
|
144
|
+
.DS_Store
|
|
145
|
+
|
|
146
|
+
# Project-specific
|
|
147
|
+
*.json
|
|
148
|
+
!pyproject.toml
|
|
149
|
+
.worktrees/
|
|
150
|
+
|
|
151
|
+
# Claude Code agent workspace. .claude/worktrees/ holds complete clones of
|
|
152
|
+
# this repo (each with its own .git), so it must never be committed, and must
|
|
153
|
+
# never reach the sdist — hatchling's default sdist only skips what git ignores.
|
|
154
|
+
.claude/
|
|
155
|
+
.claude/worktrees/
|
|
156
|
+
|
|
157
|
+
# Deployments and operational notes
|
|
158
|
+
docs/deployments/
|
|
159
|
+
|
|
160
|
+
# Local diagnostic, data, and sensitive files
|
|
161
|
+
backup.txt
|
|
162
|
+
test_login.py
|
|
163
|
+
fetch_attachments.py
|
|
164
|
+
open_classes.py
|
|
165
|
+
freqs.txt
|
|
166
|
+
downloaded_attachments/
|
|
167
|
+
/course_aliases.json
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
# Personal data / infrastructure details — never commit these.
|
|
172
|
+
DEPLOYMENT.md
|
|
173
|
+
docs/deployments/
|
|
174
|
+
course_aliases.json
|
|
175
|
+
snapshot.json
|
|
176
|
+
daemon_state.json
|
|
177
|
+
|
|
178
|
+
# `mb download` default output dir (task_<id>_<title_slug>) when a test or a
|
|
179
|
+
# manual run is invoked without --output-dir.
|
|
180
|
+
/task_*
|
|
181
|
+
|
|
@@ -0,0 +1,299 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project are documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
Release dates below are the date of the `vX.Y.Z` git tag; `0.4.0` is the first
|
|
9
|
+
release to carry one.
|
|
10
|
+
|
|
11
|
+
## [Unreleased]
|
|
12
|
+
|
|
13
|
+
Nothing yet.
|
|
14
|
+
|
|
15
|
+
## [0.4.0] - 2026-09-19
|
|
16
|
+
|
|
17
|
+
Contains a **breaking webhook signature change** — see `### Changed` below. Any
|
|
18
|
+
deployed receiver rejects every payload until it adds `X-MB-Timestamp` to its
|
|
19
|
+
signed material.
|
|
20
|
+
|
|
21
|
+
> Renamed from `mb-cli` to **`tahuti`**. The distribution, the CLI command and
|
|
22
|
+
> the repository are all `tahuti` now; the Python import path stays `mb_cli`, so
|
|
23
|
+
> `import mb_cli` and `python -m mb_cli` are unchanged. Environment variables
|
|
24
|
+
> stay `MB_CRAWLER_*` and the state directory stays `~/.config/tahuti/`.
|
|
25
|
+
> (`mb-cli`/`mb_cli` on PyPI is an unrelated project by another author, so
|
|
26
|
+
> nothing here overwrites it.)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
### Added
|
|
30
|
+
- **Per-endpoint delivery outcomes.** `WebhookDispatcher.dispatch()` returns a
|
|
31
|
+
machine-readable `outcome` per endpoint — `success`, `permanent_failure` or
|
|
32
|
+
`transient_failure` — alongside the existing `success` boolean, plus
|
|
33
|
+
`retryable`, `signed`, `attempts` and `url_display` fields. Previously the
|
|
34
|
+
only signal was a bare boolean, which collapsed "delivered", "will never
|
|
35
|
+
work" and "try again later" into one value.
|
|
36
|
+
- `WebhookDispatcher.retry_failed(event, results)` re-attempts only the
|
|
37
|
+
endpoints that still owe the event, skipping the ones that already delivered
|
|
38
|
+
and the ones that failed permanently. Module-level `retryable_results()`
|
|
39
|
+
(what is still owed) and `all_delivered()` (is anything owed) give a caller
|
|
40
|
+
everything needed to implement per-endpoint at-least-once.
|
|
41
|
+
- `daemon test-webhook` output now carries `url_display`, `outcome`,
|
|
42
|
+
`retryable`, `signed` and `attempts`.
|
|
43
|
+
|
|
44
|
+
### Fixed
|
|
45
|
+
- **A webhook with no secret no longer ships unsigned payloads silently.**
|
|
46
|
+
`if webhook.secret:` treated `""` as "no signing", so an empty secret sent
|
|
47
|
+
unsigned payloads with no warning anywhere. The dispatcher now logs an ERROR
|
|
48
|
+
once per endpoint explaining that every payload is UNSIGNED and that a
|
|
49
|
+
verifying receiver will reject it, and every result carries `signed: false`.
|
|
50
|
+
- **`daemon test-webhook` now validates the URL.** `test_ping` bypassed
|
|
51
|
+
`_validate_webhook_url`, so the scheme/host guard applied on every real
|
|
52
|
+
dispatch was skipped on the one command where a user is most likely to paste
|
|
53
|
+
a wrong URL — `file://` and friends reached `requests` and surfaced as a
|
|
54
|
+
confusing network error. An invalid URL is now rejected before any network
|
|
55
|
+
call with `invalid_webhook_url:<reason>`.
|
|
56
|
+
- **Webhook URLs are no longer logged verbatim.** `log.info`/`log.warning`/
|
|
57
|
+
`log.error` on every delivery and retry printed the full configured URL, so
|
|
58
|
+
providers that carry the credential in the path or query (Slack
|
|
59
|
+
`hooks.slack.com/services/T…/B…/<token>`, Bark `api.day.app/<key>/…`, WeCom
|
|
60
|
+
`…/send?key=…`) wrote a live token into `daemon.log`. URLs are now redacted
|
|
61
|
+
before logging — scheme, host, port and path shape are kept, credential-
|
|
62
|
+
bearing path segments and query values are masked, and any `user:password@`
|
|
63
|
+
userinfo is dropped. `daemon test-webhook` output additionally carries a
|
|
64
|
+
`url_display` field.
|
|
65
|
+
- **The webhook retry "hard ceiling" now bounds request time, not just
|
|
66
|
+
sleeps.** `MAX_TOTAL_RETRY_SECONDS` was checked only before `time.sleep`, so
|
|
67
|
+
each attempt's `requests.post(timeout=10)` was unbounded by it and the final
|
|
68
|
+
attempt always ran its full timeout. With the default `max_retries=3` one
|
|
69
|
+
hanging endpoint cost ~33s of blocked polling, and the cost was linear in the
|
|
70
|
+
number of configured endpoints (2 endpoints ≈ 66s against a 30s poll
|
|
71
|
+
interval). Each attempt's timeout is now clamped to the remaining budget and
|
|
72
|
+
the backoff sleep is clamped to it too, so one event can never exceed the
|
|
73
|
+
ceiling.
|
|
74
|
+
- **Permanent 4xx are no longer retried.** 400/401/403/404/410/422 were retried
|
|
75
|
+
three times with backoff even though no backoff can fix them, which both
|
|
76
|
+
stalled the poll loop and delayed the diagnosis. Only genuinely transient
|
|
77
|
+
statuses are retried now: 5xx, 408, 429 (and 425).
|
|
78
|
+
- **Webhook redirects are no longer followed.** `requests.post` followed
|
|
79
|
+
redirects with the signed body and the signature headers intact, so a 307
|
|
80
|
+
re-sent them to a *different* host and a 301/302 could downgrade https to
|
|
81
|
+
http — defeating the point of signing. Dispatch now passes
|
|
82
|
+
`allow_redirects=False`; a 3xx is reported as a permanent failure naming the
|
|
83
|
+
`Location` so the configured URL can be corrected.
|
|
84
|
+
|
|
85
|
+
### Changed
|
|
86
|
+
- **BREAKING PROTOCOL CHANGE — webhook signatures.** `X-MB-Signature` now covers
|
|
87
|
+
`X-MB-Timestamp` as well as the body:
|
|
88
|
+
`sha256=` + HMAC-SHA256(secret, `f"{X-MB-Timestamp}.".encode() + body`). It
|
|
89
|
+
previously covered the body alone, which left `X-MB-Timestamp`
|
|
90
|
+
unauthenticated — anyone who captured a single POST could replay it
|
|
91
|
+
indefinitely by rewriting that header, because the original digest still
|
|
92
|
+
validated and the receiver's freshness check (`MAX_TIMESTAMP_SKEW_SECONDS`)
|
|
93
|
+
waved the replay through. **Any deployed receiver rejects every payload until
|
|
94
|
+
it adds the timestamp to its signed material.** `extras/mb-notifier/bark_webhook_receiver.py`
|
|
95
|
+
and the FastAPI recipe in `docs/events.md` are updated in the same change;
|
|
96
|
+
`verify_signature` now fails closed on a missing `X-MB-Timestamp` and checks
|
|
97
|
+
the digest before freshness, so a restamped payload reports
|
|
98
|
+
`signature_mismatch` rather than merely `stale_timestamp`.
|
|
99
|
+
|
|
100
|
+
### Added
|
|
101
|
+
- `CHANGELOG.md`, `SECURITY.md`, and GitHub Actions CI (`.github/workflows/ci.yml`).
|
|
102
|
+
- `[dependency-groups]` `dev` group in `pyproject.toml` declaring the test
|
|
103
|
+
dependencies (`pytest`, `requests-mock`, `mcp`) that the suite always needed
|
|
104
|
+
but that were never declared, so a plain `uv sync` could not run the tests.
|
|
105
|
+
|
|
106
|
+
### Fixed
|
|
107
|
+
- **Piped output is now the documented JSON.** `resolve_format` returned `pretty`
|
|
108
|
+
for any unset `--format`, with no TTY check, so `mb list | jq .` failed with a
|
|
109
|
+
parse error and every JSON consumer had to remember `--format json`. It now
|
|
110
|
+
picks `pretty` for an interactive terminal and `json` otherwise (matching the
|
|
111
|
+
`--format` help text and the README), with `MB_CLI_FORMAT=json|pretty` as an
|
|
112
|
+
escape hatch for scripts that run with and without a terminal.
|
|
113
|
+
- **MCP `list_tasks` no longer answers "no homework" for a misspelled `view`.**
|
|
114
|
+
An unrecognised value matched none of the three section checks, so all three
|
|
115
|
+
lists stayed empty and the tool returned `total_count: 0` — a valid-looking
|
|
116
|
+
wrong answer. `view` is now validated against one canonical vocabulary
|
|
117
|
+
(case-insensitive, with aliases like `Upcoming` / `upcoming tasks`) shared with
|
|
118
|
+
`filters.result_views`, and an unknown value returns a structured error instead
|
|
119
|
+
of crawling anything.
|
|
120
|
+
- **`--grade` / `grade=` now match `+`/`-` modifiers.** The extraction regex
|
|
121
|
+
`^([A-F][+-]?)\b` could never capture the modifier — there is no word boundary
|
|
122
|
+
between `+`/`-` and a following space, so it always backtracked to empty and
|
|
123
|
+
`"A+ (95/100)"` was read as `"A"`. A task whose card carries only
|
|
124
|
+
`grade_score: "A+ (95/100)"` was therefore invisible to `--grade A+`.
|
|
125
|
+
- **Mixed naive/aware due dates can no longer crash the pretty renderer.**
|
|
126
|
+
`parse_due_date` returns an aware datetime for ISO input with an offset and a
|
|
127
|
+
naive one for every HTML format; sorting a section that contained both raised
|
|
128
|
+
`TypeError: can't compare offset-naive and offset-aware datetimes` out of
|
|
129
|
+
`render_pretty`, which `main()` does not catch — the user got a traceback and
|
|
130
|
+
no payload. Both `task_sort_key` and `classify_task_view` now normalise through
|
|
131
|
+
one shared helper.
|
|
132
|
+
- **MCP tools validate their inputs.** `view_task` derived the task id with
|
|
133
|
+
`target.split("core_tasks/")[-1].split("/")[0]`, so a URL without
|
|
134
|
+
`/core_tasks/` made the entire string the id; `get_class_grades` interpolated
|
|
135
|
+
`class_id` straight into a ManageBac URL path; `get_calendar_events` /
|
|
136
|
+
`get_timetable` passed arbitrary strings into query params; and `submit_file`
|
|
137
|
+
handed an unchecked path to the filesystem. Each now returns a structured,
|
|
138
|
+
actionable error for a malformed argument instead of a 404 or a raw
|
|
139
|
+
`FileNotFoundError`.
|
|
140
|
+
- **Packaging:** the sdist no longer leaks a nested copy of the repository. It
|
|
141
|
+
previously shipped 76 entries under `.claude/`, including
|
|
142
|
+
`.claude/worktrees/finish-security-audit/` — a complete clone of the repo with
|
|
143
|
+
its own `.git` (~75 MB). Fixed in both belts: `.claude/` added to
|
|
144
|
+
`.gitignore`, and an explicit `[tool.hatch.build.targets.sdist]` include/exclude
|
|
145
|
+
list in `pyproject.toml` that pins exactly what ships. The sdist went from 153
|
|
146
|
+
entries to 63 files; no `.pyc`, no `.git`, no `.venv`, and no
|
|
147
|
+
`docs/superpowers/` internal design docs ship.
|
|
148
|
+
- `mb --config` / `--session-file` help text no longer claims the format is
|
|
149
|
+
TOML; it is JSON (`config.json` / `session.json`).
|
|
150
|
+
- `docs/library.md` no longer points at a non-existent
|
|
151
|
+
`~/.config/mb-crawler/config.toml`.
|
|
152
|
+
- **The `mcp` extra is now bounded (`mcp>=1.20,<2`).** It was unbounded, so a
|
|
153
|
+
fresh resolve installed mcp 2.x, which removed `mcp.server.fastmcp` — every
|
|
154
|
+
`mb-mcp` invocation died with `ModuleNotFoundError` on import, even when the
|
|
155
|
+
extra was installed correctly.
|
|
156
|
+
|
|
157
|
+
### Changed
|
|
158
|
+
- README Installation now documents the `mcp` extra
|
|
159
|
+
(`pip install "mb-cli[mcp]"`) — the documented `mb-mcp` command imports `mcp`
|
|
160
|
+
unguarded and failed with `ModuleNotFoundError` on a plain install.
|
|
161
|
+
- README now documents `mb daemon start -b` / `--background`. `daemon start`
|
|
162
|
+
runs in the **foreground** by default and dies with the terminal; only `-b`
|
|
163
|
+
detaches it. `--pid-file` / `--log-file` documented per subcommand.
|
|
164
|
+
- README now documents the previously undocumented `mb submissions`,
|
|
165
|
+
`mb download`, `mb feedback`, and `mb daemon install` / `uninstall` /
|
|
166
|
+
`configure-channel` subcommands.
|
|
167
|
+
- README now documents the `MB_CRAWLER_PASSWORD` and `MB_CRAWLER_COOKIE`
|
|
168
|
+
environment variables alongside `MB_WEBHOOK_SECRET`.
|
|
169
|
+
- README now flags the `--poll-interval` (`mb daemon run`) vs `--interval`
|
|
170
|
+
(`mb daemon start`) flag-name asymmetry, and qualifies the claim that
|
|
171
|
+
`--config` / `--session-file` are universal — the daemon subcommands expose
|
|
172
|
+
only `--daemon-config` and/or `--pid-file` / `--log-file`, varying by
|
|
173
|
+
subcommand.
|
|
174
|
+
|
|
175
|
+
## [0.3.0] - 2026-09-17
|
|
176
|
+
|
|
177
|
+
### Added
|
|
178
|
+
- Complete library + downstream security audit.
|
|
179
|
+
- Comprehensive Python SDK and library reference (`docs/library.md`).
|
|
180
|
+
|
|
181
|
+
### Changed
|
|
182
|
+
- Bumped version to 0.3.0 and documented remote separated deployment.
|
|
183
|
+
- `ManageBacClient.from_config()` implemented; `docs/events.md` updated.
|
|
184
|
+
|
|
185
|
+
### Fixed
|
|
186
|
+
- CLI command flags, the systemd unit, and Python SDK snippets corrected in the
|
|
187
|
+
README and the notifier guide.
|
|
188
|
+
- `student_name` fallback polished and covered by a test for the default
|
|
189
|
+
`from_config` argument.
|
|
190
|
+
|
|
191
|
+
## [0.2.5] - 2026-09-14
|
|
192
|
+
|
|
193
|
+
### Added
|
|
194
|
+
- Real-time event streaming: `ManageBacDaemon.stream()` async event generator.
|
|
195
|
+
- Comprehensive event stream specification and integration guide (`docs/events.md`).
|
|
196
|
+
- Design specs and implementation plan for modular event stream and notifier
|
|
197
|
+
separation.
|
|
198
|
+
|
|
199
|
+
### Changed
|
|
200
|
+
- Standardized the `MBEvent` payload schema.
|
|
201
|
+
- Extracted the Bark webhook receiver and personal config into
|
|
202
|
+
`extras/mb-notifier`.
|
|
203
|
+
- Refreshed the README and added the downstream notifier guide.
|
|
204
|
+
|
|
205
|
+
### Fixed
|
|
206
|
+
- Stream lifecycle and queue-safety issues raised in review.
|
|
207
|
+
- Defensive fallback URL resolution in `service.py`.
|
|
208
|
+
|
|
209
|
+
## [0.2.4] - 2026-09-13
|
|
210
|
+
|
|
211
|
+
### Added
|
|
212
|
+
- `mb submissions` CLI for submission lifecycle management (`--list`, `--add`,
|
|
213
|
+
`--delete`, `--check-feedback`), with a matching MCP tool.
|
|
214
|
+
|
|
215
|
+
### Changed
|
|
216
|
+
- Bumped version; grade-release alert suppression and webhook improvements.
|
|
217
|
+
|
|
218
|
+
## [0.2.3] - 2026-09-05
|
|
219
|
+
|
|
220
|
+
### Added
|
|
221
|
+
- Unified task status and lifecycle domain model.
|
|
222
|
+
- Teacher feedback fetching, including support for modern task detail page
|
|
223
|
+
submissions and the PSPDFKit preview modal.
|
|
224
|
+
- `on_start` lifecycle callback for a full upcoming-task refresh.
|
|
225
|
+
- Just-in-time submission verification, eliminating unnecessary full recrawls.
|
|
226
|
+
- Lightweight Bark webhook receiver adapter.
|
|
227
|
+
- Real-time notification daemon and webhook engine with the MNN Hub provider and
|
|
228
|
+
a DDL scheduler, plus a complete E2E integration test suite.
|
|
229
|
+
- Compact 3-field notification layout with exact course alias resolution.
|
|
230
|
+
- Design specs for the daemon, the compact Bark layout, the on-start refresh
|
|
231
|
+
callback, unified task status, and submissions lifecycle management.
|
|
232
|
+
|
|
233
|
+
### Fixed
|
|
234
|
+
- Daemon no longer notifies for submitted or graded tasks.
|
|
235
|
+
- `_check_is_task_submitted` now checks the task page Submitted badge.
|
|
236
|
+
- Past milestones suppressed on task discovery; DDL reminders formatted with the
|
|
237
|
+
exact remaining time.
|
|
238
|
+
- `new_task` event mapped; class and task names cleaned; richer 4-line Bark
|
|
239
|
+
notification.
|
|
240
|
+
- Bark notification format enforced as a compact 4-line layout bounded to the
|
|
241
|
+
date line width.
|
|
242
|
+
- `run_forever` alias restored on `DaemonService`.
|
|
243
|
+
- Resolved code review issues in the daemon.
|
|
244
|
+
|
|
245
|
+
### Changed
|
|
246
|
+
- Deduplicated task URL parsing (`parse_task_url`), task classification logic
|
|
247
|
+
across main/client/formatters, snapshot IO and diffing in the daemon package,
|
|
248
|
+
and submission/completion/classification logic in `filters.py`; the stealth
|
|
249
|
+
crawler now reuses `client.get_submissions`.
|
|
250
|
+
- `.worktrees/` ignored; `course_aliases.json` ignore scoped to root level.
|
|
251
|
+
|
|
252
|
+
## [0.2.2] - 2026-06-13
|
|
253
|
+
|
|
254
|
+
### Added
|
|
255
|
+
- Auth health check and silent re-login on an expired cookie.
|
|
256
|
+
- `load_creds()` for external credential files.
|
|
257
|
+
|
|
258
|
+
### Fixed
|
|
259
|
+
- Cache namespaced by email hash; `list_tasks` crawl optimized.
|
|
260
|
+
- `submit_file` task ID resolution optimized.
|
|
261
|
+
- Cache no longer sleeps on cache hits inside crawler loops.
|
|
262
|
+
- Hub jitter adjusted to 1–3s for reads and removed from mutations.
|
|
263
|
+
|
|
264
|
+
## [0.2.0] - 2026-04-30
|
|
265
|
+
|
|
266
|
+
### Added
|
|
267
|
+
- Stealth daemon with active windows and index-only diffing.
|
|
268
|
+
- Daemon active hours and randomized polling interval.
|
|
269
|
+
- `channel_send` delivery mode.
|
|
270
|
+
- Remember-me login support (30-day sessions by default).
|
|
271
|
+
- Referer header and random jitter between requests.
|
|
272
|
+
|
|
273
|
+
### Fixed
|
|
274
|
+
- Pagination fixed; retry with backoff and grade frequency counting added.
|
|
275
|
+
|
|
276
|
+
## [0.1.0] - 2026-04-29
|
|
277
|
+
|
|
278
|
+
### Added
|
|
279
|
+
- Initial release: `mb` CLI, `ManageBacClient` SDK, disk-based response cache
|
|
280
|
+
with configurable TTL.
|
|
281
|
+
- Status and grade filters, auto-aggregated grades, task grade parsing.
|
|
282
|
+
- Pretty-printed task listing table with CJK/double-width alignment padding.
|
|
283
|
+
- `mb list --tag / -t` filtering and tag filtering in the MCP `list_tasks` tool.
|
|
284
|
+
- `mb list --deleted` to include tasks deleted from the server; instant
|
|
285
|
+
snapshot caching; grade-status rendering (Complete / Incomplete) and a
|
|
286
|
+
combined letter + score grade column.
|
|
287
|
+
- Pretty table formatter for `count-grade-freq`; attachment downloads;
|
|
288
|
+
student name captured and updated on dashboard loads.
|
|
289
|
+
- Legal disclaimer regarding the Faria/ManageBac terms of service.
|
|
290
|
+
- Project renamed from `mb-crawler` to `mb-cli` (command: `mb`).
|
|
291
|
+
|
|
292
|
+
[Unreleased]: https://github.com/allen/mb-crawler/compare/v0.3.0...HEAD
|
|
293
|
+
[0.3.0]: https://github.com/allen/mb-crawler/compare/v0.2.5...v0.3.0
|
|
294
|
+
[0.2.5]: https://github.com/allen/mb-crawler/compare/v0.2.4...v0.2.5
|
|
295
|
+
[0.2.4]: https://github.com/allen/mb-crawler/compare/v0.2.3...v0.2.4
|
|
296
|
+
[0.2.3]: https://github.com/allen/mb-crawler/compare/v0.2.2...v0.2.3
|
|
297
|
+
[0.2.2]: https://github.com/allen/mb-crawler/compare/v0.2.0...v0.2.2
|
|
298
|
+
[0.2.0]: https://github.com/allen/mb-crawler/compare/v0.1.0...v0.2.0
|
|
299
|
+
[0.1.0]: https://github.com/allen/mb-crawler/releases/tag/v0.1.0
|
tahuti-0.4.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Allen Sun
|
|
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.
|