studylife-cli 1.9.0__tar.gz → 1.9.2__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 (32) hide show
  1. {studylife_cli-1.9.0 → studylife_cli-1.9.2}/.github/workflows/ci.yml +18 -0
  2. studylife_cli-1.9.2/.github/workflows/dependabot-auto-merge.yml +77 -0
  3. studylife_cli-1.9.2/.github/workflows/dependabot-lockfiles.yml +80 -0
  4. {studylife_cli-1.9.0 → studylife_cli-1.9.2}/.gitignore +5 -0
  5. {studylife_cli-1.9.0 → studylife_cli-1.9.2}/CHANGELOG.md +14 -0
  6. {studylife_cli-1.9.0 → studylife_cli-1.9.2}/PKG-INFO +6 -1
  7. {studylife_cli-1.9.0 → studylife_cli-1.9.2}/README.md +5 -0
  8. studylife_cli-1.9.2/fuzz/fuzz_models_and_callback.py +68 -0
  9. {studylife_cli-1.9.0 → studylife_cli-1.9.2}/pyproject.toml +6 -0
  10. {studylife_cli-1.9.0 → studylife_cli-1.9.2}/src/studylife_cli/_version.py +2 -2
  11. {studylife_cli-1.9.0 → studylife_cli-1.9.2}/src/studylife_cli/login.py +6 -2
  12. {studylife_cli-1.9.0 → studylife_cli-1.9.2}/uv.lock +15 -1
  13. studylife_cli-1.9.0/.github/workflows/dependabot-auto-merge.yml +0 -33
  14. {studylife_cli-1.9.0 → studylife_cli-1.9.2}/.github/ISSUE_TEMPLATE/bug_report.yml +0 -0
  15. {studylife_cli-1.9.0 → studylife_cli-1.9.2}/.github/ISSUE_TEMPLATE/config.yml +0 -0
  16. {studylife_cli-1.9.0 → studylife_cli-1.9.2}/.github/ISSUE_TEMPLATE/feature_request.yml +0 -0
  17. {studylife_cli-1.9.0 → studylife_cli-1.9.2}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  18. {studylife_cli-1.9.0 → studylife_cli-1.9.2}/.github/dependabot.yml +0 -0
  19. {studylife_cli-1.9.0 → studylife_cli-1.9.2}/.github/workflows/scorecard.yml +0 -0
  20. {studylife_cli-1.9.0 → studylife_cli-1.9.2}/.releaserc.json +0 -0
  21. {studylife_cli-1.9.0 → studylife_cli-1.9.2}/LICENSE +0 -0
  22. {studylife_cli-1.9.0 → studylife_cli-1.9.2}/SECURITY.md +0 -0
  23. {studylife_cli-1.9.0 → studylife_cli-1.9.2}/src/studylife_cli/__init__.py +0 -0
  24. {studylife_cli-1.9.0 → studylife_cli-1.9.2}/src/studylife_cli/cli.py +0 -0
  25. {studylife_cli-1.9.0 → studylife_cli-1.9.2}/src/studylife_cli/client.py +0 -0
  26. {studylife_cli-1.9.0 → studylife_cli-1.9.2}/src/studylife_cli/credentials.py +0 -0
  27. {studylife_cli-1.9.0 → studylife_cli-1.9.2}/src/studylife_cli/models.py +0 -0
  28. {studylife_cli-1.9.0 → studylife_cli-1.9.2}/src/studylife_cli/tui.py +0 -0
  29. {studylife_cli-1.9.0 → studylife_cli-1.9.2}/tests/test_cli.py +0 -0
  30. {studylife_cli-1.9.0 → studylife_cli-1.9.2}/tests/test_client.py +0 -0
  31. {studylife_cli-1.9.0 → studylife_cli-1.9.2}/tests/test_login.py +0 -0
  32. {studylife_cli-1.9.0 → studylife_cli-1.9.2}/tests/test_tui.py +0 -0
@@ -10,6 +10,11 @@ concurrency:
10
10
  group: ci-${{ github.event.pull_request.number || github.ref }}
11
11
  cancel-in-progress: ${{ github.event_name == 'pull_request' }}
