hevn-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.
- hevn_cli-0.1.0/LICENSE +21 -0
- hevn_cli-0.1.0/PKG-INFO +189 -0
- hevn_cli-0.1.0/README.md +153 -0
- hevn_cli-0.1.0/pyproject.toml +56 -0
- hevn_cli-0.1.0/src/hevn_cli/__init__.py +8 -0
- hevn_cli-0.1.0/src/hevn_cli/api/__init__.py +6 -0
- hevn_cli-0.1.0/src/hevn_cli/api/app.py +163 -0
- hevn_cli-0.1.0/src/hevn_cli/api/base.py +40 -0
- hevn_cli-0.1.0/src/hevn_cli/api/mcp.py +50 -0
- hevn_cli-0.1.0/src/hevn_cli/api/public.py +16 -0
- hevn_cli-0.1.0/src/hevn_cli/client.py +123 -0
- hevn_cli-0.1.0/src/hevn_cli/commands/__init__.py +1 -0
- hevn_cli-0.1.0/src/hevn_cli/commands/account.py +451 -0
- hevn_cli-0.1.0/src/hevn_cli/commands/auth.py +217 -0
- hevn_cli-0.1.0/src/hevn_cli/commands/balance.py +237 -0
- hevn_cli-0.1.0/src/hevn_cli/commands/banks.py +233 -0
- hevn_cli-0.1.0/src/hevn_cli/commands/cards.py +442 -0
- hevn_cli-0.1.0/src/hevn_cli/commands/contacts.py +444 -0
- hevn_cli-0.1.0/src/hevn_cli/commands/contractors.py +224 -0
- hevn_cli-0.1.0/src/hevn_cli/commands/contracts.py +528 -0
- hevn_cli-0.1.0/src/hevn_cli/commands/invoices.py +676 -0
- hevn_cli-0.1.0/src/hevn_cli/commands/status.py +180 -0
- hevn_cli-0.1.0/src/hevn_cli/commands/transfer.py +494 -0
- hevn_cli-0.1.0/src/hevn_cli/config.py +73 -0
- hevn_cli-0.1.0/src/hevn_cli/env.py +75 -0
- hevn_cli-0.1.0/src/hevn_cli/formatters/__init__.py +1 -0
- hevn_cli-0.1.0/src/hevn_cli/formatters/invoices.py +61 -0
- hevn_cli-0.1.0/src/hevn_cli/main.py +167 -0
- hevn_cli-0.1.0/src/hevn_cli/normalize.py +24 -0
- hevn_cli-0.1.0/src/hevn_cli/output.py +26 -0
- hevn_cli-0.1.0/src/hevn_cli/parsing.py +57 -0
- hevn_cli-0.1.0/src/hevn_cli/progress.py +52 -0
- hevn_cli-0.1.0/src/hevn_cli/prompts.py +54 -0
- hevn_cli-0.1.0/src/hevn_cli/render.py +54 -0
- hevn_cli-0.1.0/src/hevn_cli/res/__init__.py +0 -0
- hevn_cli-0.1.0/src/hevn_cli/res/login_complete.html +11 -0
hevn_cli-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 HEVN, Inc.
|
|
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.
|
hevn_cli-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: hevn-cli
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Command-line client for the HEVN backend API and MCP transfers.
|
|
5
|
+
License: MIT
|
|
6
|
+
License-File: LICENSE
|
|
7
|
+
Keywords: hevn,cli,fintech,payments,invoicing,mcp,stablecoin
|
|
8
|
+
Author: HEVN Team
|
|
9
|
+
Author-email: team@hevn.finance
|
|
10
|
+
Requires-Python: >=3.11,<4.0
|
|
11
|
+
Classifier: Development Status :: 4 - Beta
|
|
12
|
+
Classifier: Environment :: Console
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
15
|
+
Classifier: Operating System :: OS Independent
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
21
|
+
Classifier: Topic :: Office/Business :: Financial
|
|
22
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
23
|
+
Requires-Dist: click (>=8.1.8,<8.2)
|
|
24
|
+
Requires-Dist: httpx (==0.28.1)
|
|
25
|
+
Requires-Dist: prompt-toolkit (==3.0.36)
|
|
26
|
+
Requires-Dist: python-dotenv (==1.2.2)
|
|
27
|
+
Requires-Dist: pyyaml (==6.0.3)
|
|
28
|
+
Requires-Dist: qrcode (==8.2)
|
|
29
|
+
Requires-Dist: questionary (==2.1.0)
|
|
30
|
+
Requires-Dist: rich (==13.9.4)
|
|
31
|
+
Requires-Dist: typer[all] (==0.15.1)
|
|
32
|
+
Project-URL: Homepage, https://github.com/hevn/hevn-cli
|
|
33
|
+
Project-URL: Repository, https://github.com/hevn/hevn-cli
|
|
34
|
+
Description-Content-Type: text/markdown
|
|
35
|
+
|
|
36
|
+
# HEVN CLI
|
|
37
|
+
|
|
38
|
+
Standalone command-line wrapper for the [HEVN](https://app.gethevn.com) backend API and MCP transfers.
|
|
39
|
+
|
|
40
|
+
> Requires an active HEVN account. Sign up at [app.gethevn.com](https://app.gethevn.com) before installing.
|
|
41
|
+
|
|
42
|
+
## Install
|
|
43
|
+
|
|
44
|
+
For end users:
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
pipx install hevn-cli
|
|
48
|
+
hevn login
|
|
49
|
+
hevn --help
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Before the package is published to PyPI, install from GitHub so `pipx upgrade`
|
|
53
|
+
can fetch newer commits:
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
pipx install "git+https://github.com/hevn/hevn-cli.git"
|
|
57
|
+
pipx upgrade hevn-cli
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
For local development:
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
poetry install
|
|
64
|
+
poetry run hevn --help
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
For local wheel smoke testing only:
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
poetry build
|
|
71
|
+
pipx install dist/hevn_cli-0.1.0-py3-none-any.whl
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
Local wheel installs are not a good long-term `pipx` install source because
|
|
75
|
+
`pipx upgrade hevn-cli` can only reinstall from that same local artifact.
|
|
76
|
+
|
|
77
|
+
## Publish
|
|
78
|
+
|
|
79
|
+
PyPI publishing is handled by the `Publish to PyPI` GitHub Actions workflow.
|
|
80
|
+
Run it manually from the `main` branch. The workflow runs lint, tests, and
|
|
81
|
+
`hevn --help` on Ubuntu, macOS, and Windows, then builds and publishes the
|
|
82
|
+
package once from Ubuntu.
|
|
83
|
+
|
|
84
|
+
The workflow uses PyPI trusted publishing. In the PyPI project `hevn-cli`,
|
|
85
|
+
configure a trusted publisher with GitHub owner `hevn`, repository `hevn-cli`,
|
|
86
|
+
workflow `.github/workflows/publish-pypi.yml`, and environment `pypi`.
|
|
87
|
+
|
|
88
|
+
## Environment
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
export HEVN_ENV="prod" # prod (default), dev, or local
|
|
92
|
+
export HEVN_API_KEY="<app bearer token or app api key>"
|
|
93
|
+
export HEVN_MCP_KEY="mcp_..."
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
Optional:
|
|
97
|
+
|
|
98
|
+
```bash
|
|
99
|
+
export HEVN_BASE_URL="https://api.hevn.finance/api/v1"
|
|
100
|
+
export HEVN_SITE_URL="https://app.gethevn.com"
|
|
101
|
+
export HEVN_API_KEY_HEADER="Authorization"
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
`Authorization` is the default app header and is sent as `Bearer <HEVN_API_KEY>`.
|
|
105
|
+
Use `HEVN_API_KEY_HEADER=X-API-Key` if a deployment exposes app endpoints by API key.
|
|
106
|
+
Use `HEVN_*` environment variables for CLI configuration.
|
|
107
|
+
|
|
108
|
+
Built-in environments:
|
|
109
|
+
|
|
110
|
+
| Env | Site URL | API URL |
|
|
111
|
+
| --- | --- | --- |
|
|
112
|
+
| `prod` | `https://app.gethevn.com` | `https://api.hevn.finance/api/v1` |
|
|
113
|
+
| `dev` | `https://app-beta.hevn.finance` | `https://dev-api.hevn.finance/api/v1` |
|
|
114
|
+
| `local` | `http://localhost:8081` | `http://localhost:8000/api/v1` |
|
|
115
|
+
|
|
116
|
+
You can also select the environment per command:
|
|
117
|
+
|
|
118
|
+
```bash
|
|
119
|
+
hevn --env dev account get
|
|
120
|
+
hevn --env local login
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
Clear saved CLI credentials:
|
|
124
|
+
|
|
125
|
+
```bash
|
|
126
|
+
hevn logout
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
## Examples
|
|
130
|
+
|
|
131
|
+
```bash
|
|
132
|
+
hevn contacts list
|
|
133
|
+
hevn contacts list --yaml
|
|
134
|
+
hevn login
|
|
135
|
+
hevn account list --yaml
|
|
136
|
+
hevn banks list --all
|
|
137
|
+
hevn contacts new
|
|
138
|
+
hevn contacts new --type email --name "Vendor" --email vendor@example.com
|
|
139
|
+
hevn transfer contact <contact-id> 25 --memo "Thanks"
|
|
140
|
+
hevn transfer contact --contact-id <contact-id> --amount 25 --memo "Thanks"
|
|
141
|
+
hevn transfer contact --contact-id <contact-id> --amount 25 --yes --memo "Bank payout"
|
|
142
|
+
hevn transfer contact --contact-id <contact-id> --amount 25 --bank-account-id <bank-account-id> --yes --memo "Bank payout"
|
|
143
|
+
hevn transfer contact --contact-id <contact-id> --quote-id <quote-id> --memo "Bank payout"
|
|
144
|
+
hevn invoice list
|
|
145
|
+
hevn bills --yaml
|
|
146
|
+
hevn invoice get <invoice-id>
|
|
147
|
+
hevn invoice decline --invoice-id <invoice-id> --yes --yaml
|
|
148
|
+
hevn invoice new
|
|
149
|
+
hevn invoice new --contractor-email vendor@example.com --contractor-name Vendor --item "Consulting:1:100" --due-date 2026-06-01
|
|
150
|
+
hevn invoice upload-incoming --path ./invoice.pdf --contractor-email vendor@example.com --items '[{"name":"Consulting","quantity":1,"price":"100"}]'
|
|
151
|
+
hevn invoice batch --contract '{"contractId":"<contract-id>","period":0,"memo":"May payroll","items":[{"name":"Monthly services","quantity":1,"price":"5000"}]}' --yaml
|
|
152
|
+
hevn contracts list --yaml
|
|
153
|
+
hevn contractors list --yaml
|
|
154
|
+
hevn contracts preview --id <contract-id> --yaml
|
|
155
|
+
hevn contracts new --contractor-email contractor@example.com --type default_contractor --amount 1000 --currency USD --period monthly --activation-at 2026-05-01 --yaml
|
|
156
|
+
hevn contracts new --contractor-email contractor@example.com --type custom --document-id <document-id> --item '{"name":"Design","quantity":2,"price":"100"}' --yaml
|
|
157
|
+
hevn contracts payment-methods --id <contract-id> --payment-method '{"accountType":"email","email":"contractor@example.com"}' --yaml
|
|
158
|
+
hevn contracts --id <contract-id> approve --yaml
|
|
159
|
+
hevn hire --contractor-email contractor@example.com --job-title "Engineer" --scope-description "Full-time engineering work" --amount 5000 --currency USD --period monthly --start-date 2026-05-01 --yaml
|
|
160
|
+
hevn transfer --invoice-id <invoice-id> --memo "Invoice payment"
|
|
161
|
+
hevn deposit 100 usdc base
|
|
162
|
+
hevn deposit --amount 100 --currency usdc --chain base
|
|
163
|
+
hevn rate EUR
|
|
164
|
+
hevn rate --currency AED --yaml
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
Contract statuses:
|
|
168
|
+
|
|
169
|
+
```text
|
|
170
|
+
pending_approval_by_contractor
|
|
171
|
+
pending_approval_by_client
|
|
172
|
+
active
|
|
173
|
+
completed
|
|
174
|
+
cancelled
|
|
175
|
+
paused
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
`hevn hire` is a convenience alias for creating a `contractor` contract.
|
|
179
|
+
Preview it with `hevn contracts preview --id <contract-id> --yaml`, then approve
|
|
180
|
+
with `hevn contracts --id <contract-id> approve --yaml`.
|
|
181
|
+
|
|
182
|
+
## Contract migration guidance
|
|
183
|
+
|
|
184
|
+
When a user asks to move or import contracts or contractors into HEVN, load them
|
|
185
|
+
by creating a new active contract from the contract file rather than only adding
|
|
186
|
+
contractor records. For Deel migrations, first suggest that the user create a
|
|
187
|
+
Deel API token at
|
|
188
|
+
[app.deel.com/settings/developer/tokens/create](https://app.deel.com/settings/developer/tokens/create).
|
|
189
|
+
|
hevn_cli-0.1.0/README.md
ADDED
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
# HEVN CLI
|
|
2
|
+
|
|
3
|
+
Standalone command-line wrapper for the [HEVN](https://app.gethevn.com) backend API and MCP transfers.
|
|
4
|
+
|
|
5
|
+
> Requires an active HEVN account. Sign up at [app.gethevn.com](https://app.gethevn.com) before installing.
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
For end users:
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
pipx install hevn-cli
|
|
13
|
+
hevn login
|
|
14
|
+
hevn --help
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Before the package is published to PyPI, install from GitHub so `pipx upgrade`
|
|
18
|
+
can fetch newer commits:
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
pipx install "git+https://github.com/hevn/hevn-cli.git"
|
|
22
|
+
pipx upgrade hevn-cli
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
For local development:
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
poetry install
|
|
29
|
+
poetry run hevn --help
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
For local wheel smoke testing only:
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
poetry build
|
|
36
|
+
pipx install dist/hevn_cli-0.1.0-py3-none-any.whl
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Local wheel installs are not a good long-term `pipx` install source because
|
|
40
|
+
`pipx upgrade hevn-cli` can only reinstall from that same local artifact.
|
|
41
|
+
|
|
42
|
+
## Publish
|
|
43
|
+
|
|
44
|
+
PyPI publishing is handled by the `Publish to PyPI` GitHub Actions workflow.
|
|
45
|
+
Run it manually from the `main` branch. The workflow runs lint, tests, and
|
|
46
|
+
`hevn --help` on Ubuntu, macOS, and Windows, then builds and publishes the
|
|
47
|
+
package once from Ubuntu.
|
|
48
|
+
|
|
49
|
+
The workflow uses PyPI trusted publishing. In the PyPI project `hevn-cli`,
|
|
50
|
+
configure a trusted publisher with GitHub owner `hevn`, repository `hevn-cli`,
|
|
51
|
+
workflow `.github/workflows/publish-pypi.yml`, and environment `pypi`.
|
|
52
|
+
|
|
53
|
+
## Environment
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
export HEVN_ENV="prod" # prod (default), dev, or local
|
|
57
|
+
export HEVN_API_KEY="<app bearer token or app api key>"
|
|
58
|
+
export HEVN_MCP_KEY="mcp_..."
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Optional:
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
export HEVN_BASE_URL="https://api.hevn.finance/api/v1"
|
|
65
|
+
export HEVN_SITE_URL="https://app.gethevn.com"
|
|
66
|
+
export HEVN_API_KEY_HEADER="Authorization"
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
`Authorization` is the default app header and is sent as `Bearer <HEVN_API_KEY>`.
|
|
70
|
+
Use `HEVN_API_KEY_HEADER=X-API-Key` if a deployment exposes app endpoints by API key.
|
|
71
|
+
Use `HEVN_*` environment variables for CLI configuration.
|
|
72
|
+
|
|
73
|
+
Built-in environments:
|
|
74
|
+
|
|
75
|
+
| Env | Site URL | API URL |
|
|
76
|
+
| --- | --- | --- |
|
|
77
|
+
| `prod` | `https://app.gethevn.com` | `https://api.hevn.finance/api/v1` |
|
|
78
|
+
| `dev` | `https://app-beta.hevn.finance` | `https://dev-api.hevn.finance/api/v1` |
|
|
79
|
+
| `local` | `http://localhost:8081` | `http://localhost:8000/api/v1` |
|
|
80
|
+
|
|
81
|
+
You can also select the environment per command:
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
hevn --env dev account get
|
|
85
|
+
hevn --env local login
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
Clear saved CLI credentials:
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
hevn logout
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
## Examples
|
|
95
|
+
|
|
96
|
+
```bash
|
|
97
|
+
hevn contacts list
|
|
98
|
+
hevn contacts list --yaml
|
|
99
|
+
hevn login
|
|
100
|
+
hevn account list --yaml
|
|
101
|
+
hevn banks list --all
|
|
102
|
+
hevn contacts new
|
|
103
|
+
hevn contacts new --type email --name "Vendor" --email vendor@example.com
|
|
104
|
+
hevn transfer contact <contact-id> 25 --memo "Thanks"
|
|
105
|
+
hevn transfer contact --contact-id <contact-id> --amount 25 --memo "Thanks"
|
|
106
|
+
hevn transfer contact --contact-id <contact-id> --amount 25 --yes --memo "Bank payout"
|
|
107
|
+
hevn transfer contact --contact-id <contact-id> --amount 25 --bank-account-id <bank-account-id> --yes --memo "Bank payout"
|
|
108
|
+
hevn transfer contact --contact-id <contact-id> --quote-id <quote-id> --memo "Bank payout"
|
|
109
|
+
hevn invoice list
|
|
110
|
+
hevn bills --yaml
|
|
111
|
+
hevn invoice get <invoice-id>
|
|
112
|
+
hevn invoice decline --invoice-id <invoice-id> --yes --yaml
|
|
113
|
+
hevn invoice new
|
|
114
|
+
hevn invoice new --contractor-email vendor@example.com --contractor-name Vendor --item "Consulting:1:100" --due-date 2026-06-01
|
|
115
|
+
hevn invoice upload-incoming --path ./invoice.pdf --contractor-email vendor@example.com --items '[{"name":"Consulting","quantity":1,"price":"100"}]'
|
|
116
|
+
hevn invoice batch --contract '{"contractId":"<contract-id>","period":0,"memo":"May payroll","items":[{"name":"Monthly services","quantity":1,"price":"5000"}]}' --yaml
|
|
117
|
+
hevn contracts list --yaml
|
|
118
|
+
hevn contractors list --yaml
|
|
119
|
+
hevn contracts preview --id <contract-id> --yaml
|
|
120
|
+
hevn contracts new --contractor-email contractor@example.com --type default_contractor --amount 1000 --currency USD --period monthly --activation-at 2026-05-01 --yaml
|
|
121
|
+
hevn contracts new --contractor-email contractor@example.com --type custom --document-id <document-id> --item '{"name":"Design","quantity":2,"price":"100"}' --yaml
|
|
122
|
+
hevn contracts payment-methods --id <contract-id> --payment-method '{"accountType":"email","email":"contractor@example.com"}' --yaml
|
|
123
|
+
hevn contracts --id <contract-id> approve --yaml
|
|
124
|
+
hevn hire --contractor-email contractor@example.com --job-title "Engineer" --scope-description "Full-time engineering work" --amount 5000 --currency USD --period monthly --start-date 2026-05-01 --yaml
|
|
125
|
+
hevn transfer --invoice-id <invoice-id> --memo "Invoice payment"
|
|
126
|
+
hevn deposit 100 usdc base
|
|
127
|
+
hevn deposit --amount 100 --currency usdc --chain base
|
|
128
|
+
hevn rate EUR
|
|
129
|
+
hevn rate --currency AED --yaml
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
Contract statuses:
|
|
133
|
+
|
|
134
|
+
```text
|
|
135
|
+
pending_approval_by_contractor
|
|
136
|
+
pending_approval_by_client
|
|
137
|
+
active
|
|
138
|
+
completed
|
|
139
|
+
cancelled
|
|
140
|
+
paused
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
`hevn hire` is a convenience alias for creating a `contractor` contract.
|
|
144
|
+
Preview it with `hevn contracts preview --id <contract-id> --yaml`, then approve
|
|
145
|
+
with `hevn contracts --id <contract-id> approve --yaml`.
|
|
146
|
+
|
|
147
|
+
## Contract migration guidance
|
|
148
|
+
|
|
149
|
+
When a user asks to move or import contracts or contractors into HEVN, load them
|
|
150
|
+
by creating a new active contract from the contract file rather than only adding
|
|
151
|
+
contractor records. For Deel migrations, first suggest that the user create a
|
|
152
|
+
Deel API token at
|
|
153
|
+
[app.deel.com/settings/developer/tokens/create](https://app.deel.com/settings/developer/tokens/create).
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
[tool.poetry]
|
|
2
|
+
name = "hevn-cli"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "Command-line client for the HEVN backend API and MCP transfers."
|
|
5
|
+
authors = ["HEVN Team <team@hevn.finance>"]
|
|
6
|
+
license = "MIT"
|
|
7
|
+
readme = "README.md"
|
|
8
|
+
homepage = "https://github.com/hevn/hevn-cli"
|
|
9
|
+
repository = "https://github.com/hevn/hevn-cli"
|
|
10
|
+
keywords = ["hevn", "cli", "fintech", "payments", "invoicing", "mcp", "stablecoin"]
|
|
11
|
+
classifiers = [
|
|
12
|
+
"Development Status :: 4 - Beta",
|
|
13
|
+
"Environment :: Console",
|
|
14
|
+
"Intended Audience :: Developers",
|
|
15
|
+
"License :: OSI Approved :: MIT License",
|
|
16
|
+
"Operating System :: OS Independent",
|
|
17
|
+
"Programming Language :: Python :: 3",
|
|
18
|
+
"Programming Language :: Python :: 3.11",
|
|
19
|
+
"Programming Language :: Python :: 3.12",
|
|
20
|
+
"Topic :: Office/Business :: Financial",
|
|
21
|
+
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
22
|
+
]
|
|
23
|
+
packages = [{ include = "hevn_cli", from = "src" }]
|
|
24
|
+
include = [{ path = "src/hevn_cli/res/*", format = ["sdist", "wheel"] }]
|
|
25
|
+
|
|
26
|
+
[tool.poetry.dependencies]
|
|
27
|
+
python = "^3.11"
|
|
28
|
+
httpx = "0.28.1"
|
|
29
|
+
click = ">=8.1.8,<8.2"
|
|
30
|
+
python-dotenv = "1.2.2"
|
|
31
|
+
qrcode = "8.2"
|
|
32
|
+
rich = "13.9.4"
|
|
33
|
+
typer = { extras = ["all"], version = "0.15.1" }
|
|
34
|
+
questionary = "2.1.0"
|
|
35
|
+
prompt-toolkit = "3.0.36"
|
|
36
|
+
pyyaml = "6.0.3"
|
|
37
|
+
|
|
38
|
+
[tool.poetry.group.dev.dependencies]
|
|
39
|
+
pytest = "8.4.2"
|
|
40
|
+
ruff = "0.7.4"
|
|
41
|
+
twine = "6.1.0"
|
|
42
|
+
|
|
43
|
+
[tool.poetry.scripts]
|
|
44
|
+
hevn = "hevn_cli.main:run"
|
|
45
|
+
|
|
46
|
+
[tool.ruff]
|
|
47
|
+
line-length = 120
|
|
48
|
+
target-version = "py311"
|
|
49
|
+
|
|
50
|
+
[tool.ruff.lint]
|
|
51
|
+
select = ["E", "F", "I", "W", "UP", "B"]
|
|
52
|
+
ignore = ["E501", "B008"]
|
|
53
|
+
|
|
54
|
+
[build-system]
|
|
55
|
+
requires = ["poetry-core"]
|
|
56
|
+
build-backend = "poetry.core.masonry.api"
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
5
|
+
from hevn_cli.api.base import ApiClient, AuthMode
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class AppApi:
|
|
9
|
+
def __init__(self, client: ApiClient | None = None) -> None:
|
|
10
|
+
self.client = client or ApiClient(AuthMode.APP)
|
|
11
|
+
|
|
12
|
+
def current_user(self) -> dict[str, Any]:
|
|
13
|
+
return self.client.get("/user")
|
|
14
|
+
|
|
15
|
+
def update_profile(self, payload: dict[str, Any]) -> dict[str, Any]:
|
|
16
|
+
return self.client.put("/user/kyc", json=payload)
|
|
17
|
+
|
|
18
|
+
def kyc_link(self) -> dict[str, Any]:
|
|
19
|
+
return self.client.post("/user/kyc_link")
|
|
20
|
+
|
|
21
|
+
def kyc_status(self, *, provider: str = "swipelux") -> dict[str, Any]:
|
|
22
|
+
return self.client.get("/user/kyc/status", params={"provider": provider})
|
|
23
|
+
|
|
24
|
+
def balance(self) -> dict[str, Any]:
|
|
25
|
+
return self.client.get("/balance")
|
|
26
|
+
|
|
27
|
+
def list_contacts(self, *, limit: int = 100, offset: int = 0) -> dict[str, Any]:
|
|
28
|
+
return self.client.get("/user/contacts", params={"limit": limit, "offset": offset})
|
|
29
|
+
|
|
30
|
+
def list_transactions(
|
|
31
|
+
self,
|
|
32
|
+
*,
|
|
33
|
+
limit: int = 50,
|
|
34
|
+
offset: int = 0,
|
|
35
|
+
transaction_type: str | None = None,
|
|
36
|
+
status: str | None = None,
|
|
37
|
+
income_only: bool | None = None,
|
|
38
|
+
bank_account_id: str | None = None,
|
|
39
|
+
) -> dict[str, Any]:
|
|
40
|
+
return self.client.get(
|
|
41
|
+
"/transactions",
|
|
42
|
+
params={
|
|
43
|
+
"limit": limit,
|
|
44
|
+
"offset": offset,
|
|
45
|
+
"type": transaction_type,
|
|
46
|
+
"status": status,
|
|
47
|
+
"incomeOnly": income_only,
|
|
48
|
+
"bankAccountId": bank_account_id,
|
|
49
|
+
},
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
def create_contact(self, payload: dict[str, Any]) -> dict[str, Any]:
|
|
53
|
+
return self.client.post("/user/contact", json=payload)
|
|
54
|
+
|
|
55
|
+
def update_contact(self, contact_id: str, payload: dict[str, Any]) -> dict[str, Any]:
|
|
56
|
+
return self.client.patch(f"/user/contacts/{contact_id}", json=payload)
|
|
57
|
+
|
|
58
|
+
def delete_contact(self, contact_id: str) -> Any:
|
|
59
|
+
return self.client.delete(f"/user/contacts/{contact_id}")
|
|
60
|
+
|
|
61
|
+
def upload_document(self, payload: dict[str, Any]) -> dict[str, Any]:
|
|
62
|
+
return self.client.post("/documents/upload", json=payload)
|
|
63
|
+
|
|
64
|
+
def list_invoices(self) -> dict[str, Any]:
|
|
65
|
+
return self.client.get("/documents/contracts/invoices")
|
|
66
|
+
|
|
67
|
+
def get_invoice(self, invoice_id: str) -> dict[str, Any]:
|
|
68
|
+
return self.client.get(f"/documents/contracts/invoices/{invoice_id}")
|
|
69
|
+
|
|
70
|
+
def create_invoice(self, payload: dict[str, Any]) -> dict[str, Any]:
|
|
71
|
+
return self.client.post("/documents/contracts/invoices", json=payload)
|
|
72
|
+
|
|
73
|
+
def create_uploaded_invoice(self, payload: dict[str, Any]) -> dict[str, Any]:
|
|
74
|
+
return self.client.post("/documents/contracts/invoices/uploaded", json=payload)
|
|
75
|
+
|
|
76
|
+
def update_invoice(self, invoice_id: str, payload: dict[str, Any]) -> dict[str, Any]:
|
|
77
|
+
return self.client.put(f"/documents/contracts/invoices/{invoice_id}", json=payload)
|
|
78
|
+
|
|
79
|
+
def create_invoice_from_contract(self, contract_id: str, payload: dict[str, Any]) -> dict[str, Any]:
|
|
80
|
+
return self.client.post(f"/documents/contracts/{contract_id}/create-invoice", json=payload)
|
|
81
|
+
|
|
82
|
+
def batch_invoicing(self, payload: list[dict[str, Any]]) -> dict[str, Any]:
|
|
83
|
+
return self.client.post("/documents/contracts/invoices/batch_invoicing", json=payload)
|
|
84
|
+
|
|
85
|
+
def list_contracts(self) -> dict[str, Any]:
|
|
86
|
+
return self.client.get("/documents/contracts")
|
|
87
|
+
|
|
88
|
+
def get_contract(self, contract_id: str) -> dict[str, Any]:
|
|
89
|
+
return self.client.get(f"/documents/contracts/{contract_id}")
|
|
90
|
+
|
|
91
|
+
def preview_contract(self, contract_id: str) -> dict[str, Any]:
|
|
92
|
+
return self.client.get(f"/documents/contracts/{contract_id}/preview")
|
|
93
|
+
|
|
94
|
+
def create_contract(self, payload: dict[str, Any]) -> dict[str, Any]:
|
|
95
|
+
return self.client.post("/documents/contracts", json=payload)
|
|
96
|
+
|
|
97
|
+
def pause_contract(self, contract_id: str) -> dict[str, Any]:
|
|
98
|
+
return self.client.post(f"/documents/contracts/{contract_id}/pause")
|
|
99
|
+
|
|
100
|
+
def approve_contract(self, contract_id: str) -> dict[str, Any]:
|
|
101
|
+
return self.client.post(f"/documents/contracts/{contract_id}/approve")
|
|
102
|
+
|
|
103
|
+
def update_contract_payment_methods(
|
|
104
|
+
self, contract_id: str, payment_methods: list[dict[str, Any]]
|
|
105
|
+
) -> dict[str, Any]:
|
|
106
|
+
return self.client.put(
|
|
107
|
+
f"/documents/contracts/{contract_id}/payment_methods",
|
|
108
|
+
json={"paymentMethods": payment_methods},
|
|
109
|
+
)
|
|
110
|
+
|
|
111
|
+
def delete_contract(self, contract_id: str) -> dict[str, Any]:
|
|
112
|
+
return self.client.delete(f"/documents/contracts/{contract_id}")
|
|
113
|
+
|
|
114
|
+
def list_banks(self) -> dict[str, Any]:
|
|
115
|
+
return self.client.get("/banks")
|
|
116
|
+
|
|
117
|
+
def activate_banks(self, rails: list[str]) -> dict[str, Any]:
|
|
118
|
+
return self.client.post("/banks/activate", json={"rails": rails})
|
|
119
|
+
|
|
120
|
+
def validate_bank(self, payload: dict[str, Any]) -> dict[str, Any]:
|
|
121
|
+
return self.client.post("/user/contact/bank/validate", json=payload)
|
|
122
|
+
|
|
123
|
+
def payin_quote(self, payload: dict[str, Any]) -> dict[str, Any]:
|
|
124
|
+
return self.client.post("/balance/payin/quote", json=payload)
|
|
125
|
+
|
|
126
|
+
def bank_payin_quote(self, payload: dict[str, Any]) -> dict[str, Any]:
|
|
127
|
+
return self.client.post("/banks/payin/quote", json=payload)
|
|
128
|
+
|
|
129
|
+
def submit_bank_payin_quote(self, payload: dict[str, Any]) -> dict[str, Any]:
|
|
130
|
+
return self.client.post("/banks/payin/quote/submit", json=payload)
|
|
131
|
+
|
|
132
|
+
def payout_quote(self, payload: dict[str, Any]) -> dict[str, Any]:
|
|
133
|
+
return self.client.post("/balance/payout/quote", json=payload)
|
|
134
|
+
|
|
135
|
+
def list_cards(self) -> dict[str, Any]:
|
|
136
|
+
return self.client.get("/cards")
|
|
137
|
+
|
|
138
|
+
def create_card(self, payload: dict[str, Any]) -> dict[str, Any]:
|
|
139
|
+
return self.client.post("/cards", json=payload)
|
|
140
|
+
|
|
141
|
+
def get_card(self, card_id: str) -> dict[str, Any]:
|
|
142
|
+
return self.client.get(f"/cards/{card_id}")
|
|
143
|
+
|
|
144
|
+
def card_details(self, card_id: str, payload: dict[str, Any]) -> dict[str, Any]:
|
|
145
|
+
return self.client.post(f"/cards/{card_id}/details", json=payload)
|
|
146
|
+
|
|
147
|
+
def update_card_label(self, card_id: str, label: str) -> dict[str, Any]:
|
|
148
|
+
return self.client.put(f"/cards/{card_id}/label", json={"label": label})
|
|
149
|
+
|
|
150
|
+
def update_card_limit(self, card_id: str, payload: dict[str, Any]) -> dict[str, Any]:
|
|
151
|
+
return self.client.put(f"/cards/{card_id}/limit", json=payload)
|
|
152
|
+
|
|
153
|
+
def freeze_card(self, card_id: str) -> Any:
|
|
154
|
+
return self.client.post(f"/cards/{card_id}/freeze")
|
|
155
|
+
|
|
156
|
+
def unfreeze_card(self, card_id: str) -> Any:
|
|
157
|
+
return self.client.post(f"/cards/{card_id}/unfreeze")
|
|
158
|
+
|
|
159
|
+
def card_kyc_link(self) -> dict[str, Any]:
|
|
160
|
+
return self.client.post("/cards/kyc/link")
|
|
161
|
+
|
|
162
|
+
def pre_approve_cards(self) -> dict[str, Any]:
|
|
163
|
+
return self.client.post("/cards/pre-approve")
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from enum import Enum
|
|
4
|
+
from typing import Any
|
|
5
|
+
|
|
6
|
+
from hevn_cli.client import app_headers, mcp_headers, request
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class AuthMode(str, Enum):
|
|
10
|
+
APP = "app"
|
|
11
|
+
MCP = "mcp"
|
|
12
|
+
PUBLIC = "public"
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class ApiClient:
|
|
16
|
+
def __init__(self, auth_mode: AuthMode, *, idempotency_key: str | None = None) -> None:
|
|
17
|
+
self.auth_mode = auth_mode
|
|
18
|
+
self.idempotency_key = idempotency_key
|
|
19
|
+
|
|
20
|
+
def _headers(self) -> dict[str, str]:
|
|
21
|
+
if self.auth_mode == AuthMode.APP:
|
|
22
|
+
return app_headers()
|
|
23
|
+
if self.auth_mode == AuthMode.MCP:
|
|
24
|
+
return mcp_headers(self.idempotency_key)
|
|
25
|
+
return {"Accept": "application/json"}
|
|
26
|
+
|
|
27
|
+
def get(self, path: str, *, params: dict[str, Any] | None = None) -> Any:
|
|
28
|
+
return request("GET", path, headers=self._headers(), params=params)
|
|
29
|
+
|
|
30
|
+
def post(self, path: str, *, json: dict[str, Any] | None = None) -> Any:
|
|
31
|
+
return request("POST", path, headers=self._headers(), json_body=json)
|
|
32
|
+
|
|
33
|
+
def put(self, path: str, *, json: dict[str, Any] | None = None) -> Any:
|
|
34
|
+
return request("PUT", path, headers=self._headers(), json_body=json)
|
|
35
|
+
|
|
36
|
+
def patch(self, path: str, *, json: dict[str, Any] | None = None) -> Any:
|
|
37
|
+
return request("PATCH", path, headers=self._headers(), json_body=json)
|
|
38
|
+
|
|
39
|
+
def delete(self, path: str) -> Any:
|
|
40
|
+
return request("DELETE", path, headers=self._headers())
|