@acedatacloud/skills 2026.726.4 → 2026.726.6
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 +1 -1
- package/skills/google-ads/SKILL.md +27 -13
- package/skills/youtube/SKILL.md +42 -4
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@acedatacloud/skills",
|
|
3
|
-
"version": "2026.726.
|
|
3
|
+
"version": "2026.726.6",
|
|
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,8 @@ when_to_use: |
|
|
|
5
5
|
Trigger when the user wants Google Ads reporting — list accessible
|
|
6
6
|
customers, campaign / ad-group / keyword performance, spend and
|
|
7
7
|
conversions. Read via GAQL. Needs the OAuth token PLUS a platform
|
|
8
|
-
developer token
|
|
9
|
-
|
|
8
|
+
developer token; if the developer token is absent the connector
|
|
9
|
+
isn't fully provisioned yet — tell the user.
|
|
10
10
|
connections: [google/ads]
|
|
11
11
|
allowed_tools: [Bash]
|
|
12
12
|
license: Apache-2.0
|
|
@@ -15,26 +15,31 @@ metadata:
|
|
|
15
15
|
version: "1.0"
|
|
16
16
|
---
|
|
17
17
|
|
|
18
|
-
Query the **Google Ads API** via `curl + jq`.
|
|
18
|
+
Query the **Google Ads API** via `curl + jq`. Two credentials plus one context
|
|
19
|
+
header:
|
|
19
20
|
|
|
20
21
|
- `$GOOGLE_ADS_TOKEN` — the user's OAuth bearer (`adwords` scope) →
|
|
21
22
|
`Authorization: Bearer $GOOGLE_ADS_TOKEN`
|
|
22
23
|
- `$GOOGLE_ADS_DEVELOPER_TOKEN` — the platform's developer token (injected
|
|
23
24
|
server-side) → header `developer-token: $GOOGLE_ADS_DEVELOPER_TOKEN`
|
|
24
|
-
- `login-customer-id` —
|
|
25
|
-
|
|
26
|
-
dashes).
|
|
25
|
+
- `login-customer-id` — **only for manager (MCC) scoped calls**: the manager id
|
|
26
|
+
the request runs under, or `$GOOGLE_ADS_LOGIN_CUSTOMER_ID` if set (digits
|
|
27
|
+
only, no dashes). Not needed by `customers:listAccessibleCustomers`.
|
|
27
28
|
|
|
28
29
|
> **API version:** the base is `https://googleads.googleapis.com/<vNN>`. Google
|
|
29
|
-
> ships a new `vNN` every ~4 months and retires old ones
|
|
30
|
-
>
|
|
31
|
-
>
|
|
30
|
+
> ships a new `vNN` every ~4 months and retires old ones, so a hardcoded version
|
|
31
|
+
> eventually stops working. The example uses `v25` (supported as of 2026-07). If
|
|
32
|
+
> **every** call 404s, the version is unavailable — either retired or not yet
|
|
33
|
+
> released. Probe `customers:listAccessibleCustomers`: a supported version
|
|
34
|
+
> answers 401/200, an unavailable one 404. Step **down** one version at a time
|
|
35
|
+
> from the example, and check Google's supported-versions table before assuming
|
|
36
|
+
> a newer `vNN` exists.
|
|
32
37
|
|
|
33
38
|
If `$GOOGLE_ADS_DEVELOPER_TOKEN` is empty, the connector isn't fully provisioned —
|
|
34
39
|
say so rather than calling the API (it would 401/DEVELOPER_TOKEN_NOT_APPROVED).
|
|
35
40
|
|
|
36
41
|
```bash
|
|
37
|
-
VER="
|
|
42
|
+
VER="v25"; BASE="https://googleads.googleapis.com/$VER"
|
|
38
43
|
AUTH="Authorization: Bearer $GOOGLE_ADS_TOKEN"; DEV="developer-token: $GOOGLE_ADS_DEVELOPER_TOKEN"
|
|
39
44
|
# Customers the OAuth user can access (ids are returned as customers/<id>)
|
|
40
45
|
curl -sS -H "$AUTH" -H "$DEV" "$BASE/customers:listAccessibleCustomers" | jq '.resourceNames'
|
|
@@ -44,7 +49,10 @@ curl -sS -H "$AUTH" -H "$DEV" "$BASE/customers:listAccessibleCustomers" | jq '.r
|
|
|
44
49
|
|
|
45
50
|
```bash
|
|
46
51
|
CID="1234567890" # target customer id, digits only
|
|
47
|
-
|
|
52
|
+
# Manager (MCC) access → send login-customer-id; direct access → drop the header.
|
|
53
|
+
# Array, not a plain string: the header value contains a space and would split.
|
|
54
|
+
LOGIN=(); [ -n "$GOOGLE_ADS_LOGIN_CUSTOMER_ID" ] && LOGIN=(-H "login-customer-id: $GOOGLE_ADS_LOGIN_CUSTOMER_ID")
|
|
55
|
+
curl -sS -H "$AUTH" -H "$DEV" "${LOGIN[@]}" \
|
|
48
56
|
-H "Content-Type: application/json" -d '{
|
|
49
57
|
"query":"SELECT campaign.name, metrics.cost_micros, metrics.clicks, metrics.conversions FROM campaign WHERE segments.date DURING LAST_30_DAYS ORDER BY metrics.cost_micros DESC"
|
|
50
58
|
}' "$BASE/customers/$CID/googleAds:searchStream" \
|
|
@@ -56,8 +64,14 @@ GAQL resources: `campaign`, `ad_group`, `ad_group_criterion` (keywords),
|
|
|
56
64
|
|
|
57
65
|
## Gotchas
|
|
58
66
|
|
|
59
|
-
- **
|
|
60
|
-
is
|
|
67
|
+
- **A 404 on *every* endpoint points at `VER`, not at credentials** — the
|
|
68
|
+
version is retired or unreleased. A 404 on a *single* call is about that call:
|
|
69
|
+
a missing resource (bad customer id) or a wrong path. Sanity-check the version
|
|
70
|
+
with `customers:listAccessibleCustomers` before debugging auth.
|
|
71
|
+
- **Two credentials plus a context header.** `developer-token` is required on
|
|
72
|
+
every call; `login-customer-id` is only needed when acting under a manager
|
|
73
|
+
(MCC) account — `customers:listAccessibleCustomers` works without it. Omitting
|
|
74
|
+
it on a manager-scoped call is the #1 cause of 401/403 here.
|
|
61
75
|
- Customer ids are **digits only** in URLs/headers (strip the dashes from
|
|
62
76
|
`123-456-7890`).
|
|
63
77
|
- `searchStream` returns an array of chunks each with `.results[]` — flatten with
|
package/skills/youtube/SKILL.md
CHANGED
|
@@ -110,8 +110,39 @@ curl -sS -H "Authorization: Bearer $GOOGLE_YOUTUBE_TOKEN" \
|
|
|
110
110
|
file you're about to upload. Uploads are a two-step *resumable* flow:
|
|
111
111
|
init with metadata → PUT the bytes.
|
|
112
112
|
|
|
113
|
+
### If the source is a URL, fetch it first
|
|
114
|
+
|
|
115
|
+
Videos produced by a generation API (Maestro, Seedance, Veo, …) come back as a
|
|
116
|
+
CDN URL, not a local file. Download it and verify you actually got a video
|
|
117
|
+
before starting the upload:
|
|
118
|
+
|
|
113
119
|
```bash
|
|
114
|
-
|
|
120
|
+
SRC="https://example.cdn.acedata.cloud/path/video.mp4"
|
|
121
|
+
FILE=$(mktemp) # plain mktemp — `mktemp /tmp/x-XXXXXX.mp4` is not portable
|
|
122
|
+
|
|
123
|
+
# -f: fail on 4xx/5xx instead of saving the error page. -L: follow CDN redirects.
|
|
124
|
+
curl -fsSL -o "$FILE" -w 'http=%{http_code} size=%{size_download}\n' "$SRC"
|
|
125
|
+
|
|
126
|
+
# Two distinct failures, two distinct messages — a 404/DNS error is a bad URL,
|
|
127
|
+
# not a bad video, and saying so saves the next step from misdiagnosing it.
|
|
128
|
+
[ -s "$FILE" ] \
|
|
129
|
+
|| { echo "download failed (see http= above) — nothing to upload"; rm -f "$FILE"; exit 1; }
|
|
130
|
+
|
|
131
|
+
# A 200 can still be an HTML interstitial or an expired-link page. Reject that
|
|
132
|
+
# rather than allow-listing one container, so WebM/MOV/AVI still pass. `grep -a`
|
|
133
|
+
# is required — without it grep treats a binary header as "no match".
|
|
134
|
+
head -c 512 "$FILE" | grep -qai '<html\|<!doctype' \
|
|
135
|
+
&& { echo "got an HTML page, not a video"; rm -f "$FILE"; exit 1; }
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
`--upload-file` ignores the filename; YouTube keys off the
|
|
139
|
+
`Content-Type: video/*` header, so the extensionless temp file is fine.
|
|
140
|
+
|
|
141
|
+
### The upload itself
|
|
142
|
+
|
|
143
|
+
```bash
|
|
144
|
+
# Falls back to the placeholder only if the fetch step above didn't set FILE.
|
|
145
|
+
FILE="${FILE:-/path/to/video.mp4}"
|
|
115
146
|
TITLE="My title"
|
|
116
147
|
DESC="My description"
|
|
117
148
|
# privacyStatus: public | unlisted | private
|
|
@@ -129,12 +160,19 @@ UPLOAD_URL=$(curl -sS -D - -o /dev/null \
|
|
|
129
160
|
-d "$META" | tr -d '\r' | awk '/^[Ll]ocation:/{print $2}')
|
|
130
161
|
|
|
131
162
|
# 2. Upload the bytes -> returns the created video resource (has .id).
|
|
132
|
-
curl -sS -H "Authorization: Bearer $GOOGLE_YOUTUBE_TOKEN" \
|
|
163
|
+
RESULT=$(curl -sS -H "Authorization: Bearer $GOOGLE_YOUTUBE_TOKEN" \
|
|
133
164
|
-H "Content-Type: video/*" \
|
|
134
|
-
-X PUT --upload-file "$FILE" "$UPLOAD_URL"
|
|
135
|
-
|
|
165
|
+
-X PUT --upload-file "$FILE" "$UPLOAD_URL")
|
|
166
|
+
echo "$RESULT" | jq -e .id >/dev/null 2>&1 \
|
|
167
|
+
|| { echo "upload failed: $(echo "$RESULT" | jq -r '.error.message' 2>/dev/null || echo "$RESULT")"; exit 1; }
|
|
168
|
+
echo "$RESULT" | jq '{id: .id, url: ("https://www.youtube.com/watch?v=" + .id), privacy: .status.privacyStatus}'
|
|
136
169
|
```
|
|
137
170
|
|
|
171
|
+
Delete a downloaded temp file only **after** you've confirmed an id came back
|
|
172
|
+
(`echo "$RESULT" | jq -e .id >/dev/null && rm -f "$FILE"`). On a 401/403 or a
|
|
173
|
+
dropped connection the download is still good and re-uploading beats re-fetching
|
|
174
|
+
a few hundred MB.
|
|
175
|
+
|
|
138
176
|
`categoryId` `22` = "People & Blogs" (a safe default). To set a custom
|
|
139
177
|
thumbnail (needs the file to be processed first), call
|
|
140
178
|
`POST /upload/youtube/v3/thumbnails/set?videoId=VIDEO_ID` with the image.
|