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.
Files changed (35) hide show
  1. morning_cli-0.1.0/LICENSE +21 -0
  2. morning_cli-0.1.0/PKG-INFO +262 -0
  3. morning_cli-0.1.0/README.md +212 -0
  4. morning_cli-0.1.0/cli_anything/greeninvoice/README.md +33 -0
  5. morning_cli-0.1.0/cli_anything/greeninvoice/__init__.py +11 -0
  6. morning_cli-0.1.0/cli_anything/greeninvoice/__main__.py +6 -0
  7. morning_cli-0.1.0/cli_anything/greeninvoice/core/__init__.py +0 -0
  8. morning_cli-0.1.0/cli_anything/greeninvoice/core/auth.py +138 -0
  9. morning_cli-0.1.0/cli_anything/greeninvoice/core/businesses.py +68 -0
  10. morning_cli-0.1.0/cli_anything/greeninvoice/core/clients.py +54 -0
  11. morning_cli-0.1.0/cli_anything/greeninvoice/core/documents.py +80 -0
  12. morning_cli-0.1.0/cli_anything/greeninvoice/core/expenses.py +85 -0
  13. morning_cli-0.1.0/cli_anything/greeninvoice/core/items.py +30 -0
  14. morning_cli-0.1.0/cli_anything/greeninvoice/core/partners.py +29 -0
  15. morning_cli-0.1.0/cli_anything/greeninvoice/core/payments.py +25 -0
  16. morning_cli-0.1.0/cli_anything/greeninvoice/core/session.py +113 -0
  17. morning_cli-0.1.0/cli_anything/greeninvoice/core/suppliers.py +40 -0
  18. morning_cli-0.1.0/cli_anything/greeninvoice/core/tools.py +69 -0
  19. morning_cli-0.1.0/cli_anything/greeninvoice/greeninvoice_cli.py +1207 -0
  20. morning_cli-0.1.0/cli_anything/greeninvoice/skills/SKILL.md +174 -0
  21. morning_cli-0.1.0/cli_anything/greeninvoice/tests/__init__.py +0 -0
  22. morning_cli-0.1.0/cli_anything/greeninvoice/tests/conftest.py +70 -0
  23. morning_cli-0.1.0/cli_anything/greeninvoice/tests/test_core.py +428 -0
  24. morning_cli-0.1.0/cli_anything/greeninvoice/tests/test_full_e2e.py +502 -0
  25. morning_cli-0.1.0/cli_anything/greeninvoice/utils/__init__.py +0 -0
  26. morning_cli-0.1.0/cli_anything/greeninvoice/utils/greeninvoice_backend.py +402 -0
  27. morning_cli-0.1.0/cli_anything/greeninvoice/utils/repl_skin.py +521 -0
  28. morning_cli-0.1.0/morning_cli.egg-info/PKG-INFO +262 -0
  29. morning_cli-0.1.0/morning_cli.egg-info/SOURCES.txt +33 -0
  30. morning_cli-0.1.0/morning_cli.egg-info/dependency_links.txt +1 -0
  31. morning_cli-0.1.0/morning_cli.egg-info/entry_points.txt +3 -0
  32. morning_cli-0.1.0/morning_cli.egg-info/requires.txt +7 -0
  33. morning_cli-0.1.0/morning_cli.egg-info/top_level.txt +1 -0
  34. morning_cli-0.1.0/setup.cfg +4 -0
  35. 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"
@@ -0,0 +1,6 @@
1
+ """Allow ``python -m cli_anything.greeninvoice``."""
2
+
3
+ from cli_anything.greeninvoice.greeninvoice_cli import cli
4
+
5
+ if __name__ == "__main__":
6
+ cli()
@@ -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