morning-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.
- morning_cli-0.1.0/LICENSE +21 -0
- morning_cli-0.1.0/PKG-INFO +262 -0
- morning_cli-0.1.0/README.md +212 -0
- morning_cli-0.1.0/cli_anything/greeninvoice/README.md +33 -0
- morning_cli-0.1.0/cli_anything/greeninvoice/__init__.py +11 -0
- morning_cli-0.1.0/cli_anything/greeninvoice/__main__.py +6 -0
- morning_cli-0.1.0/cli_anything/greeninvoice/core/__init__.py +0 -0
- morning_cli-0.1.0/cli_anything/greeninvoice/core/auth.py +138 -0
- morning_cli-0.1.0/cli_anything/greeninvoice/core/businesses.py +68 -0
- morning_cli-0.1.0/cli_anything/greeninvoice/core/clients.py +54 -0
- morning_cli-0.1.0/cli_anything/greeninvoice/core/documents.py +80 -0
- morning_cli-0.1.0/cli_anything/greeninvoice/core/expenses.py +85 -0
- morning_cli-0.1.0/cli_anything/greeninvoice/core/items.py +30 -0
- morning_cli-0.1.0/cli_anything/greeninvoice/core/partners.py +29 -0
- morning_cli-0.1.0/cli_anything/greeninvoice/core/payments.py +25 -0
- morning_cli-0.1.0/cli_anything/greeninvoice/core/session.py +113 -0
- morning_cli-0.1.0/cli_anything/greeninvoice/core/suppliers.py +40 -0
- morning_cli-0.1.0/cli_anything/greeninvoice/core/tools.py +69 -0
- morning_cli-0.1.0/cli_anything/greeninvoice/greeninvoice_cli.py +1207 -0
- morning_cli-0.1.0/cli_anything/greeninvoice/skills/SKILL.md +174 -0
- morning_cli-0.1.0/cli_anything/greeninvoice/tests/__init__.py +0 -0
- morning_cli-0.1.0/cli_anything/greeninvoice/tests/conftest.py +70 -0
- morning_cli-0.1.0/cli_anything/greeninvoice/tests/test_core.py +428 -0
- morning_cli-0.1.0/cli_anything/greeninvoice/tests/test_full_e2e.py +502 -0
- morning_cli-0.1.0/cli_anything/greeninvoice/utils/__init__.py +0 -0
- morning_cli-0.1.0/cli_anything/greeninvoice/utils/greeninvoice_backend.py +402 -0
- morning_cli-0.1.0/cli_anything/greeninvoice/utils/repl_skin.py +521 -0
- morning_cli-0.1.0/morning_cli.egg-info/PKG-INFO +262 -0
- morning_cli-0.1.0/morning_cli.egg-info/SOURCES.txt +33 -0
- morning_cli-0.1.0/morning_cli.egg-info/dependency_links.txt +1 -0
- morning_cli-0.1.0/morning_cli.egg-info/entry_points.txt +3 -0
- morning_cli-0.1.0/morning_cli.egg-info/requires.txt +7 -0
- morning_cli-0.1.0/morning_cli.egg-info/top_level.txt +1 -0
- morning_cli-0.1.0/setup.cfg +4 -0
- morning_cli-0.1.0/setup.py +90 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 JangoAI (https://jango-ai.com)
|
|
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.
|
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: morning-cli
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Agent-native CLI for the morning by Green Invoice REST API — 66 endpoints, JSON envelopes, sandbox-first, built by JangoAI.
|
|
5
|
+
Home-page: https://github.com/jango-ai-com/morning-cli
|
|
6
|
+
Author: JangoAI
|
|
7
|
+
Author-email: hello@jango-ai.com
|
|
8
|
+
License: MIT
|
|
9
|
+
Project-URL: Homepage, https://jango-ai.com
|
|
10
|
+
Project-URL: Source, https://github.com/jango-ai-com/morning-cli
|
|
11
|
+
Project-URL: Tracker, https://github.com/jango-ai-com/morning-cli/issues
|
|
12
|
+
Project-URL: morning API docs, https://www.greeninvoice.co.il/api-docs
|
|
13
|
+
Keywords: morning,green-invoice,greeninvoice,invoicing,israel,rest-api,cli,agent,cli-anything,jangoai
|
|
14
|
+
Classifier: Development Status :: 4 - Beta
|
|
15
|
+
Classifier: Environment :: Console
|
|
16
|
+
Classifier: Intended Audience :: Developers
|
|
17
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
18
|
+
Classifier: Natural Language :: Hebrew
|
|
19
|
+
Classifier: Operating System :: MacOS
|
|
20
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
21
|
+
Classifier: Programming Language :: Python :: 3
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
23
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
24
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
25
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
26
|
+
Classifier: Topic :: Office/Business :: Financial :: Accounting
|
|
27
|
+
Requires-Python: >=3.10
|
|
28
|
+
Description-Content-Type: text/markdown
|
|
29
|
+
License-File: LICENSE
|
|
30
|
+
Requires-Dist: click>=8.1
|
|
31
|
+
Requires-Dist: httpx>=0.27
|
|
32
|
+
Requires-Dist: prompt-toolkit>=3.0
|
|
33
|
+
Requires-Dist: rich>=13.0
|
|
34
|
+
Provides-Extra: test
|
|
35
|
+
Requires-Dist: pytest>=7; extra == "test"
|
|
36
|
+
Dynamic: author
|
|
37
|
+
Dynamic: author-email
|
|
38
|
+
Dynamic: classifier
|
|
39
|
+
Dynamic: description
|
|
40
|
+
Dynamic: description-content-type
|
|
41
|
+
Dynamic: home-page
|
|
42
|
+
Dynamic: keywords
|
|
43
|
+
Dynamic: license
|
|
44
|
+
Dynamic: license-file
|
|
45
|
+
Dynamic: project-url
|
|
46
|
+
Dynamic: provides-extra
|
|
47
|
+
Dynamic: requires-dist
|
|
48
|
+
Dynamic: requires-python
|
|
49
|
+
Dynamic: summary
|
|
50
|
+
|
|
51
|
+
# morning-cli
|
|
52
|
+
|
|
53
|
+
**Agent-native command-line interface for [morning by Green Invoice](https://www.greeninvoice.co.il) — Israel's leading invoicing SaaS.**
|
|
54
|
+
|
|
55
|
+
Built by [JangoAI](https://jango-ai.com) using the [cli-anything](https://github.com/HKUDS/CLI-Anything) methodology. Hebrew README: [README.he.md](README.he.md).
|
|
56
|
+
|
|
57
|
+
- **66 endpoints** across 10 resource groups (Businesses, Clients, Suppliers, Items, Documents, Expenses, Payments, Partners, Tools)
|
|
58
|
+
- **Interactive setup wizard** — `morning-cli auth init` walks you through API key creation
|
|
59
|
+
- **REPL** as the default mode — run `morning-cli` with no args
|
|
60
|
+
- **`--json` envelopes** for AI-agent consumption — stable shape, documented error codes
|
|
61
|
+
- **Automatic JWT refresh** on 401, with transparent retry
|
|
62
|
+
- **Sandbox-first** — default environment is sandbox so accidents don't touch production
|
|
63
|
+
- **Locked session file** at `~/.greeninvoice/session.json` (mode 0600)
|
|
64
|
+
- **55 tests**, including 22 end-to-end against the real sandbox API
|
|
65
|
+
- **Hebrew error messages** preserved end-to-end
|
|
66
|
+
|
|
67
|
+
## Install
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
pip install morning-cli
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Python 3.10+ required.
|
|
74
|
+
|
|
75
|
+
## Quick start
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
# 1. Run the interactive onboarding wizard
|
|
79
|
+
morning-cli auth init
|
|
80
|
+
|
|
81
|
+
# 2. Try the REPL
|
|
82
|
+
morning-cli
|
|
83
|
+
|
|
84
|
+
# Or use one-shot commands
|
|
85
|
+
morning-cli --json business current
|
|
86
|
+
morning-cli --json document types --lang he
|
|
87
|
+
morning-cli --json client search
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### What `auth init` does
|
|
91
|
+
|
|
92
|
+
```
|
|
93
|
+
morning-cli setup wizard
|
|
94
|
+
──────────────────────────
|
|
95
|
+
|
|
96
|
+
Step 1/4 — choose environment
|
|
97
|
+
sandbox (recommended — safe, no real money)
|
|
98
|
+
production (live data)
|
|
99
|
+
env [sandbox]:
|
|
100
|
+
|
|
101
|
+
Step 2/4 — get your API keys (sandbox)
|
|
102
|
+
Open this URL in your browser and log in:
|
|
103
|
+
|
|
104
|
+
https://app.sandbox.d.greeninvoice.co.il/settings/developers/api
|
|
105
|
+
|
|
106
|
+
Then: Settings → Advanced → Developers → 'יצירת מפתח API'
|
|
107
|
+
Copy the ID and the Secret. The Secret is shown ONCE — save it now.
|
|
108
|
+
|
|
109
|
+
Step 3/4 — paste your credentials
|
|
110
|
+
API Key ID: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
|
|
111
|
+
API Key Secret: ●●●●●●●●●●●●●●●●●●●●●●●●●●●●
|
|
112
|
+
|
|
113
|
+
Step 4/4 — verifying against the real API...
|
|
114
|
+
✓ Authenticated successfully
|
|
115
|
+
business: Your Business Name
|
|
116
|
+
business_id: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
|
|
117
|
+
env: sandbox
|
|
118
|
+
saved to: ~/.greeninvoice/credentials.json
|
|
119
|
+
|
|
120
|
+
All set. Try:
|
|
121
|
+
morning-cli business current
|
|
122
|
+
morning-cli --json document types --lang he
|
|
123
|
+
morning-cli # interactive REPL
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
The wizard saves your keys to `~/.greeninvoice/credentials.json` (mode 0600) and verifies them live before writing anything.
|
|
127
|
+
|
|
128
|
+
### Non-interactive setup (for scripts and CI)
|
|
129
|
+
|
|
130
|
+
```bash
|
|
131
|
+
export MORNING_API_KEY_ID=<your-id>
|
|
132
|
+
export MORNING_API_KEY_SECRET=<your-secret>
|
|
133
|
+
export MORNING_ENV=sandbox # or production
|
|
134
|
+
morning-cli auth init --non-interactive --force
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
The legacy prefix `GREENINVOICE_*` is also supported for backwards compatibility.
|
|
138
|
+
|
|
139
|
+
## Commands
|
|
140
|
+
|
|
141
|
+
| Group | Endpoints | Key commands |
|
|
142
|
+
|---|---|---|
|
|
143
|
+
| `auth` | local | `init` (wizard), `login`, `logout`, `whoami`, `refresh` |
|
|
144
|
+
| `session` | local | `show`, `reset`, `history` |
|
|
145
|
+
| `business` | 10 | `list`, `current`, `get`, `update`, `numbering-get`, `numbering-update`, `file-upload`, `file-delete`, `footer`, `types` |
|
|
146
|
+
| `client` | 8 | `add`, `get`, `update`, `delete`, `search`, `assoc`, `merge`, `balance` |
|
|
147
|
+
| `supplier` | 6 | `add`, `get`, `update`, `delete`, `search`, `merge` |
|
|
148
|
+
| `item` | 5 | `add`, `get`, `update`, `delete`, `search` |
|
|
149
|
+
| `document` | 13 | `create`, `preview`, `get`, `search`, `close`, `open`, `linked`, `download`, `info`, `types`, `statuses`, `templates`, `payments-search` |
|
|
150
|
+
| `expense` | 13 | `add`, `get`, `update`, `delete`, `search`, `open`, `close`, `statuses`, `accounting-classifications`, `file-url`, `draft-from-file`, `update-file`, `drafts-search` |
|
|
151
|
+
| `payment` | 3 | `form`, `tokens-search`, `charge` |
|
|
152
|
+
| `partner` | 4 | `users`, `connect`, `get`, `disconnect` |
|
|
153
|
+
| `tools` | 4 | `occupations`, `countries`, `cities`, `currencies` |
|
|
154
|
+
|
|
155
|
+
### Passing JSON bodies
|
|
156
|
+
|
|
157
|
+
Any command that needs a request body accepts either `--data '<json>'` or `--file path/to/body.json`:
|
|
158
|
+
|
|
159
|
+
```bash
|
|
160
|
+
morning-cli --json client add --data '{"name":"Acme","emails":["x@y.com"],"taxCode":1}'
|
|
161
|
+
morning-cli --json document create --file invoice.json
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
### Example — create a proforma invoice (חשבון עסקה)
|
|
165
|
+
|
|
166
|
+
```bash
|
|
167
|
+
cat > /tmp/proforma.json <<'JSON'
|
|
168
|
+
{
|
|
169
|
+
"description": "Monthly retainer",
|
|
170
|
+
"type": 300,
|
|
171
|
+
"lang": "he",
|
|
172
|
+
"currency": "ILS",
|
|
173
|
+
"vatType": 0,
|
|
174
|
+
"client": {"name": "Acme Ltd", "emails": ["ap@acme.com"], "country": "IL"},
|
|
175
|
+
"income": [
|
|
176
|
+
{"description": "שירותי ייעוץ", "quantity": 10, "price": 300, "currency": "ILS", "vatType": 0}
|
|
177
|
+
]
|
|
178
|
+
}
|
|
179
|
+
JSON
|
|
180
|
+
morning-cli --json document preview --file /tmp/proforma.json
|
|
181
|
+
morning-cli --json document create --file /tmp/proforma.json
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
## Output envelope (for agents)
|
|
185
|
+
|
|
186
|
+
Every `--json` command returns a consistent envelope:
|
|
187
|
+
|
|
188
|
+
```json
|
|
189
|
+
{"ok": true, "op": "document.create", "data": {"id": "...", "number": 12345}}
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
Errors carry the Green Invoice `errorCode` and the (Hebrew) `errorMessage`:
|
|
193
|
+
|
|
194
|
+
```json
|
|
195
|
+
{"ok": false, "op": "document.create", "error": {"code": 1110, "message": "מחיר לא תקין.", "http_status": 400}}
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
**Exit codes:** `0` ok · `1` usage/local error · `2` API error · `3` 5xx/network
|
|
199
|
+
|
|
200
|
+
## Environment variables
|
|
201
|
+
|
|
202
|
+
| Variable | Purpose |
|
|
203
|
+
|---|---|
|
|
204
|
+
| `MORNING_API_KEY_ID` | API key ID (fallback: `GREENINVOICE_API_KEY_ID`) |
|
|
205
|
+
| `MORNING_API_KEY_SECRET` | API key secret (fallback: `GREENINVOICE_API_KEY_SECRET`) |
|
|
206
|
+
| `MORNING_ENV` | `sandbox` or `production` (fallback: `GREENINVOICE_ENV`, default `sandbox`) |
|
|
207
|
+
| `MORNING_BASE_URL` | Override base URL entirely (proxies, custom deploys) |
|
|
208
|
+
|
|
209
|
+
## Tests
|
|
210
|
+
|
|
211
|
+
```bash
|
|
212
|
+
pip install -e ".[test]"
|
|
213
|
+
|
|
214
|
+
# Unit tests — no network
|
|
215
|
+
pytest cli_anything/greeninvoice/tests/test_core.py -v
|
|
216
|
+
|
|
217
|
+
# Full suite including live sandbox E2E
|
|
218
|
+
export MORNING_SANDBOX_ID=<your-sandbox-id>
|
|
219
|
+
export MORNING_SANDBOX_SECRET=<your-sandbox-secret>
|
|
220
|
+
CLI_ANYTHING_FORCE_INSTALLED=1 pytest cli_anything/greeninvoice/tests/ -v
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
**55 tests** — 27 unit tests (mock `httpx`) + 4 offline E2E (help, version, error envelopes) + 22 live sandbox E2E (auth, business, documents, items, suppliers, expenses, tools, read-only smoke across all 10 resource groups). See [tests/TEST.md](cli_anything/greeninvoice/tests/TEST.md) for the full plan and results.
|
|
224
|
+
|
|
225
|
+
## Methodology
|
|
226
|
+
|
|
227
|
+
Built with the cli-anything 7-phase SOP:
|
|
228
|
+
|
|
229
|
+
1. **Codebase analysis** — parsed Apiary spec → 66 endpoints mapped
|
|
230
|
+
2. **Architecture** — 10 resource groups + local auth/session, REPL-first
|
|
231
|
+
3. **Implementation** — Python namespace package `cli_anything.greeninvoice`
|
|
232
|
+
4. **Test planning** — `tests/TEST.md`
|
|
233
|
+
5. **Test implementation** — unit + live E2E
|
|
234
|
+
6. **Test documentation** — results captured
|
|
235
|
+
7. **PyPI publishing** — `pip install morning-cli`
|
|
236
|
+
|
|
237
|
+
Full methodology in [GREENINVOICE.md](GREENINVOICE.md).
|
|
238
|
+
|
|
239
|
+
## For AI agents
|
|
240
|
+
|
|
241
|
+
`morning-cli` ships with a [SKILL.md](cli_anything/greeninvoice/skills/SKILL.md) file that's auto-discovered by the REPL banner and Claude Code / Cursor. If you're an agent reading this, check the skill file first — it has concrete invocation examples and Green Invoice error code references.
|
|
242
|
+
|
|
243
|
+
## Contributing
|
|
244
|
+
|
|
245
|
+
PRs welcome at [github.com/jango-ai-com/morning-cli](https://github.com/jango-ai-com/morning-cli). Run the tests before submitting:
|
|
246
|
+
|
|
247
|
+
```bash
|
|
248
|
+
pip install -e ".[test]"
|
|
249
|
+
pytest cli_anything/greeninvoice/tests/test_core.py -v
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
## About JangoAI
|
|
253
|
+
|
|
254
|
+
[JangoAI](https://jango-ai.com) is an Israeli AI automation studio. We build agent-native tooling for Israeli developers and businesses — n8n workflows, Monday.com integrations, Supabase data models, and CLIs like this one.
|
|
255
|
+
|
|
256
|
+
## License
|
|
257
|
+
|
|
258
|
+
MIT. See [LICENSE](LICENSE).
|
|
259
|
+
|
|
260
|
+
---
|
|
261
|
+
|
|
262
|
+
*Created by JangoAI · [jango-ai.com](https://jango-ai.com) · [@jango-ai-com on GitHub](https://github.com/jango-ai-com)*
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
# morning-cli
|
|
2
|
+
|
|
3
|
+
**Agent-native command-line interface for [morning by Green Invoice](https://www.greeninvoice.co.il) — Israel's leading invoicing SaaS.**
|
|
4
|
+
|
|
5
|
+
Built by [JangoAI](https://jango-ai.com) using the [cli-anything](https://github.com/HKUDS/CLI-Anything) methodology. Hebrew README: [README.he.md](README.he.md).
|
|
6
|
+
|
|
7
|
+
- **66 endpoints** across 10 resource groups (Businesses, Clients, Suppliers, Items, Documents, Expenses, Payments, Partners, Tools)
|
|
8
|
+
- **Interactive setup wizard** — `morning-cli auth init` walks you through API key creation
|
|
9
|
+
- **REPL** as the default mode — run `morning-cli` with no args
|
|
10
|
+
- **`--json` envelopes** for AI-agent consumption — stable shape, documented error codes
|
|
11
|
+
- **Automatic JWT refresh** on 401, with transparent retry
|
|
12
|
+
- **Sandbox-first** — default environment is sandbox so accidents don't touch production
|
|
13
|
+
- **Locked session file** at `~/.greeninvoice/session.json` (mode 0600)
|
|
14
|
+
- **55 tests**, including 22 end-to-end against the real sandbox API
|
|
15
|
+
- **Hebrew error messages** preserved end-to-end
|
|
16
|
+
|
|
17
|
+
## Install
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
pip install morning-cli
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Python 3.10+ required.
|
|
24
|
+
|
|
25
|
+
## Quick start
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
# 1. Run the interactive onboarding wizard
|
|
29
|
+
morning-cli auth init
|
|
30
|
+
|
|
31
|
+
# 2. Try the REPL
|
|
32
|
+
morning-cli
|
|
33
|
+
|
|
34
|
+
# Or use one-shot commands
|
|
35
|
+
morning-cli --json business current
|
|
36
|
+
morning-cli --json document types --lang he
|
|
37
|
+
morning-cli --json client search
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
### What `auth init` does
|
|
41
|
+
|
|
42
|
+
```
|
|
43
|
+
morning-cli setup wizard
|
|
44
|
+
──────────────────────────
|
|
45
|
+
|
|
46
|
+
Step 1/4 — choose environment
|
|
47
|
+
sandbox (recommended — safe, no real money)
|
|
48
|
+
production (live data)
|
|
49
|
+
env [sandbox]:
|
|
50
|
+
|
|
51
|
+
Step 2/4 — get your API keys (sandbox)
|
|
52
|
+
Open this URL in your browser and log in:
|
|
53
|
+
|
|
54
|
+
https://app.sandbox.d.greeninvoice.co.il/settings/developers/api
|
|
55
|
+
|
|
56
|
+
Then: Settings → Advanced → Developers → 'יצירת מפתח API'
|
|
57
|
+
Copy the ID and the Secret. The Secret is shown ONCE — save it now.
|
|
58
|
+
|
|
59
|
+
Step 3/4 — paste your credentials
|
|
60
|
+
API Key ID: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
|
|
61
|
+
API Key Secret: ●●●●●●●●●●●●●●●●●●●●●●●●●●●●
|
|
62
|
+
|
|
63
|
+
Step 4/4 — verifying against the real API...
|
|
64
|
+
✓ Authenticated successfully
|
|
65
|
+
business: Your Business Name
|
|
66
|
+
business_id: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
|
|
67
|
+
env: sandbox
|
|
68
|
+
saved to: ~/.greeninvoice/credentials.json
|
|
69
|
+
|
|
70
|
+
All set. Try:
|
|
71
|
+
morning-cli business current
|
|
72
|
+
morning-cli --json document types --lang he
|
|
73
|
+
morning-cli # interactive REPL
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
The wizard saves your keys to `~/.greeninvoice/credentials.json` (mode 0600) and verifies them live before writing anything.
|
|
77
|
+
|
|
78
|
+
### Non-interactive setup (for scripts and CI)
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
export MORNING_API_KEY_ID=<your-id>
|
|
82
|
+
export MORNING_API_KEY_SECRET=<your-secret>
|
|
83
|
+
export MORNING_ENV=sandbox # or production
|
|
84
|
+
morning-cli auth init --non-interactive --force
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
The legacy prefix `GREENINVOICE_*` is also supported for backwards compatibility.
|
|
88
|
+
|
|
89
|
+
## Commands
|
|
90
|
+
|
|
91
|
+
| Group | Endpoints | Key commands |
|
|
92
|
+
|---|---|---|
|
|
93
|
+
| `auth` | local | `init` (wizard), `login`, `logout`, `whoami`, `refresh` |
|
|
94
|
+
| `session` | local | `show`, `reset`, `history` |
|
|
95
|
+
| `business` | 10 | `list`, `current`, `get`, `update`, `numbering-get`, `numbering-update`, `file-upload`, `file-delete`, `footer`, `types` |
|
|
96
|
+
| `client` | 8 | `add`, `get`, `update`, `delete`, `search`, `assoc`, `merge`, `balance` |
|
|
97
|
+
| `supplier` | 6 | `add`, `get`, `update`, `delete`, `search`, `merge` |
|
|
98
|
+
| `item` | 5 | `add`, `get`, `update`, `delete`, `search` |
|
|
99
|
+
| `document` | 13 | `create`, `preview`, `get`, `search`, `close`, `open`, `linked`, `download`, `info`, `types`, `statuses`, `templates`, `payments-search` |
|
|
100
|
+
| `expense` | 13 | `add`, `get`, `update`, `delete`, `search`, `open`, `close`, `statuses`, `accounting-classifications`, `file-url`, `draft-from-file`, `update-file`, `drafts-search` |
|
|
101
|
+
| `payment` | 3 | `form`, `tokens-search`, `charge` |
|
|
102
|
+
| `partner` | 4 | `users`, `connect`, `get`, `disconnect` |
|
|
103
|
+
| `tools` | 4 | `occupations`, `countries`, `cities`, `currencies` |
|
|
104
|
+
|
|
105
|
+
### Passing JSON bodies
|
|
106
|
+
|
|
107
|
+
Any command that needs a request body accepts either `--data '<json>'` or `--file path/to/body.json`:
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
morning-cli --json client add --data '{"name":"Acme","emails":["x@y.com"],"taxCode":1}'
|
|
111
|
+
morning-cli --json document create --file invoice.json
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
### Example — create a proforma invoice (חשבון עסקה)
|
|
115
|
+
|
|
116
|
+
```bash
|
|
117
|
+
cat > /tmp/proforma.json <<'JSON'
|
|
118
|
+
{
|
|
119
|
+
"description": "Monthly retainer",
|
|
120
|
+
"type": 300,
|
|
121
|
+
"lang": "he",
|
|
122
|
+
"currency": "ILS",
|
|
123
|
+
"vatType": 0,
|
|
124
|
+
"client": {"name": "Acme Ltd", "emails": ["ap@acme.com"], "country": "IL"},
|
|
125
|
+
"income": [
|
|
126
|
+
{"description": "שירותי ייעוץ", "quantity": 10, "price": 300, "currency": "ILS", "vatType": 0}
|
|
127
|
+
]
|
|
128
|
+
}
|
|
129
|
+
JSON
|
|
130
|
+
morning-cli --json document preview --file /tmp/proforma.json
|
|
131
|
+
morning-cli --json document create --file /tmp/proforma.json
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
## Output envelope (for agents)
|
|
135
|
+
|
|
136
|
+
Every `--json` command returns a consistent envelope:
|
|
137
|
+
|
|
138
|
+
```json
|
|
139
|
+
{"ok": true, "op": "document.create", "data": {"id": "...", "number": 12345}}
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
Errors carry the Green Invoice `errorCode` and the (Hebrew) `errorMessage`:
|
|
143
|
+
|
|
144
|
+
```json
|
|
145
|
+
{"ok": false, "op": "document.create", "error": {"code": 1110, "message": "מחיר לא תקין.", "http_status": 400}}
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
**Exit codes:** `0` ok · `1` usage/local error · `2` API error · `3` 5xx/network
|
|
149
|
+
|
|
150
|
+
## Environment variables
|
|
151
|
+
|
|
152
|
+
| Variable | Purpose |
|
|
153
|
+
|---|---|
|
|
154
|
+
| `MORNING_API_KEY_ID` | API key ID (fallback: `GREENINVOICE_API_KEY_ID`) |
|
|
155
|
+
| `MORNING_API_KEY_SECRET` | API key secret (fallback: `GREENINVOICE_API_KEY_SECRET`) |
|
|
156
|
+
| `MORNING_ENV` | `sandbox` or `production` (fallback: `GREENINVOICE_ENV`, default `sandbox`) |
|
|
157
|
+
| `MORNING_BASE_URL` | Override base URL entirely (proxies, custom deploys) |
|
|
158
|
+
|
|
159
|
+
## Tests
|
|
160
|
+
|
|
161
|
+
```bash
|
|
162
|
+
pip install -e ".[test]"
|
|
163
|
+
|
|
164
|
+
# Unit tests — no network
|
|
165
|
+
pytest cli_anything/greeninvoice/tests/test_core.py -v
|
|
166
|
+
|
|
167
|
+
# Full suite including live sandbox E2E
|
|
168
|
+
export MORNING_SANDBOX_ID=<your-sandbox-id>
|
|
169
|
+
export MORNING_SANDBOX_SECRET=<your-sandbox-secret>
|
|
170
|
+
CLI_ANYTHING_FORCE_INSTALLED=1 pytest cli_anything/greeninvoice/tests/ -v
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
**55 tests** — 27 unit tests (mock `httpx`) + 4 offline E2E (help, version, error envelopes) + 22 live sandbox E2E (auth, business, documents, items, suppliers, expenses, tools, read-only smoke across all 10 resource groups). See [tests/TEST.md](cli_anything/greeninvoice/tests/TEST.md) for the full plan and results.
|
|
174
|
+
|
|
175
|
+
## Methodology
|
|
176
|
+
|
|
177
|
+
Built with the cli-anything 7-phase SOP:
|
|
178
|
+
|
|
179
|
+
1. **Codebase analysis** — parsed Apiary spec → 66 endpoints mapped
|
|
180
|
+
2. **Architecture** — 10 resource groups + local auth/session, REPL-first
|
|
181
|
+
3. **Implementation** — Python namespace package `cli_anything.greeninvoice`
|
|
182
|
+
4. **Test planning** — `tests/TEST.md`
|
|
183
|
+
5. **Test implementation** — unit + live E2E
|
|
184
|
+
6. **Test documentation** — results captured
|
|
185
|
+
7. **PyPI publishing** — `pip install morning-cli`
|
|
186
|
+
|
|
187
|
+
Full methodology in [GREENINVOICE.md](GREENINVOICE.md).
|
|
188
|
+
|
|
189
|
+
## For AI agents
|
|
190
|
+
|
|
191
|
+
`morning-cli` ships with a [SKILL.md](cli_anything/greeninvoice/skills/SKILL.md) file that's auto-discovered by the REPL banner and Claude Code / Cursor. If you're an agent reading this, check the skill file first — it has concrete invocation examples and Green Invoice error code references.
|
|
192
|
+
|
|
193
|
+
## Contributing
|
|
194
|
+
|
|
195
|
+
PRs welcome at [github.com/jango-ai-com/morning-cli](https://github.com/jango-ai-com/morning-cli). Run the tests before submitting:
|
|
196
|
+
|
|
197
|
+
```bash
|
|
198
|
+
pip install -e ".[test]"
|
|
199
|
+
pytest cli_anything/greeninvoice/tests/test_core.py -v
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
## About JangoAI
|
|
203
|
+
|
|
204
|
+
[JangoAI](https://jango-ai.com) is an Israeli AI automation studio. We build agent-native tooling for Israeli developers and businesses — n8n workflows, Monday.com integrations, Supabase data models, and CLIs like this one.
|
|
205
|
+
|
|
206
|
+
## License
|
|
207
|
+
|
|
208
|
+
MIT. See [LICENSE](LICENSE).
|
|
209
|
+
|
|
210
|
+
---
|
|
211
|
+
|
|
212
|
+
*Created by JangoAI · [jango-ai.com](https://jango-ai.com) · [@jango-ai-com on GitHub](https://github.com/jango-ai-com)*
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# morning-cli (Python package)
|
|
2
|
+
|
|
3
|
+
Installed sub-package under the `cli_anything.greeninvoice` namespace.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pip install morning-cli
|
|
9
|
+
# or, from source:
|
|
10
|
+
pip install -e .
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Two console scripts are registered:
|
|
14
|
+
- `morning-cli` — primary user-facing command
|
|
15
|
+
- `cli-anything-greeninvoice` — alias preserved for cli-anything methodology compat
|
|
16
|
+
|
|
17
|
+
## Run
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
morning-cli # REPL (default)
|
|
21
|
+
morning-cli auth init # interactive setup wizard
|
|
22
|
+
morning-cli --help # command reference
|
|
23
|
+
morning-cli --json business current
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## About
|
|
27
|
+
|
|
28
|
+
Built by [JangoAI](https://jango-ai.com) using the
|
|
29
|
+
[cli-anything](https://github.com/HKUDS/CLI-Anything) methodology.
|
|
30
|
+
|
|
31
|
+
See the top-level [README.md](../../README.md) and
|
|
32
|
+
[GREENINVOICE.md](../../GREENINVOICE.md) for full usage, architecture, and
|
|
33
|
+
credential setup.
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"""morning-cli — agent-native CLI for the morning by Green Invoice REST API.
|
|
2
|
+
|
|
3
|
+
Built by JangoAI (https://jango-ai.com) with the cli-anything methodology.
|
|
4
|
+
The Python namespace ``cli_anything.greeninvoice`` is intentional and follows
|
|
5
|
+
PEP 420 namespace packaging so multiple ``cli-anything-<software>`` harnesses
|
|
6
|
+
can coexist. User-facing name on PyPI and on PATH: ``morning-cli``.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
__version__ = "0.1.0"
|
|
10
|
+
__author__ = "JangoAI"
|
|
11
|
+
__url__ = "https://jango-ai.com"
|
|
File without changes
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
"""Authentication primitives (token acquire/refresh/whoami + onboarding).
|
|
2
|
+
|
|
3
|
+
These live at the core layer so both the CLI and the REPL can call them
|
|
4
|
+
without importing Click.
|
|
5
|
+
"""
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
import json
|
|
9
|
+
import os
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
from typing import Any
|
|
12
|
+
|
|
13
|
+
import httpx
|
|
14
|
+
|
|
15
|
+
from cli_anything.greeninvoice.utils.greeninvoice_backend import (
|
|
16
|
+
CREDENTIALS_PATH,
|
|
17
|
+
ENVIRONMENTS,
|
|
18
|
+
GreenInvoiceAPIError,
|
|
19
|
+
GreenInvoiceBackend,
|
|
20
|
+
GreenInvoiceError,
|
|
21
|
+
find_credentials,
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
# URLs pointing at the morning dashboard API-keys screen, for the wizard
|
|
25
|
+
# to deep-link users to the right place.
|
|
26
|
+
DASHBOARD_URLS = {
|
|
27
|
+
"production": "https://app.greeninvoice.co.il/settings/developers/api",
|
|
28
|
+
"sandbox": "https://app.sandbox.d.greeninvoice.co.il/settings/developers/api",
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def login(session: dict[str, Any], env: str | None = None) -> dict[str, Any]:
|
|
33
|
+
"""Force-acquire a fresh token and cache it in ``session``."""
|
|
34
|
+
backend = GreenInvoiceBackend.from_session(session, env_override=env)
|
|
35
|
+
try:
|
|
36
|
+
backend.acquire_token(force=True)
|
|
37
|
+
finally:
|
|
38
|
+
backend.close()
|
|
39
|
+
return session["token"]
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def logout(session: dict[str, Any]) -> None:
|
|
43
|
+
"""Drop the cached token (but keep api_key_id + env context)."""
|
|
44
|
+
session["token"] = None
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def verify_credentials(
|
|
48
|
+
api_key_id: str,
|
|
49
|
+
api_key_secret: str,
|
|
50
|
+
env: str,
|
|
51
|
+
base_url_override: str | None = None,
|
|
52
|
+
) -> dict[str, Any]:
|
|
53
|
+
"""Probe a pair of credentials against the real API.
|
|
54
|
+
|
|
55
|
+
Returns a dict with {token, expires_at, business_name, business_id}.
|
|
56
|
+
Raises ``GreenInvoiceAPIError`` on a bad key pair, or
|
|
57
|
+
``GreenInvoiceError`` on network issues / unexpected responses.
|
|
58
|
+
"""
|
|
59
|
+
base_url = base_url_override or ENVIRONMENTS.get(env)
|
|
60
|
+
if not base_url:
|
|
61
|
+
raise GreenInvoiceError(f"Unknown env {env!r}")
|
|
62
|
+
|
|
63
|
+
probe_session: dict[str, Any] = {}
|
|
64
|
+
client = httpx.Client(
|
|
65
|
+
timeout=30.0,
|
|
66
|
+
headers={"User-Agent": "morning-cli/auth-init (+https://jango-ai.com)"},
|
|
67
|
+
)
|
|
68
|
+
backend = GreenInvoiceBackend(
|
|
69
|
+
api_key_id=api_key_id,
|
|
70
|
+
api_key_secret=api_key_secret,
|
|
71
|
+
base_url=base_url,
|
|
72
|
+
session=probe_session,
|
|
73
|
+
client=client,
|
|
74
|
+
)
|
|
75
|
+
try:
|
|
76
|
+
token = backend.acquire_token(force=True)
|
|
77
|
+
business = backend.get("/businesses/me")
|
|
78
|
+
finally:
|
|
79
|
+
backend.close()
|
|
80
|
+
|
|
81
|
+
result = {
|
|
82
|
+
"token": token,
|
|
83
|
+
"expires_at": probe_session["token"]["expires_at"],
|
|
84
|
+
"business_name": (business or {}).get("name") if isinstance(business, dict) else None,
|
|
85
|
+
"business_id": (business or {}).get("id") if isinstance(business, dict) else None,
|
|
86
|
+
"base_url": base_url,
|
|
87
|
+
}
|
|
88
|
+
return result
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def write_credentials_file(
|
|
92
|
+
api_key_id: str,
|
|
93
|
+
api_key_secret: str,
|
|
94
|
+
env: str,
|
|
95
|
+
path: Path | None = None,
|
|
96
|
+
) -> Path:
|
|
97
|
+
"""Write credentials JSON with 0600 perms. Returns the written path."""
|
|
98
|
+
target = path or CREDENTIALS_PATH
|
|
99
|
+
target.parent.mkdir(parents=True, exist_ok=True)
|
|
100
|
+
try:
|
|
101
|
+
target.parent.chmod(0o700)
|
|
102
|
+
except PermissionError:
|
|
103
|
+
pass
|
|
104
|
+
|
|
105
|
+
payload = {"id": api_key_id, "secret": api_key_secret, "env": env}
|
|
106
|
+
# Atomic-ish write: write to tmp then rename, with strict perms.
|
|
107
|
+
tmp = target.with_suffix(target.suffix + ".tmp")
|
|
108
|
+
fd = os.open(str(tmp), os.O_WRONLY | os.O_CREAT | os.O_TRUNC, 0o600)
|
|
109
|
+
try:
|
|
110
|
+
with os.fdopen(fd, "w", encoding="utf-8") as fh:
|
|
111
|
+
json.dump(payload, fh, ensure_ascii=False, indent=2)
|
|
112
|
+
except Exception:
|
|
113
|
+
tmp.unlink(missing_ok=True)
|
|
114
|
+
raise
|
|
115
|
+
os.replace(tmp, target)
|
|
116
|
+
try:
|
|
117
|
+
target.chmod(0o600)
|
|
118
|
+
except PermissionError:
|
|
119
|
+
pass
|
|
120
|
+
return target
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def whoami(session: dict[str, Any]) -> dict[str, Any]:
|
|
124
|
+
"""Return a safe view of who we'd be acting as.
|
|
125
|
+
|
|
126
|
+
Does not hit the network unless ``session.token`` is missing or expired —
|
|
127
|
+
in which case it calls ``/businesses/me`` to verify live access.
|
|
128
|
+
"""
|
|
129
|
+
creds = find_credentials(env_override=session.get("env"))
|
|
130
|
+
info: dict[str, Any] = {
|
|
131
|
+
"env": creds["env"],
|
|
132
|
+
"base_url": creds["base_url"],
|
|
133
|
+
"api_key_id": creds["id"],
|
|
134
|
+
"token_cached": bool((session.get("token") or {}).get("value")),
|
|
135
|
+
"token_expires_at": (session.get("token") or {}).get("expires_at"),
|
|
136
|
+
"business_id": (session.get("context") or {}).get("business_id"),
|
|
137
|
+
}
|
|
138
|
+
return info
|