@acedatacloud/skills 2026.628.5 → 2026.628.7
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.
package/README.md
CHANGED
|
@@ -207,7 +207,7 @@ Skills provide **knowledge** (when to use, parameters, gotchas). MCP servers pro
|
|
|
207
207
|
| nano-banana-image | [mcp-nano-banana](https://pypi.org/project/mcp-nano-banana/) | `pip install mcp-nano-banana` | `https://nano-banana.mcp.acedata.cloud/mcp` |
|
|
208
208
|
| short-url | [mcp-shorturl](https://pypi.org/project/mcp-shorturl/) | `pip install mcp-shorturl` | `https://short-url.mcp.acedata.cloud/mcp` |
|
|
209
209
|
| wan-video | [mcp-wan](https://pypi.org/project/mcp-wan/) | `pip install mcp-wan` | `https://wan.mcp.acedata.cloud/mcp` |
|
|
210
|
-
| acedatacloud | [mcp-acedatacloud](https://pypi.org/project/mcp-acedatacloud/) | `pip install mcp-acedatacloud` | `https://
|
|
210
|
+
| acedatacloud | [mcp-acedatacloud](https://pypi.org/project/mcp-acedatacloud/) | `pip install mcp-acedatacloud` | `https://mcp.acedata.cloud/mcp` |
|
|
211
211
|
|
|
212
212
|
**Using hosted MCP endpoints** (no local install needed):
|
|
213
213
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@acedatacloud/skills",
|
|
3
|
-
"version": "2026.628.
|
|
3
|
+
"version": "2026.628.7",
|
|
4
4
|
"description": "Agent Skills for AceDataCloud AI services — music, image, video generation, LLM chat, web search. Compatible with Claude Code, GitHub Copilot, Gemini CLI, OpenAI Codex, and 30+ AI coding agents.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"agent-skills",
|
|
@@ -18,7 +18,7 @@ Each AceDataCloud service has a corresponding MCP server that provides tool-use
|
|
|
18
18
|
| nano-banana-image | `pip install mcp-nano-banana` | `https://nano-banana.mcp.acedata.cloud/mcp` |
|
|
19
19
|
| short-url | `pip install mcp-shorturl` | `https://short-url.mcp.acedata.cloud/mcp` |
|
|
20
20
|
| wan-video | `pip install mcp-wan` | `https://wan.mcp.acedata.cloud/mcp` |
|
|
21
|
-
| acedatacloud | `pip install mcp-acedatacloud` | `https://
|
|
21
|
+
| acedatacloud | `pip install mcp-acedatacloud` | `https://mcp.acedata.cloud/mcp` |
|
|
22
22
|
|
|
23
23
|
## Configuration Example
|
|
24
24
|
|
|
@@ -5,8 +5,11 @@ description: |
|
|
|
5
5
|
(platform.acedata.cloud). Use when the user wants to check their balance /
|
|
6
6
|
remaining credits, look up API call (usage) records and spend, list or create
|
|
7
7
|
or delete API keys (credentials), list subscribed services, list/create/pay
|
|
8
|
-
recharge orders, manage platform tokens,
|
|
9
|
-
publish an announcement.
|
|
8
|
+
recharge orders, manage platform tokens, view referral/affiliate earnings, or
|
|
9
|
+
(admins) publish an announcement. Also covers the PUBLIC catalog & docs (no
|
|
10
|
+
token needed): service detail & pricing, API list & OpenAPI specs, datasets,
|
|
11
|
+
integrations, full-text documentation search, and the model catalog with
|
|
12
|
+
per-model credit pricing. This is the self-service "console" API — distinct
|
|
10
13
|
from the data-generation APIs (image/video/music/search).
|
|
11
14
|
license: Apache-2.0
|
|
12
15
|
metadata:
|
|
@@ -19,7 +22,9 @@ compatibility: Requires ACEDATACLOUD_PLATFORM_TOKEN (a platform or user token).
|
|
|
19
22
|
# AceDataCloud Platform Management
|
|
20
23
|
|
|
21
24
|
Programmatically manage your AceDataCloud account: balances, usage records, API
|
|
22
|
-
keys, services, orders, platform tokens, models, and
|
|
25
|
+
keys, services, orders, platform tokens, models, announcements and referral
|
|
26
|
+
earnings — plus browse the public catalog & docs (service pricing, API specs,
|
|
27
|
+
datasets, integrations, documentation search, model catalog) without a token.
|
|
23
28
|
|
|
24
29
|
This is the **management / console** API at `https://platform.acedata.cloud/api/v1`
|
|
25
30
|
— the same surface the web console uses. It is **different** from the
|
|
@@ -68,6 +73,20 @@ python3 $ADC keys # list API keys (credentials)
|
|
|
68
73
|
python3 $ADC orders --state Finished # recharge orders
|
|
69
74
|
python3 $ADC tokens # platform tokens
|
|
70
75
|
python3 $ADC models # available chat models
|
|
76
|
+
python3 $ADC distributions # referral status + commission history
|
|
77
|
+
|
|
78
|
+
# Catalog & docs (PUBLIC — work without a token)
|
|
79
|
+
python3 $ADC get-service --service suno # one service's detail
|
|
80
|
+
python3 $ADC pricing --service suno # unit, free_amount and cost
|
|
81
|
+
python3 $ADC apis --service suno # API endpoints for a service
|
|
82
|
+
python3 $ADC spec --path /suno/audios # one API's OpenAPI definition + cost
|
|
83
|
+
python3 $ADC datasets # downloadable datasets
|
|
84
|
+
python3 $ADC integrations # third-party integrations
|
|
85
|
+
python3 $ADC docs-search --query "suno lyrics" --lang en # full-text doc search
|
|
86
|
+
python3 $ADC docs-list # browse documentation pages
|
|
87
|
+
python3 $ADC doc --id <document-uuid> # one doc's full content
|
|
88
|
+
python3 $ADC model-catalog --modality chat # rich model catalog + credit pricing
|
|
89
|
+
python3 $ADC model --model claude # look up a model by id/name
|
|
71
90
|
|
|
72
91
|
# Safe write (require --yes to actually execute)
|
|
73
92
|
python3 $ADC create-key --application <app-id> --name "ci" --yes
|
|
@@ -206,6 +225,22 @@ Filters: `api_id`, `application_id`, `status_code`, `created_at_from`,
|
|
|
206
225
|
OpenAI-style (no pagination): `{ "object": "list", "data": [ { "id": "gpt-4.1",
|
|
207
226
|
"label": "GPT-4.1", "owned_by": "openai", "type": "chat", "capabilities": ["vision"] } ] }`
|
|
208
227
|
|
|
228
|
+
### Catalog & docs (PUBLIC — no token required)
|
|
229
|
+
|
|
230
|
+
These work without auth (send the token if you have one; not required):
|
|
231
|
+
|
|
232
|
+
- **Service detail / pricing** — `GET /services/?id=<uuid>` → `items[0]` (full: `cost`, `unit`,
|
|
233
|
+
`free_amount`, `title`). The `services/?alias=` filter is **ignored** server-side, so resolve an
|
|
234
|
+
alias by paging `services/` and matching `alias` client-side. The `services/{id}/` detail route is broken (500).
|
|
235
|
+
- **APIs** — `GET /apis/?path=<path>` → exactly one item with its OpenAPI `definition` + `cost`.
|
|
236
|
+
`apis/?service_id=` is **ignored** server-side; filter by `service_id` client-side. `apis/{id}/` is broken (500).
|
|
237
|
+
- **Datasets / integrations** — `GET /datasets/`, `GET /integrations/`.
|
|
238
|
+
- **Doc search** — `GET /search/?query=<text>&lang=<code>` → `{ results: [ { id, alias, title, type, snippet, url } ] }`.
|
|
239
|
+
The param is **`query`** (not `keyword`).
|
|
240
|
+
- **Doc content** — `GET /documents/?id=<uuid>` → `items[0].content`. `documents/{id}/` (id or slug) is broken (404).
|
|
241
|
+
- **Model catalog** — `GET /models/catalog/` → `{ rates, modalities, count, items:[{ id, name, provider,
|
|
242
|
+
modality, unit, capabilities, pricing:{ input_credits, output_credits, official_* } }] }`. Filter client-side.
|
|
243
|
+
|
|
209
244
|
### Announcements — `GET /announcements/`
|
|
210
245
|
|
|
211
246
|
Public read: `{ "count": 20, "items": [ { "id", "title", "content",
|
|
@@ -241,4 +276,4 @@ publishing announcements are **irreversible or money-related**. Always:
|
|
|
241
276
|
- Announcement endpoints under `/admin/` require a **superuser** token; a normal
|
|
242
277
|
token gets `403`.
|
|
243
278
|
|
|
244
|
-
> **MCP:** `pip install mcp-acedatacloud` | See [all MCP servers](../_shared/mcp-servers.md). The MCP exposes these as tools (`
|
|
279
|
+
> **MCP:** `pip install mcp-acedatacloud` | Hosted: `https://mcp.acedata.cloud/mcp` | See [all MCP servers](../_shared/mcp-servers.md). The MCP exposes these as tools (`acedatacloud_get_balance`, `acedatacloud_list_usage`, `acedatacloud_get_pricing`, `acedatacloud_search_docs`, `acedatacloud_get_api_spec`, `acedatacloud_create_credential`, …) with the same write-confirmation guard.
|
|
@@ -18,6 +18,7 @@ import argparse
|
|
|
18
18
|
import datetime as dt
|
|
19
19
|
import json
|
|
20
20
|
import os
|
|
21
|
+
import re
|
|
21
22
|
import sys
|
|
22
23
|
import urllib.error
|
|
23
24
|
import urllib.parse
|
|
@@ -32,18 +33,9 @@ def _die(msg: str, code: int = 1) -> None:
|
|
|
32
33
|
sys.exit(code)
|
|
33
34
|
|
|
34
35
|
|
|
35
|
-
def _token(args: argparse.Namespace) -> str:
|
|
36
|
-
token = args.token or os.environ.get("ACEDATACLOUD_PLATFORM_TOKEN", "")
|
|
37
|
-
if not token:
|
|
38
|
-
_die(
|
|
39
|
-
"no platform token. Set ACEDATACLOUD_PLATFORM_TOKEN or pass --token.\n"
|
|
40
|
-
"Create one at https://platform.acedata.cloud/console/platform-tokens"
|
|
41
|
-
)
|
|
42
|
-
return token
|
|
43
|
-
|
|
44
|
-
|
|
45
36
|
def _request(args: argparse.Namespace, method: str, path: str,
|
|
46
|
-
params: dict | None = None, body: dict | None = None
|
|
37
|
+
params: dict | None = None, body: dict | None = None,
|
|
38
|
+
auth_required: bool = True) -> tuple[int, object]:
|
|
47
39
|
base = args.base_url.rstrip("/")
|
|
48
40
|
url = f"{base}/api/v1{path}"
|
|
49
41
|
if params:
|
|
@@ -51,11 +43,16 @@ def _request(args: argparse.Namespace, method: str, path: str,
|
|
|
51
43
|
if clean:
|
|
52
44
|
url += "?" + urllib.parse.urlencode(clean, doseq=True)
|
|
53
45
|
data = json.dumps(body).encode() if body is not None else None
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
"
|
|
58
|
-
|
|
46
|
+
headers = {"accept": "application/json", "content-type": "application/json"}
|
|
47
|
+
token = args.token or os.environ.get("ACEDATACLOUD_PLATFORM_TOKEN", "")
|
|
48
|
+
if token:
|
|
49
|
+
headers["authorization"] = f"Bearer {token}"
|
|
50
|
+
elif auth_required:
|
|
51
|
+
_die(
|
|
52
|
+
"no platform token. Set ACEDATACLOUD_PLATFORM_TOKEN or pass --token.\n"
|
|
53
|
+
"Create one at https://platform.acedata.cloud/console/platform-tokens"
|
|
54
|
+
)
|
|
55
|
+
req = urllib.request.Request(url, data=data, method=method, headers=headers)
|
|
59
56
|
try:
|
|
60
57
|
with urllib.request.urlopen(req, timeout=args.timeout) as resp:
|
|
61
58
|
raw = resp.read().decode("utf-8")
|
|
@@ -72,7 +69,7 @@ def _request(args: argparse.Namespace, method: str, path: str,
|
|
|
72
69
|
|
|
73
70
|
|
|
74
71
|
def _get_all(args: argparse.Namespace, path: str, params: dict | None = None,
|
|
75
|
-
page: int = 200, cap: int = 2000) -> tuple[int, list]:
|
|
72
|
+
page: int = 200, cap: int = 2000, auth_required: bool = True) -> tuple[int, list]:
|
|
76
73
|
"""Fetch every page of a {count, items} list endpoint (capped)."""
|
|
77
74
|
base = dict(params or {})
|
|
78
75
|
out: list = []
|
|
@@ -80,7 +77,7 @@ def _get_all(args: argparse.Namespace, path: str, params: dict | None = None,
|
|
|
80
77
|
total = 0
|
|
81
78
|
while len(out) < cap:
|
|
82
79
|
base.update({"limit": page, "offset": offset})
|
|
83
|
-
status, payload = _request(args, "GET", path, base)
|
|
80
|
+
status, payload = _request(args, "GET", path, base, auth_required=auth_required)
|
|
84
81
|
_check_ok(status, payload)
|
|
85
82
|
items = payload.get("items", []) if isinstance(payload, dict) else []
|
|
86
83
|
total = payload.get("count", 0) if isinstance(payload, dict) else 0
|
|
@@ -142,6 +139,29 @@ def _since(days: int | None) -> str | None:
|
|
|
142
139
|
return (dt.datetime.now(dt.timezone.utc) - dt.timedelta(days=days)).strftime("%Y-%m-%dT%H:%M:%SZ")
|
|
143
140
|
|
|
144
141
|
|
|
142
|
+
_UUID = re.compile(r"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$")
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
def _resolve_service(args, ref: str) -> dict | None:
|
|
146
|
+
"""Resolve a service by UUID (services/?id=) or alias (paginated match).
|
|
147
|
+
|
|
148
|
+
The platform's services/?alias= filter is ignored server-side, so alias
|
|
149
|
+
lookups page through the catalog and match client-side.
|
|
150
|
+
"""
|
|
151
|
+
ref = ref.strip()
|
|
152
|
+
if _UUID.match(ref):
|
|
153
|
+
status, payload = _request(args, "GET", "/services/", {"id": ref}, auth_required=False)
|
|
154
|
+
_check_ok(status, payload)
|
|
155
|
+
items = payload.get("items", []) if isinstance(payload, dict) else []
|
|
156
|
+
return items[0] if items else None
|
|
157
|
+
target = ref.lower()
|
|
158
|
+
_, items = _get_all(args, "/services/", page=50, cap=600, auth_required=False)
|
|
159
|
+
for it in items:
|
|
160
|
+
if (it.get("alias") or "").lower() == target:
|
|
161
|
+
return it
|
|
162
|
+
return None
|
|
163
|
+
|
|
164
|
+
|
|
145
165
|
# ---- read commands -------------------------------------------------------
|
|
146
166
|
|
|
147
167
|
def cmd_balance(args):
|
|
@@ -261,6 +281,179 @@ def cmd_announcements(args):
|
|
|
261
281
|
_emit(args, payload, rows, ("id", "title", "rank", "publish_at"))
|
|
262
282
|
|
|
263
283
|
|
|
284
|
+
# ---- catalog & docs commands (public, no token needed) ------------------
|
|
285
|
+
|
|
286
|
+
def cmd_get_service(args):
|
|
287
|
+
svc = _resolve_service(args, args.service)
|
|
288
|
+
if not svc:
|
|
289
|
+
_die(f"no service matched '{args.service}'", code=2)
|
|
290
|
+
_emit(args, svc)
|
|
291
|
+
|
|
292
|
+
|
|
293
|
+
def cmd_pricing(args):
|
|
294
|
+
svc = _resolve_service(args, args.service)
|
|
295
|
+
if not svc:
|
|
296
|
+
_die(f"no service matched '{args.service}'", code=2)
|
|
297
|
+
out = {
|
|
298
|
+
"service_id": svc.get("id"),
|
|
299
|
+
"alias": svc.get("alias"),
|
|
300
|
+
"title": svc.get("title"),
|
|
301
|
+
"type": svc.get("type"),
|
|
302
|
+
"unit": svc.get("unit"),
|
|
303
|
+
"free_amount": svc.get("free_amount"),
|
|
304
|
+
"cost": svc.get("cost"),
|
|
305
|
+
}
|
|
306
|
+
_emit(args, out)
|
|
307
|
+
|
|
308
|
+
|
|
309
|
+
def cmd_apis(args):
|
|
310
|
+
sid = None
|
|
311
|
+
if args.service:
|
|
312
|
+
svc = _resolve_service(args, args.service)
|
|
313
|
+
if not svc:
|
|
314
|
+
_die(f"no service matched '{args.service}'", code=2)
|
|
315
|
+
sid = svc.get("id")
|
|
316
|
+
# apis/?service_id= is ignored server-side, so filter client-side.
|
|
317
|
+
_, items = _get_all(args, "/apis/", page=50, cap=2000, auth_required=False)
|
|
318
|
+
if sid:
|
|
319
|
+
items = [it for it in items if it.get("service_id") == sid]
|
|
320
|
+
items = items[: args.limit]
|
|
321
|
+
slim = [{k: v for k, v in it.items() if k != "definition"} for it in items]
|
|
322
|
+
rows = [(
|
|
323
|
+
(it.get("path") or ""),
|
|
324
|
+
it.get("method"),
|
|
325
|
+
it.get("stage"),
|
|
326
|
+
(it.get("title") or "")[:30],
|
|
327
|
+
) for it in items]
|
|
328
|
+
_emit(args, {"count": len(slim), "items": slim}, rows, ("path", "method", "stage", "title"))
|
|
329
|
+
|
|
330
|
+
|
|
331
|
+
def cmd_spec(args):
|
|
332
|
+
status, payload = _request(args, "GET", "/apis/", {"path": args.path}, auth_required=False)
|
|
333
|
+
_check_ok(status, payload)
|
|
334
|
+
items = payload.get("items", []) if isinstance(payload, dict) else []
|
|
335
|
+
if not items:
|
|
336
|
+
_die(f"no API matched path '{args.path}'", code=2)
|
|
337
|
+
api = items[0]
|
|
338
|
+
_emit(args, {
|
|
339
|
+
"id": api.get("id"),
|
|
340
|
+
"service_id": api.get("service_id"),
|
|
341
|
+
"title": api.get("title"),
|
|
342
|
+
"path": api.get("path"),
|
|
343
|
+
"method": api.get("method"),
|
|
344
|
+
"stage": api.get("stage"),
|
|
345
|
+
"cost": api.get("cost"),
|
|
346
|
+
"definition": api.get("definition"),
|
|
347
|
+
})
|
|
348
|
+
|
|
349
|
+
|
|
350
|
+
def cmd_datasets(args):
|
|
351
|
+
status, payload = _request(args, "GET", "/datasets/", {"limit": args.limit}, auth_required=False)
|
|
352
|
+
_check_ok(status, payload)
|
|
353
|
+
items = payload.get("items", []) if isinstance(payload, dict) else []
|
|
354
|
+
rows = [(it.get("id", "")[:8], (it.get("title") or "")[:30], it.get("price"), (it.get("part") or "")) for it in items]
|
|
355
|
+
_emit(args, payload, rows, ("id", "title", "price", "part"))
|
|
356
|
+
|
|
357
|
+
|
|
358
|
+
def cmd_integrations(args):
|
|
359
|
+
status, payload = _request(args, "GET", "/integrations/", {"limit": args.limit}, auth_required=False)
|
|
360
|
+
_check_ok(status, payload)
|
|
361
|
+
items = payload.get("items", []) if isinstance(payload, dict) else []
|
|
362
|
+
rows = [(it.get("id", "")[:8], (it.get("title") or "")[:34], it.get("stage")) for it in items]
|
|
363
|
+
_emit(args, payload, rows, ("id", "title", "stage"))
|
|
364
|
+
|
|
365
|
+
|
|
366
|
+
def cmd_docs_search(args):
|
|
367
|
+
status, payload = _request(args, "GET", "/search/", {"query": args.query, "lang": args.lang}, auth_required=False)
|
|
368
|
+
_check_ok(status, payload)
|
|
369
|
+
results = payload.get("results", []) if isinstance(payload, dict) else []
|
|
370
|
+
rows = [((r.get("title") or "")[:44], r.get("type"), r.get("matched_lang"), (r.get("id") or "")[:8]) for r in results]
|
|
371
|
+
_emit(args, payload, rows, ("title", "type", "lang", "id"))
|
|
372
|
+
|
|
373
|
+
|
|
374
|
+
def cmd_docs_list(args):
|
|
375
|
+
status, payload = _request(args, "GET", "/documents/", {"limit": args.limit, "type": args.type}, auth_required=False)
|
|
376
|
+
_check_ok(status, payload)
|
|
377
|
+
items = payload.get("items", []) if isinstance(payload, dict) else []
|
|
378
|
+
rows = [(it.get("id", "")[:8], (it.get("title") or "")[:40], it.get("type"), it.get("alias")) for it in items]
|
|
379
|
+
_emit(args, {"count": payload.get("count") if isinstance(payload, dict) else None, "items": items}, rows,
|
|
380
|
+
("id", "title", "type", "alias"))
|
|
381
|
+
|
|
382
|
+
|
|
383
|
+
def cmd_doc(args):
|
|
384
|
+
if not _UUID.match(args.id.strip()):
|
|
385
|
+
_die("doc id must be a document UUID", code=2)
|
|
386
|
+
status, payload = _request(args, "GET", "/documents/", {"id": args.id.strip()}, auth_required=False)
|
|
387
|
+
_check_ok(status, payload)
|
|
388
|
+
items = payload.get("items", []) if isinstance(payload, dict) else []
|
|
389
|
+
if not items:
|
|
390
|
+
_die(f"no document with id '{args.id}'", code=2)
|
|
391
|
+
_emit(args, items[0])
|
|
392
|
+
|
|
393
|
+
|
|
394
|
+
def cmd_model_catalog(args):
|
|
395
|
+
status, payload = _request(args, "GET", "/models/catalog/", auth_required=False)
|
|
396
|
+
_check_ok(status, payload)
|
|
397
|
+
items = payload.get("items", []) if isinstance(payload, dict) else []
|
|
398
|
+
if args.modality:
|
|
399
|
+
m = args.modality.lower()
|
|
400
|
+
items = [it for it in items if (it.get("modality") or "").lower() == m]
|
|
401
|
+
if args.provider:
|
|
402
|
+
pv = args.provider.lower()
|
|
403
|
+
items = [it for it in items if pv in (it.get("provider") or "").lower()]
|
|
404
|
+
rows = [(
|
|
405
|
+
it.get("id"),
|
|
406
|
+
it.get("provider"),
|
|
407
|
+
it.get("modality"),
|
|
408
|
+
(it.get("pricing") or {}).get("input_credits"),
|
|
409
|
+
(it.get("pricing") or {}).get("output_credits"),
|
|
410
|
+
) for it in items]
|
|
411
|
+
out = {"modalities": payload.get("modalities") if isinstance(payload, dict) else None,
|
|
412
|
+
"count": len(items), "items": items}
|
|
413
|
+
_emit(args, out, rows, ("id", "provider", "modality", "in_credits", "out_credits"))
|
|
414
|
+
|
|
415
|
+
|
|
416
|
+
def cmd_model(args):
|
|
417
|
+
status, payload = _request(args, "GET", "/models/catalog/", auth_required=False)
|
|
418
|
+
_check_ok(status, payload)
|
|
419
|
+
q = args.model.strip().lower()
|
|
420
|
+
items = [it for it in (payload.get("items", []) if isinstance(payload, dict) else [])
|
|
421
|
+
if q in (it.get("id") or "").lower() or q in (it.get("name") or "").lower()]
|
|
422
|
+
if not items:
|
|
423
|
+
_die(f"no model matched '{args.model}'", code=2)
|
|
424
|
+
rows = [(
|
|
425
|
+
it.get("id"),
|
|
426
|
+
it.get("provider"),
|
|
427
|
+
it.get("modality"),
|
|
428
|
+
(it.get("pricing") or {}).get("input_credits"),
|
|
429
|
+
(it.get("pricing") or {}).get("output_credits"),
|
|
430
|
+
) for it in items]
|
|
431
|
+
_emit(args, {"count": len(items), "items": items}, rows,
|
|
432
|
+
("id", "provider", "modality", "in_credits", "out_credits"))
|
|
433
|
+
|
|
434
|
+
|
|
435
|
+
def cmd_distributions(args):
|
|
436
|
+
status, status_payload = _request(args, "GET", "/distribution-statuses/", {"limit": 1})
|
|
437
|
+
_check_ok(status, status_payload)
|
|
438
|
+
st_items = status_payload.get("items", []) if isinstance(status_payload, dict) else []
|
|
439
|
+
hstatus, hist = _request(args, "GET", "/distribution-histories/", {"limit": args.limit})
|
|
440
|
+
_check_ok(hstatus, hist)
|
|
441
|
+
hitems = hist.get("items", []) if isinstance(hist, dict) else []
|
|
442
|
+
out = {
|
|
443
|
+
"status": st_items[0] if st_items else None,
|
|
444
|
+
"history_count": hist.get("count") if isinstance(hist, dict) else None,
|
|
445
|
+
"history": hitems,
|
|
446
|
+
}
|
|
447
|
+
rows = [(
|
|
448
|
+
(it.get("created_at") or "")[:10],
|
|
449
|
+
it.get("price"),
|
|
450
|
+
it.get("reward"),
|
|
451
|
+
it.get("percentage"),
|
|
452
|
+
"invalid" if it.get("invalid") else "ok",
|
|
453
|
+
) for it in hitems]
|
|
454
|
+
_emit(args, out, rows, ("date", "price", "reward", "pct", "state"))
|
|
455
|
+
|
|
456
|
+
|
|
264
457
|
# ---- write commands (gated by --yes) -------------------------------------
|
|
265
458
|
|
|
266
459
|
def _guard(args, action: str, detail: dict) -> bool:
|
|
@@ -405,6 +598,47 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
405
598
|
sp = add("announcements", cmd_announcements, "list announcements")
|
|
406
599
|
sp.add_argument("--limit", type=int, default=20)
|
|
407
600
|
|
|
601
|
+
sp = add("distributions", cmd_distributions, "referral status + commission history")
|
|
602
|
+
sp.add_argument("--limit", type=int, default=20)
|
|
603
|
+
|
|
604
|
+
# ---- catalog & docs (public, no token needed) ----
|
|
605
|
+
sp = add("get-service", cmd_get_service, "one service's detail by alias or id")
|
|
606
|
+
sp.add_argument("--service", required=True, help="service alias or UUID")
|
|
607
|
+
|
|
608
|
+
sp = add("pricing", cmd_pricing, "a service's unit, free_amount and cost")
|
|
609
|
+
sp.add_argument("--service", required=True, help="service alias or UUID")
|
|
610
|
+
|
|
611
|
+
sp = add("apis", cmd_apis, "list API endpoints (optionally per service)")
|
|
612
|
+
sp.add_argument("--service", help="scope to a service alias or UUID")
|
|
613
|
+
sp.add_argument("--limit", type=int, default=50)
|
|
614
|
+
|
|
615
|
+
sp = add("spec", cmd_spec, "one API's OpenAPI definition + cost, by path")
|
|
616
|
+
sp.add_argument("--path", required=True, help="API path, e.g. /suno/audios")
|
|
617
|
+
|
|
618
|
+
sp = add("datasets", cmd_datasets, "list downloadable datasets")
|
|
619
|
+
sp.add_argument("--limit", type=int, default=50)
|
|
620
|
+
|
|
621
|
+
sp = add("integrations", cmd_integrations, "list third-party integrations")
|
|
622
|
+
sp.add_argument("--limit", type=int, default=50)
|
|
623
|
+
|
|
624
|
+
sp = add("docs-search", cmd_docs_search, "full-text search the documentation")
|
|
625
|
+
sp.add_argument("--query", required=True)
|
|
626
|
+
sp.add_argument("--lang", help="language code, e.g. en, zh-cn")
|
|
627
|
+
|
|
628
|
+
sp = add("docs-list", cmd_docs_list, "browse documentation pages")
|
|
629
|
+
sp.add_argument("--limit", type=int, default=30)
|
|
630
|
+
sp.add_argument("--type", help="document type filter, e.g. Text")
|
|
631
|
+
|
|
632
|
+
sp = add("doc", cmd_doc, "fetch one doc's full content by UUID")
|
|
633
|
+
sp.add_argument("--id", required=True, help="document UUID")
|
|
634
|
+
|
|
635
|
+
sp = add("model-catalog", cmd_model_catalog, "rich model catalog with credit pricing")
|
|
636
|
+
sp.add_argument("--modality", help="chat/video/image/music/search/embedding")
|
|
637
|
+
sp.add_argument("--provider", help="filter by provider substring")
|
|
638
|
+
|
|
639
|
+
sp = add("model", cmd_model, "look up a model by id/name")
|
|
640
|
+
sp.add_argument("--model", required=True)
|
|
641
|
+
|
|
408
642
|
sp = add("create-key", cmd_create_key, "create an API key (needs --yes)")
|
|
409
643
|
sp.add_argument("--application", required=True)
|
|
410
644
|
sp.add_argument("--name")
|