makers-sdk 0.1.0b1__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,375 @@
1
+ Metadata-Version: 2.4
2
+ Name: makers-sdk
3
+ Version: 0.1.0b1
4
+ Summary: Synchronous Python client for the EdgeOne Makers SDK
5
+ License-Expression: MIT
6
+ Requires-Python: >=3.10
7
+ Description-Content-Type: text/markdown
8
+ Requires-Dist: cos-python-sdk-v5==1.9.44
9
+ Provides-Extra: test
10
+ Requires-Dist: jsonschema[format]>=4.18; extra == "test"
11
+
12
+ # EdgeOne Makers Python SDK
13
+
14
+ [English](README.md) | [中文](README.zh-CN.md)
15
+
16
+ Synchronous Python 3.10+ client for EdgeOne Makers projects, environment variables, and artifact deployments. There is no async client.
17
+
18
+ PyPI package: `makers-sdk` · `import makers_sdk` · Version `0.1.0` · Contract `0.1.23` · MIT License
19
+
20
+ ## Installation
21
+
22
+ ```sh
23
+ pip install makers-sdk
24
+ ```
25
+
26
+ ```python
27
+ from makers_sdk import Client
28
+ ```
29
+
30
+ ---
31
+
32
+ ## Client
33
+
34
+ ### Constructor
35
+
36
+ ```python
37
+ import os
38
+ from makers_sdk import Client
39
+
40
+ client = Client(
41
+ token=os.environ["MAKERS_API_TOKEN"],
42
+ source="cli",
43
+ region="china",
44
+ )
45
+ ```
46
+
47
+ | Parameter | Type | Required | Default | Description |
48
+ |-----------|------|:--------:|---------|-------------|
49
+ | `token` | `str` | Yes | — | EdgeOne Makers API Token |
50
+ | `source` | `str` | Yes | — | Only confirmed value is `"cli"` |
51
+ | `region` | `str` | No | auto-detect | `"china"` → `pages-api.cloud.tencent.com/v1`; `"global"` → `pages-api.edgeone.ai/v1` |
52
+ | `base_url` | `str` | No | — | Overrides `region`; must be HTTPS (localhost may use HTTP) |
53
+ | `timeout` | `float` | No | `30` | Per-request timeout in **seconds** |
54
+ | `retries` | `int` | No | `3` | Max retry count for queries; writes do not retry |
55
+ | `logger` | `Logger` | No | — | Must implement `debug`, `info`, `warn`, `error` |
56
+
57
+ Auto-detection probes china then global and caches the result per Client instance.
58
+
59
+ ### Public members
60
+
61
+ | Member | Type | Description |
62
+ |--------|------|-------------|
63
+ | `client.projects` | `Projects` | Project and environment variable operations |
64
+ | `client.deployments` | `Deployments` | Deployment operations |
65
+ | `client.region` | `str \| None` | Read-only; configured or detected region |
66
+
67
+ ---
68
+
69
+ ## Projects
70
+
71
+ All return values are `dict` with `snake_case` keys. Optional fields may be absent — always use `.get()`.
72
+
73
+ ### `projects.create(*, name, area?, initial_env_vars?)`
74
+
75
+ Creates a project. Returns `{"project_id": ...}` only — query `get` for the full model.
76
+
77
+ ```python
78
+ created = client.projects.create(
79
+ name="docs-site",
80
+ area="overseas",
81
+ initial_env_vars=[{"key": "API_URL", "value": "https://example.com"}],
82
+ )
83
+ project_id = created["project_id"]
84
+ ```
85
+
86
+ | Parameter | Type | Required | Description |
87
+ |-----------|------|:--------:|-------------|
88
+ | `name` | `str` | Yes | Unique per account; duplicate raises `ConflictError` |
89
+ | `area` | `str` | No | `"mainland"` / `"overseas"` / `"global"`; omitted = not sent |
90
+ | `initial_env_vars` | `list[dict]` | No | Set env vars at creation time |
91
+
92
+ ### `projects.list(**options)`
93
+
94
+ ```python
95
+ page = client.projects.list(
96
+ name="docs",
97
+ page=0,
98
+ page_size=20,
99
+ order={"field": "created_on", "direction": "desc"},
100
+ )
101
+ ```
102
+
103
+ | Parameter | Type | Default | Description |
104
+ |-----------|------|---------|-------------|
105
+ | `project_ids` | `list[str]` | — | Filter by ID list |
106
+ | `name` | `str` | — | Filter by name |
107
+ | `status` | `str` | — | Filter by status |
108
+ | `provider` | `str` | — | Filter by provider |
109
+ | `page` | `int` | `0` | Zero-based page index |
110
+ | `page_size` | `int` | `20` | 1–100 |
111
+ | `order` | `dict` | — | `field`: `"created_on"` or `"modified_on"`; `direction`: `"asc"` or `"desc"` |
112
+
113
+ **Returns** `dict`: `{ "items", "page", "page_size", "total", "has_next" }`.
114
+
115
+ ### `projects.list_all(**options)`
116
+
117
+ ```python
118
+ for project in client.projects.list_all():
119
+ print(project["project_id"], project["name"])
120
+ ```
121
+
122
+ Same options as `list` except no `page`. Returns `Iterator[dict]`.
123
+
124
+ ### `projects.get(*, project_id)`
125
+
126
+ Returns the full project dict. This is the only way to obtain `preset_domain`.
127
+
128
+ ```python
129
+ project = client.projects.get(project_id=project_id)
130
+ print(project.get("preset_domain"))
131
+ ```
132
+
133
+ ### `projects.update(*, project_id, **fields)`
134
+
135
+ Updates project settings. Only provided fields are sent; omitted fields are not cleared.
136
+
137
+ ```python
138
+ client.projects.update(
139
+ project_id=project_id,
140
+ name="new-name",
141
+ root_dir=".",
142
+ output_dir="dist",
143
+ build_cmd="npm run build",
144
+ install_cmd="npm install",
145
+ framework="other",
146
+ nodejs_version="20",
147
+ )
148
+ ```
149
+
150
+ | Parameter | Type | Description |
151
+ |-----------|------|-------------|
152
+ | `project_id` | `str` | Required |
153
+ | `name` | `str` | Project name |
154
+ | `root_dir` | `str` | Root directory |
155
+ | `output_dir` | `str` | Output directory |
156
+ | `build_cmd` | `str` | Build command |
157
+ | `install_cmd` | `str` | Install command |
158
+ | `framework` | `str` | Framework identifier |
159
+ | `nodejs_version` | `str` | Node.js version |
160
+
161
+ ### `projects.delete(*, project_id)`
162
+
163
+ Deletes a project. **Irreversible.**
164
+
165
+ ---
166
+
167
+ ## Environment variables
168
+
169
+ Methods live on `client.projects`. Public fields: `key`, `value`, optional `comment`.
170
+
171
+ ### `projects.list_envs(*, project_id)`
172
+
173
+ Returns `list[dict]`. Values are automatically added to the redaction list.
174
+
175
+ ### `projects.set_envs(*, project_id, env_vars)`
176
+
177
+ Batch upsert. Internally reads existing vars first, then writes. Not atomic. Duplicate keys raise `ValidationError` before any network request.
178
+
179
+ ```python
180
+ client.projects.set_envs(
181
+ project_id=project_id,
182
+ env_vars=[
183
+ {"key": "API_URL", "value": "https://example.com", "comment": "origin"},
184
+ ],
185
+ )
186
+ ```
187
+
188
+ ### `projects.delete_envs(*, project_id, keys)`
189
+
190
+ Deletes env vars by key. Duplicate keys raise `ValidationError`.
191
+
192
+ ---
193
+
194
+ ## Deployments
195
+
196
+ ### `deployments.deploy(*, project_id, artifact, **options)`
197
+
198
+ Deploys an artifact. Accepts exactly one variant:
199
+
200
+ | Variant | Type | Behavior |
201
+ |---------|------|----------|
202
+ | `"files"` | `dict[str, str \| bytes]` | SDK zips in memory |
203
+ | `"directory"` | `str` (+ optional `"exclude_patterns": list[str]`) | SDK zips the directory |
204
+ | `"archive"` | `str` (zip file path) | Uploaded directly after safety validation |
205
+
206
+ ```python
207
+ # Inline files
208
+ client.deployments.deploy(
209
+ project_id=project_id,
210
+ artifact={"files": {"index.html": "<h1>ok</h1>"}},
211
+ wait=True,
212
+ )
213
+
214
+ # Directory
215
+ client.deployments.deploy(
216
+ project_id=project_id,
217
+ artifact={"directory": "./dist", "exclude_patterns": ["**/*.map"]},
218
+ )
219
+
220
+ # Zip archive
221
+ client.deployments.deploy(
222
+ project_id=project_id,
223
+ artifact={"archive": "./site.zip"},
224
+ )
225
+ ```
226
+
227
+ | Parameter | Type | Default | Description |
228
+ |-----------|------|---------|-------------|
229
+ | `project_id` | `str` | — | Required |
230
+ | `artifact` | `dict` | — | Required; exactly one variant |
231
+ | `env` | `str` | `"Production"` | `"Preview"` requires an existing production deployment |
232
+ | `wait` | `bool` | `False` | Poll until terminal status |
233
+ | `timeout` | `float` | `900` | Max wait time in **seconds** (15 min) |
234
+ | `poll_interval` | `float` | `5` | Poll interval in **seconds** |
235
+ | `env_vars` | `list[dict]` | — | Deployment-scoped env vars |
236
+ | `upload_progress` | callable | — | Fires once after upload completes |
237
+ | `status_change` | callable | — | Fires on each status transition |
238
+
239
+ **Returns** `dict`. With `wait=False`, only `deployment_id`, `project_id`, and `env` are populated.
240
+
241
+ **Note**: `timeout` here is the overall wait budget (default 900s), distinct from the per-request `Client(timeout=30)`.
242
+
243
+ **Directory safety**: rejects symlinks; ignores `.git`, `node_modules`, `.edgeone`, `.env`, logs, temp and system files. `exclude_patterns` appends POSIX globs; negation (`!`) is rejected.
244
+
245
+ ### `deployments.wait(*, project_id, deployment_id, **options)`
246
+
247
+ Polls until a terminal status: `Success`, `Failed`, `Timeout`, `Cancelled`, or `Invalid`.
248
+
249
+ ```python
250
+ result = client.deployments.wait(
251
+ project_id=project_id,
252
+ deployment_id=deployment_id,
253
+ timeout=900,
254
+ poll_interval=5,
255
+ status_change=lambda e: print(e["deployment"].get("status")),
256
+ )
257
+ ```
258
+
259
+ | Parameter | Type | Default | Description |
260
+ |-----------|------|---------|-------------|
261
+ | `project_id` | `str` | — | Required |
262
+ | `deployment_id` | `str` | — | Required |
263
+ | `timeout` | `float` | `900` | Raises `DeploymentTimeoutError` on expiry; **does not cancel the deployment** |
264
+ | `poll_interval` | `float` | `5` | |
265
+ | `status_change` | callable | — | |
266
+
267
+ ### `deployments.list(*, project_id, **options)`
268
+
269
+ | Parameter | Type | Default | Description |
270
+ |-----------|------|---------|-------------|
271
+ | `project_id` | `str` | — | Required |
272
+ | `status` | `list[str]` | — | Filter by status |
273
+ | `time_range` | `dict` | — | `{"start": ..., "end": ...}` ISO 8601 |
274
+ | `repo_branch` | `list[str]` | — | Filter by branch |
275
+ | `page` | `int` | `0` | |
276
+ | `page_size` | `int` | `20` | |
277
+ | `order` | `dict` | — | |
278
+
279
+ ### `deployments.list_all(*, project_id, **options)`
280
+
281
+ Same as `list` without `page`. Returns `Iterator[dict]`.
282
+
283
+ ### `deployments.get(*, project_id, deployment_id)`
284
+
285
+ Returns a single deployment dict. Raises `NotFoundError` if not found.
286
+
287
+ ### `deployments.get_log(*, project_id, deployment_id)`
288
+
289
+ Returns `{"log_url": ...}` — the build log URL.
290
+
291
+ ---
292
+
293
+ ## Data models
294
+
295
+ All values are plain `dict`. Optional fields may be absent — use `.get()` to access them.
296
+
297
+ ### Project
298
+
299
+ | Field | Type | Always present | Description |
300
+ |-------|------|:--------------:|-------------|
301
+ | `project_id` | `str` | Yes | |
302
+ | `name` | `str` | Yes | Unique per account |
303
+ | `status` | `str` | Yes | |
304
+ | `area` | `str` | No | `"mainland"` / `"overseas"` / `"global"` |
305
+ | `preset_domain` | `str` | No | Production domain; may be absent for new projects |
306
+ | `created_on` | `str` | Yes | ISO 8601 |
307
+ | `modified_on` | `str` | Yes | ISO 8601 |
308
+
309
+ ### Deployment
310
+
311
+ | Field | Type | Always present | Description |
312
+ |-------|------|:--------------:|-------------|
313
+ | `deployment_id` | `str` | Yes | |
314
+ | `project_id` | `str` | Yes | |
315
+ | `env` | `str` | Yes | `"Production"` or `"Preview"` |
316
+ | `status` | `str` | No | Terminal: `Success` / `Failed` / `Timeout` / `Cancelled` / `Invalid` |
317
+ | `preview_url` | `str` | No | Only for Preview deployments |
318
+ | `code` | `str` | No | |
319
+ | `created_on` | `str` | No | Not returned with `wait=False` |
320
+ | `modified_on` | `str` | No | Not returned with `wait=False` |
321
+
322
+ ### EnvVar
323
+
324
+ | Field | Type | Always present |
325
+ |-------|------|:--------------:|
326
+ | `key` | `str` | Yes |
327
+ | `value` | `str` | Yes |
328
+ | `comment` | `str` | No |
329
+
330
+ ### Callbacks
331
+
332
+ **upload_progress event**: `{"uploaded_bytes", "total_bytes", "completed_files", "total_files"}`
333
+
334
+ **status_change event**: `{"deployment": dict, "previous_status": str | None}`
335
+
336
+ ---
337
+
338
+ ## Errors
339
+
340
+ All errors extend `MakersError` with fields: `code`, `message`, `request_id`, `http_status`, `cause`.
341
+
342
+ | Error class | Trigger |
343
+ |-------------|---------|
344
+ | `AuthError` | Code 105 or HTTP 401 |
345
+ | `ValidationError` | Code contains "Invalid" or HTTP 400 |
346
+ | `NotFoundError` | Code contains "NotFound" or HTTP 404 |
347
+ | `ConflictError` | Code contains "Conflict" or HTTP 409 |
348
+ | `RateLimitError` | Code 110 or HTTP 429 |
349
+ | `UploadError` | COS upload failure |
350
+ | `TimeoutError` | Request timeout |
351
+ | `DeploymentTimeoutError` | Wait timeout (extends `TimeoutError`) |
352
+
353
+ ```python
354
+ from makers_sdk import MakersError, NotFoundError, DeploymentTimeoutError
355
+
356
+ try:
357
+ client.projects.get(project_id="missing")
358
+ except NotFoundError as error:
359
+ print(error.code, error.request_id, error.http_status)
360
+ except MakersError:
361
+ raise
362
+ ```
363
+
364
+ ### Retry behavior
365
+
366
+ Queries retry on network errors, HTTP 408/429/5xx, and outer `Code: 110`, up to `retries` times (default 3). Exponential backoff with full jitter, capped at 10 seconds. `Retry-After` header is respected when present. Creates, updates, and COS credential requests do not retry.
367
+
368
+ ---
369
+
370
+ ## Development
371
+
372
+ ```sh
373
+ python -m pip install -e ".[test]"
374
+ python -m unittest discover -s tests -v
375
+ ```