@acedatacloud/skills 2026.622.0 → 2026.622.1
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.622.
|
|
3
|
+
"version": "2026.622.1",
|
|
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",
|
package/skills/bilibili/SKILL.md
CHANGED
|
@@ -38,6 +38,7 @@ BILI=$SKILL_DIR/scripts/bilibili.py
|
|
|
38
38
|
python3 $BILI whoami # who is logged in (mid, name)
|
|
39
39
|
python3 $BILI articles --limit 20 # my 专栏 articles + stats
|
|
40
40
|
python3 $BILI article <cvid> # one article's stats (cv id)
|
|
41
|
+
python3 $BILI drafts --limit 50 # list saved drafts (aid + title)
|
|
41
42
|
```
|
|
42
43
|
|
|
43
44
|
Stats come straight from Bilibili: `view` (阅读), `like` (点赞), `reply` (评论),
|
|
@@ -70,6 +71,22 @@ python3 $BILI publish --title "标题" --content-file a.html --confirm
|
|
|
70
71
|
risk-control (HTTP 412). When that happens the CLI reports the saved draft +
|
|
71
72
|
edit URL so the user can publish from the web editor. Default to `--draft-only`.
|
|
72
73
|
|
|
74
|
+
## Managing drafts (the 999-draft cap)
|
|
75
|
+
|
|
76
|
+
Bilibili caps 专栏 drafts at **999**; once full, saving a new draft fails with
|
|
77
|
+
`code 37106 草稿数已达最大上限`. List drafts and delete the ones you don't need:
|
|
78
|
+
|
|
79
|
+
```sh
|
|
80
|
+
python3 $BILI drafts --limit 50 # list (aid + title)
|
|
81
|
+
python3 $BILI delete-draft <aid> <aid2> ... # dry-run (shows what would delete)
|
|
82
|
+
python3 $BILI delete-draft <aid> <aid2> ... --confirm # PERMANENTLY delete those drafts
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
- `delete-draft` is **GATED** (dry-run unless trailing `--confirm`) and deletion
|
|
86
|
+
is **permanent** — always show the dry-run + the titles and get an explicit
|
|
87
|
+
"yes" before `--confirm`. Pass multiple aids to batch a few per call.
|
|
88
|
+
- Never bulk-delete blindly: list first, confirm the titles are junk/duplicates.
|
|
89
|
+
|
|
73
90
|
## Gotchas
|
|
74
91
|
|
|
75
92
|
- **This is the user's real Bilibili account.** Confirm before any publish.
|
|
@@ -354,11 +354,60 @@ def cmd_publish(jar, args):
|
|
|
354
354
|
f"web editor. Detail: {(r or {}).get('message') if r else text[:200]}"})
|
|
355
355
|
|
|
356
356
|
|
|
357
|
+
def _drafts_of(d: dict) -> list:
|
|
358
|
+
al = (d.get("data") or {}).get("artlist") or d.get("artlist") or {}
|
|
359
|
+
return al.get("drafts") or []
|
|
360
|
+
|
|
361
|
+
|
|
362
|
+
def cmd_drafts(jar, args):
|
|
363
|
+
# 专栏 drafts are capped at 999; this lists them so they can be pruned.
|
|
364
|
+
d = get_json(f"{API}/x/article/creative/draft/list?pn={args.page}&ps={args.limit}",
|
|
365
|
+
jar, referer="https://member.bilibili.com/")
|
|
366
|
+
if d.get("code"): # 0 = ok; anything truthy is an auth/API error
|
|
367
|
+
die(f"draft list error (code={d.get('code')}): {d.get('message')} — "
|
|
368
|
+
f"cookie may be expired; reconnect at https://auth.acedata.cloud/user/connections.")
|
|
369
|
+
items = _drafts_of(d)
|
|
370
|
+
out({"page": args.page, "count": len(items), "drafts": [{
|
|
371
|
+
"aid": x.get("id"),
|
|
372
|
+
"title": x.get("title"),
|
|
373
|
+
"edit_url": f"https://member.bilibili.com/article-text/home?aid={x.get('id')}",
|
|
374
|
+
} for x in items]})
|
|
375
|
+
|
|
376
|
+
|
|
377
|
+
def cmd_delete_draft(jar, args):
|
|
378
|
+
if not args.aids:
|
|
379
|
+
die("provide one or more draft aids: delete-draft <aid> [<aid> ...] --confirm")
|
|
380
|
+
bad = [a for a in args.aids if not str(a).isdigit()]
|
|
381
|
+
if bad:
|
|
382
|
+
die(f"invalid draft aid(s) — must be numeric: {bad}")
|
|
383
|
+
csrf = cookie_value(jar, "bili_jct")
|
|
384
|
+
if not csrf:
|
|
385
|
+
die("no bili_jct cookie (CSRF token) — reconnect Bilibili.")
|
|
386
|
+
if not CONFIRM:
|
|
387
|
+
out({"dry_run": True, "command": "delete-draft", "platform": "bilibili",
|
|
388
|
+
"aids": args.aids, "note": "Deletion is PERMANENT. Re-run with --confirm "
|
|
389
|
+
"as the LAST argument to actually delete these draft(s)."})
|
|
390
|
+
return
|
|
391
|
+
results = []
|
|
392
|
+
for aid in args.aids:
|
|
393
|
+
_, text = request("POST", f"{API}/x/article/creative/draft/delete", jar,
|
|
394
|
+
referer="https://member.bilibili.com/", form={"aid": aid, "csrf": csrf})
|
|
395
|
+
try:
|
|
396
|
+
code = json.loads(text).get("code")
|
|
397
|
+
except json.JSONDecodeError:
|
|
398
|
+
code = None
|
|
399
|
+
results.append({"aid": aid, "deleted": code == 0, "code": code})
|
|
400
|
+
out({"command": "delete-draft", "deleted": sum(1 for r in results if r["deleted"]),
|
|
401
|
+
"results": results})
|
|
402
|
+
|
|
403
|
+
|
|
357
404
|
COMMANDS = {
|
|
358
405
|
"whoami": cmd_whoami,
|
|
359
406
|
"articles": cmd_articles,
|
|
360
407
|
"article": cmd_article,
|
|
361
408
|
"publish": cmd_publish,
|
|
409
|
+
"drafts": cmd_drafts,
|
|
410
|
+
"delete-draft": cmd_delete_draft,
|
|
362
411
|
}
|
|
363
412
|
|
|
364
413
|
|
|
@@ -375,6 +424,11 @@ def main() -> None:
|
|
|
375
424
|
sp.add_argument("--content", help="HTML content inline")
|
|
376
425
|
sp.add_argument("--content-file", help="path to an HTML file")
|
|
377
426
|
sp.add_argument("--draft-only", action="store_true", help="save a draft; do NOT submit")
|
|
427
|
+
sp = sub.add_parser("drafts", help="list 专栏 drafts (id+title); use to prune the 999-draft cap")
|
|
428
|
+
sp.add_argument("--limit", type=int, default=50)
|
|
429
|
+
sp.add_argument("--page", type=int, default=1)
|
|
430
|
+
sp = sub.add_parser("delete-draft", help="delete draft(s) by aid (GATED by trailing --confirm)")
|
|
431
|
+
sp.add_argument("aids", nargs="*", help="one or more draft aids to delete")
|
|
378
432
|
args = p.parse_args(ARGV)
|
|
379
433
|
jar = load_cookies()
|
|
380
434
|
COMMANDS[args.command](jar, args)
|
package/skills/medium/SKILL.md
CHANGED
|
@@ -71,6 +71,8 @@ publish). `--draft-only` stops at the draft (visible only at the user's
|
|
|
71
71
|
- Markdown→Medium conversion is paragraph-level (headings, quotes, code, body);
|
|
72
72
|
complex inline formatting / images aren't converted — the user can polish in
|
|
73
73
|
the Medium editor before going public.
|
|
74
|
-
- Medium sits behind Cloudflare; an occasional 403/429 is transient
|
|
74
|
+
- Medium sits behind Cloudflare; an occasional 403/429 is transient — the CLI
|
|
75
|
+
auto-retries once after a short pause. A *persistent* 403 means the cookie is
|
|
76
|
+
genuinely expired (reconnect).
|
|
75
77
|
- **Never print `MEDIUM_COOKIES`** — it is full account access.
|
|
76
78
|
- **ToS**: acts only on the user's own account with their own captured cookie.
|
|
@@ -30,6 +30,7 @@ import json
|
|
|
30
30
|
import os
|
|
31
31
|
import re
|
|
32
32
|
import sys
|
|
33
|
+
import time
|
|
33
34
|
import urllib.error
|
|
34
35
|
import urllib.parse
|
|
35
36
|
import urllib.request
|
|
@@ -147,8 +148,15 @@ def request(method, url, jar, *, headers=None, body=None, accept="application/js
|
|
|
147
148
|
die(f"network error reaching {url}: {e.reason}")
|
|
148
149
|
|
|
149
150
|
|
|
150
|
-
def api(method, url, jar, *, body=None):
|
|
151
|
+
def api(method, url, jar, *, body=None, _retried=False):
|
|
151
152
|
status, text = request(method, url, jar, body=body)
|
|
153
|
+
# Medium sits behind Cloudflare, which intermittently 403s/429s an otherwise
|
|
154
|
+
# valid session; one retry after a short pause clears the transient block.
|
|
155
|
+
# Only retry idempotent GETs — never replay a POST (new-story/deltas/publish),
|
|
156
|
+
# which could duplicate a write if the origin already processed it.
|
|
157
|
+
if status in (403, 429) and method == "GET" and not _retried:
|
|
158
|
+
time.sleep(1.5)
|
|
159
|
+
return api(method, url, jar, body=body, _retried=True)
|
|
152
160
|
if status in (401, 403):
|
|
153
161
|
die(f"auth failed ({status}) on {url} — cookie likely expired. "
|
|
154
162
|
f"Reconnect at https://auth.acedata.cloud/user/connections.")
|