commune-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.
@@ -0,0 +1,82 @@
1
+ node_modules/
2
+ **/node_modules/
3
+
4
+ # Python
5
+ __pycache__/
6
+ *.py[cod]
7
+ *$py.class
8
+ *.so
9
+ .Python
10
+ build/
11
+ develop-eggs/
12
+ dist/
13
+ downloads/
14
+ eggs/
15
+ .eggs/
16
+ /lib/
17
+ /lib64/
18
+ parts/
19
+ sdist/
20
+ var/
21
+ wheels/
22
+ *.egg-info/
23
+ .installed.cfg
24
+ *.egg
25
+ MANIFEST
26
+
27
+ # Virtual environments
28
+ venv/
29
+ .venv/
30
+ env/
31
+ ENV/
32
+ env.bak/
33
+ venv.bak/
34
+
35
+ # IDEs
36
+ .vscode/
37
+ .idea/
38
+ *.swp
39
+ *.swo
40
+ *~
41
+
42
+ # OS
43
+ .DS_Store
44
+ .DS_Store?
45
+ ._*
46
+ .Spotlight-V100
47
+ .Trashes
48
+ ehthumbs.db
49
+ Thumbs.db
50
+
51
+ # Logs
52
+ *.log
53
+ logs/
54
+
55
+ # Environment variables
56
+ .env
57
+ .env.local
58
+ .env.development.local
59
+ .env.test.local
60
+ .env.production.local
61
+
62
+ # Build artifacts
63
+ dist/
64
+ build/
65
+ .next/
66
+ .cache/
67
+
68
+ # Remotion
69
+ # ./remotion
70
+
71
+ # Video files
72
+ *.mp4
73
+ *.mov
74
+ *.avi
75
+ *.mkv
76
+
77
+ # Large/generated folders
78
+ .pytest_cache/
79
+ .mypy_cache/
80
+ .ruff_cache/
81
+ *.egg-info/
82
+ pip-wheel-metadata/
@@ -0,0 +1,243 @@
1
+ Metadata-Version: 2.4
2
+ Name: commune-cli
3
+ Version: 0.1.0
4
+ Summary: Official CLI for the Commune email API — agent-native, pipe-friendly, covers every API surface.
5
+ Project-URL: Homepage, https://commune.email
6
+ Project-URL: Documentation, https://docs.commune.email
7
+ Project-URL: Repository, https://github.com/commune-email/commune
8
+ License: MIT
9
+ Keywords: agent,ai,cli,commune,email
10
+ Classifier: Development Status :: 4 - Beta
11
+ Classifier: Environment :: Console
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: License :: OSI Approved :: MIT License
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Programming Language :: Python :: 3.9
16
+ Classifier: Programming Language :: Python :: 3.10
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Topic :: Communications :: Email
20
+ Classifier: Topic :: Utilities
21
+ Requires-Python: >=3.9
22
+ Requires-Dist: cryptography>=41.0.0
23
+ Requires-Dist: httpx>=0.27.0
24
+ Requires-Dist: platformdirs>=4.0.0
25
+ Requires-Dist: rich>=13.0.0
26
+ Requires-Dist: tomli>=2.0.0; python_version < '3.11'
27
+ Requires-Dist: typer>=0.12.0
28
+ Description-Content-Type: text/markdown
29
+
30
+ # commune-cli
31
+
32
+ Official command-line interface for the [Commune](https://commune.email) email API.
33
+
34
+ Covers every API surface: domains, inboxes, messages, threads, attachments, search, delivery analytics, webhooks, DMARC, and data deletion.
35
+
36
+ ---
37
+
38
+ ## Install
39
+
40
+ ```bash
41
+ pip install commune-cli
42
+ ```
43
+
44
+ Requires Python ≥ 3.9.
45
+
46
+ ---
47
+
48
+ ## Quick Start
49
+
50
+ ```bash
51
+ # Set your API key
52
+ export COMMUNE_API_KEY=comm_...
53
+
54
+ # Send an email
55
+ commune messages send \
56
+ --to recipient@example.com \
57
+ --subject "Hello from CLI" \
58
+ --text "Sent from the terminal."
59
+
60
+ # List inboxes
61
+ commune inboxes list
62
+
63
+ # JSON output (automatic when stdout is piped)
64
+ commune inboxes list --json | jq '.data[].email'
65
+ ```
66
+
67
+ ---
68
+
69
+ ## Authentication
70
+
71
+ Set your API key in one of these ways (highest priority wins):
72
+
73
+ | Priority | Source |
74
+ |----------|--------|
75
+ | 1 | `--api-key` flag |
76
+ | 2 | `COMMUNE_API_KEY` env var |
77
+ | 3 | `~/.commune/config.toml` → `api_key` |
78
+
79
+ Store permanently:
80
+
81
+ ```bash
82
+ commune config set api_key comm_...
83
+ commune config show
84
+ ```
85
+
86
+ ---
87
+
88
+ ## Output
89
+
90
+ - **TTY**: rich tables and panels with color
91
+ - **Piped / `--json`**: clean JSON to stdout; status messages to stderr
92
+
93
+ ```bash
94
+ # JSON list format (always consistent)
95
+ commune threads list --json
96
+ # {"data": [...], "has_more": false, "next_cursor": null}
97
+ ```
98
+
99
+ ---
100
+
101
+ ## Exit Codes
102
+
103
+ | Code | Meaning |
104
+ |------|---------|
105
+ | 0 | Success |
106
+ | 1 | API / validation error |
107
+ | 2 | Auth error (401/403) |
108
+ | 3 | Not found (404) |
109
+ | 4 | Rate limit or plan gate (429/403) |
110
+ | 5 | Network / connection error |
111
+
112
+ ---
113
+
114
+ ## Error Format (stderr)
115
+
116
+ ```json
117
+ {"error": {"code": "not_found", "message": "Inbox not found.", "status_code": 404}}
118
+ ```
119
+
120
+ ---
121
+
122
+ ## Commands
123
+
124
+ ```
125
+ commune
126
+ ├── config
127
+ │ ├── set <key> <value>
128
+ │ ├── get <key>
129
+ │ ├── show [--reveal]
130
+ │ ├── unset <key>
131
+ │ └── path
132
+
133
+ ├── domains
134
+ │ ├── list [--limit] [--cursor]
135
+ │ ├── get <domain-id>
136
+ │ ├── create <name>
137
+ │ ├── verify <domain-id>
138
+ │ └── records <domain-id>
139
+
140
+ ├── inboxes
141
+ │ ├── list [--domain-id] [--limit] [--cursor]
142
+ │ ├── get <inbox-id>
143
+ │ ├── create [--local-part] [--domain-id] [--name] [--webhook-url]
144
+ │ ├── update <inbox-id> [--name] [--webhook-url]
145
+ │ ├── delete <inbox-id> --domain-id [--yes]
146
+ │ ├── set-webhook <inbox-id> --domain-id --url
147
+ │ └── extraction-schema
148
+ │ ├── set <inbox-id> --domain-id --schema <json>
149
+ │ └── remove <inbox-id> --domain-id
150
+
151
+ ├── messages
152
+ │ ├── send --to --subject [--text] [--html] [--from] [--inbox-id] [--domain-id]
153
+ │ │ [--cc] [--bcc] [--reply-to] [--thread-id]
154
+ │ └── list [--inbox-id] [--domain-id] [--sender] [--limit] [--order] [--before] [--after]
155
+
156
+ ├── threads
157
+ │ ├── list [--inbox-id] [--domain-id] [--limit] [--cursor] [--order]
158
+ │ ├── messages <thread-id> [--limit] [--order] [--cursor]
159
+ │ ├── metadata <thread-id>
160
+ │ ├── set-status <thread-id> <open|needs_reply|waiting|closed>
161
+ │ ├── assign <thread-id> [--to <user>]
162
+ │ └── tags
163
+ │ ├── add <thread-id> <tag...>
164
+ │ └── remove <thread-id> <tag...>
165
+
166
+ ├── attachments
167
+ │ ├── upload <file>
168
+ │ ├── get <attachment-id>
169
+ │ └── url <attachment-id> [--expires-in]
170
+
171
+ ├── search
172
+ │ └── threads <query> [--inbox-id] [--domain-id] [--limit]
173
+
174
+ ├── delivery
175
+ │ ├── metrics [--domain-id] [--inbox-id] [--period]
176
+ │ ├── events [--domain-id] [--inbox-id] [--limit]
177
+ │ └── suppressions [--domain-id] [--inbox-id] [--limit]
178
+
179
+ ├── webhooks
180
+ │ ├── list [--inbox-id] [--status] [--endpoint] [--limit]
181
+ │ ├── get <delivery-id>
182
+ │ ├── retry <delivery-id>
183
+ │ └── health
184
+
185
+ ├── dmarc
186
+ │ ├── reports <domain> [--limit]
187
+ │ └── summary <domain> [--days]
188
+
189
+ └── data
190
+ ├── delete-request [--email] [--inbox-id] [--domain-id]
191
+ ├── delete-confirm <id> [--yes]
192
+ └── delete-status <id>
193
+ ```
194
+
195
+ ---
196
+
197
+ ## Global Flags
198
+
199
+ ```
200
+ --api-key TEXT API key (overrides env/config)
201
+ --base-url TEXT API base URL (default: https://api.commune.email)
202
+ --json Output raw JSON
203
+ --quiet / -q Suppress status messages
204
+ --no-color Disable color output
205
+ --version / -V Show version and exit
206
+ ```
207
+
208
+ ---
209
+
210
+ ## Config File
211
+
212
+ Stored at `~/.commune/config.toml` (or `$COMMUNE_CONFIG_DIR/config.toml`).
213
+ Created with `chmod 600` to protect your API key.
214
+
215
+ ```toml
216
+ api_key = "comm_..."
217
+ base_url = "https://api.commune.email"
218
+ ```
219
+
220
+ ---
221
+
222
+ ## Scripting
223
+
224
+ ```bash
225
+ # Non-TTY → auto JSON
226
+ inboxes=$(commune inboxes list)
227
+ echo "$inboxes" | jq '.data[0].email'
228
+
229
+ # Pipe body from stdin
230
+ echo "Email body" | commune messages send \
231
+ --to user@example.com \
232
+ --subject "Automated" \
233
+ --text -
234
+
235
+ # Exit code handling
236
+ if commune messages send --to user@example.com --subject Test --text "Hi" --json; then
237
+ echo "Sent"
238
+ else
239
+ echo "Failed: $?"
240
+ fi
241
+ ```
242
+
243
+ Full API reference: https://docs.commune.email
@@ -0,0 +1,214 @@
1
+ # commune-cli
2
+
3
+ Official command-line interface for the [Commune](https://commune.email) email API.
4
+
5
+ Covers every API surface: domains, inboxes, messages, threads, attachments, search, delivery analytics, webhooks, DMARC, and data deletion.
6
+
7
+ ---
8
+
9
+ ## Install
10
+
11
+ ```bash
12
+ pip install commune-cli
13
+ ```
14
+
15
+ Requires Python ≥ 3.9.
16
+
17
+ ---
18
+
19
+ ## Quick Start
20
+
21
+ ```bash
22
+ # Set your API key
23
+ export COMMUNE_API_KEY=comm_...
24
+
25
+ # Send an email
26
+ commune messages send \
27
+ --to recipient@example.com \
28
+ --subject "Hello from CLI" \
29
+ --text "Sent from the terminal."
30
+
31
+ # List inboxes
32
+ commune inboxes list
33
+
34
+ # JSON output (automatic when stdout is piped)
35
+ commune inboxes list --json | jq '.data[].email'
36
+ ```
37
+
38
+ ---
39
+
40
+ ## Authentication
41
+
42
+ Set your API key in one of these ways (highest priority wins):
43
+
44
+ | Priority | Source |
45
+ |----------|--------|
46
+ | 1 | `--api-key` flag |
47
+ | 2 | `COMMUNE_API_KEY` env var |
48
+ | 3 | `~/.commune/config.toml` → `api_key` |
49
+
50
+ Store permanently:
51
+
52
+ ```bash
53
+ commune config set api_key comm_...
54
+ commune config show
55
+ ```
56
+
57
+ ---
58
+
59
+ ## Output
60
+
61
+ - **TTY**: rich tables and panels with color
62
+ - **Piped / `--json`**: clean JSON to stdout; status messages to stderr
63
+
64
+ ```bash
65
+ # JSON list format (always consistent)
66
+ commune threads list --json
67
+ # {"data": [...], "has_more": false, "next_cursor": null}
68
+ ```
69
+
70
+ ---
71
+
72
+ ## Exit Codes
73
+
74
+ | Code | Meaning |
75
+ |------|---------|
76
+ | 0 | Success |
77
+ | 1 | API / validation error |
78
+ | 2 | Auth error (401/403) |
79
+ | 3 | Not found (404) |
80
+ | 4 | Rate limit or plan gate (429/403) |
81
+ | 5 | Network / connection error |
82
+
83
+ ---
84
+
85
+ ## Error Format (stderr)
86
+
87
+ ```json
88
+ {"error": {"code": "not_found", "message": "Inbox not found.", "status_code": 404}}
89
+ ```
90
+
91
+ ---
92
+
93
+ ## Commands
94
+
95
+ ```
96
+ commune
97
+ ├── config
98
+ │ ├── set <key> <value>
99
+ │ ├── get <key>
100
+ │ ├── show [--reveal]
101
+ │ ├── unset <key>
102
+ │ └── path
103
+
104
+ ├── domains
105
+ │ ├── list [--limit] [--cursor]
106
+ │ ├── get <domain-id>
107
+ │ ├── create <name>
108
+ │ ├── verify <domain-id>
109
+ │ └── records <domain-id>
110
+
111
+ ├── inboxes
112
+ │ ├── list [--domain-id] [--limit] [--cursor]
113
+ │ ├── get <inbox-id>
114
+ │ ├── create [--local-part] [--domain-id] [--name] [--webhook-url]
115
+ │ ├── update <inbox-id> [--name] [--webhook-url]
116
+ │ ├── delete <inbox-id> --domain-id [--yes]
117
+ │ ├── set-webhook <inbox-id> --domain-id --url
118
+ │ └── extraction-schema
119
+ │ ├── set <inbox-id> --domain-id --schema <json>
120
+ │ └── remove <inbox-id> --domain-id
121
+
122
+ ├── messages
123
+ │ ├── send --to --subject [--text] [--html] [--from] [--inbox-id] [--domain-id]
124
+ │ │ [--cc] [--bcc] [--reply-to] [--thread-id]
125
+ │ └── list [--inbox-id] [--domain-id] [--sender] [--limit] [--order] [--before] [--after]
126
+
127
+ ├── threads
128
+ │ ├── list [--inbox-id] [--domain-id] [--limit] [--cursor] [--order]
129
+ │ ├── messages <thread-id> [--limit] [--order] [--cursor]
130
+ │ ├── metadata <thread-id>
131
+ │ ├── set-status <thread-id> <open|needs_reply|waiting|closed>
132
+ │ ├── assign <thread-id> [--to <user>]
133
+ │ └── tags
134
+ │ ├── add <thread-id> <tag...>
135
+ │ └── remove <thread-id> <tag...>
136
+
137
+ ├── attachments
138
+ │ ├── upload <file>
139
+ │ ├── get <attachment-id>
140
+ │ └── url <attachment-id> [--expires-in]
141
+
142
+ ├── search
143
+ │ └── threads <query> [--inbox-id] [--domain-id] [--limit]
144
+
145
+ ├── delivery
146
+ │ ├── metrics [--domain-id] [--inbox-id] [--period]
147
+ │ ├── events [--domain-id] [--inbox-id] [--limit]
148
+ │ └── suppressions [--domain-id] [--inbox-id] [--limit]
149
+
150
+ ├── webhooks
151
+ │ ├── list [--inbox-id] [--status] [--endpoint] [--limit]
152
+ │ ├── get <delivery-id>
153
+ │ ├── retry <delivery-id>
154
+ │ └── health
155
+
156
+ ├── dmarc
157
+ │ ├── reports <domain> [--limit]
158
+ │ └── summary <domain> [--days]
159
+
160
+ └── data
161
+ ├── delete-request [--email] [--inbox-id] [--domain-id]
162
+ ├── delete-confirm <id> [--yes]
163
+ └── delete-status <id>
164
+ ```
165
+
166
+ ---
167
+
168
+ ## Global Flags
169
+
170
+ ```
171
+ --api-key TEXT API key (overrides env/config)
172
+ --base-url TEXT API base URL (default: https://api.commune.email)
173
+ --json Output raw JSON
174
+ --quiet / -q Suppress status messages
175
+ --no-color Disable color output
176
+ --version / -V Show version and exit
177
+ ```
178
+
179
+ ---
180
+
181
+ ## Config File
182
+
183
+ Stored at `~/.commune/config.toml` (or `$COMMUNE_CONFIG_DIR/config.toml`).
184
+ Created with `chmod 600` to protect your API key.
185
+
186
+ ```toml
187
+ api_key = "comm_..."
188
+ base_url = "https://api.commune.email"
189
+ ```
190
+
191
+ ---
192
+
193
+ ## Scripting
194
+
195
+ ```bash
196
+ # Non-TTY → auto JSON
197
+ inboxes=$(commune inboxes list)
198
+ echo "$inboxes" | jq '.data[0].email'
199
+
200
+ # Pipe body from stdin
201
+ echo "Email body" | commune messages send \
202
+ --to user@example.com \
203
+ --subject "Automated" \
204
+ --text -
205
+
206
+ # Exit code handling
207
+ if commune messages send --to user@example.com --subject Test --text "Hi" --json; then
208
+ echo "Sent"
209
+ else
210
+ echo "Failed: $?"
211
+ fi
212
+ ```
213
+
214
+ Full API reference: https://docs.commune.email
@@ -0,0 +1,3 @@
1
+ """Commune CLI — official command-line interface for the Commune email API."""
2
+
3
+ __version__ = "0.1.0"
@@ -0,0 +1,111 @@
1
+ """CommuneClient — thin httpx wrapper with API key auth.
2
+
3
+ Auth: Authorization: Bearer comm_...
4
+
5
+ Raises:
6
+ httpx.ConnectError / httpx.TimeoutException — caller wraps with network_error()
7
+ All non-2xx responses — caller checks response.is_success and calls api_error()
8
+ """
9
+
10
+ from __future__ import annotations
11
+
12
+ from typing import Any, Optional
13
+
14
+ import httpx
15
+
16
+ from .state import AppState
17
+
18
+ DEFAULT_TIMEOUT = 30.0
19
+
20
+
21
+ class CommuneClient:
22
+ """HTTP client for the Commune API.
23
+
24
+ Usage:
25
+ client = CommuneClient.from_state(state)
26
+ r = client.get("/v1/domains")
27
+ if not r.is_success:
28
+ api_error(r, json_output=state.should_json())
29
+ """
30
+
31
+ def __init__(
32
+ self,
33
+ base_url: str,
34
+ api_key: Optional[str] = None,
35
+ timeout: float = DEFAULT_TIMEOUT,
36
+ ):
37
+ self.base_url = base_url.rstrip("/")
38
+ self.api_key = api_key
39
+ self.timeout = timeout
40
+
41
+ @classmethod
42
+ def from_state(cls, state: AppState) -> "CommuneClient":
43
+ return cls(
44
+ base_url=state.base_url,
45
+ api_key=state.api_key,
46
+ timeout=DEFAULT_TIMEOUT,
47
+ )
48
+
49
+ def _base_headers(self) -> dict[str, str]:
50
+ headers: dict[str, str] = {
51
+ "Content-Type": "application/json",
52
+ "User-Agent": "commune-cli/0.1.0",
53
+ }
54
+ if self.api_key:
55
+ headers["Authorization"] = f"Bearer {self.api_key}"
56
+ return headers
57
+
58
+ def _url(self, path: str) -> str:
59
+ return self.base_url + path
60
+
61
+ def _req(
62
+ self,
63
+ method: str,
64
+ path: str,
65
+ *,
66
+ params: Optional[dict[str, Any]] = None,
67
+ json: Optional[Any] = None,
68
+ data: Optional[bytes] = None,
69
+ extra_headers: Optional[dict[str, str]] = None,
70
+ ) -> httpx.Response:
71
+ headers = self._base_headers()
72
+ if extra_headers:
73
+ headers.update(extra_headers)
74
+ if data is not None:
75
+ headers.pop("Content-Type", None)
76
+
77
+ if params:
78
+ params = {k: v for k, v in params.items() if v is not None}
79
+
80
+ with httpx.Client(timeout=self.timeout) as client:
81
+ return client.request(
82
+ method,
83
+ self._url(path),
84
+ headers=headers,
85
+ params=params or None,
86
+ json=json,
87
+ content=data,
88
+ )
89
+
90
+ def get(self, path: str, *, params: Optional[dict[str, Any]] = None) -> httpx.Response:
91
+ return self._req("GET", path, params=params)
92
+
93
+ def post(
94
+ self,
95
+ path: str,
96
+ *,
97
+ json: Optional[Any] = None,
98
+ data: Optional[bytes] = None,
99
+ extra_headers: Optional[dict[str, str]] = None,
100
+ params: Optional[dict[str, Any]] = None,
101
+ ) -> httpx.Response:
102
+ return self._req("POST", path, json=json, data=data, extra_headers=extra_headers, params=params)
103
+
104
+ def patch(self, path: str, *, json: Optional[Any] = None) -> httpx.Response:
105
+ return self._req("PATCH", path, json=json)
106
+
107
+ def delete(self, path: str, *, params: Optional[dict[str, Any]] = None) -> httpx.Response:
108
+ return self._req("DELETE", path, params=params)
109
+
110
+ def put(self, path: str, *, json: Optional[Any] = None) -> httpx.Response:
111
+ return self._req("PUT", path, json=json)
@@ -0,0 +1 @@
1
+ """Command modules for the Commune CLI."""