extendvcc-cli 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.
- extendvcc_cli-0.1.0/.github/ISSUE_TEMPLATE/bug_report.yml +76 -0
- extendvcc_cli-0.1.0/.github/ISSUE_TEMPLATE/config.yml +5 -0
- extendvcc_cli-0.1.0/.github/ISSUE_TEMPLATE/feature_request.yml +36 -0
- extendvcc_cli-0.1.0/.github/workflows/ci.yml +42 -0
- extendvcc_cli-0.1.0/.github/workflows/release.yml +94 -0
- extendvcc_cli-0.1.0/.gitignore +17 -0
- extendvcc_cli-0.1.0/AGENTS.md +95 -0
- extendvcc_cli-0.1.0/CHANGELOG.md +32 -0
- extendvcc_cli-0.1.0/CLAUDE.md +67 -0
- extendvcc_cli-0.1.0/CONTRIBUTING.md +24 -0
- extendvcc_cli-0.1.0/LICENSE +21 -0
- extendvcc_cli-0.1.0/PKG-INFO +179 -0
- extendvcc_cli-0.1.0/README.md +151 -0
- extendvcc_cli-0.1.0/SECURITY.md +41 -0
- extendvcc_cli-0.1.0/docs/testing-policy.md +97 -0
- extendvcc_cli-0.1.0/pyproject.toml +57 -0
- extendvcc_cli-0.1.0/src/extendvcc/__init__.py +46 -0
- extendvcc_cli-0.1.0/src/extendvcc/_exit_codes.py +24 -0
- extendvcc_cli-0.1.0/src/extendvcc/_jsonl.py +35 -0
- extendvcc_cli-0.1.0/src/extendvcc/_paths.py +28 -0
- extendvcc_cli-0.1.0/src/extendvcc/auth.py +900 -0
- extendvcc_cli-0.1.0/src/extendvcc/cards.py +761 -0
- extendvcc_cli-0.1.0/src/extendvcc/cli.py +883 -0
- extendvcc_cli-0.1.0/src/extendvcc/client.py +491 -0
- extendvcc_cli-0.1.0/src/extendvcc/imap_otp.py +170 -0
- extendvcc_cli-0.1.0/src/extendvcc/ledger.py +535 -0
- extendvcc_cli-0.1.0/src/extendvcc/models.py +74 -0
- extendvcc_cli-0.1.0/src/extendvcc/py.typed +0 -0
- extendvcc_cli-0.1.0/tests/fixtures/op_credit_card_template.json +89 -0
- extendvcc_cli-0.1.0/tests/test_auth.py +329 -0
- extendvcc_cli-0.1.0/tests/test_cards.py +1233 -0
- extendvcc_cli-0.1.0/tests/test_cli.py +424 -0
- extendvcc_cli-0.1.0/tests/test_client.py +188 -0
- extendvcc_cli-0.1.0/tests/test_imap_otp.py +39 -0
- extendvcc_cli-0.1.0/tests/test_ledger.py +373 -0
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
name: Bug Report
|
|
2
|
+
description: Something isn't working as expected.
|
|
3
|
+
title: "bug: "
|
|
4
|
+
labels: ["bug"]
|
|
5
|
+
body:
|
|
6
|
+
- type: markdown
|
|
7
|
+
attributes:
|
|
8
|
+
value: |
|
|
9
|
+
Before submitting: confirm the bug is reproducible with the latest version. **Do not include card numbers, CVCs, API tokens, or any account credentials in this report.**
|
|
10
|
+
|
|
11
|
+
- type: textarea
|
|
12
|
+
id: description
|
|
13
|
+
attributes:
|
|
14
|
+
label: Description
|
|
15
|
+
description: What happened? What did you expect to happen instead?
|
|
16
|
+
validations:
|
|
17
|
+
required: true
|
|
18
|
+
|
|
19
|
+
- type: textarea
|
|
20
|
+
id: reproduction
|
|
21
|
+
attributes:
|
|
22
|
+
label: Steps to Reproduce
|
|
23
|
+
description: Minimal steps to trigger the bug.
|
|
24
|
+
placeholder: |
|
|
25
|
+
1. Run `extendvcc ...`
|
|
26
|
+
2. Observe ...
|
|
27
|
+
validations:
|
|
28
|
+
required: true
|
|
29
|
+
|
|
30
|
+
- type: textarea
|
|
31
|
+
id: expected
|
|
32
|
+
attributes:
|
|
33
|
+
label: Expected Behavior
|
|
34
|
+
description: What should have happened?
|
|
35
|
+
validations:
|
|
36
|
+
required: true
|
|
37
|
+
|
|
38
|
+
- type: textarea
|
|
39
|
+
id: actual
|
|
40
|
+
attributes:
|
|
41
|
+
label: Actual Behavior
|
|
42
|
+
description: What actually happened? Include the full error output (with secrets redacted).
|
|
43
|
+
validations:
|
|
44
|
+
required: true
|
|
45
|
+
|
|
46
|
+
- type: input
|
|
47
|
+
id: os
|
|
48
|
+
attributes:
|
|
49
|
+
label: Operating System
|
|
50
|
+
placeholder: "e.g. macOS 14.5, Ubuntu 22.04"
|
|
51
|
+
validations:
|
|
52
|
+
required: true
|
|
53
|
+
|
|
54
|
+
- type: input
|
|
55
|
+
id: python
|
|
56
|
+
attributes:
|
|
57
|
+
label: Python Version
|
|
58
|
+
placeholder: "e.g. 3.12.3"
|
|
59
|
+
validations:
|
|
60
|
+
required: true
|
|
61
|
+
|
|
62
|
+
- type: input
|
|
63
|
+
id: extendvcc
|
|
64
|
+
attributes:
|
|
65
|
+
label: extendvcc Version
|
|
66
|
+
placeholder: "e.g. 0.1.0 (run: extendvcc --version)"
|
|
67
|
+
validations:
|
|
68
|
+
required: true
|
|
69
|
+
|
|
70
|
+
- type: checkboxes
|
|
71
|
+
id: no_secrets
|
|
72
|
+
attributes:
|
|
73
|
+
label: Security Confirmation
|
|
74
|
+
options:
|
|
75
|
+
- label: I have confirmed this report contains no card numbers, CVCs, API tokens, IMAP passwords, session data, or any other sensitive credentials.
|
|
76
|
+
required: true
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
name: Feature Request
|
|
2
|
+
description: Propose a new feature or improvement.
|
|
3
|
+
title: "feat: "
|
|
4
|
+
labels: ["enhancement"]
|
|
5
|
+
body:
|
|
6
|
+
- type: textarea
|
|
7
|
+
id: problem
|
|
8
|
+
attributes:
|
|
9
|
+
label: Problem
|
|
10
|
+
description: What problem does this solve? What can't you do today, or what's unnecessarily hard?
|
|
11
|
+
validations:
|
|
12
|
+
required: true
|
|
13
|
+
|
|
14
|
+
- type: textarea
|
|
15
|
+
id: solution
|
|
16
|
+
attributes:
|
|
17
|
+
label: Proposed Solution
|
|
18
|
+
description: What would you like to see? Describe the behavior, CLI flags, or API changes you have in mind.
|
|
19
|
+
validations:
|
|
20
|
+
required: true
|
|
21
|
+
|
|
22
|
+
- type: textarea
|
|
23
|
+
id: alternatives
|
|
24
|
+
attributes:
|
|
25
|
+
label: Alternatives Considered
|
|
26
|
+
description: What workarounds have you tried? Are there other ways to solve this?
|
|
27
|
+
validations:
|
|
28
|
+
required: false
|
|
29
|
+
|
|
30
|
+
- type: textarea
|
|
31
|
+
id: context
|
|
32
|
+
attributes:
|
|
33
|
+
label: Additional Context
|
|
34
|
+
description: Anything else that would help — links, related issues, examples from other tools.
|
|
35
|
+
validations:
|
|
36
|
+
required: false
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
lint:
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
steps:
|
|
12
|
+
- uses: actions/checkout@v4
|
|
13
|
+
- uses: astral-sh/setup-uv@v6
|
|
14
|
+
- run: uv venv && uv pip install -e '.[dev]'
|
|
15
|
+
- run: uv run ruff check src/ tests/
|
|
16
|
+
- run: uv run ruff format --check src/ tests/
|
|
17
|
+
|
|
18
|
+
test:
|
|
19
|
+
runs-on: ubuntu-latest
|
|
20
|
+
strategy:
|
|
21
|
+
matrix:
|
|
22
|
+
python-version: ["3.11", "3.12", "3.13"]
|
|
23
|
+
steps:
|
|
24
|
+
- uses: actions/checkout@v4
|
|
25
|
+
- uses: astral-sh/setup-uv@v6
|
|
26
|
+
with:
|
|
27
|
+
python-version: ${{ matrix.python-version }}
|
|
28
|
+
- run: uv venv && uv pip install -e '.[dev]'
|
|
29
|
+
- run: uv run pytest tests/ -v
|
|
30
|
+
|
|
31
|
+
gitleaks:
|
|
32
|
+
runs-on: ubuntu-latest
|
|
33
|
+
steps:
|
|
34
|
+
- uses: actions/checkout@v4
|
|
35
|
+
with:
|
|
36
|
+
fetch-depth: 0
|
|
37
|
+
- name: Install gitleaks
|
|
38
|
+
run: |
|
|
39
|
+
curl -sSfL "https://github.com/gitleaks/gitleaks/releases/download/v8.24.3/gitleaks_8.24.3_linux_x64.tar.gz" \
|
|
40
|
+
| tar xz -C /usr/local/bin gitleaks
|
|
41
|
+
- name: Scan
|
|
42
|
+
run: gitleaks detect --source . -v --redact
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
name: Release
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags: ["v*"]
|
|
6
|
+
|
|
7
|
+
permissions:
|
|
8
|
+
contents: write
|
|
9
|
+
|
|
10
|
+
jobs:
|
|
11
|
+
build:
|
|
12
|
+
strategy:
|
|
13
|
+
matrix:
|
|
14
|
+
include:
|
|
15
|
+
- os: macos-latest
|
|
16
|
+
arch: arm64
|
|
17
|
+
artifact: extendvcc-macos-arm64
|
|
18
|
+
- os: macos-13
|
|
19
|
+
arch: x86_64
|
|
20
|
+
artifact: extendvcc-macos-x86_64
|
|
21
|
+
- os: ubuntu-latest
|
|
22
|
+
arch: x86_64
|
|
23
|
+
artifact: extendvcc-linux-x86_64
|
|
24
|
+
- os: windows-latest
|
|
25
|
+
arch: x86_64
|
|
26
|
+
artifact: extendvcc-windows-x86_64.exe
|
|
27
|
+
|
|
28
|
+
runs-on: ${{ matrix.os }}
|
|
29
|
+
steps:
|
|
30
|
+
- uses: actions/checkout@v4
|
|
31
|
+
- uses: actions/setup-python@v5
|
|
32
|
+
with:
|
|
33
|
+
python-version: "3.13"
|
|
34
|
+
- run: pip install pyinstaller
|
|
35
|
+
- run: pip install -e .
|
|
36
|
+
- run: pyinstaller --onefile --name ${{ matrix.artifact }} --strip src/extendvcc/cli.py
|
|
37
|
+
shell: bash
|
|
38
|
+
- uses: actions/upload-artifact@v4
|
|
39
|
+
with:
|
|
40
|
+
name: ${{ matrix.artifact }}
|
|
41
|
+
path: dist/${{ matrix.artifact }}*
|
|
42
|
+
|
|
43
|
+
test:
|
|
44
|
+
runs-on: ubuntu-latest
|
|
45
|
+
steps:
|
|
46
|
+
- uses: actions/checkout@v4
|
|
47
|
+
- uses: actions/setup-python@v5
|
|
48
|
+
with:
|
|
49
|
+
python-version: "3.13"
|
|
50
|
+
- run: pip install -e ".[dev]"
|
|
51
|
+
- run: ruff check src/ tests/
|
|
52
|
+
- run: ruff format --check src/ tests/
|
|
53
|
+
- run: pytest tests/ -q
|
|
54
|
+
|
|
55
|
+
release:
|
|
56
|
+
needs: [build, test]
|
|
57
|
+
runs-on: ubuntu-latest
|
|
58
|
+
steps:
|
|
59
|
+
- uses: actions/download-artifact@v4
|
|
60
|
+
with:
|
|
61
|
+
merge-multiple: true
|
|
62
|
+
- uses: softprops/action-gh-release@v2
|
|
63
|
+
with:
|
|
64
|
+
files: extendvcc-*
|
|
65
|
+
generate_release_notes: true
|
|
66
|
+
|
|
67
|
+
build-dist:
|
|
68
|
+
needs: [test]
|
|
69
|
+
runs-on: ubuntu-latest
|
|
70
|
+
steps:
|
|
71
|
+
- uses: actions/checkout@v4
|
|
72
|
+
- uses: actions/setup-python@v5
|
|
73
|
+
with:
|
|
74
|
+
python-version: "3.13"
|
|
75
|
+
- run: pip install build
|
|
76
|
+
- run: python -m build
|
|
77
|
+
- uses: actions/upload-artifact@v4
|
|
78
|
+
with:
|
|
79
|
+
name: python-dist
|
|
80
|
+
path: dist/
|
|
81
|
+
|
|
82
|
+
publish:
|
|
83
|
+
needs: [build-dist, test]
|
|
84
|
+
runs-on: ubuntu-latest
|
|
85
|
+
environment: release
|
|
86
|
+
permissions:
|
|
87
|
+
id-token: write
|
|
88
|
+
contents: read
|
|
89
|
+
steps:
|
|
90
|
+
- uses: actions/download-artifact@v4
|
|
91
|
+
with:
|
|
92
|
+
name: python-dist
|
|
93
|
+
path: dist/
|
|
94
|
+
- uses: pypa/gh-action-pypi-publish@cef221092ed1bacb1cc03d23a2d87d1d172e277b # v1.14.0
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
# AGENTS.md: Contributor Guide
|
|
2
|
+
|
|
3
|
+
Tool-agnostic guide for AI agents and human contributors. For Claude Code specifics, see `CLAUDE.md`.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Project Overview
|
|
8
|
+
|
|
9
|
+
`extendvcc` is an unofficial Python client and CLI for Extend's private virtual card API (`api.paywithextend.com`). It handles Cognito SRP authentication with device remembering and email OTP, the full virtual-card lifecycle (create, list, update, cancel, close, reveal), parent credit-card enrollment, and a JSONL audit ledger for all card mutations. HTTP uses `impit` for Chrome TLS fingerprinting, which is necessary because Extend blocks non-browser TLS profiles.
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## Build, Lint, Test
|
|
14
|
+
|
|
15
|
+
All three must be clean before any change is considered done:
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
uv run ruff check src/ tests/
|
|
19
|
+
uv run ruff format --check src/ tests/
|
|
20
|
+
uv run pytest tests/ -v
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Install dev dependencies first if needed: `uv pip install -e '.[dev]'`
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
## Module Map
|
|
28
|
+
|
|
29
|
+
```
|
|
30
|
+
src/extendvcc/
|
|
31
|
+
├── __init__.py # Public API re-exports
|
|
32
|
+
├── _paths.py # Lazy state_dir() / ledger_path() with CLI override, env, default
|
|
33
|
+
├── _jsonl.py # Vendored append_jsonl helper
|
|
34
|
+
├── auth.py # Cognito SRP login, device remembering, token refresh, session persistence
|
|
35
|
+
├── client.py # HTTP client (impit), kill switch, rate limiting, account-risk detection
|
|
36
|
+
├── cards.py # Card CRUD — create, list, get, update, cancel, close, reveal, enroll, bulk
|
|
37
|
+
├── imap_otp.py # IMAP-based OTP retrieval for Cognito EMAIL_OTP challenges
|
|
38
|
+
├── ledger.py # JSONL audit ledger for card mutations (pending/confirm/fail)
|
|
39
|
+
├── models.py # CardStatus, VirtualCard, CreditCard, Issuer, Recurrence
|
|
40
|
+
└── cli.py # Full lifecycle CLI (login, cards, create, reveal, etc.)
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
---
|
|
44
|
+
|
|
45
|
+
## Coding Style
|
|
46
|
+
|
|
47
|
+
- **HTTP:** All network requests go through `impit` with Chrome TLS fingerprinting. Never import `httpx` or `requests` directly; Extend detects and blocks non-browser TLS profiles.
|
|
48
|
+
- **Paths:** Use `_paths.py` helpers (`state_dir()`, `ledger_path()`). Never compute paths at module level. Always resolve lazily so CLI flags and env vars take effect.
|
|
49
|
+
- **Functions:** One function at a time, max ~30 lines. Search existing code before adding anything new.
|
|
50
|
+
- **Logging:** Never log or print card numbers, CVCs, or full tokens. Mask to last 4 digits when logging is necessary.
|
|
51
|
+
- **Tests:** All tests run offline with fakes. Mock only at the I/O boundary (HTTP client, filesystem, IMAP). See `docs/testing-policy.md`.
|
|
52
|
+
|
|
53
|
+
---
|
|
54
|
+
|
|
55
|
+
## Commit Conventions
|
|
56
|
+
|
|
57
|
+
Follow Conventional Commits:
|
|
58
|
+
|
|
59
|
+
```
|
|
60
|
+
feat: add bulk-create pacing option
|
|
61
|
+
fix: handle expired session token on first request
|
|
62
|
+
chore: bump impit to 0.13.0
|
|
63
|
+
style: fix trailing whitespace
|
|
64
|
+
docs: add reveal example to README
|
|
65
|
+
test: add ledger concurrent-write invariant
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
Scope is optional. Keep the subject line under 72 characters.
|
|
69
|
+
|
|
70
|
+
---
|
|
71
|
+
|
|
72
|
+
## What Needs Approval vs. Proceed
|
|
73
|
+
|
|
74
|
+
**Get maintainer approval before changing:**
|
|
75
|
+
- Auth flow (Cognito SRP, device remembering, OTP, token refresh)
|
|
76
|
+
- Anything that touches card number or CVC handling
|
|
77
|
+
- Public API surface (`__init__.py` exports, CLI command names/flags)
|
|
78
|
+
- Destructive operations (data deletion, credential rotation)
|
|
79
|
+
|
|
80
|
+
**Proceed without asking:**
|
|
81
|
+
- Bug fixes inside existing functions
|
|
82
|
+
- New tests
|
|
83
|
+
- Documentation updates
|
|
84
|
+
- Dependency version bumps
|
|
85
|
+
- Refactors that don't change the public API
|
|
86
|
+
|
|
87
|
+
---
|
|
88
|
+
|
|
89
|
+
## Security Rules
|
|
90
|
+
|
|
91
|
+
- **Never store or log PAN/CVC:** the ledger never persists card numbers or CVCs. Mask to last 4 when logging.
|
|
92
|
+
- **Never make real Extend API calls in tests:** all tests run offline with fakes. Network access in tests is not skipped; it is deleted.
|
|
93
|
+
- **Never commit session files, credential caches, or `.env*` files:** these contain live auth tokens.
|
|
94
|
+
- **Credentials via env vars:** `EXTENDVCC_EMAIL`, `EXTENDVCC_PASSWORD`, `EXTENDVCC_IMAP_*`. Interactive prompts in CLI. No hardcoded credentials anywhere.
|
|
95
|
+
- Session and state files are written with `0600` permissions. The HTTP client has a kill switch that disables itself on risk signals (403, WAF blocks, verification prompts).
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## [Unreleased]
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## [0.1.0] - 2026-06-14
|
|
14
|
+
|
|
15
|
+
### Added
|
|
16
|
+
|
|
17
|
+
- **Authentication:** Cognito SRP login with device remembering; automatic token refresh; session persistence to disk with `0600` permissions.
|
|
18
|
+
- **Email OTP:** IMAP-based OTP retrieval for Cognito `EMAIL_OTP` challenges, configurable via `EXTENDVCC_IMAP_*` env vars.
|
|
19
|
+
- **Virtual card lifecycle:** create, list, get, update, cancel, close, and reveal (PAN + CVC + expiry) virtual cards.
|
|
20
|
+
- **Reveal:** masked stdout output by default; `--json-path` writes full credentials to a file with `0600` permissions.
|
|
21
|
+
- **Parent credit cards:** enroll and activate parent credit cards.
|
|
22
|
+
- **Bulk create:** create multiple virtual cards with configurable pacing to avoid rate limits.
|
|
23
|
+
- **Recurring cards:** support for `DAILY`, `WEEKLY`, and `MONTHLY` recurrence periods with configurable terminators.
|
|
24
|
+
- **JSONL audit ledger:** append-only ledger records every card mutation as pending → confirmed or failed; `reconcile` command flags unconfirmed entries.
|
|
25
|
+
- **Kill switch:** HTTP client disables itself on account-risk signals (403, WAF blocks, verification prompts) to avoid account suspension.
|
|
26
|
+
- **Chrome TLS fingerprinting:** all HTTP via `impit`; Extend blocks non-browser TLS profiles.
|
|
27
|
+
- **CLI:** full lifecycle commands: `login`, `accounts`, `issuers`, `cards`, `card`, `usage`, `enroll`, `activate`, `create`, `bulk`, `update`, `cancel`, `close`, `reveal`, `reconcile`, `status`, `clear-disabled`.
|
|
28
|
+
- **Python API:** public re-exports in `extendvcc.__init__` for programmatic use.
|
|
29
|
+
- **Typed:** `py.typed` marker (PEP 561); type hints throughout.
|
|
30
|
+
|
|
31
|
+
[Unreleased]: https://github.com/4LAU/extendvcc/compare/v0.1.0...HEAD
|
|
32
|
+
[0.1.0]: https://github.com/4LAU/extendvcc/releases/tag/v0.1.0
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# extendvcc
|
|
2
|
+
|
|
3
|
+
Unofficial Python client and CLI for Extend's private virtual card API.
|
|
4
|
+
|
|
5
|
+
**Stack:** Python 3.11+, src/ layout, hatchling build, impit (Chrome TLS fingerprinting), filelock, argparse CLI, pytest, ruff.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Critical Rules
|
|
10
|
+
|
|
11
|
+
1. **NEVER expose secrets:** card numbers, CVCs, API tokens, session files, PII. If exposed: STOP and rotate immediately.
|
|
12
|
+
2. **NEVER use `git add .`:** add files individually. gitleaks pre-commit enforces this.
|
|
13
|
+
3. **NEVER log or print card numbers, CVCs, or full tokens.** Mask to last 4 digits when logging is necessary.
|
|
14
|
+
4. **NEVER make real Extend API calls in tests.** All tests run offline with fakes.
|
|
15
|
+
5. **NEVER commit session files, credential caches, or `.env*` files.**
|
|
16
|
+
6. **NEVER claim tests pass without showing actual output.**
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
## Definition of Done
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
uv run ruff check src/ tests/
|
|
24
|
+
uv run ruff format --check src/ tests/
|
|
25
|
+
uv run pytest tests/ -v
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
All three clean before claiming done.
|
|
29
|
+
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
## Code Practices
|
|
33
|
+
|
|
34
|
+
**Ask maintainer approval for:** auth flow changes, anything touching card number/CVC handling, public API surface changes, destructive ops. Everything else: proceed.
|
|
35
|
+
|
|
36
|
+
**Generation:** Search existing code first. One function at a time (max 30 lines).
|
|
37
|
+
|
|
38
|
+
**impit:** All HTTP goes through `impit` with Chrome TLS fingerprinting. Never use bare `httpx` or `requests`; Extend fingerprints non-browser clients.
|
|
39
|
+
|
|
40
|
+
**Credentials:** Env vars (`EXTENDVCC_EMAIL`, `EXTENDVCC_PASSWORD`, `EXTENDVCC_IMAP_*`). Interactive prompts in CLI. No 1Password integration in this package.
|
|
41
|
+
|
|
42
|
+
**Paths:** Lazy resolution via `_paths.py`. CLI flags override env vars override defaults. Never use module-level `Path` constants that resolve at import time.
|
|
43
|
+
|
|
44
|
+
---
|
|
45
|
+
|
|
46
|
+
## Module Map
|
|
47
|
+
|
|
48
|
+
```
|
|
49
|
+
src/extendvcc/
|
|
50
|
+
├── __init__.py # Public API re-exports
|
|
51
|
+
├── _paths.py # Lazy state_dir() / ledger_path() with CLI override, env, default
|
|
52
|
+
├── _jsonl.py # Vendored append_jsonl helper
|
|
53
|
+
├── auth.py # Cognito SRP login, device remembering, token refresh, session persistence
|
|
54
|
+
├── client.py # HTTP client (impit), kill switch, rate limiting, account-risk detection
|
|
55
|
+
├── cards.py # Card CRUD — create, list, get, update, cancel, close, reveal, enroll, bulk
|
|
56
|
+
├── imap_otp.py # IMAP-based OTP retrieval for Cognito EMAIL_OTP challenges
|
|
57
|
+
├── ledger.py # JSONL audit ledger for card mutations (pending/confirm/fail)
|
|
58
|
+
├── models.py # CardStatus, VirtualCard, CreditCard, Issuer, Recurrence
|
|
59
|
+
└── cli.py # Full lifecycle CLI (login, cards, create, reveal, etc.)
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
---
|
|
63
|
+
|
|
64
|
+
## Testing
|
|
65
|
+
|
|
66
|
+
See `docs/testing-policy.md`. All tests offline, mock only at I/O boundary, every test protects a named invariant.
|
|
67
|
+
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# Contributing
|
|
2
|
+
|
|
3
|
+
Fork the repo, create a feature branch, open a PR.
|
|
4
|
+
|
|
5
|
+
## Setup
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pip install -e '.[dev]'
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Checks
|
|
12
|
+
|
|
13
|
+
Both must pass before merging:
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
ruff check src/ tests/
|
|
17
|
+
pytest tests/ -v
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
All tests run offline; no real API calls are made.
|
|
21
|
+
|
|
22
|
+
## Pre-commit
|
|
23
|
+
|
|
24
|
+
A gitleaks pre-commit hook is required to prevent accidental secret leaks.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 extendvcc 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.
|