addashboard-cli 0.17.0 → 0.17.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/dist/lib/apiError.js
CHANGED
|
@@ -43,7 +43,11 @@ function describeErrorBody(data) {
|
|
|
43
43
|
`formatGoogleAdsError()` が組み立てた本文が CLI に届かず、
|
|
44
44
|
運用者は `✗ google_ads_error` しか見られない。両方あるなら両方出す。 */
|
|
45
45
|
if (typeof e === 'string' && e) {
|
|
46
|
-
|
|
46
|
+
const reasons = [
|
|
47
|
+
...(typeof d.message === 'string' && d.message ? [d.message] : []),
|
|
48
|
+
...describeIssues(d.issues),
|
|
49
|
+
];
|
|
50
|
+
return reasons.length > 0 ? `${e}: ${reasons.join(' / ')}` : e;
|
|
47
51
|
}
|
|
48
52
|
if (e && typeof e === 'object') {
|
|
49
53
|
const m = e.message;
|
|
@@ -56,6 +60,26 @@ function describeErrorBody(data) {
|
|
|
56
60
|
// error / message どちらも無い形(想定外)は握り潰さず中身を見せる
|
|
57
61
|
return safeJson(d);
|
|
58
62
|
}
|
|
63
|
+
/**
|
|
64
|
+
* zod の検証エラー (`zodErrorBody()` の `issues: [{ path, message }]`) を
|
|
65
|
+
* `campaigns.0.status: 理由` の形に並べる。読まないと CLI には `validation_error` しか出ず、
|
|
66
|
+
* どのフィールドが何の理由で弾かれたか分からない (#396)。
|
|
67
|
+
*/
|
|
68
|
+
function describeIssues(issues) {
|
|
69
|
+
if (!Array.isArray(issues))
|
|
70
|
+
return [];
|
|
71
|
+
const lines = issues.flatMap((i) => {
|
|
72
|
+
const { path, message } = (i ?? {});
|
|
73
|
+
if (typeof message !== 'string' || !message)
|
|
74
|
+
return [];
|
|
75
|
+
return [typeof path === 'string' && path ? `${path}: ${message}` : message];
|
|
76
|
+
});
|
|
77
|
+
// 大きな計画で全件並べると1行が数KBになり読めない。先頭だけ出して残りは件数で示す
|
|
78
|
+
return lines.length > MAX_ISSUES
|
|
79
|
+
? [...lines.slice(0, MAX_ISSUES), `ほか ${lines.length - MAX_ISSUES} 件`]
|
|
80
|
+
: lines;
|
|
81
|
+
}
|
|
82
|
+
const MAX_ISSUES = 10;
|
|
59
83
|
function safeJson(v) {
|
|
60
84
|
try {
|
|
61
85
|
const s = JSON.stringify(v);
|
package/dist/lib/apiError.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"apiError.js","sourceRoot":"","sources":["../../src/lib/apiError.ts"],"names":[],"mappings":";;AAaA,4CASC;AAGD,
|
|
1
|
+
{"version":3,"file":"apiError.js","sourceRoot":"","sources":["../../src/lib/apiError.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
package/skills/ad-ops/SKILL.md
CHANGED
|
@@ -9,7 +9,7 @@ Google Drive から手動 DL 済みの素材を各媒体に半自動入稿する
|
|
|
9
9
|
|
|
10
10
|
- **動画の API 入稿フロー(`submit`)**: 現状 **YDA のみ**。
|
|
11
11
|
- **入稿プランナー(`ad-planner.html` → 入稿計画JSON → `submit-plan`)**: **YDA / Meta / Google / TikTok 対応**(ヘッダーの「案件 × 媒体」統合プルダウンで切替)。
|
|
12
|
-
- **Google (Demand Gen)**: プランナー・dry-run・本実行に対応。**2026-08-23 に実 API での入稿を確認済み**(PAUSED・1
|
|
12
|
+
- **Google (Demand Gen)**: プランナー・dry-run・本実行に対応。**2026-08-23 に実 API での入稿を確認済み**(PAUSED・1キャンペーン)。ただし配信実績はまだ無いので、初回は最小予算から始めて管理画面で目視確認する。**キャンペーンは常に PAUSED で入り、広告グループ・広告は既定で ENABLED**(#396)。キャンペーンをオンにした時点で配信が始まるので、オンにする前に検品を済ませる。詳細は下の「Google (Demand Gen) の対応範囲」を参照。
|
|
13
13
|
- **TikTok / ByteDance**: プランナー・dry-run・本実行に対応(**単一動画のみ**)。**2026-08-23 に実 API を初めて叩き、キャンペーン作成と自動ロールバックまで確認**。広告グループ以降は地域IDの取得手段が無くて未達だったため `regions` を追加した。**配信オン (ENABLE) は backend が拒否する**。詳細は下の「TikTok (単一動画) の対応範囲」を参照。
|
|
14
14
|
|
|
15
15
|
配置・発火方法・ログイン手順は [cli/README.md](../../README.md) の「初期設定: Skill として配置する」章を参照。
|
|
@@ -405,7 +405,7 @@ skills/ad-ops/scripts/adops.sh open yda <accountId> campaigns
|
|
|
405
405
|
|---|---|
|
|
406
406
|
| プランナーで計画を組む | ✅ できる(統合プルダウンで「Google」を選ぶ) |
|
|
407
407
|
| dry-run で不備を潰す | ✅ できる |
|
|
408
|
-
| **本実行(実際の入稿)** | ✅ **できる**(2026-08-23 に実 API
|
|
408
|
+
| **本実行(実際の入稿)** | ✅ **できる**(2026-08-23 に実 API で確認済み)。ただし**キャンペーンは配信オン (ENABLED) では入稿できない** — backend が 400 で拒否し、lint も ERROR で止める。**広告グループ・広告は ENABLED で入稿できる**(プランナーの既定。キャンペーンは常に PAUSED で書き出す)。**入稿 → 管理画面で検品 → キャンペーンをオンにするだけで配信が始まる**運用(#396)。配下が PAUSED だと lint が WARN を出す(キャンペーンをオンにしても配信されない)。広告が1件も作れなかった CP は自動削除される |
|
|
409
409
|
| 既存キャンペーン・広告グループへの広告追加 | ❌ 未対応。毎回あたらしいキャンペーンを作る形になる |
|
|
410
410
|
| 動画ファイルのアップロード | ✅ **対応**(#246)。プランナーの広告行「動画パス」にローカル動画を指定すると、本実行の直前に CLI がアップロードして YouTube 動画IDに解決する(dry-run では上げない)。動画IDとパスを同時に載せない。⚠ **上がり先は Google 管理チャンネルの限定公開で、広告主のチャンネルには置けない**(手上げ運用とは置き場所が変わる)。⚠ **実 API での検証は未了** |
|
|
411
411
|
| 1 広告に動画を複数本(縦・横など) | ✅ **対応**(#347)。動画広告の「クリエイティブ番号」セル → 右サイドバーで**押すたびに追加/解除**(最大 5 本・選んだ順。セルに `542, 543` と打っても同じ)。**先頭が代表**で、広告名はその番号から付く。縦横の区別は不要(Google が配信面ごとに選ぶ)。2本目以降は登録済みの番号に限る。入稿計画は 2 本以上のときだけ `videos: [{youtubeVideoId}\|{videoPath}]`、1 本なら旧形式。backend / CLI は 5 本まで受ける(公式ヘルプ「Upload 1-5 videos per ad」。API リファレンスには本数の記載が無い)。⚠ **実 API での検証は未了** |
|
|
@@ -748,7 +748,7 @@ finalUrl = baseURL + paramCode + "?ad=" + paramCode + "&creative=" + …
|
|
|
748
748
|
<div id="mt-google" style="display:none">
|
|
749
749
|
<div class="eyebrow">Google Ads (Demand Gen) — 対応範囲・要件一覧</div>
|
|
750
750
|
<h1 class="title">Google 広告入稿 <span class="pill green" style="vertical-align:middle">実 API 確認済み</span></h1>
|
|
751
|
-
<p class="sub">2026-08-23 更新。<b>実 API での入稿を確認済み</b>(PAUSED で 1 キャンペーン)。<b
|
|
751
|
+
<p class="sub">2026-08-23 更新。<b>実 API での入稿を確認済み</b>(PAUSED で 1 キャンペーン)。<b>キャンペーンは配信オフ(PAUSED)でしか入稿できない</b> — 意図しない配信・課金を防ぐため backend が 400 で拒否する。<b>広告グループ・広告は配信オン(ENABLED)で入稿でき、プランナーの既定もオン</b>。入稿 → 管理画面で検品 → <b>キャンペーンをオンにするだけで配信が始まる</b>運用(2026-10-29・#396)。Google Ads API v23 の proto 定義に照合して実装。</p>
|
|
752
752
|
|
|
753
753
|
<details class="sec" open><summary><span class="num">1</span><span class="stitle">概要 — なぜ Demand Gen なのか</span><span class="chev"></span></summary>
|
|
754
754
|
<div class="body">
|
|
@@ -756,7 +756,7 @@ finalUrl = baseURL + paramCode + "?ad=" + paramCode + "&creative=" + …
|
|
|
756
756
|
<p>Demand Gen は YouTube(インストリーム / インフィード / ショート)・Discover・Gmail・ディスプレイに配信できる。画像広告と動画広告を同じキャンペーンで扱えるため、広告行の「形式」列で切り替える(Meta と同じ作り)。</p>
|
|
757
757
|
<table class="g"><thead><tr><th>レイヤ</th><th>状態</th><th>内容</th></tr></thead><tbody>
|
|
758
758
|
<tr><td>backend</td><td><span class="pill green">実装済み</span></td><td><code>/api/submission/google</code> + <code>GoogleSubmissionConnector</code>。Demand Gen キャンペーン / 広告グループ / 動画・画像広告 / 目標CPA入札 / 地域・言語ターゲティング / 配信面の制御</td></tr>
|
|
759
|
-
<tr><td>CLI</td><td><span class="pill green">実 API 確認済み</span></td><td><code>webgram submit-plan</code> が Google に対応(dry-run / 本実行とも)。2026-08-23 に PAUSED で入稿を確認した。初回は <b>
|
|
759
|
+
<tr><td>CLI</td><td><span class="pill green">実 API 確認済み</span></td><td><code>webgram submit-plan</code> が Google に対応(dry-run / 本実行とも)。2026-08-23 に PAUSED で入稿を確認した。初回は <b>1キャンペーン・最小予算</b>で流し、管理画面で目視確認する。<b>キャンペーンは常に PAUSED で入り、広告グループ・広告は既定で ENABLED</b>(#396)。キャンペーンをオンにした時点で配信が始まるので、オンにする前に検品を済ませる。広告が1件も作れなかったキャンペーンは自動で削除される</td></tr>
|
|
760
760
|
<tr><td>プランナーGUI</td><td><span class="pill green">実装済み</span></td><td>ヘッダーの統合プルダウンで Google に切替。シート(広告 / 広告グループ / キャンペーン)・ツリー・参照データ・入稿JSON書き出しに対応</td></tr>
|
|
761
761
|
<tr><td>実 API 検証</td><td><span class="pill green">実施済み</span></td><td>2026-08-23 に本番アカウントで Demand Gen 画像広告を PAUSED 入稿し、作成を確認した。developer token のアクセスレベルは Basic 以上(読み取りが通っているため)。<b>ただし配信オン(ENABLED)での実配信は未検証</b>。また<b>アカウントによって書き込み可否が違う</b>(停止・未有効化のものがある)</td></tr>
|
|
762
762
|
</tbody></table>
|
|
@@ -918,7 +918,7 @@ finalUrl = baseURL + paramCode + "?ad=" + paramCode + "&creative=" + …
|
|
|
918
918
|
(cli/test/unit/plannerVersion.test.ts が package.json との一致を検証するので、
|
|
919
919
|
手で書き換えて放置するとテストが落ちる)。
|
|
920
920
|
利用者が「どの版のプランナーを開いているか」を言えないと、不具合報告が噛み合わない。 */
|
|
921
|
-
const APP_VERSION = '0.17.
|
|
921
|
+
const APP_VERSION = '0.17.1'; /* version:sync */
|
|
922
922
|
/* 案A: embed廃止。参照データ(refs)・初期プラン・共通設定は外部JSONから起動時に読み込む。
|
|
923
923
|
同梱デフォルト: アカウント/<媒体>/index.json の先頭アカウント (refs+principal+projectName+baseArticleUrl) と サンプルプラン.json (project+campaigns)。
|
|
924
924
|
固有名詞はハードコードせず、すべて外部JSON由来にする。 */
|
|
@@ -3938,7 +3938,8 @@ const GOOGLE_COLS = {
|
|
|
3938
3938
|
{field:'tg_channel', label:'配信面', kind:'tgpick', tgKey:'tg_channel', readonlyOnExisting:true, roEmptyDash:true,
|
|
3939
3939
|
title:'YouTube だけに出したい場合はここで絞る。「すべて」なら Google の既定に任せる'},
|
|
3940
3940
|
{field:'deliveryStatus', label:'配信', kind:'select', options:['','オフ','オン'], minWidth:'66px',
|
|
3941
|
-
readonlyOnExisting:true, roEmptyDash:true, coerce:v=>{const s=String(v).trim(); return (s==='オフ'||s==='オン')?s:'';}
|
|
3941
|
+
readonlyOnExisting:true, roEmptyDash:true, coerce:v=>{const s=String(v).trim(); return (s==='オフ'||s==='オン')?s:'';},
|
|
3942
|
+
title:'この広告グループと配下の広告の配信。空欄なら案件設定の「配信」に従う。キャンペーンは常にオフで入稿します'}
|
|
3942
3943
|
],
|
|
3943
3944
|
campaigns:[
|
|
3944
3945
|
{field:'campaignId', label:'キャンペーンID', kind:'badge', alwaysReadonly:true},
|
|
@@ -4053,7 +4054,10 @@ PLATFORMS.google = {
|
|
|
4053
4054
|
options:['詳しくはこちら','購入する','登録する','ダウンロード','お問い合わせ','今すぐ申し込む',
|
|
4054
4055
|
'予約する','見積もりを取得','購読する','インストール','サイトへアクセス','もっと見る','ボタンなし'],
|
|
4055
4056
|
title:'画像広告のボタン文言。使える値はアカウントによって違い、弾かれると入稿時にエラーが出ます。動画広告ではアセット登録が要るため今は送りません'},
|
|
4056
|
-
|
|
4057
|
+
/* キャンペーンは常に PAUSED で書き出す(backend が CP の ENABLED を拒否する)。
|
|
4058
|
+
この「配信」は広告グループ・広告にだけ効く (#396)。 */
|
|
4059
|
+
{key:'deliveryStatus', label:'配信(広告グループ・広告)', kind:'select', options:['オフ','オン'],
|
|
4060
|
+
title:'キャンペーンは常にオフ(PAUSED)で入稿します。ここは広告グループ・広告の配信です。オンにしておくと、検品後にキャンペーンをオンにするだけで配信が始まります'},
|
|
4057
4061
|
{key:'baseArticleUrl', label:'記事URL', kind:'text', width:'16ch', fallback:''},
|
|
4058
4062
|
{key:'urlParams', label:'URLパラメータ', kind:'toggle', fallback:true,
|
|
4059
4063
|
title:'OFF にすると最終リンク先URLに ?ad= を付けません(案件単位)'}
|
|
@@ -4121,7 +4125,10 @@ PLATFORMS.google = {
|
|
|
4121
4125
|
targetCpa:8500,
|
|
4122
4126
|
project:{
|
|
4123
4127
|
businessName:'', logoAsset:'', logoImagePath:'', cta:'詳しくはこちら', startDate:'',
|
|
4124
|
-
|
|
4128
|
+
/* 広告グループ・広告の配信。キャンペーンは常に PAUSED なので、既定をオンにして
|
|
4129
|
+
「検品後にキャンペーンをオンにするだけで配信」にする (#396)。
|
|
4130
|
+
保存済みのプランに入っている値(オフ)はそのまま使う。 */
|
|
4131
|
+
deliveryStatus:'オン'
|
|
4125
4132
|
},
|
|
4126
4133
|
ad:{format:'VIDEO', imageNo:'', moreVideoNos:'', titleNo:'', longNo:'', descNo:'', videoPath:'', adName:''},
|
|
4127
4134
|
adGroup:{name:'', paramCode:'', isExisting:false,
|
|
@@ -4428,7 +4435,9 @@ PLATFORMS.google = {
|
|
|
4428
4435
|
const P=PLATFORMS.google, p=ctx.project;
|
|
4429
4436
|
const cp={
|
|
4430
4437
|
name:c.name,
|
|
4431
|
-
|
|
4438
|
+
/* キャンペーンは常に PAUSED。backend が CP の ENABLED を 400 で拒否する(実配信が未検証)。
|
|
4439
|
+
案件設定の「配信」は広告グループ・広告にだけ効く (#396)。 */
|
|
4440
|
+
status:P.enums.statusOff,
|
|
4432
4441
|
budgetAmountYen:Number(c.dailyBudget)||0,
|
|
4433
4442
|
advertisingChannelType:P.enums.advertisingChannelType
|
|
4434
4443
|
};
|
|
@@ -516,6 +516,18 @@ def lint(plan: dict, plan_path: str = "") -> None:
|
|
|
516
516
|
" まとめて入稿すると2件目以降が落ちます")
|
|
517
517
|
if key:
|
|
518
518
|
plan_cp_names.add(key)
|
|
519
|
+
# Google はキャンペーンを PAUSED でしか入稿できない(backend が ENABLED を 400)。
|
|
520
|
+
# 配下まで PAUSED だと、検品後にキャンペーンだけオンにしても配信されない
|
|
521
|
+
# (2026-10-28 桑の葉69 の5CPが実績0)。配下の ENABLED は backend が通す (#396)。
|
|
522
|
+
paused = [f"広告グループ「{g.get('name', '')}」" for g in ad_groups(c)
|
|
523
|
+
if status_of(g) not in ON_STATUSES]
|
|
524
|
+
paused += [f"広告「{a.get('adName') or a.get('name') or '?'}」"
|
|
525
|
+
for g in ad_groups(c) for a in g.get("ads") or []
|
|
526
|
+
if status_of(a) not in ON_STATUSES]
|
|
527
|
+
if paused and cp_status not in ON_STATUSES:
|
|
528
|
+
more = f" ほか{len(paused) - 3}件" if len(paused) > 3 else ""
|
|
529
|
+
warn(f"CP「{name}」の配下に PAUSED があります({'、'.join(paused[:3])}{more})。"
|
|
530
|
+
" キャンペーンをオンにしても、PAUSED の広告グループ・広告は配信されません")
|
|
519
531
|
|
|
520
532
|
for g in ad_groups(c):
|
|
521
533
|
gname = g.get("name", "")
|
|
@@ -120,6 +120,43 @@ python3 "$SCRIPT" --wait --timeout 8 >/dev/null 2>&1; rc=$?
|
|
|
120
120
|
check "timeout した(サブディレクトリは対象外)" 124 "$rc"
|
|
121
121
|
wait
|
|
122
122
|
|
|
123
|
+
echo "=== 8. Google: CP は PAUSED 必須・配下の PAUSED は WARN (#396) ==="
|
|
124
|
+
rm -rf "$ADOPS_PLAN_DIR"/* "$ADOPS_DOWNLOADS"/*.json
|
|
125
|
+
mkdir -p "$SKILL/アカウント/google"
|
|
126
|
+
cat > "$SKILL/アカウント/google/テスト案件G.json" <<'EOF'
|
|
127
|
+
{"accountJsonVersion":2,"platform":"google","accountId":"1234567890","name":"テスト案件G",
|
|
128
|
+
"baseArticleUrl":"https://example.com/ab/","urlParamsEnabled":false,"refs":{}}
|
|
129
|
+
EOF
|
|
130
|
+
# $1=出力先 $2=CP status $3=AG status $4=広告 status
|
|
131
|
+
google_plan_json() {
|
|
132
|
+
cat > "$1" <<EOF
|
|
133
|
+
{"planVersion":1,"platform":"google","accountId":"1234567890","customerId":"1234567890",
|
|
134
|
+
"campaigns":[{"name":"CP_G","status":"$2","budgetAmountYen":5000,
|
|
135
|
+
"adGroups":[{"name":"AG1","status":"$3","ads":[
|
|
136
|
+
{"type":"DEMAND_GEN_VIDEO_RESPONSIVE_AD","adName":"AD1","status":"$4","youtubeVideoId":"abc",
|
|
137
|
+
"headlines":["見出し"],"descriptions":["説明"],"businessName":"テスト",
|
|
138
|
+
"finalUrls":["https://example.com/ab/x"]}]}]}]}
|
|
139
|
+
EOF
|
|
140
|
+
}
|
|
141
|
+
google_plan_json "$ADOPS_PLAN_DIR/g1.json" PAUSED ENABLED ENABLED
|
|
142
|
+
out=$(python3 "$SCRIPT" "$ADOPS_PLAN_DIR/g1.json" 2>&1); rc=$?
|
|
143
|
+
# exit 0 も見る(異常終了で指摘が出なかっただけ、を通さない)
|
|
144
|
+
[ "$rc" = "0" ] && ! echo "$out" | grep -q "ENABLED(配信オン)\|配下に PAUSED" \
|
|
145
|
+
&& { echo " ✓ CP PAUSED +配下 ENABLED は通る"; pass=$((pass+1)); } \
|
|
146
|
+
|| { echo " ✗ CP PAUSED +配下 ENABLED が通らない (exit=$rc)"; echo "$out" | sed 's/^/ /'; fail=$((fail+1)); }
|
|
147
|
+
|
|
148
|
+
google_plan_json "$ADOPS_PLAN_DIR/g2.json" PAUSED PAUSED PAUSED
|
|
149
|
+
out=$(python3 "$SCRIPT" "$ADOPS_PLAN_DIR/g2.json" 2>&1)
|
|
150
|
+
echo "$out" | grep -q "配下に PAUSED があります" \
|
|
151
|
+
&& { echo " ✓ 配下が PAUSED なら WARN"; pass=$((pass+1)); } \
|
|
152
|
+
|| { echo " ✗ 配下 PAUSED の WARN が出ない"; echo "$out" | sed 's/^/ /'; fail=$((fail+1)); }
|
|
153
|
+
|
|
154
|
+
google_plan_json "$ADOPS_PLAN_DIR/g3.json" ENABLED ENABLED ENABLED
|
|
155
|
+
out=$(python3 "$SCRIPT" "$ADOPS_PLAN_DIR/g3.json" 2>&1); rc=$?
|
|
156
|
+
echo "$out" | grep -q "CP「CP_G」が ENABLED(配信オン)" && [ "$rc" != "0" ] \
|
|
157
|
+
&& { echo " ✓ CP ENABLED は ERROR (exit=$rc)"; pass=$((pass+1)); } \
|
|
158
|
+
|| { echo " ✗ CP ENABLED が ERROR にならない (exit=$rc)"; echo "$out" | sed 's/^/ /'; fail=$((fail+1)); }
|
|
159
|
+
|
|
123
160
|
echo
|
|
124
161
|
echo "══ 結果: ${pass} passed / ${fail} failed ══"
|
|
125
162
|
rm -rf "$ROOT"
|