12
12
 
13
+ # Least-privilege default for GITHUB_TOKEN (CodeQL actions/missing-workflow-permissions):
14
+ # jobs that need more declare it themselves.
15
+ permissions:
16
+ contents: read
17
+
13
18
  jobs:
14
19
  lint:
15
20
  runs-on: ubuntu-latest
@@ -41,6 +46,19 @@ jobs:
41
46
  # (unlike studylife-webhooks/studylife-developers): this is a CLI, it
42
47
  # never runs as a server, so there's nothing to containerize.
43
48
  # ---------------------------------------------------------------------
49
+ # Coverage-guided fuzzing of the API models and the login callback parser (fuzz/): a short
50
+ # fixed budget per run, enough to catch a crash on the next code change.
51
+ fuzz:
52
+ runs-on: ubuntu-latest
53
+ steps:
54
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
55
+ - uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v10.0.1
56
+ with:
57
+ version: "0.5.8"
58
+ - run: uv python install 3.12
59
+ - run: uv sync --frozen --group fuzz
60
+ - run: uv run python fuzz/fuzz_models_and_callback.py -max_total_time=30 -rss_limit_mb=1024
61
+
44
62
  get-version:
45
63
  needs: [lint, test]
46
64
  if: (github.event_name == 'push' || github.event_name == 'workflow_dispatch')
