@acedatacloud/skills 2026.628.6 → 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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@acedatacloud/skills",
3
- "version": "2026.628.6",
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",
@@ -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, list available models, or (admins)
9
- publish an announcement. This is the self-service "console" API distinct
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 announcements.
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 (`platform_get_balance`, `platform_list_usage`, `platform_create_credential`, `platform_create_announcement`, …) with the same write-confirmation guard.
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) -> tuple[int, object]:
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
- req = urllib.request.Request(url, data=data, method=method, headers={
55
- "accept": "application/json",
56
- "authorization": f"Bearer {_token(args)}",
57
- "content-type": "application/json",
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")