@acedatacloud/skills 2026.707.0 → 2026.712.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.707.0",
3
+ "version": "2026.712.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",
@@ -16,29 +16,41 @@ metadata:
16
16
  version: "1.0"
17
17
  ---
18
18
 
19
- Call the **Hashnode Public GraphQL API** with `curl + jq`. The user's Personal
20
- Access Token is in `$HASHNODE_TOKEN`. Single endpoint: `https://gql.hashnode.com`
19
+ Call the **Hashnode GraphQL API** with `curl + jq`. The user's Personal Access
20
+ Token is in `$HASHNODE_TOKEN`. Single endpoint: `https://gql-beta.hashnode.com`
21
21
  (always `POST` a JSON body `{query, variables}`).
22
22
 
23
+ > The old `https://gql.hashnode.com` host is **deprecated** — it now 301-redirects
24
+ > to a changelog page and returns HTML, not JSON. Always use `gql-beta.hashnode.com`.
25
+
23
26
  Every request needs these headers:
24
27
 
25
28
  ```
26
- Authorization: $HASHNODE_TOKEN # raw token — NO "Bearer " prefix
29
+ Authorization: Bearer $HASHNODE_TOKEN
27
30
  Content-Type: application/json
28
31
  ```
29
32
 
30
33
  GraphQL always returns HTTP `200`; real failures live in the JSON `errors`
31
- array — always inspect it and show it verbatim. An `errors` entry mentioning
32
- `Unauthorized` / `not authenticated` means the token is invalid → the user must
33
- re-connect the Hashnode connector.
34
+ array — always inspect it and show it verbatim. Two error cases matter:
35
+
36
+ - `Unauthorized` / `not authenticated` → the token is invalid → the user must
37
+ re-connect the Hashnode connector.
38
+ - `extensions.code == "FORBIDDEN"` with a message like *"Publication does not have
39
+ an active Pro plan"* → this is **not** a token problem. As of 2026-05-13 Hashnode
40
+ moved its API behind a paid plan: **every write mutation** (`publishPost`,
41
+ `createDraft`, `updatePost`) **and publication-scoped reads require the target
42
+ publication to be on [Hashnode Pro](https://hashnode.com/pro)**. Do NOT tell the
43
+ user to reconnect and do NOT retry in a loop — tell them the publication owner
44
+ must upgrade to Hashnode Pro (blog dashboard → Billing → Upgrade to Pro), after
45
+ which the API works immediately. Public reads (posts/feeds/users/tags) stay free.
34
46
 
35
47
  Helper — send a query/mutation (`$1` = query string, `$2` = variables JSON):
36
48
 
37
49
  ```bash
38
50
  gql() {
39
51
  jq -n --arg q "$1" --argjson v "${2:-null}" '{query:$q, variables:$v}' \
40
- | curl -sS -X POST https://gql.hashnode.com \
41
- -H "Authorization: $HASHNODE_TOKEN" \
52
+ | curl -sS -X POST https://gql-beta.hashnode.com \
53
+ -H "Authorization: Bearer $HASHNODE_TOKEN" \
42
54
  -H "Content-Type: application/json" \
43
55
  -d @-
44
56
  }
@@ -136,7 +148,15 @@ gql 'query GetPost($id: ObjectId!) { post(id: $id) { title url views reactionCou
136
148
 
137
149
  ## Gotchas
138
150
 
139
- - **No `Bearer` prefix** — the `Authorization` header carries the raw token.
151
+ - **Endpoint is `https://gql-beta.hashnode.com`** — the old `gql.hashnode.com`
152
+ host is deprecated (301 → HTML changelog page, never valid GraphQL JSON).
153
+ - **`Authorization: Bearer $HASHNODE_TOKEN`** — send the token with the `Bearer`
154
+ prefix (this is what Hashnode's own official skill uses).
155
+ - **Pro plan required for writes** — since 2026-05-13, `publishPost` / `createDraft`
156
+ / `updatePost` and publication-scoped reads only work if the target publication
157
+ has an active **Hashnode Pro** plan; otherwise the call returns a `FORBIDDEN`
158
+ *"does not have an active Pro plan"* error. That is a billing state on the user's
159
+ blog, not something the skill or a reconnect can fix — surface it and stop.
140
160
  - **`publicationId` is mandatory** for publishing/drafting — never guess it, read
141
161
  it from the `me` query.
142
162
  - **`tags` is required on `publishPost`** — supply at least one `{name, slug}`;
@@ -98,6 +98,7 @@ def current_user_id_from_cookies() -> str | None:
98
98
 
99
99
 
100
100
  def make_client():
101
+ import httpx
101
102
  from twikit import Client
102
103
  patch_twikit_transaction_resolver()
103
104
  patch_twikit_model_defaults()
@@ -107,7 +108,12 @@ def make_client():
107
108
  or os.environ.get("ALL_PROXY") or os.environ.get("all_proxy")
108
109
  or None
109
110
  )
110
- client = Client("en-US", proxy=proxy, user_agent=UA)
111
+ # twikit builds its httpx.AsyncClient with httpx's 5s default timeout, which
112
+ # trips a ReadTimeout on media uploads (INIT/APPEND/FINALIZE + status poll)
113
+ # from datacenter egress — a text-only tweet is one fast call and squeaks
114
+ # under 5s, but --media does not. Give network ops generous headroom.
115
+ timeout = httpx.Timeout(60.0, connect=15.0)
116
+ client = Client("en-US", proxy=proxy, user_agent=UA, timeout=timeout)
111
117
  client.set_cookies(load_cookie_dict())
112
118
  return client
113
119
 
@@ -494,6 +500,14 @@ def main() -> None:
494
500
  # twikit scrapes X's non-public API; a bare error here usually means an
495
501
  # expired cookie OR that X changed its internal endpoints and twikit
496
502
  # needs a compatibility fix.
503
+ import httpx
504
+ if isinstance(e, httpx.TimeoutException):
505
+ die(
506
+ "X request timed out talking to X (network was slow, not an "
507
+ "auth problem) — this is transient, retry the same command. "
508
+ "Timeouts are most common on --media because the image upload "
509
+ "is the slow leg; if it recurs, try a smaller image."
510
+ )
497
511
  if "Couldn't get KEY_BYTE indices" in str(e):
498
512
  die(
499
513
  "X request failed because twikit cannot derive X's current "