@@ -0,0 +1,77 @@
1
+ name: Dependabot auto-merge
2
+
3
+ # Merges Dependabot patch and minor updates once every required check is green; majors stay
4
+ # manual. Runs on pull_request_target so the base branch's workflow definition and the repo
5
+ # secrets apply (a Dependabot-triggered pull_request run gets neither). Merges with
6
+ # AUTOMERGE_TOKEN, a fine-grained PAT (contents + pull requests: write): a merge performed with
7
+ # GITHUB_TOKEN would never start the release workflow, because GitHub does not run workflows for
8
+ # events caused by GITHUB_TOKEN. Deliberately no checkout - PR code is never executed here.
9
+ on:
10
+ pull_request_target:
11
+ # auto_merge_enabled: a PR that is already behind when someone arms auto-merge would
12
+ # otherwise wait for the next push to be brought up to date.
13
+ types: [opened, synchronize, reopened, auto_merge_enabled]
14
+ # Safety net for a PR armed while behind when nothing else is pushed for a while.
15
+ schedule:
16
+ - cron: "23 * * * *"
17
+ # The default branch requires pull requests to be up to date before they merge (ruleset).
18
+ # Every push to it therefore leaves the open Dependabot PRs "behind"; the update-stale job
19
+ # below brings them back onto the tip so their auto-merge can proceed without a manual rebase.
20
+ push:
21
+ branches: [main]
22
+
23
+ # GITHUB_TOKEN only reads PR metadata; the merge itself runs with AUTOMERGE_TOKEN (above).
24
+ permissions:
25
+ pull-requests: read
26
+
27
+ jobs:
28
+ auto-merge:
29
+ if: github.event_name == 'pull_request_target' && github.event.action != 'auto_merge_enabled' && github.actor == 'dependabot[bot]'
30
+ runs-on: ubuntu-latest
31
+ env:
32
+ HAS_TOKEN: ${{ secrets.AUTOMERGE_TOKEN != '' }}
33
+ steps:
34
+ - id: meta
35
+ uses: dependabot/fetch-metadata@21025c705c08248db411dc16f3619e6b5f9ea21a # v2.5.0
36
+ with:
37
+ github-token: ${{ secrets.GITHUB_TOKEN }}
38
+ - if: env.HAS_TOKEN == 'true' && (steps.meta.outputs.update-type == 'version-update:semver-patch' || steps.meta.outputs.update-type == 'version-update:semver-minor')
39
+ env:
40
+ GH_TOKEN: ${{ secrets.AUTOMERGE_TOKEN }}
41
+ run: gh pr merge --auto --merge "${{ github.event.pull_request.html_url }}"
42
+ - if: env.HAS_TOKEN != 'true'
43
+ run: echo "AUTOMERGE_TOKEN is not set - leaving this PR for a manual merge"
44
+
45
+ update-stale:
46
+ if: github.event_name == 'push' || github.event_name == 'schedule' || github.event.action == 'auto_merge_enabled'
47
+ runs-on: ubuntu-latest
48
+ env:
49
+ HAS_TOKEN: ${{ secrets.AUTOMERGE_TOKEN != '' }}
50
+ steps:
51
+ - if: env.HAS_TOKEN == 'true'
52
+ env:
53
+ GH_TOKEN: ${{ secrets.AUTOMERGE_TOKEN }}
54
+ REPO: ${{ github.repository }}
55
+ # The default branch requires PRs to be up to date before they merge, so every push
56
+ # here leaves the other open PRs "behind". This brings them back onto the tip: every
57
+ # non-draft PR that has auto-merge enabled or was opened by a person (a Dependabot PR
58
+ # left for a manual decision, i.e. a major bump, is not rebuilt on every push - whoever
59
+ # merges it updates it). GitHub computes the merge state lazily after a push, so UNKNOWN
60
+ # is polled for a while; DIRTY (conflicts) cannot be updated automatically.
61
+ run: |
62
+ for n in $(gh pr list --repo "$REPO" --state open --json number --jq '.[].number'); do
63
+ info="$(gh pr view "$n" --repo "$REPO" --json isDraft,author,autoMergeRequest --jq '"\(.isDraft) \(.author.login) \(.autoMergeRequest != null)"')"
64
+ set -- $info
65
+ draft=$1; author=$2; auto=$3
66
+ [ "$draft" = "true" ] && continue
67
+ if [ "$auto" != "true" ] && [ "$author" = "app/dependabot" ]; then continue; fi
68
+ for attempt in 1 2 3 4 5 6; do
69
+ state="$(gh pr view "$n" --repo "$REPO" --json mergeStateStatus --jq .mergeStateStatus)"
70
+ [ "$state" != "UNKNOWN" ] && break
71
+ sleep 10
72
+ done
73
+ echo "#$n ($author, auto-merge=$auto): $state"
74
+ if [ "$state" = "BEHIND" ]; then
75
+ gh api -X PUT "repos/$REPO/pulls/$n/update-branch" >/dev/null && echo " updated onto main" || echo "::warning::could not update #$n"
76
+ fi
77
+ done
@@ -0,0 +1,80 @@
1
+ name: Dependabot lock files
2
+
3
+ # Dependabot regenerates the lock file of the project whose manifest it bumped, but not the
4
+ # lock files of the projects that depend on it (NuGet records a reference's dependencies in
5
+ # the referencing project's packages.lock.json) - and its uv lock output has shipped unparsable
6
+ # files. Locked-mode restores then fail on the PR. This job runs on Dependabot's own PRs,
7
+ # regenerates every lock file with the same tool versions CI uses and pushes the result back
8
+ # onto the PR branch, after which the normal CI run and the auto-merge proceed.
9
+ #
10
+ # Deliberately a plain pull_request run (no pull_request_target): the PR's own restore runs in
11
+ # the untrusted context with a read-only GITHUB_TOKEN, and the only credential involved - the
12
+ # push token - is a Dependabot secret, which GitHub exposes solely to runs Dependabot triggered
13
+ # and only to the single step that pushes. Nothing from the PR ever sees it.
14
+ on:
15
+ pull_request:
16
+ types: [opened, synchronize, reopened]
17
+
18
+ permissions:
19
+ contents: read
20
+
21
+ jobs:
22
+ refresh:
23
+ if: github.actor == 'dependabot[bot]'
24
+ runs-on: ubuntu-latest
25
+ env:
26
+ HAS_TOKEN: ${{ secrets.AUTOMERGE_TOKEN != '' }}
27
+ HEAD_REF: ${{ github.head_ref }}
28
+ steps:
29
+ - if: env.HAS_TOKEN != 'true'
30
+ run: echo "AUTOMERGE_TOKEN is not set as a Dependabot secret - lock files are not refreshed automatically"
31
+ - if: env.HAS_TOKEN == 'true'
32
+ uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
33
+ with:
34
+ ref: ${{ github.head_ref }}
35
+ # Full history so the base branch's uv.lock is available locally (private repos
36
+ # cannot be fetched anonymously later, and the push token stays out of this step).
37
+ fetch-depth: 0
38
+ persist-credentials: false
39
+ - if: env.HAS_TOKEN == 'true' && hashFiles('**/packages.lock.json') != ''
40
+ uses: actions/setup-dotnet@a98b56852c35b8e3190ac28c8c2271da59106c68 # v6.0.0
41
+ with:
42
+ dotnet-version: "10.0.x"
43
+ - if: env.HAS_TOKEN == 'true' && hashFiles('**/packages.lock.json') != ''
44
+ name: Regenerate every packages.lock.json
45
+ run: |
46
+ set -eu
47
+ for lock in $(git ls-files '**/packages.lock.json' 'packages.lock.json'); do
48
+ proj="$(ls "$(dirname "$lock")"/*.csproj | head -1)"
49
+ echo "restoring $proj"
50
+ dotnet restore "$proj" --force-evaluate -p:RestoreLockedMode=false
51
+ done
52
+ - if: env.HAS_TOKEN == 'true' && hashFiles('uv.lock') != ''
53
+ uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v10.0.1
54
+ with:
55
+ version: "0.5.8"
56
+ - if: env.HAS_TOKEN == 'true' && hashFiles('uv.lock') != ''
57
+ name: Regenerate uv.lock
58
+ env:
59
+ BASE_REF: ${{ github.base_ref }}
60
+ run: |
61
+ # Dependabot's own uv.lock output has been unparsable, and `uv lock` refuses to start
62
+ # from a broken file - so resolve from the base branch's lock plus the PR's pyproject.
63
+ git show "origin/$BASE_REF:uv.lock" > uv.lock
64
+ uv python install 3.12
65
+ uv lock
66
+ - if: env.HAS_TOKEN == 'true'
67
+ name: Push the refreshed lock files back onto the PR
68
+ env:
69
+ PUSH_TOKEN: ${{ secrets.AUTOMERGE_TOKEN }}
70
+ run: |
71
+ # Only tracked files change here (the lock files); untracked build output stays out.
72
+ git add -u
73
+ if git diff --cached --quiet; then
74
+ echo "lock files already consistent"
75
+ exit 0
76
+ fi
77
+ git config user.name "lukislp"
78
+ git config user.email "koerber.lukas@t-online.de"
79
+ git commit -m "chore(deps): refresh the lock files after the Dependabot update"
80
+ git push "https://x-access-token:${PUSH_TOKEN}@github.com/${GITHUB_REPOSITORY}.git" "HEAD:refs/heads/${HEAD_REF}"
@@ -10,3 +10,8 @@ build/
10
10
  *.egg-info/
