freelancer-payment-protection-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.
- freelancer_payment_protection_cli-0.1.0/.gitignore +50 -0
- freelancer_payment_protection_cli-0.1.0/LICENSE +35 -0
- freelancer_payment_protection_cli-0.1.0/PKG-INFO +246 -0
- freelancer_payment_protection_cli-0.1.0/README.md +213 -0
- freelancer_payment_protection_cli-0.1.0/pyproject.toml +71 -0
- freelancer_payment_protection_cli-0.1.0/src/freelancer_payment_protection_cli/__init__.py +6 -0
- freelancer_payment_protection_cli-0.1.0/src/freelancer_payment_protection_cli/api.py +89 -0
- freelancer_payment_protection_cli-0.1.0/src/freelancer_payment_protection_cli/auth.py +223 -0
- freelancer_payment_protection_cli-0.1.0/src/freelancer_payment_protection_cli/commands/__init__.py +0 -0
- freelancer_payment_protection_cli-0.1.0/src/freelancer_payment_protection_cli/commands/auth_cmds.py +84 -0
- freelancer_payment_protection_cli-0.1.0/src/freelancer_payment_protection_cli/commands/client_cmds.py +111 -0
- freelancer_payment_protection_cli-0.1.0/src/freelancer_payment_protection_cli/commands/escalation_cmds.py +142 -0
- freelancer_payment_protection_cli-0.1.0/src/freelancer_payment_protection_cli/commands/invoice_cmds.py +145 -0
- freelancer_payment_protection_cli-0.1.0/src/freelancer_payment_protection_cli/config.py +45 -0
- freelancer_payment_protection_cli-0.1.0/src/freelancer_payment_protection_cli/main.py +43 -0
- freelancer_payment_protection_cli-0.1.0/src/freelancer_payment_protection_cli/output.py +72 -0
- freelancer_payment_protection_cli-0.1.0/tests/__init__.py +0 -0
- freelancer_payment_protection_cli-0.1.0/tests/conftest.py +49 -0
- freelancer_payment_protection_cli-0.1.0/tests/test_auth.py +100 -0
- freelancer_payment_protection_cli-0.1.0/tests/test_auth_commands.py +48 -0
- freelancer_payment_protection_cli-0.1.0/tests/test_client_commands.py +88 -0
- freelancer_payment_protection_cli-0.1.0/tests/test_escalation_commands.py +123 -0
- freelancer_payment_protection_cli-0.1.0/tests/test_invoice_commands.py +132 -0
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# Dependencies
|
|
2
|
+
node_modules/
|
|
3
|
+
.pnp
|
|
4
|
+
.pnp.js
|
|
5
|
+
|
|
6
|
+
# Build outputs
|
|
7
|
+
.next/
|
|
8
|
+
.turbo/
|
|
9
|
+
dist/
|
|
10
|
+
build/
|
|
11
|
+
out/
|
|
12
|
+
|
|
13
|
+
# Environment files — NEVER commit real secrets
|
|
14
|
+
.env
|
|
15
|
+
.env.local
|
|
16
|
+
.env.*.local
|
|
17
|
+
*.env
|
|
18
|
+
|
|
19
|
+
# Python
|
|
20
|
+
__pycache__/
|
|
21
|
+
*.pyc
|
|
22
|
+
*.pyo
|
|
23
|
+
.venv/
|
|
24
|
+
venv/
|
|
25
|
+
*.egg-info/
|
|
26
|
+
.pytest_cache/
|
|
27
|
+
htmlcov/
|
|
28
|
+
.coverage
|
|
29
|
+
coverage.xml
|
|
30
|
+
|
|
31
|
+
# Local dev databases — no credentials in these
|
|
32
|
+
*.db
|
|
33
|
+
*.sqlite
|
|
34
|
+
*.sqlite3
|
|
35
|
+
|
|
36
|
+
# OS
|
|
37
|
+
.DS_Store
|
|
38
|
+
Thumbs.db
|
|
39
|
+
|
|
40
|
+
# IDE
|
|
41
|
+
.idea/
|
|
42
|
+
.vscode/settings.json
|
|
43
|
+
*.swp
|
|
44
|
+
|
|
45
|
+
# Reference docs (local strategy docs — not for public repo)
|
|
46
|
+
reference-docs-context/
|
|
47
|
+
|
|
48
|
+
# Generated PDFs / docs (created at runtime)
|
|
49
|
+
generated-docs/
|
|
50
|
+
tmp/
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
Copyright (c) 2026 Rudrendu Paul and Sourav Nandy. All rights reserved.
|
|
2
|
+
|
|
3
|
+
PROPRIETARY LICENSE
|
|
4
|
+
|
|
5
|
+
This software and associated documentation files (the "Software") are the
|
|
6
|
+
exclusive intellectual property of Rudrendu Paul and Sourav Nandy
|
|
7
|
+
("the Owners").
|
|
8
|
+
|
|
9
|
+
RESTRICTIONS
|
|
10
|
+
|
|
11
|
+
1. No use, copying, modification, merging, publishing, distribution,
|
|
12
|
+
sublicensing, or selling of this Software — in whole or in part — is
|
|
13
|
+
permitted without prior written approval from both Owners.
|
|
14
|
+
|
|
15
|
+
2. Any personal, academic, commercial, or scheduled use of this Software
|
|
16
|
+
requires explicit written permission obtained directly from:
|
|
17
|
+
|
|
18
|
+
Rudrendu Paul — https://github.com/RudrenduPaul
|
|
19
|
+
Sourav Nandy — (contact via Rudrendu Paul)
|
|
20
|
+
|
|
21
|
+
3. Unauthorized use of this Software for any purpose, including but not
|
|
22
|
+
limited to building competing products, white-labeling, or redistribution,
|
|
23
|
+
is strictly prohibited and may result in legal action.
|
|
24
|
+
|
|
25
|
+
TO REQUEST PERMISSION
|
|
26
|
+
|
|
27
|
+
Submit a written request describing your intended use to the Owners. Approval
|
|
28
|
+
is granted solely at the discretion of both Owners and may be subject to
|
|
29
|
+
additional terms or a commercial licensing agreement.
|
|
30
|
+
|
|
31
|
+
DISCLAIMER
|
|
32
|
+
|
|
33
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
34
|
+
IMPLIED. IN NO EVENT SHALL THE OWNERS BE LIABLE FOR ANY CLAIM, DAMAGES, OR
|
|
35
|
+
OTHER LIABILITY ARISING FROM THE USE OF OR INABILITY TO USE THE SOFTWARE.
|
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: freelancer-payment-protection-cli
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Command-line client for freelancer-payment-protection: automated invoice escalation, legal document generation, and client risk scoring for freelancers.
|
|
5
|
+
Project-URL: Homepage, https://github.com/RudrenduPaul/freelancer-payment-protection
|
|
6
|
+
Project-URL: Repository, https://github.com/RudrenduPaul/freelancer-payment-protection
|
|
7
|
+
Project-URL: Bug Tracker, https://github.com/RudrenduPaul/freelancer-payment-protection/issues
|
|
8
|
+
Author: Rudrendu Paul, Sourav Nandy
|
|
9
|
+
License: Proprietary
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Keywords: agent-native,cli,fastapi,freelance,invoice-escalation,invoicing,risk-scoring,supabase
|
|
12
|
+
Classifier: Development Status :: 3 - Alpha
|
|
13
|
+
Classifier: Environment :: Console
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: Operating System :: OS Independent
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
21
|
+
Classifier: Topic :: Office/Business :: Financial :: Accounting
|
|
22
|
+
Classifier: Topic :: Utilities
|
|
23
|
+
Requires-Python: >=3.10
|
|
24
|
+
Requires-Dist: click<9,>=8.1
|
|
25
|
+
Requires-Dist: httpx<1,>=0.27
|
|
26
|
+
Provides-Extra: dev
|
|
27
|
+
Requires-Dist: build<2,>=1.0; extra == 'dev'
|
|
28
|
+
Requires-Dist: pytest-cov<7,>=5.0; extra == 'dev'
|
|
29
|
+
Requires-Dist: pytest<9,>=8.0; extra == 'dev'
|
|
30
|
+
Requires-Dist: respx<1,>=0.21; extra == 'dev'
|
|
31
|
+
Requires-Dist: twine<7,>=5.0; extra == 'dev'
|
|
32
|
+
Description-Content-Type: text/markdown
|
|
33
|
+
|
|
34
|
+
# freelancer-payment-protection-cli
|
|
35
|
+
|
|
36
|
+
Command-line client for [freelancer-payment-protection](https://github.com/RudrenduPaul/freelancer-payment-protection):
|
|
37
|
+
automated invoice escalation, legal document generation, and client risk
|
|
38
|
+
scoring for freelancers. This package wraps the project's FastAPI backend
|
|
39
|
+
(`apps/api/app/routers/*.py`) so the same clients, invoices, escalations,
|
|
40
|
+
and risk-scoring data you'd otherwise reach through the dashboard or the
|
|
41
|
+
raw HTTP API is scriptable from a terminal or an agent.
|
|
42
|
+
|
|
43
|
+

|
|
44
|
+
|
|
45
|
+
## Install
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
pip install freelancer-payment-protection-cli
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Or with [uv](https://docs.astral.sh/uv/) / [pipx](https://pipx.pypa.io/):
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
uvx freelancer-payment-protection-cli --help
|
|
55
|
+
pipx install freelancer-payment-protection-cli
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
Requires Python 3.10+. The console command is installed as both `fpp`
|
|
59
|
+
(short form) and `freelancer-payment-protection` (full form) — they are the
|
|
60
|
+
same entry point.
|
|
61
|
+
|
|
62
|
+
## Configuration
|
|
63
|
+
|
|
64
|
+
The CLI needs to know two things: which backend API to talk to, and which
|
|
65
|
+
Supabase project issues your login tokens.
|
|
66
|
+
|
|
67
|
+
| Variable | Purpose | Default |
|
|
68
|
+
|---|---|---|
|
|
69
|
+
| `FPP_API_URL` | Backend API base URL | `http://localhost:8000` |
|
|
70
|
+
| `FPP_SUPABASE_URL` (or `SUPABASE_URL`) | Your Supabase project URL | none, required for `login` |
|
|
71
|
+
| `FPP_SUPABASE_ANON_KEY` (or `SUPABASE_ANON_KEY`) | Your Supabase project's anon key | none, required for `login` |
|
|
72
|
+
|
|
73
|
+
The anon key is the same public key the web app's Supabase client uses —
|
|
74
|
+
it is not a secret, and it's the key already in `apps/web/.env.example` as
|
|
75
|
+
`NEXT_PUBLIC_SUPABASE_ANON_KEY`.
|
|
76
|
+
|
|
77
|
+
## Login and authentication
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
export FPP_SUPABASE_URL="https://your-project.supabase.co"
|
|
81
|
+
export FPP_SUPABASE_ANON_KEY="your-anon-key"
|
|
82
|
+
fpp login
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
`fpp login` prompts for your email and password, then calls Supabase's own
|
|
86
|
+
REST auth endpoint directly:
|
|
87
|
+
|
|
88
|
+
```
|
|
89
|
+
POST {SUPABASE_URL}/auth/v1/token?grant_type=password
|
|
90
|
+
Header: apikey: <anon key>
|
|
91
|
+
Body: {"email": ..., "password": ...}
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
This is the same call the web dashboard's Supabase client makes — the CLI
|
|
95
|
+
never talks to a login endpoint of its own, because the backend
|
|
96
|
+
(`apps/api/app/middleware/auth.py`) doesn't mint tokens; it only validates
|
|
97
|
+
Supabase-issued JWTs. On success, the returned `access_token` and
|
|
98
|
+
`refresh_token` are cached to `~/.config/freelancer-payment-protection-cli/credentials.json`
|
|
99
|
+
(mode 600). Every later command reads the JWT's `workspace_id` claim the
|
|
100
|
+
same way `get_current_workspace()` does server-side, and transparently
|
|
101
|
+
refreshes the access token with the cached refresh token once it expires.
|
|
102
|
+
|
|
103
|
+
Run `fpp logout` to delete the cached credentials, or `fpp whoami` to see
|
|
104
|
+
which workspace and email the cached token resolves to.
|
|
105
|
+
|
|
106
|
+
## Commands
|
|
107
|
+
|
|
108
|
+
Every data-returning command supports `--json` for scripting and agent use.
|
|
109
|
+
|
|
110
|
+
```
|
|
111
|
+
fpp login Log in (prompts for email/password)
|
|
112
|
+
fpp logout Remove cached credentials
|
|
113
|
+
fpp whoami [--json] Show cached workspace/session info
|
|
114
|
+
|
|
115
|
+
fpp invoice list [--status] [--client-id] [--page] [--page-size] [--json]
|
|
116
|
+
fpp invoice create --client-id --invoice-number --amount --due-date [...] [--json]
|
|
117
|
+
fpp invoice show <invoice-id> [--json]
|
|
118
|
+
fpp invoice set-status <invoice-id> <status> [--json]
|
|
119
|
+
|
|
120
|
+
fpp escalation list [--json] Active escalations, grouped by stage
|
|
121
|
+
fpp escalation status <invoice-id> [--json] Current stage + full history
|
|
122
|
+
fpp escalation advance <invoice-id> [--json] AI-draft the next stage's email
|
|
123
|
+
|
|
124
|
+
fpp client list [--risk-level] [--search] [--page] [--page-size] [--json]
|
|
125
|
+
fpp client show <client-id> [--json]
|
|
126
|
+
fpp client risk <client-id> [--json] Compute/refresh the AI risk score
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
Run `fpp --help` or `fpp <command> --help` for full flag references.
|
|
130
|
+
|
|
131
|
+

|
|
132
|
+
|
|
133
|
+
### A note on `escalation advance`
|
|
134
|
+
|
|
135
|
+
The backend's escalation router (`apps/api/app/routers/escalations.py`)
|
|
136
|
+
exposes exactly three routes: list active escalations, draft the next
|
|
137
|
+
stage's email, and read history. There is no route that persists a stage
|
|
138
|
+
change — `draft_escalation_email` in `escalation_service.py` computes and
|
|
139
|
+
returns what the next stage's email would say, but never writes
|
|
140
|
+
`escalation_stage` back to the database. `fpp escalation advance` calls
|
|
141
|
+
that draft endpoint and shows you the preview; it does not claim to move
|
|
142
|
+
the invoice to the next stage, because the API it wraps doesn't do that
|
|
143
|
+
either.
|
|
144
|
+
|
|
145
|
+
### JSON output for agents/scripts
|
|
146
|
+
|
|
147
|
+
```bash
|
|
148
|
+
fpp invoice list --status overdue --json | jq '.[] | {id, invoiceNumber, daysPastDue}'
|
|
149
|
+
fpp client risk "$CLIENT_ID" --json | jq '.level'
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
Field names in `--json` output match the backend's actual Pydantic response
|
|
153
|
+
models exactly (camelCase, e.g. `invoiceNumber`, `daysPastDue`,
|
|
154
|
+
`escalationStage`) since the CLI passes the API's response straight
|
|
155
|
+
through rather than remapping it.
|
|
156
|
+
|
|
157
|
+
## FAQ
|
|
158
|
+
|
|
159
|
+
**What is this, and how is it different from just calling the API with curl?**
|
|
160
|
+
It's a typed command-line wrapper around the same backend the web
|
|
161
|
+
dashboard uses — invoices, escalations, and client risk scoring — with
|
|
162
|
+
persistent login (so you're not re-attaching a bearer token to every
|
|
163
|
+
request), human-readable tables by default, and a `--json` flag on every
|
|
164
|
+
data command for piping into `jq`, scripts, or an agent's tool-calling
|
|
165
|
+
loop. The differentiator versus curl is session handling (login once,
|
|
166
|
+
silent token refresh after) and consistent, documented output shapes
|
|
167
|
+
instead of hand-rolled request construction.
|
|
168
|
+
|
|
169
|
+
**What platforms and Python versions does it support?**
|
|
170
|
+
Python 3.10 through 3.13, on any OS `pip`/`uvx`/`pipx` runs on (Linux,
|
|
171
|
+
macOS, Windows). It has no compiled dependencies — the only runtime
|
|
172
|
+
dependencies are `click` and `httpx`, both pure-Python-installable wheels.
|
|
173
|
+
|
|
174
|
+
**How do I log in, and where are my credentials stored?**
|
|
175
|
+
Run `fpp login`, enter your email and password when prompted. The CLI
|
|
176
|
+
calls Supabase's password-grant token endpoint directly (see "Login and
|
|
177
|
+
authentication" above) and caches the resulting access and refresh tokens
|
|
178
|
+
to `~/.config/freelancer-payment-protection-cli/credentials.json` with
|
|
179
|
+
file mode 600 (owner read/write only). Nothing is sent anywhere except
|
|
180
|
+
Supabase's own auth endpoint and the backend API you configure via
|
|
181
|
+
`FPP_API_URL`.
|
|
182
|
+
|
|
183
|
+
**I ran a command and got `Error: Internal Server Error` — is that a CLI bug?**
|
|
184
|
+
Usually not. The CLI's error handling surfaces whatever the backend
|
|
185
|
+
returned; a 500 means the backend itself failed. Two real, backend-side
|
|
186
|
+
causes to check first: (1) `fpp client risk` and `fpp escalation advance`
|
|
187
|
+
both trigger AI calls through `packages/legal_ai/client.py` — if
|
|
188
|
+
`ANTHROPIC_API_KEY` isn't a real key on the server, `client risk` falls
|
|
189
|
+
back to a heuristic score automatically, but `escalation advance` has no
|
|
190
|
+
such fallback and will 500. (2) Confirm you're running a backend build
|
|
191
|
+
that includes the `packages/legal_ai` module-name fix and the
|
|
192
|
+
`risk_scoring.py` request-parameter fix — both were real bugs in this
|
|
193
|
+
repo (a hyphenated `packages/legal-ai` directory that the code imports as
|
|
194
|
+
`packages.legal_ai`, and a rate-limiter/parameter-name collision on
|
|
195
|
+
`/api/v1/risk/score`) that this CLI's own end-to-end testing surfaced and
|
|
196
|
+
this same change fixed. A plain 401 means your cached token expired and
|
|
197
|
+
couldn't refresh — run `fpp login` again.
|
|
198
|
+
|
|
199
|
+
**Does `fpp escalation advance` actually send the escalation email or move the invoice to the next stage?**
|
|
200
|
+
No. It calls the backend's `/api/v1/escalations/{id}/draft` endpoint,
|
|
201
|
+
which only returns an AI-drafted preview of what the next stage's email
|
|
202
|
+
would say. The backend has no endpoint today that persists a stage change
|
|
203
|
+
or sends the email — see "A note on `escalation advance`" above.
|
|
204
|
+
|
|
205
|
+
**Can I point this at a self-hosted or non-default backend?**
|
|
206
|
+
Yes. Set `FPP_API_URL` to your backend's base URL (default is
|
|
207
|
+
`http://localhost:8000`, matching the FastAPI dev server's default port).
|
|
208
|
+
Every command reads that variable at call time, so switching environments
|
|
209
|
+
is just re-exporting it.
|
|
210
|
+
|
|
211
|
+
**What's the licensing situation — can I use or modify this commercially?**
|
|
212
|
+
This CLI ships from the same repository as, and under the same license
|
|
213
|
+
as, freelancer-payment-protection itself: a proprietary license, copyright
|
|
214
|
+
Rudrendu Paul and Sourav Nandy, all rights reserved. Personal, academic,
|
|
215
|
+
commercial, or scheduled use requires explicit written permission from
|
|
216
|
+
both owners — see the `LICENSE` file. This is not an MIT/Apache-style
|
|
217
|
+
open-source license; publishing it to PyPI makes it installable, not
|
|
218
|
+
freely reusable.
|
|
219
|
+
|
|
220
|
+
**Why isn't there a hosted/default API URL I can just start using?**
|
|
221
|
+
freelancer-payment-protection is self-hosted software, not a hosted
|
|
222
|
+
multi-tenant SaaS with a public API endpoint today. You (or whoever
|
|
223
|
+
operates your workspace) runs the backend; the CLI just needs its URL.
|
|
224
|
+
|
|
225
|
+
## Development
|
|
226
|
+
|
|
227
|
+
```bash
|
|
228
|
+
cd packages/cli
|
|
229
|
+
python -m venv .venv && source .venv/bin/activate
|
|
230
|
+
pip install -e ".[dev]"
|
|
231
|
+
pytest
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
All HTTP calls (to Supabase and to the backend API) are mocked in tests
|
|
235
|
+
with `respx` — the test suite makes no live network calls.
|
|
236
|
+
|
|
237
|
+
## Contributing
|
|
238
|
+
|
|
239
|
+
This package lives inside the freelancer-payment-protection monorepo. See
|
|
240
|
+
the repository root for contribution guidelines.
|
|
241
|
+
|
|
242
|
+
## License
|
|
243
|
+
|
|
244
|
+
Proprietary — see [`LICENSE`](./LICENSE). Contact the owners (see
|
|
245
|
+
`LICENSE`) before any use beyond installing and running the package as
|
|
246
|
+
published.
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
# freelancer-payment-protection-cli
|
|
2
|
+
|
|
3
|
+
Command-line client for [freelancer-payment-protection](https://github.com/RudrenduPaul/freelancer-payment-protection):
|
|
4
|
+
automated invoice escalation, legal document generation, and client risk
|
|
5
|
+
scoring for freelancers. This package wraps the project's FastAPI backend
|
|
6
|
+
(`apps/api/app/routers/*.py`) so the same clients, invoices, escalations,
|
|
7
|
+
and risk-scoring data you'd otherwise reach through the dashboard or the
|
|
8
|
+
raw HTTP API is scriptable from a terminal or an agent.
|
|
9
|
+
|
|
10
|
+

|
|
11
|
+
|
|
12
|
+
## Install
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
pip install freelancer-payment-protection-cli
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
Or with [uv](https://docs.astral.sh/uv/) / [pipx](https://pipx.pypa.io/):
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
uvx freelancer-payment-protection-cli --help
|
|
22
|
+
pipx install freelancer-payment-protection-cli
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Requires Python 3.10+. The console command is installed as both `fpp`
|
|
26
|
+
(short form) and `freelancer-payment-protection` (full form) — they are the
|
|
27
|
+
same entry point.
|
|
28
|
+
|
|
29
|
+
## Configuration
|
|
30
|
+
|
|
31
|
+
The CLI needs to know two things: which backend API to talk to, and which
|
|
32
|
+
Supabase project issues your login tokens.
|
|
33
|
+
|
|
34
|
+
| Variable | Purpose | Default |
|
|
35
|
+
|---|---|---|
|
|
36
|
+
| `FPP_API_URL` | Backend API base URL | `http://localhost:8000` |
|
|
37
|
+
| `FPP_SUPABASE_URL` (or `SUPABASE_URL`) | Your Supabase project URL | none, required for `login` |
|
|
38
|
+
| `FPP_SUPABASE_ANON_KEY` (or `SUPABASE_ANON_KEY`) | Your Supabase project's anon key | none, required for `login` |
|
|
39
|
+
|
|
40
|
+
The anon key is the same public key the web app's Supabase client uses —
|
|
41
|
+
it is not a secret, and it's the key already in `apps/web/.env.example` as
|
|
42
|
+
`NEXT_PUBLIC_SUPABASE_ANON_KEY`.
|
|
43
|
+
|
|
44
|
+
## Login and authentication
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
export FPP_SUPABASE_URL="https://your-project.supabase.co"
|
|
48
|
+
export FPP_SUPABASE_ANON_KEY="your-anon-key"
|
|
49
|
+
fpp login
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
`fpp login` prompts for your email and password, then calls Supabase's own
|
|
53
|
+
REST auth endpoint directly:
|
|
54
|
+
|
|
55
|
+
```
|
|
56
|
+
POST {SUPABASE_URL}/auth/v1/token?grant_type=password
|
|
57
|
+
Header: apikey: <anon key>
|
|
58
|
+
Body: {"email": ..., "password": ...}
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
This is the same call the web dashboard's Supabase client makes — the CLI
|
|
62
|
+
never talks to a login endpoint of its own, because the backend
|
|
63
|
+
(`apps/api/app/middleware/auth.py`) doesn't mint tokens; it only validates
|
|
64
|
+
Supabase-issued JWTs. On success, the returned `access_token` and
|
|
65
|
+
`refresh_token` are cached to `~/.config/freelancer-payment-protection-cli/credentials.json`
|
|
66
|
+
(mode 600). Every later command reads the JWT's `workspace_id` claim the
|
|
67
|
+
same way `get_current_workspace()` does server-side, and transparently
|
|
68
|
+
refreshes the access token with the cached refresh token once it expires.
|
|
69
|
+
|
|
70
|
+
Run `fpp logout` to delete the cached credentials, or `fpp whoami` to see
|
|
71
|
+
which workspace and email the cached token resolves to.
|
|
72
|
+
|
|
73
|
+
## Commands
|
|
74
|
+
|
|
75
|
+
Every data-returning command supports `--json` for scripting and agent use.
|
|
76
|
+
|
|
77
|
+
```
|
|
78
|
+
fpp login Log in (prompts for email/password)
|
|
79
|
+
fpp logout Remove cached credentials
|
|
80
|
+
fpp whoami [--json] Show cached workspace/session info
|
|
81
|
+
|
|
82
|
+
fpp invoice list [--status] [--client-id] [--page] [--page-size] [--json]
|
|
83
|
+
fpp invoice create --client-id --invoice-number --amount --due-date [...] [--json]
|
|
84
|
+
fpp invoice show <invoice-id> [--json]
|
|
85
|
+
fpp invoice set-status <invoice-id> <status> [--json]
|
|
86
|
+
|
|
87
|
+
fpp escalation list [--json] Active escalations, grouped by stage
|
|
88
|
+
fpp escalation status <invoice-id> [--json] Current stage + full history
|
|
89
|
+
fpp escalation advance <invoice-id> [--json] AI-draft the next stage's email
|
|
90
|
+
|
|
91
|
+
fpp client list [--risk-level] [--search] [--page] [--page-size] [--json]
|
|
92
|
+
fpp client show <client-id> [--json]
|
|
93
|
+
fpp client risk <client-id> [--json] Compute/refresh the AI risk score
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
Run `fpp --help` or `fpp <command> --help` for full flag references.
|
|
97
|
+
|
|
98
|
+

|
|
99
|
+
|
|
100
|
+
### A note on `escalation advance`
|
|
101
|
+
|
|
102
|
+
The backend's escalation router (`apps/api/app/routers/escalations.py`)
|
|
103
|
+
exposes exactly three routes: list active escalations, draft the next
|
|
104
|
+
stage's email, and read history. There is no route that persists a stage
|
|
105
|
+
change — `draft_escalation_email` in `escalation_service.py` computes and
|
|
106
|
+
returns what the next stage's email would say, but never writes
|
|
107
|
+
`escalation_stage` back to the database. `fpp escalation advance` calls
|
|
108
|
+
that draft endpoint and shows you the preview; it does not claim to move
|
|
109
|
+
the invoice to the next stage, because the API it wraps doesn't do that
|
|
110
|
+
either.
|
|
111
|
+
|
|
112
|
+
### JSON output for agents/scripts
|
|
113
|
+
|
|
114
|
+
```bash
|
|
115
|
+
fpp invoice list --status overdue --json | jq '.[] | {id, invoiceNumber, daysPastDue}'
|
|
116
|
+
fpp client risk "$CLIENT_ID" --json | jq '.level'
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
Field names in `--json` output match the backend's actual Pydantic response
|
|
120
|
+
models exactly (camelCase, e.g. `invoiceNumber`, `daysPastDue`,
|
|
121
|
+
`escalationStage`) since the CLI passes the API's response straight
|
|
122
|
+
through rather than remapping it.
|
|
123
|
+
|
|
124
|
+
## FAQ
|
|
125
|
+
|
|
126
|
+
**What is this, and how is it different from just calling the API with curl?**
|
|
127
|
+
It's a typed command-line wrapper around the same backend the web
|
|
128
|
+
dashboard uses — invoices, escalations, and client risk scoring — with
|
|
129
|
+
persistent login (so you're not re-attaching a bearer token to every
|
|
130
|
+
request), human-readable tables by default, and a `--json` flag on every
|
|
131
|
+
data command for piping into `jq`, scripts, or an agent's tool-calling
|
|
132
|
+
loop. The differentiator versus curl is session handling (login once,
|
|
133
|
+
silent token refresh after) and consistent, documented output shapes
|
|
134
|
+
instead of hand-rolled request construction.
|
|
135
|
+
|
|
136
|
+
**What platforms and Python versions does it support?**
|
|
137
|
+
Python 3.10 through 3.13, on any OS `pip`/`uvx`/`pipx` runs on (Linux,
|
|
138
|
+
macOS, Windows). It has no compiled dependencies — the only runtime
|
|
139
|
+
dependencies are `click` and `httpx`, both pure-Python-installable wheels.
|
|
140
|
+
|
|
141
|
+
**How do I log in, and where are my credentials stored?**
|
|
142
|
+
Run `fpp login`, enter your email and password when prompted. The CLI
|
|
143
|
+
calls Supabase's password-grant token endpoint directly (see "Login and
|
|
144
|
+
authentication" above) and caches the resulting access and refresh tokens
|
|
145
|
+
to `~/.config/freelancer-payment-protection-cli/credentials.json` with
|
|
146
|
+
file mode 600 (owner read/write only). Nothing is sent anywhere except
|
|
147
|
+
Supabase's own auth endpoint and the backend API you configure via
|
|
148
|
+
`FPP_API_URL`.
|
|
149
|
+
|
|
150
|
+
**I ran a command and got `Error: Internal Server Error` — is that a CLI bug?**
|
|
151
|
+
Usually not. The CLI's error handling surfaces whatever the backend
|
|
152
|
+
returned; a 500 means the backend itself failed. Two real, backend-side
|
|
153
|
+
causes to check first: (1) `fpp client risk` and `fpp escalation advance`
|
|
154
|
+
both trigger AI calls through `packages/legal_ai/client.py` — if
|
|
155
|
+
`ANTHROPIC_API_KEY` isn't a real key on the server, `client risk` falls
|
|
156
|
+
back to a heuristic score automatically, but `escalation advance` has no
|
|
157
|
+
such fallback and will 500. (2) Confirm you're running a backend build
|
|
158
|
+
that includes the `packages/legal_ai` module-name fix and the
|
|
159
|
+
`risk_scoring.py` request-parameter fix — both were real bugs in this
|
|
160
|
+
repo (a hyphenated `packages/legal-ai` directory that the code imports as
|
|
161
|
+
`packages.legal_ai`, and a rate-limiter/parameter-name collision on
|
|
162
|
+
`/api/v1/risk/score`) that this CLI's own end-to-end testing surfaced and
|
|
163
|
+
this same change fixed. A plain 401 means your cached token expired and
|
|
164
|
+
couldn't refresh — run `fpp login` again.
|
|
165
|
+
|
|
166
|
+
**Does `fpp escalation advance` actually send the escalation email or move the invoice to the next stage?**
|
|
167
|
+
No. It calls the backend's `/api/v1/escalations/{id}/draft` endpoint,
|
|
168
|
+
which only returns an AI-drafted preview of what the next stage's email
|
|
169
|
+
would say. The backend has no endpoint today that persists a stage change
|
|
170
|
+
or sends the email — see "A note on `escalation advance`" above.
|
|
171
|
+
|
|
172
|
+
**Can I point this at a self-hosted or non-default backend?**
|
|
173
|
+
Yes. Set `FPP_API_URL` to your backend's base URL (default is
|
|
174
|
+
`http://localhost:8000`, matching the FastAPI dev server's default port).
|
|
175
|
+
Every command reads that variable at call time, so switching environments
|
|
176
|
+
is just re-exporting it.
|
|
177
|
+
|
|
178
|
+
**What's the licensing situation — can I use or modify this commercially?**
|
|
179
|
+
This CLI ships from the same repository as, and under the same license
|
|
180
|
+
as, freelancer-payment-protection itself: a proprietary license, copyright
|
|
181
|
+
Rudrendu Paul and Sourav Nandy, all rights reserved. Personal, academic,
|
|
182
|
+
commercial, or scheduled use requires explicit written permission from
|
|
183
|
+
both owners — see the `LICENSE` file. This is not an MIT/Apache-style
|
|
184
|
+
open-source license; publishing it to PyPI makes it installable, not
|
|
185
|
+
freely reusable.
|
|
186
|
+
|
|
187
|
+
**Why isn't there a hosted/default API URL I can just start using?**
|
|
188
|
+
freelancer-payment-protection is self-hosted software, not a hosted
|
|
189
|
+
multi-tenant SaaS with a public API endpoint today. You (or whoever
|
|
190
|
+
operates your workspace) runs the backend; the CLI just needs its URL.
|
|
191
|
+
|
|
192
|
+
## Development
|
|
193
|
+
|
|
194
|
+
```bash
|
|
195
|
+
cd packages/cli
|
|
196
|
+
python -m venv .venv && source .venv/bin/activate
|
|
197
|
+
pip install -e ".[dev]"
|
|
198
|
+
pytest
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
All HTTP calls (to Supabase and to the backend API) are mocked in tests
|
|
202
|
+
with `respx` — the test suite makes no live network calls.
|
|
203
|
+
|
|
204
|
+
## Contributing
|
|
205
|
+
|
|
206
|
+
This package lives inside the freelancer-payment-protection monorepo. See
|
|
207
|
+
the repository root for contribution guidelines.
|
|
208
|
+
|
|
209
|
+
## License
|
|
210
|
+
|
|
211
|
+
Proprietary — see [`LICENSE`](./LICENSE). Contact the owners (see
|
|
212
|
+
`LICENSE`) before any use beyond installing and running the package as
|
|
213
|
+
published.
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "freelancer-payment-protection-cli"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Command-line client for freelancer-payment-protection: automated invoice escalation, legal document generation, and client risk scoring for freelancers."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.10"
|
|
11
|
+
license = { text = "Proprietary" }
|
|
12
|
+
authors = [
|
|
13
|
+
{ name = "Rudrendu Paul" },
|
|
14
|
+
{ name = "Sourav Nandy" },
|
|
15
|
+
]
|
|
16
|
+
keywords = ["invoicing", "freelance", "cli", "invoice-escalation", "risk-scoring", "agent-native", "supabase", "fastapi"]
|
|
17
|
+
classifiers = [
|
|
18
|
+
"Development Status :: 3 - Alpha",
|
|
19
|
+
"Environment :: Console",
|
|
20
|
+
"Intended Audience :: Developers",
|
|
21
|
+
"Operating System :: OS Independent",
|
|
22
|
+
"Programming Language :: Python :: 3",
|
|
23
|
+
"Programming Language :: Python :: 3.10",
|
|
24
|
+
"Programming Language :: Python :: 3.11",
|
|
25
|
+
"Programming Language :: Python :: 3.12",
|
|
26
|
+
"Programming Language :: Python :: 3.13",
|
|
27
|
+
"Topic :: Office/Business :: Financial :: Accounting",
|
|
28
|
+
"Topic :: Utilities",
|
|
29
|
+
]
|
|
30
|
+
dependencies = [
|
|
31
|
+
"click>=8.1,<9",
|
|
32
|
+
"httpx>=0.27,<1",
|
|
33
|
+
]
|
|
34
|
+
|
|
35
|
+
[project.optional-dependencies]
|
|
36
|
+
dev = [
|
|
37
|
+
"pytest>=8.0,<9",
|
|
38
|
+
"pytest-cov>=5.0,<7",
|
|
39
|
+
"respx>=0.21,<1",
|
|
40
|
+
"build>=1.0,<2",
|
|
41
|
+
"twine>=5.0,<7",
|
|
42
|
+
]
|
|
43
|
+
|
|
44
|
+
[project.urls]
|
|
45
|
+
Homepage = "https://github.com/RudrenduPaul/freelancer-payment-protection"
|
|
46
|
+
Repository = "https://github.com/RudrenduPaul/freelancer-payment-protection"
|
|
47
|
+
"Bug Tracker" = "https://github.com/RudrenduPaul/freelancer-payment-protection/issues"
|
|
48
|
+
|
|
49
|
+
[project.scripts]
|
|
50
|
+
fpp = "freelancer_payment_protection_cli.main:main"
|
|
51
|
+
freelancer-payment-protection = "freelancer_payment_protection_cli.main:main"
|
|
52
|
+
|
|
53
|
+
[tool.hatch.build]
|
|
54
|
+
# This package lives in a subdirectory (packages/cli/) of a larger
|
|
55
|
+
# monorepo whose .git directory is at the repo root, alongside Node/
|
|
56
|
+
# Turborepo config that has nothing to do with this Python package.
|
|
57
|
+
ignore-vcs = true
|
|
58
|
+
|
|
59
|
+
[tool.hatch.build.targets.wheel]
|
|
60
|
+
packages = ["src/freelancer_payment_protection_cli"]
|
|
61
|
+
|
|
62
|
+
[tool.hatch.build.targets.sdist]
|
|
63
|
+
only-include = [
|
|
64
|
+
"src/freelancer_payment_protection_cli",
|
|
65
|
+
"tests",
|
|
66
|
+
"README.md",
|
|
67
|
+
"LICENSE",
|
|
68
|
+
]
|
|
69
|
+
|
|
70
|
+
[tool.pytest.ini_options]
|
|
71
|
+
testpaths = ["tests"]
|