leetvault 0.1.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.
- leetvault-0.1.0/.github/workflows/ci.yml +41 -0
- leetvault-0.1.0/.github/workflows/publish.yml +58 -0
- leetvault-0.1.0/.gitignore +17 -0
- leetvault-0.1.0/CHANGELOG.md +23 -0
- leetvault-0.1.0/CLAUDE.md +62 -0
- leetvault-0.1.0/CONTRIBUTING.md +45 -0
- leetvault-0.1.0/LICENSE +21 -0
- leetvault-0.1.0/PKG-INFO +120 -0
- leetvault-0.1.0/PLAN.md +307 -0
- leetvault-0.1.0/README.md +81 -0
- leetvault-0.1.0/docs/ARCHITECTURE.md +83 -0
- leetvault-0.1.0/docs/DEVELOPER.md +83 -0
- leetvault-0.1.0/docs/FAQ.md +50 -0
- leetvault-0.1.0/docs/TROUBLESHOOTING.md +71 -0
- leetvault-0.1.0/leetvault_kickoff.md +19 -0
- leetvault-0.1.0/pyproject.toml +78 -0
- leetvault-0.1.0/src/leetvault/__init__.py +3 -0
- leetvault-0.1.0/src/leetvault/auth.py +174 -0
- leetvault-0.1.0/src/leetvault/cli.py +99 -0
- leetvault-0.1.0/src/leetvault/client.py +352 -0
- leetvault-0.1.0/src/leetvault/config.py +122 -0
- leetvault-0.1.0/src/leetvault/db.py +67 -0
- leetvault-0.1.0/src/leetvault/git_writer.py +206 -0
- leetvault-0.1.0/src/leetvault/models.py +93 -0
- leetvault-0.1.0/src/leetvault/py.typed +0 -0
- leetvault-0.1.0/src/leetvault/readme.py +205 -0
- leetvault-0.1.0/src/leetvault/sync.py +390 -0
- leetvault-0.1.0/src/leetvault/templates/README.md.j2 +48 -0
- leetvault-0.1.0/src/leetvault/watch.py +104 -0
- leetvault-0.1.0/tests/__init__.py +0 -0
- leetvault-0.1.0/tests/conftest.py +44 -0
- leetvault-0.1.0/tests/test_auth.py +186 -0
- leetvault-0.1.0/tests/test_cli.py +23 -0
- leetvault-0.1.0/tests/test_client.py +206 -0
- leetvault-0.1.0/tests/test_config.py +51 -0
- leetvault-0.1.0/tests/test_db.py +43 -0
- leetvault-0.1.0/tests/test_git_writer.py +184 -0
- leetvault-0.1.0/tests/test_models.py +53 -0
- leetvault-0.1.0/tests/test_readme.py +147 -0
- leetvault-0.1.0/tests/test_sync.py +398 -0
- leetvault-0.1.0/tests/test_watch.py +107 -0
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
workflow_dispatch:
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
test:
|
|
11
|
+
strategy:
|
|
12
|
+
fail-fast: false
|
|
13
|
+
matrix:
|
|
14
|
+
os: [ubuntu-latest, windows-latest, macos-latest]
|
|
15
|
+
python-version: ["3.11", "3.12", "3.13"]
|
|
16
|
+
runs-on: ${{ matrix.os }}
|
|
17
|
+
steps:
|
|
18
|
+
- uses: actions/checkout@v4
|
|
19
|
+
- uses: actions/setup-python@v5
|
|
20
|
+
with:
|
|
21
|
+
python-version: ${{ matrix.python-version }}
|
|
22
|
+
- name: Install
|
|
23
|
+
run: pip install -e ".[dev]"
|
|
24
|
+
- name: Test
|
|
25
|
+
run: pytest
|
|
26
|
+
|
|
27
|
+
lint:
|
|
28
|
+
runs-on: ubuntu-latest
|
|
29
|
+
steps:
|
|
30
|
+
- uses: actions/checkout@v4
|
|
31
|
+
- uses: actions/setup-python@v5
|
|
32
|
+
with:
|
|
33
|
+
python-version: "3.12"
|
|
34
|
+
- name: Install
|
|
35
|
+
run: pip install -e ".[dev]"
|
|
36
|
+
- name: Ruff check
|
|
37
|
+
run: ruff check .
|
|
38
|
+
- name: Ruff format check
|
|
39
|
+
run: ruff format --check .
|
|
40
|
+
- name: Mypy strict
|
|
41
|
+
run: mypy --strict
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
name: Publish
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags:
|
|
6
|
+
- "v*"
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
build:
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
steps:
|
|
12
|
+
- uses: actions/checkout@v4
|
|
13
|
+
- uses: actions/setup-python@v5
|
|
14
|
+
with:
|
|
15
|
+
python-version: "3.12"
|
|
16
|
+
- name: Install build
|
|
17
|
+
run: pip install build
|
|
18
|
+
- name: Build sdist + wheel
|
|
19
|
+
run: python -m build
|
|
20
|
+
- uses: actions/upload-artifact@v4
|
|
21
|
+
with:
|
|
22
|
+
name: dist
|
|
23
|
+
path: dist/
|
|
24
|
+
|
|
25
|
+
# Dry-run: every tag push publishes to TestPyPI first. Real PyPI only follows if this
|
|
26
|
+
# succeeds, so a broken release never reaches real users.
|
|
27
|
+
publish-testpypi:
|
|
28
|
+
needs: build
|
|
29
|
+
runs-on: ubuntu-latest
|
|
30
|
+
environment:
|
|
31
|
+
name: testpypi
|
|
32
|
+
url: https://test.pypi.org/p/leetvault
|
|
33
|
+
permissions:
|
|
34
|
+
id-token: write
|
|
35
|
+
steps:
|
|
36
|
+
- uses: actions/download-artifact@v4
|
|
37
|
+
with:
|
|
38
|
+
name: dist
|
|
39
|
+
path: dist/
|
|
40
|
+
- uses: pypa/gh-action-pypi-publish@release/v1
|
|
41
|
+
with:
|
|
42
|
+
repository-url: https://test.pypi.org/legacy/
|
|
43
|
+
skip-existing: true
|
|
44
|
+
|
|
45
|
+
publish-pypi:
|
|
46
|
+
needs: publish-testpypi
|
|
47
|
+
runs-on: ubuntu-latest
|
|
48
|
+
environment:
|
|
49
|
+
name: pypi
|
|
50
|
+
url: https://pypi.org/p/leetvault
|
|
51
|
+
permissions:
|
|
52
|
+
id-token: write
|
|
53
|
+
steps:
|
|
54
|
+
- uses: actions/download-artifact@v4
|
|
55
|
+
with:
|
|
56
|
+
name: dist
|
|
57
|
+
path: dist/
|
|
58
|
+
- uses: pypa/gh-action-pypi-publish@release/v1
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project are documented here. Format follows
|
|
4
|
+
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
|
|
5
|
+
|
|
6
|
+
## [Unreleased]
|
|
7
|
+
|
|
8
|
+
## [0.1.0] - 2026-07-11
|
|
9
|
+
|
|
10
|
+
Initial release.
|
|
11
|
+
|
|
12
|
+
- `login`/`logout`/`status`: keyring-backed LeetCode session storage, live session validation,
|
|
13
|
+
JWT expiry decoding, optional GitHub PAT storage.
|
|
14
|
+
- `import`/`sync`: resumable full-history import and incremental sync of accepted submissions,
|
|
15
|
+
same-day dedup (`--keep-all` to disable), REST + GraphQL enrichment.
|
|
16
|
+
- Git layer: batched commit + transient-PAT push per run, never persisted to `.git/config`.
|
|
17
|
+
- Auto-generated README dashboard: progress, difficulty/language/topic breakdowns, streaks,
|
|
18
|
+
recent solves, full searchable solutions table.
|
|
19
|
+
- `watch`: polling loop with graceful shutdown and session-expiry warnings.
|
|
20
|
+
- `config`: get/set persistent settings.
|
|
21
|
+
|
|
22
|
+
[Unreleased]: https://github.com/priyadip/LeetVault/compare/v0.1.0...HEAD
|
|
23
|
+
[0.1.0]: https://github.com/priyadip/LeetVault/releases/tag/v0.1.0
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# CLAUDE.md — leetvault
|
|
2
|
+
|
|
3
|
+
Project memory for Claude Code (auto-loaded each session). This file is authoritative. Keep the **Phase checklist** at the bottom updated as work completes.
|
|
4
|
+
|
|
5
|
+
## What this is
|
|
6
|
+
`leetvault` — a cross-platform (Windows/Linux/macOS) Python CLI that mirrors *my* LeetCode account (accepted submissions + source code + metadata) into a normalized SQLite DB and a GitHub repo with an auto-generated README dashboard. Sync is **account-based** via my authenticated session — never browser-dependent. My LeetCode account is the single source of truth. Free/OSS only; the only external services are LeetCode and GitHub.
|
|
7
|
+
|
|
8
|
+
## v1 scope
|
|
9
|
+
Commands: `login`, `import` (full history), `sync` (incremental), `watch` (polling), `status`, `logout`, `config`. Plus SQLite persistence, GitHub commit/push, auto-generated README, PyPI packaging + CI.
|
|
10
|
+
**NOT in v1** (leave `# FUTURE:` notes only, build nothing): browser extension, AI/weak-topic dashboard, standalone binaries, deep contest analytics.
|
|
11
|
+
|
|
12
|
+
## Ground-truth API facts — do NOT re-derive; verify exact field shapes live in Phase 2
|
|
13
|
+
LeetCode has no official API — every endpoint is reverse-engineered. Isolate all LeetCode access behind one client module.
|
|
14
|
+
|
|
15
|
+
**Auth**
|
|
16
|
+
- Cookies `LEETCODE_SESSION` (a JWT) + `csrftoken`; I paste them at `login`. No password flow.
|
|
17
|
+
- Every authed request sends: both cookies + header `x-csrftoken: <csrftoken>` + `Referer: https://leetcode.com`.
|
|
18
|
+
- Decode the JWT `exp` for `status` + expiry warnings; don't hardcode a lifetime (varies ~1 wk to several wks).
|
|
19
|
+
- Store secrets (both cookies + GitHub PAT) via **`keyring`**, service name `leetvault`. Never in `.env`, `.git/config`, logs, or commits.
|
|
20
|
+
|
|
21
|
+
**Fetching**
|
|
22
|
+
- Full history → REST `https://leetcode.com/api/submissions/?offset=0&limit=20` (20/page; includes `code`, `id`, `question_id`, `title`, `title_slug`, `status_display`, `lang`, `runtime`, `memory`, `timestamp`, `url`; top-level `has_next`/`last_key`). Prefer for bulk — code + metadata in one pass.
|
|
23
|
+
- Enrichment/fallback → GraphQL `https://leetcode.com/graphql`: `submissionList(offset,limit,questionSlug)` + `submissionDetails(submissionId)` (code, runtimePercentile, memoryPercentile). One extra call per submission — use only when REST `code` is missing or percentiles are needed.
|
|
24
|
+
- Incremental/watch → `recentAcSubmissions` / `recentSubmissionList` (capped at 20; detection only, useless for history).
|
|
25
|
+
|
|
26
|
+
**Rate limits (empirical, not published — design defensively)**
|
|
27
|
+
- ~480 sequential requests → HTTP 403. Stay under 20 req / 10 s.
|
|
28
|
+
- Bulk import: 300–500 ms between pages; exponential backoff 1→3→9→27 s on 403/429; honor `Retry-After`.
|
|
29
|
+
- **Import MUST be resumable** — persist last offset / submission id after every page.
|
|
30
|
+
- GitHub write API has a **separate** quota — batch/space commits; never one push per submission.
|
|
31
|
+
- `--site com|cn` (default `com`); for `cn` swap base URL + stricter limiter (~1 req/10 s).
|
|
32
|
+
|
|
33
|
+
**Honest limits (state in docs, never fake)**
|
|
34
|
+
- `watch` = polling (60–120 s), not real-time (no LeetCode webhook/streaming exists).
|
|
35
|
+
- Cloudflare may challenge HTTP clients — fail gracefully; `# FUTURE:` optional Playwright real-browser fetcher.
|
|
36
|
+
- Stored-cookie automation may breach LeetCode ToS — neutral README disclaimer.
|
|
37
|
+
|
|
38
|
+
## Stack (exact)
|
|
39
|
+
Typer (+`rich`) · `httpx` + **`httpx-retries`** (NOT `httpx-retry`) + custom rate limiter · SQLAlchemy 2.0 declarative (`DeclarativeBase`/`Mapped`/`mapped_column`/`select`) on SQLite · Jinja2 (`PackageLoader`) · GitPython (PAT injected transiently at push time, never in remote URL / `.git/config`; fine-grained PAT scoped to one repo, Contents: write) · `keyring` · `pytest`+cov / `ruff` / `mypy --strict` · **hatchling** + `src/` layout + entry point `leetvault = "leetvault.cli:app"` · **PyPI Trusted Publishing (OIDC)**. All config in `pyproject.toml`.
|
|
40
|
+
|
|
41
|
+
## Data model (SQLite)
|
|
42
|
+
`problems`(question_id PK, frontend_id, title, title_slug, difficulty, paid_only, url) · `submissions`(submission_id PK, question_id FK, lang, status, runtime, memory, runtime_percentile, memory_percentile, timestamp, code_hash, is_accepted) · `submission_code`(submission_id PK/FK, code) · `topics` + `problem_topics` (M2M) · `sync_state`(id, site, last_offset, last_submission_id, last_synced_timestamp, last_full_import_completed_at).
|
|
43
|
+
History: store every accepted submission. Disk layout per problem: `Problems/<slug>/{latest.<ext>, history/submission_<id>.<ext>, notes.md, metadata.json}`. Default dedup = same problem within same day (86400 s); `--keep-all` opts out.
|
|
44
|
+
|
|
45
|
+
## Working protocol
|
|
46
|
+
- **Run continuously.** Execute Phases 0 → 7 end-to-end without pausing for my approval between phases. Never ask "should I proceed / continue / go ahead?" — just proceed. Don't stop per-file.
|
|
47
|
+
- Per phase, loop: state what/why in 1–2 lines → build (full type hints, DI, SOLID, clean architecture) → tests green → `ruff check` + `ruff format --check` + `mypy --strict` clean → Conventional Commit → one-line progress note → immediately start the next phase.
|
|
48
|
+
- **Only stop to ask me when you need something ONLY I can provide.** In v1 that is exactly two moments: (1) **Phase 2** — my `LEETCODE_SESSION` + `csrftoken` (I paste them into the `login` flow); (2) **Phase 4** — a fine-grained GitHub PAT + target repo URL (for push). Ask once at each, then continue autonomously. Also stop before any irreversible action *outside this repo* (force-push, history rewrite, deleting files you didn't create).
|
|
49
|
+
- **Decide, don't ask, for everything else.** For small choices (naming, layout, library minutiae, config defaults) pick the sensible option, record it in `PLAN.md`, and keep moving. Surface a decision only if it's a true one-way door.
|
|
50
|
+
- **Verify, don't assume:** confirm real REST + `submissionDetails` fields live in Phase 2 before locking schema; adapt to reality; note any divergence in `PLAN.md`.
|
|
51
|
+
- **Never fake functionality** — no stubbed "pretend it worked," no fabricated data, no swallowed errors. If genuinely blocked, say so and propose a real fallback rather than faking or silently skipping.
|
|
52
|
+
- Commit after every phase so all work is recoverable via git. Never print or commit secrets. Keep `PLAN.md` + `docs/ARCHITECTURE.md` current as you go.
|
|
53
|
+
|
|
54
|
+
## Phase checklist (update as completed)
|
|
55
|
+
- [x] **0 Scaffold** — `src/` layout, `pyproject.toml` (hatchling + deps + entry + ruff/mypy/pytest), command stubs, CI skeleton, `PLAN.md`, `docs/`. ✔ `pip install -e .` + `leetvault --help` lists all commands.
|
|
56
|
+
- [x] **1 Data layer** — models, engine/session factory, `create_all`, `SyncState` helpers, unit tests + temp-DB round-trip.
|
|
57
|
+
- [x] **2 Auth + client** — keyring store, httpx client (headers/timeouts/retries/limiter), JWT `exp`, session validation; `login`/`logout`/`status`. Verify live API. ✔ real-account `status` reports validity/expiry.
|
|
58
|
+
- [x] **3 Sync engine** — `import` (resumable, throttled, backoff) + `sync` (incremental, stop at last-known id); dedup; DB + `submission_code` + disk files; progress bars; mocked tests + opt-in live smoke. ✔ full import correct + re-run does no redundant work.
|
|
59
|
+
- [x] **4 Git** — layout writer, commit-template rendering, transient-PAT batched push. ✔ real push with correct messages, no dupes on re-run.
|
|
60
|
+
- [x] **5 README** — Jinja2 templates + stats aggregation (progress, difficulty, topics, langs, streaks, recent, searchable table w/ links, % bars), regenerate-after-sync, aggregation-math tests.
|
|
61
|
+
- [x] **6 Watch** — polling loop (`--interval`, 60–120 s), reconcile `SyncState`, run sync→write→README→commit→push on new AC, graceful shutdown, expiry warning. ✔ picks up a fresh solve within one interval.
|
|
62
|
+
- [x] **7 Packaging/CI/docs** — finalize `pyproject.toml`; Actions (pytest matrix ubuntu/windows/macos × py3.11–3.13, `fail-fast:false`; ruff + `mypy --strict` on ubuntu; tag-triggered Trusted Publishing `id-token: write`, TestPyPI dry-run first); README + Install + Architecture + Developer + Contribution + FAQ + Troubleshooting. Workflows built and locally verified (wheel build + install, YAML validity, pytest/ruff/mypy all clean); **not yet run on GitHub's own runners** — this local repo has no GitHub remote configured for leetvault's own source (only for the separate solutions-mirror repo used in the Phase 4 live test), and PyPI Trusted Publisher setup is an account-level action only the repo owner can do. See PLAN.md Phase 7 for the exact gap and what's needed to close it.
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# Contributing
|
|
2
|
+
|
|
3
|
+
Thanks for considering a contribution to leetvault.
|
|
4
|
+
|
|
5
|
+
## Before you start
|
|
6
|
+
|
|
7
|
+
- For anything beyond a small fix, open an issue first to discuss the approach - especially
|
|
8
|
+
for anything touching `client.py` (LeetCode's API is entirely reverse-engineered; changes
|
|
9
|
+
there need live verification, see [docs/DEVELOPER.md](docs/DEVELOPER.md)).
|
|
10
|
+
- This project is free/open-source and intentionally scoped to LeetCode + GitHub as its only
|
|
11
|
+
external services. PRs adding new third-party services are unlikely to be accepted.
|
|
12
|
+
|
|
13
|
+
## Workflow
|
|
14
|
+
|
|
15
|
+
1. Fork and branch from `main`.
|
|
16
|
+
2. Install dev dependencies: `pip install -e ".[dev]"`.
|
|
17
|
+
3. Make your change. Match the existing style: full type hints, no comments unless they
|
|
18
|
+
explain a non-obvious *why*, no speculative abstraction.
|
|
19
|
+
4. Before opening a PR, all of these must be clean:
|
|
20
|
+
```bash
|
|
21
|
+
pytest
|
|
22
|
+
ruff check .
|
|
23
|
+
ruff format --check .
|
|
24
|
+
mypy --strict
|
|
25
|
+
```
|
|
26
|
+
5. Write a [Conventional Commit](https://www.conventionalcommits.org/) message
|
|
27
|
+
(`feat: ...`, `fix: ...`, `docs: ...`, etc.).
|
|
28
|
+
6. Open a PR against `main`. CI runs the full matrix (Ubuntu/Windows/macOS × Python 3.11-3.13)
|
|
29
|
+
plus lint/type-check on every PR.
|
|
30
|
+
|
|
31
|
+
## Reporting bugs
|
|
32
|
+
|
|
33
|
+
Open a GitHub issue with:
|
|
34
|
+
|
|
35
|
+
- your OS and Python version
|
|
36
|
+
- the command you ran and its full output (`leetvault --help` output plus the failing command)
|
|
37
|
+
- **never include your `LEETCODE_SESSION`, `csrftoken`, or GitHub PAT** in an issue - those are
|
|
38
|
+
credentials, not debug info
|
|
39
|
+
|
|
40
|
+
## Security
|
|
41
|
+
|
|
42
|
+
leetvault stores your LeetCode session cookies and GitHub PAT via the OS keyring, never in
|
|
43
|
+
plaintext files, `.env`, `.git/config`, logs, or commits. If you find a way those could leak,
|
|
44
|
+
please report it privately rather than opening a public issue - see
|
|
45
|
+
[docs/TROUBLESHOOTING.md](docs/TROUBLESHOOTING.md) for how credentials are handled.
|
leetvault-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 leetvault contributors
|
|
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.
|
leetvault-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: leetvault
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Mirror your LeetCode account (accepted submissions + source + metadata) into a normalized SQLite DB and a GitHub repo with an auto-generated README dashboard.
|
|
5
|
+
Project-URL: Homepage, https://github.com/priyadip/LeetVault
|
|
6
|
+
Project-URL: Repository, https://github.com/priyadip/LeetVault
|
|
7
|
+
Project-URL: Issues, https://github.com/priyadip/LeetVault/issues
|
|
8
|
+
Project-URL: Changelog, https://github.com/priyadip/LeetVault/blob/main/CHANGELOG.md
|
|
9
|
+
Author: leetvault
|
|
10
|
+
License: MIT
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
Keywords: backup,cli,github,leetcode,sqlite,sync
|
|
13
|
+
Classifier: Environment :: Console
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
16
|
+
Classifier: Operating System :: OS Independent
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
20
|
+
Classifier: Topic :: Software Development :: Version Control :: Git
|
|
21
|
+
Requires-Python: >=3.11
|
|
22
|
+
Requires-Dist: gitpython>=3.1
|
|
23
|
+
Requires-Dist: httpx-retries>=0.2
|
|
24
|
+
Requires-Dist: httpx>=0.27
|
|
25
|
+
Requires-Dist: jinja2>=3.1
|
|
26
|
+
Requires-Dist: keyring>=25.0
|
|
27
|
+
Requires-Dist: pyjwt>=2.8
|
|
28
|
+
Requires-Dist: rich>=13.7
|
|
29
|
+
Requires-Dist: sqlalchemy>=2.0
|
|
30
|
+
Requires-Dist: typer>=0.12
|
|
31
|
+
Provides-Extra: dev
|
|
32
|
+
Requires-Dist: mypy>=1.11; extra == 'dev'
|
|
33
|
+
Requires-Dist: pytest-cov>=5.0; extra == 'dev'
|
|
34
|
+
Requires-Dist: pytest-mock>=3.14; extra == 'dev'
|
|
35
|
+
Requires-Dist: pytest>=8.0; extra == 'dev'
|
|
36
|
+
Requires-Dist: respx>=0.21; extra == 'dev'
|
|
37
|
+
Requires-Dist: ruff>=0.6; extra == 'dev'
|
|
38
|
+
Description-Content-Type: text/markdown
|
|
39
|
+
|
|
40
|
+
# leetvault
|
|
41
|
+
|
|
42
|
+
Mirror your LeetCode account (accepted submissions + source code + metadata) into a normalized
|
|
43
|
+
SQLite database and a GitHub repository with an auto-generated README dashboard.
|
|
44
|
+
|
|
45
|
+
Sync is **account-based**, driven by your authenticated LeetCode session — not a browser
|
|
46
|
+
extension. Your LeetCode account is the single source of truth. Free and open source; the only
|
|
47
|
+
external services involved are LeetCode and GitHub.
|
|
48
|
+
|
|
49
|
+
## Install
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
pip install leetvault
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Requires Python 3.11+. See [docs/DEVELOPER.md](docs/DEVELOPER.md) for an editable/dev install.
|
|
56
|
+
|
|
57
|
+
## Quickstart
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
leetvault login # paste LEETCODE_SESSION + csrftoken
|
|
61
|
+
leetvault config repo_url https://github.com/you/repo.git # optional: enable GitHub push
|
|
62
|
+
leetvault import # one-time full history
|
|
63
|
+
leetvault sync # incremental, run anytime
|
|
64
|
+
leetvault watch # or: poll automatically
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## Commands
|
|
68
|
+
|
|
69
|
+
- `leetvault login` — store your `LEETCODE_SESSION` + `csrftoken` (and optionally a GitHub PAT)
|
|
70
|
+
in the OS keyring.
|
|
71
|
+
- `leetvault import` — full history import of every accepted submission (resumable, one-time
|
|
72
|
+
per site).
|
|
73
|
+
- `leetvault sync` — incremental sync of new accepted submissions since the last run.
|
|
74
|
+
- `leetvault watch` — poll LeetCode and sync automatically (`--interval`, default 90s).
|
|
75
|
+
- `leetvault status` — show session validity/expiry and sync state.
|
|
76
|
+
- `leetvault logout` — remove stored credentials.
|
|
77
|
+
- `leetvault config` — get/set persistent configuration (repo URL, DB path, dedup window, ...).
|
|
78
|
+
|
|
79
|
+
## What gets stored
|
|
80
|
+
|
|
81
|
+
A normalized SQLite database (problems, submissions, source code, topics, sync state) plus a
|
|
82
|
+
disk layout per problem:
|
|
83
|
+
|
|
84
|
+
```
|
|
85
|
+
Problems/<slug>/
|
|
86
|
+
latest.<ext> the most recent accepted submission
|
|
87
|
+
history/submission_<id>.<ext> every kept accepted submission
|
|
88
|
+
metadata.json difficulty, topics, runtime/memory percentiles, ...
|
|
89
|
+
notes.md yours - never overwritten once created
|
|
90
|
+
README.md auto-generated dashboard: progress, streaks, full solutions table
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
By default, same-day accepted submissions for the same problem are deduplicated (only the
|
|
94
|
+
newest is kept); pass `--keep-all` to `import`/`sync` to disable that.
|
|
95
|
+
|
|
96
|
+
## Honest limits
|
|
97
|
+
|
|
98
|
+
- `watch` is polling (default 90s, configurable), not a real-time push — LeetCode has no public
|
|
99
|
+
webhook/streaming API.
|
|
100
|
+
- LeetCode may Cloudflare-challenge automated HTTP clients; leetvault fails gracefully rather
|
|
101
|
+
than faking success.
|
|
102
|
+
- Storing session cookies for automated access may be against LeetCode's Terms of Service. Use
|
|
103
|
+
at your own risk, against your own account only.
|
|
104
|
+
- LeetCode has no official API — every endpoint leetvault uses is reverse-engineered and could
|
|
105
|
+
change without notice.
|
|
106
|
+
|
|
107
|
+
## Docs
|
|
108
|
+
|
|
109
|
+
- [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md) — data flow, module responsibilities, and every
|
|
110
|
+
place live API behavior diverged from initial assumptions.
|
|
111
|
+
- [docs/DEVELOPER.md](docs/DEVELOPER.md) — dev setup, project layout, running checks.
|
|
112
|
+
- [docs/FAQ.md](docs/FAQ.md)
|
|
113
|
+
- [docs/TROUBLESHOOTING.md](docs/TROUBLESHOOTING.md)
|
|
114
|
+
- [CONTRIBUTING.md](CONTRIBUTING.md)
|
|
115
|
+
- [CHANGELOG.md](CHANGELOG.md)
|
|
116
|
+
- [PLAN.md](PLAN.md) — the running decision log from this project's initial build.
|
|
117
|
+
|
|
118
|
+
## License
|
|
119
|
+
|
|
120
|
+
MIT — see [LICENSE](LICENSE).
|