11
11
  .env
12
12
  credentials.json
13
+
14
+ # libFuzzer crash artifacts written by fuzz/ harnesses
15
+ crash-*
16
+ leak-*
17
+ timeout-*
@@ -1,3 +1,17 @@
1
+ ## [1.9.2](https://github.com/lukislp/studylife-cli/compare/v1.9.1...v1.9.2) (2026-09-12)
2
+
3
+
4
+ ### Bug Fixes
5
+
6
+ * **login:** compare the callback state as bytes so non-ASCII input cannot crash the handler ([#23](https://github.com/lukislp/studylife-cli/issues/23)) ([4b8de15](https://github.com/lukislp/studylife-cli/commit/4b8de157801ee840c7081261cdd524746792283a))
7
+
8
+ ## [1.9.1](https://github.com/lukislp/studylife-cli/compare/v1.9.0...v1.9.1) (2026-09-11)
9
+
10
+
11
+ ### Bug Fixes
12
+
13
+ * **ci:** read-only GITHUB_TOKEN in the Dependabot auto-merge workflow ([5d4eadf](https://github.com/lukislp/studylife-cli/commit/5d4eadf42f640133e46a8b27b2b2f724e9d8e829))
14
+
1
15
  # [1.9.0](https://github.com/lukislp/studylife-cli/compare/v1.8.3...v1.9.0) (2026-09-11)
2
16
 
3
17
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: studylife-cli
3
- Version: 1.9.0
3
+ Version: 1.9.2
4
4
  Summary: Command-line client for StudyLife (self-hosted study organizer) - notes, sessions, course goals, and webhooks from your terminal.
5
5
  License-File: LICENSE
6
6
  Requires-Python: >=3.12
@@ -14,6 +14,11 @@ Description-Content-Type: text/markdown
14
14
 
15
15
  # studylife-cli
16
16
 
17
+ [![CI](https://github.com/lukislp/studylife-cli/actions/workflows/ci.yml/badge.svg)](https://github.com/lukislp/studylife-cli/actions/workflows/ci.yml) [![OpenSSF Scorecard](https://api.scorecard.dev/projects/github.com/lukislp/studylife-cli/badge)](https://scorecard.dev/viewer/?uri=github.com/lukislp/studylife-cli) [![CodeQL](https://github.com/lukislp/studylife-cli/actions/workflows/github-code-scanning/codeql/badge.svg)](https://github.com/lukislp/studylife-cli/security/code-scanning)
18
+ [![Release](https://img.shields.io/github/v/release/lukislp/studylife-cli)](https://github.com/lukislp/studylife-cli/releases)
19
+ [![License: AGPL-3.0](https://img.shields.io/github/license/lukislp/studylife-cli)](LICENSE)
20
+ [![Python](https://img.shields.io/badge/Python-3.12-3776AB)](https://www.python.org/)
21
+
17
22
  A command-line client for [StudyLife](https://github.com/lukislp/studylife), the self-hosted
18
23
  study organizer. Manage notes, sessions, course goals, and webhooks from your terminal, get study
19
24
  time reports and exports, or watch a live dashboard - scripts and shells welcome.
@@ -1,5 +1,10 @@
1
1
  # studylife-cli
2
2
 
3
+ [![CI](https://github.com/lukislp/studylife-cli/actions/workflows/ci.yml/badge.svg)](https://github.com/lukislp/studylife-cli/actions/workflows/ci.yml) [![OpenSSF Scorecard](https://api.scorecard.dev/projects/github.com/lukislp/studylife-cli/badge)](https://scorecard.dev/viewer/?uri=github.com/lukislp/studylife-cli) [![CodeQL](https://github.com/lukislp/studylife-cli/actions/workflows/github-code-scanning/codeql/badge.svg)](https://github.com/lukislp/studylife-cli/security/code-scanning)
4
+ [![Release](https://img.shields.io/github/v/release/lukislp/studylife-cli)](https://github.com/lukislp/studylife-cli/releases)
5
+ [![License: AGPL-3.0](https://img.shields.io/github/license/lukislp/studylife-cli)](LICENSE)
6
+ [![Python](https://img.shields.io/badge/Python-3.12-3776AB)](https://www.python.org/)
7
+
3
8
  A command-line client for [StudyLife](https://github.com/lukislp/studylife), the self-hosted
4
9
  study organizer. Manage notes, sessions, course goals, and webhooks from your terminal, get study
5
10
  time reports and exports, or watch a live dashboard - scripts and shells welcome.
@@ -0,0 +1,68 @@
1
+ """Atheris fuzz harness for the API models and the login callback parser.
2
+
3
+ Contract under test: the StudyLife DTO models either validate a JSON body or raise pydantic's
4
+ ValidationError - never anything else; the loopback callback parser and the constant-time
5
+ state comparison never raise for any query string.
6
+
7
+ Run locally (Linux, needs the atheris wheel):
8
+ uv sync --frozen --group fuzz
9
+ uv run python fuzz/fuzz_models_and_callback.py -max_total_time=60
10
+ CI runs the same harness for a short, fixed time budget (see .github/workflows/ci.yml).
11
+ """
12
+
13
+ from __future__ import annotations
14
+
15
+ import contextlib
16
+ import sys
17
+
18
+ import atheris
19
+ from pydantic import ValidationError
20
+
21
+ from studylife_cli.credentials import Credentials
22
+ from studylife_cli.login import _parse_callback_query, _states_match
23
+ from studylife_cli.models import (
24
+ Course,
25
+ CourseGoal,
26
+ MetricsSummary,
27
+ Note,
28
+ Session,
29
+ StudyProgramDetail,
30
+ StudyProgramSummary,
31
+ TimerState,
32
+ Webhook,
33
+ Whoami,
34
+ )
35
+
36
+ MODELS = (
37
+ Course,
38
+ CourseGoal,
39
+ MetricsSummary,
40
+ Note,
41
+ Session,
42
+ StudyProgramDetail,
43
+ StudyProgramSummary,
44
+ TimerState,
45
+ Webhook,
46
+ Whoami,
47
+ Credentials,
48
+ )
49
+
50
+
51
+ def test_one_input(data: bytes) -> None:
52
+ fdp = atheris.FuzzedDataProvider(data)
53
+ query = fdp.ConsumeUnicodeNoSurrogates(256)
54
+ result = _parse_callback_query(query)
55
+ _states_match(result.state, fdp.ConsumeUnicodeNoSurrogates(64))
56
+ body = fdp.ConsumeBytes(512)
57
+ for model in MODELS:
58
+ with contextlib.suppress(ValidationError):
59
+ model.model_validate_json(body)
60
+
61
+
62
+ if __name__ == "__main__":
63
+ # instrument_all() instead of instrument_imports(): the package is loaded through uv's
64
+ # editable-install loader, which the import hook does not see (no coverage feedback,
65
+ # so libFuzzer would never grow its inputs past a few bytes).
66
+ atheris.instrument_all()
67
+ atheris.Setup(sys.argv, test_one_input)
68
+ atheris.Fuzz()
@@ -68,3 +68,9 @@ extend-immutable-calls = ["typer.Argument", "typer.Option"]
68
68
  testpaths = ["tests"]
69
69
  asyncio_mode = "auto"
70
70
  pythonpath = ["src"]
71
+
72
+ [dependency-groups]
73
+ # Fuzzing harness (fuzz/), Linux only - the atheris wheel does not exist for Windows.
74
+ fuzz = [
75
+ "atheris>=2.3",
76
+ ]
@@ -18,7 +18,7 @@ version_tuple: tuple[int | str, ...]
18
18
  commit_id: str | None
19
19
  __commit_id__: str | None
20
20
 
21
- __version__ = version = '1.9.0'
22
- __version_tuple__ = version_tuple = (1, 9, 0)
21
+ __version__ = version = '1.9.2'
22
+ __version_tuple__ = version_tuple = (1, 9, 2)
23
23
 
24
24
  __commit_id__ = commit_id = None
@@ -64,8 +64,12 @@ def _parse_callback_query(query_string: str) -> CallbackResult:
64
64
 
65
65
  def _states_match(received: str, expected: str) -> bool:
66
66
  """Constant-time comparison - the state isn't secret, but there's no reason to prefer a
67
- timing-observable comparison over a safe one that's just as easy to write."""
68
- return hmac.compare_digest(received, expected)
67
+ timing-observable comparison over a safe one that's just as easy to write.
68
+
69
+ Compared as UTF-8 bytes: hmac.compare_digest raises TypeError for str arguments with
70
+ non-ASCII characters, and `received` comes straight from whoever hits the loopback
71
+ callback URL (found by fuzz/fuzz_models_and_callback.py)."""
72
+ return hmac.compare_digest(received.encode("utf-8"), expected.encode("utf-8"))
69
73
 
70
74
 
71
75
  _CALLBACK_PAGE = """<!doctype html>
@@ -32,6 +32,16 @@ wheels = [
32
32
  { url = "https://files.pythonhosted.org/packages/da/35/f2287558c17e29fafc8ef3daf819bb9834061cfa43bff8014f7df7f63bdc/anyio-4.14.2-py3-none-any.whl", hash = "sha256:9f505dda5ac9f0c8309b5e8bd445a8c2bf7246f3ce950121e45ea15bc41d1494", size = 125813 },
33
33
  ]
34
34
 
35
+ [[package]]
36
+ name = "atheris"
37
+ version = "3.1.0"
38
+ source = { registry = "https://pypi.org/simple" }
39
+ wheels = [
40
+ { url = "https://files.pythonhosted.org/packages/0a/88/fd6ad595dafa9c7ce56dbfcaff0c7244988dac3af86c771166c6516ccf6b/atheris-3.1.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ec5e11f21a4c197fe91f7aea2b2de88e623c73a21fc07b105ac6329a1588457b", size = 36875908 },
41
+ { url = "https://files.pythonhosted.org/packages/4e/18/e19718c384fd7d801d0da7485407daef9af6194b6d8c8818175bec5efec6/atheris-3.1.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:f8a9f51ce8369026e8eb7b7174835e8c4c85a1a6db5d9add36c15100779d2a39", size = 36800563 },
42
+ { url = "https://files.pythonhosted.org/packages/c3/ff/ae7a5bfe99033e510bea4ed09934e636d93777317a48147369bc0dc2b71f/atheris-3.1.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:315a0b5c819852b1ffe1ca72efc389c7724881f2c33e4aacb8c6bcec49bd5011", size = 36772569 },
43
+ ]
44
+
35
45
  [[package]]
36
46
  name = "certifi"
37
47
  version = "2026.7.22"
@@ -395,7 +405,7 @@ wheels = [
395
405
 
396
406
  [[package]]
397
407
  name = "studylife-cli"
398
- version = "1.6.1.dev0+g47a9ecdea.d20260831"
408
+ version = "1.9.2.dev9+g1002804fa.d20260912"
399
409
  source = { editable = "." }
400
410
  dependencies = [
401
411
  { name = "httpx" },
@@ -413,6 +423,9 @@ dev = [
413
423
  { name = "respx" },
414
424
  { name = "ruff" },
415
425
  ]
426
+ fuzz = [
427
+ { name = "atheris" },
428
+ ]
416
429
 
417
430
  [package.metadata]
418
431
  requires-dist = [
@@ -431,6 +444,7 @@ dev = [
431
444
  { name = "respx", specifier = ">=0.21" },
432
445
  { name = "ruff", specifier = ">=0.7" },
433
446
  ]
447
+ fuzz = [{ name = "atheris", specifier = ">=2.3" }]
434
448
 
435
449
  [[package]]
436
450
  name = "textual"
@@ -1,33 +0,0 @@
1
- name: Dependabot auto-merge
2
-
3
- # Merges Dependabot patch and minor updates once every required check is green; majors stay
4
- # manual. Runs on pull_request_target so the base branch's workflow definition and the repo
5
- # secrets apply (a Dependabot-triggered pull_request run gets neither). Merges with
6
- # AUTOMERGE_TOKEN, a fine-grained PAT (contents + pull requests: write): a merge performed with
7
- # GITHUB_TOKEN would never start the release workflow, because GitHub does not run workflows for
8
- # events caused by GITHUB_TOKEN. Deliberately no checkout - PR code is never executed here.
9
- on:
10
- pull_request_target:
11
- types: [opened, synchronize, reopened]
12
-
13
- permissions:
14
- contents: write
15
- pull-requests: write
16
-
17
- jobs:
18
- auto-merge:
19
- if: github.actor == 'dependabot[bot]'
20
- runs-on: ubuntu-latest
21
- env:
22
- HAS_TOKEN: ${{ secrets.AUTOMERGE_TOKEN != '' }}
23
- steps:
24
- - id: meta
25
- uses: dependabot/fetch-metadata@21025c705c08248db411dc16f3619e6b5f9ea21a # v2.5.0
26
- with:
27
- github-token: ${{ secrets.GITHUB_TOKEN }}
28
- - if: env.HAS_TOKEN == 'true' && (steps.meta.outputs.update-type == 'version-update:semver-patch' || steps.meta.outputs.update-type == 'version-update:semver-minor')
29
- env:
30
- GH_TOKEN: ${{ secrets.AUTOMERGE_TOKEN }}
31
- run: gh pr merge --auto --merge "${{ github.event.pull_request.html_url }}"
32
- - if: env.HAS_TOKEN != 'true'
33
- run: echo "AUTOMERGE_TOKEN is not set - leaving this PR for a manual merge"
File without changes
File without changes