@clize/clize 0.26.0 → 0.27.0

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.
@@ -0,0 +1,583 @@
1
+ import { ClizeError } from "../lib/errors.js";
2
+ import { sha256hex } from "../lib/crypto.js";
3
+ import { DataForSeo, KEYWORDS_PER_CALL, normalizeHost, } from "../providers/seo/dataforseo.js";
4
+ import { classifySerp } from "./seo-classify.js";
5
+ import { Gsc, toGscProperty } from "../providers/seo/gsc.js";
6
+ import { readTraffic } from "./seo-traffic.js";
7
+ /** 对客加价:成本 × (1 + 4) = ×5。刻意不复用 platform.billing.markupPct(0.2)——
8
+ * 那是给域名 / 媒体等大额供给定的,套在美分级数据上会让定价失真(一次 serp 收 8 厘)。 */
9
+ export const SEO_MARKUP_PCT = 4;
10
+ /** 无 domain 归属的查询(keywords/competitors/serp)按租户走的月度兜底。
11
+ * 有 domain 的路径(PR2 的 check)用 seo_domains.budget_monthly_usd,同一个默认值。 */
12
+ export const SEO_BUDGET_DEFAULT_USD = 5;
13
+ /** 缓存 TTL:词/竞品指标月更口径 30 天;结果页时效短,7 天。 */
14
+ const TTL_DAYS = { keywords: 30, competitors: 30, serp: 7 };
15
+ /** competitors 每个域取多少个词(成本 ≈ $0.01 + 行数 × $0.0001)。 */
16
+ const RANKED_KEYWORDS_LIMIT = 25;
17
+ /** serp 抓多深(前 20 条足够看清占位格局;判定只用前 10)。 */
18
+ const SERP_DEPTH = 20;
19
+ /** 支持的 locale(上游要 location_code + language_code,CLI 只收 `en-US` 这种人话)。 */
20
+ const LOCALES = {
21
+ "en-US": { locationCode: 2840, languageCode: "en" },
22
+ "en-GB": { locationCode: 2826, languageCode: "en" },
23
+ "en-CA": { locationCode: 2124, languageCode: "en" },
24
+ "en-AU": { locationCode: 2036, languageCode: "en" },
25
+ "de-DE": { locationCode: 2276, languageCode: "de" },
26
+ "fr-FR": { locationCode: 2250, languageCode: "fr" },
27
+ "es-ES": { locationCode: 2724, languageCode: "es" },
28
+ "ja-JP": { locationCode: 2392, languageCode: "ja" },
29
+ };
30
+ export const DEFAULT_LOCALE = "en-US";
31
+ export function resolveLocale(tag) {
32
+ const t = (tag || DEFAULT_LOCALE).trim();
33
+ const codes = LOCALES[t] ?? LOCALES[t.replace(/_/g, "-")];
34
+ if (!codes)
35
+ throw new ClizeError(`unsupported locale "${t}" — supported: ${Object.keys(LOCALES).join(", ")}`, 400);
36
+ return { tag: LOCALES[t] ? t : t.replace(/_/g, "-"), codes };
37
+ }
38
+ /** 对客金额(4 位小数;上游成本是厘级,2 位会把一次 serp 抹成 $0.02 vs $0.02 —— 保 4 位如实)。 */
39
+ export function chargedForSeo(upstreamCostUsd) {
40
+ return Number((upstreamCostUsd * (1 + SEO_MARKUP_PCT)).toFixed(4));
41
+ }
42
+ export function keywordBand(sv, kd) {
43
+ if (sv === null || sv === 0)
44
+ return "no_volume";
45
+ if (kd === null)
46
+ return "stretch"; // 有量但上游没给 KD:落中档,不假装知道
47
+ if (kd < 10)
48
+ return "attackable";
49
+ if (kd <= 30)
50
+ return "stretch";
51
+ return "wall";
52
+ }
53
+ /** 行动优先级排序:能打的在前(低 KD 优先,同档大量优先),无量的沉底。 */
54
+ const BAND_ORDER = { attackable: 0, stretch: 1, wall: 2, no_volume: 3 };
55
+ // ---------------- 计费与预算 ----------------
56
+ /** 本月该租户(可选:限定 domain)的 seo 支出,口径 = 用户实付额。 */
57
+ export async function seoSpentThisMonth(db, tenantId, domain) {
58
+ const month = new Date().toISOString().slice(0, 7);
59
+ const sql = "SELECT COALESCE(SUM(amount_usd),0) AS s FROM ledger WHERE tenant_id=? AND kind='debit' AND action='seo' AND substr(ts,1,7)=?" +
60
+ (domain ? " AND json_extract(detail,'$.domain')=?" : "");
61
+ const binds = domain ? [tenantId, month, domain] : [tenantId, month];
62
+ const row = await db
63
+ .prepare(sql)
64
+ .bind(...binds)
65
+ .first();
66
+ return row?.s ?? 0;
67
+ }
68
+ /** 预算闸:超了给 429 + 解法(免逐次 confirm 的对价是花钱透明 + 有上限兜底)。 */
69
+ export async function assertSeoBudget(ctx, db, opts = {}) {
70
+ const domain = opts.domain;
71
+ let cap = SEO_BUDGET_DEFAULT_USD;
72
+ if (domain) {
73
+ const row = await db
74
+ .prepare("SELECT budget_monthly_usd FROM seo_domains WHERE domain=? AND tenant_id=?")
75
+ .bind(domain, ctx.tenantId)
76
+ .first();
77
+ if (row?.budget_monthly_usd != null)
78
+ cap = Number(row.budget_monthly_usd);
79
+ }
80
+ const spent = await seoSpentThisMonth(db, ctx.tenantId, domain);
81
+ if (spent < cap)
82
+ return;
83
+ const scope = domain ? `for ${domain}` : "this month";
84
+ const raise = domain
85
+ ? `clize seo check --domain ${domain} --budget ${Math.ceil(cap * 2)}`
86
+ : "top up the cap by contacting support (per-domain budgets are set by `clize seo check --budget`)";
87
+ throw new ClizeError(`monthly seo budget reached ${scope}: $${spent.toFixed(2)} of $${cap.toFixed(2)}. raise: ${raise}`, 429);
88
+ }
89
+ /** 扣费 + 入账(缓存命中不扣、不入账,ledger 里就不会出现一条 $0 噪音行)。 */
90
+ async function chargeSeo(ctx, op, upstreamCostUsd, detail = {}) {
91
+ const usd = chargedForSeo(upstreamCostUsd);
92
+ if (usd <= 0)
93
+ return { usd: 0, cacheHit: false };
94
+ await ctx.store.chargeBalance(ctx.tenantId, usd, {
95
+ action: "seo",
96
+ detail: { op, upstreamCost: Number(upstreamCostUsd.toFixed(5)), ...detail },
97
+ });
98
+ return { usd, cacheHit: false };
99
+ }
100
+ // ---------------- 缓存 ----------------
101
+ async function cacheKey(op, target, locale) {
102
+ // key 不含 tenant:租户隔离靠 tenant_id 列;Phase 1 开跨租户共享只需去掉查询条件。
103
+ return sha256hex(`${op}:${target.toLowerCase()}:${locale}`);
104
+ }
105
+ async function readCache(db, tenantId, key, ttlDays) {
106
+ const row = await db
107
+ .prepare("SELECT payload, fetched_at FROM seo_keyword_cache WHERE tenant_id=? AND cache_key=?")
108
+ .bind(tenantId, key)
109
+ .first();
110
+ if (!row)
111
+ return null;
112
+ const age = Date.now() - Date.parse(row.fetched_at);
113
+ if (!Number.isFinite(age) || age > ttlDays * 86_400_000)
114
+ return null;
115
+ try {
116
+ return JSON.parse(row.payload);
117
+ }
118
+ catch {
119
+ return null;
120
+ }
121
+ }
122
+ async function writeCache(db, tenantId, key, payload, costUsd) {
123
+ await db
124
+ .prepare("INSERT INTO seo_keyword_cache (tenant_id, cache_key, payload, cost_usd, fetched_at) VALUES (?,?,?,?,?)" +
125
+ " ON CONFLICT(tenant_id, cache_key) DO UPDATE SET payload=excluded.payload, cost_usd=excluded.cost_usd, fetched_at=excluded.fetched_at")
126
+ .bind(tenantId, key, JSON.stringify(payload), costUsd, new Date().toISOString())
127
+ .run();
128
+ }
129
+ function client(ctx) {
130
+ const e = ctx.platform.seo;
131
+ return new DataForSeo({ login: e.dataforseoLogin, password: e.dataforseoPassword });
132
+ }
133
+ /** 词指标。批量便宜(一次调用固定开销 + 每词几厘),所以 seeds 一次给全比逐个查省钱。 */
134
+ export async function keywords(ctx, db, opts) {
135
+ const seeds = [...new Set(opts.seeds.map((s) => s.trim().toLowerCase()).filter(Boolean))];
136
+ if (!seeds.length)
137
+ throw new ClizeError("no keywords given", 400);
138
+ if (seeds.length > KEYWORDS_PER_CALL)
139
+ throw new ClizeError(`too many keywords (${seeds.length}); max ${KEYWORDS_PER_CALL} per call`, 400);
140
+ const { tag, codes } = resolveLocale(opts.locale);
141
+ const op = opts.ai ? "keywords+ai" : "keywords";
142
+ const key = await cacheKey(op, seeds.slice().sort().join("|"), tag);
143
+ const cached = await readCache(db, ctx.tenantId, key, TTL_DAYS.keywords);
144
+ if (cached)
145
+ return { locale: tag, keywords: cached, charged: { usd: 0, cacheHit: true } };
146
+ await assertSeoBudget(ctx, db);
147
+ const api = client(ctx);
148
+ const base = await api.keywordOverview(seeds, codes);
149
+ let cost = base.costUsd;
150
+ const aiByKw = new Map();
151
+ if (opts.ai) {
152
+ const ai = await api.aiKeywordData(seeds, codes);
153
+ cost += ai.costUsd;
154
+ for (const r of ai.result)
155
+ aiByKw.set(r.keyword.toLowerCase(), r.aiSv);
156
+ }
157
+ const rows = base.result
158
+ .map((m) => ({
159
+ keyword: m.keyword,
160
+ sv: m.sv,
161
+ kd: m.kd,
162
+ cpc: m.cpc,
163
+ intent: m.intent,
164
+ ...(opts.ai ? { aiSv: aiByKw.get(m.keyword.toLowerCase()) ?? null } : {}),
165
+ band: keywordBand(m.sv, m.kd),
166
+ }))
167
+ .sort((a, b) => BAND_ORDER[a.band] - BAND_ORDER[b.band] ||
168
+ (a.kd ?? 999) - (b.kd ?? 999) ||
169
+ (b.sv ?? 0) - (a.sv ?? 0));
170
+ await writeCache(db, ctx.tenantId, key, rows, cost);
171
+ const charged = await chargeSeo(ctx, op, cost, { keywords: seeds.length, locale: tag });
172
+ return { locale: tag, keywords: rows, charged };
173
+ }
174
+ /** 竞品反查:一次问多个域的流量画像 + 每个域靠什么词活。 */
175
+ export async function competitors(ctx, db, opts) {
176
+ const domains = [...new Set(opts.domains.map(normalizeHost).filter(Boolean))];
177
+ if (!domains.length)
178
+ throw new ClizeError("no domains given", 400);
179
+ if (domains.length > 10)
180
+ throw new ClizeError(`too many domains (${domains.length}); max 10`, 400);
181
+ const { tag, codes } = resolveLocale(opts.locale);
182
+ const limit = Math.min(Math.max(opts.limit ?? RANKED_KEYWORDS_LIMIT, 1), 100);
183
+ const key = await cacheKey(`competitors:${limit}`, domains.slice().sort().join("|"), tag);
184
+ const cached = await readCache(db, ctx.tenantId, key, TTL_DAYS.competitors);
185
+ if (cached)
186
+ return { locale: tag, competitors: cached, charged: { usd: 0, cacheHit: true } };
187
+ await assertSeoBudget(ctx, db);
188
+ const api = client(ctx);
189
+ const traffic = await api.bulkTraffic(domains, codes);
190
+ let cost = traffic.costUsd;
191
+ const byDomain = new Map(traffic.result.map((t) => [t.domain, t]));
192
+ const rows = [];
193
+ for (const domain of domains) {
194
+ const ranked = await api.rankedKeywords(domain, codes, limit);
195
+ cost += ranked.costUsd;
196
+ const t = byDomain.get(domain);
197
+ rows.push({
198
+ domain,
199
+ etv: t?.etv ?? null,
200
+ keywordCount: t?.keywordCount ?? null,
201
+ topKeywords: ranked.result.items.map((k) => ({
202
+ keyword: k.keyword,
203
+ position: k.position,
204
+ sv: k.sv,
205
+ kd: k.kd,
206
+ etv: k.etv,
207
+ url: k.url,
208
+ })),
209
+ });
210
+ }
211
+ rows.sort((a, b) => (b.etv ?? 0) - (a.etv ?? 0));
212
+ await writeCache(db, ctx.tenantId, key, rows, cost);
213
+ const charged = await chargeSeo(ctx, "competitors", cost, { domains: domains.length, locale: tag });
214
+ return { locale: tag, competitors: rows, charged };
215
+ }
216
+ /** 结果页真相:自然结果 + 占位判定 + 寄生目标。 */
217
+ export async function serp(ctx, db, opts) {
218
+ const keyword = opts.keyword.trim();
219
+ if (!keyword)
220
+ throw new ClizeError("no keyword given", 400);
221
+ const { tag, codes } = resolveLocale(opts.locale);
222
+ const depth = Math.min(Math.max(opts.depth ?? SERP_DEPTH, 10), 100);
223
+ const key = await cacheKey(`serp:${depth}`, keyword, tag);
224
+ const cached = await readCache(db, ctx.tenantId, key, TTL_DAYS.serp);
225
+ if (cached)
226
+ return { ...cached, charged: { usd: 0, cacheHit: true } };
227
+ await assertSeoBudget(ctx, db);
228
+ const { result: page, costUsd } = await client(ctx).serpLiveAdvanced(keyword, codes, depth);
229
+ const classified = classifySerp(keyword, page.items);
230
+ const payload = {
231
+ keyword: page.keyword,
232
+ locale: tag,
233
+ verdict: classified.verdict,
234
+ aiOverview: page.aiOverview,
235
+ itemTypes: page.itemTypes,
236
+ counts: classified.counts,
237
+ items: classified.items.map((i) => ({
238
+ pos: i.pos,
239
+ host: i.host,
240
+ url: i.url,
241
+ title: i.title,
242
+ type: i.type,
243
+ })),
244
+ parasiteTargets: classified.parasiteTargets,
245
+ checkedAt: page.checkedAt,
246
+ };
247
+ await writeCache(db, ctx.tenantId, key, payload, costUsd);
248
+ const charged = await chargeSeo(ctx, "serp", costUsd, { keyword, locale: tag });
249
+ return { ...payload, charged };
250
+ }
251
+ /** GSC 窗口重拉的天数:Google 会事后修正近日数据,固定重拉最近 N 天并替换。 */
252
+ const GSC_WINDOW_DAYS = 30;
253
+ /** GSC 新词回填上限(按曝光降序取):词表是给人/agent 看的,不是倾倒 GSC 全量。 */
254
+ const GSC_BACKFILL_MAX = 20;
255
+ /** 位次抽查的词数上限(每词一次 serp,成本线性;超出的词只留在词表里不抽查)。 */
256
+ const RANK_SAMPLE_MAX = 50;
257
+ /** 位次抽查抓多深。**刻意与 `seo serp` 的 20 不同**:两条命令问的不是同一个问题——
258
+ * serp 问「这页被谁占着」(前 10 就够判占位),check 问「我在第几位」。
259
+ * 2026-08-30 实测这个差别是决定性的:clize.ai 在 `best seo mcp servers` 上,
260
+ * depth=20 完全看不见(位次恒为 null,时序是一条废线),depth=100 报出第 73 位。
261
+ * 冷启动站的词本来就全在 60–90 位,浅抓等于把跟踪功能关掉。
262
+ * 代价:$0.004 → $0.020/词(实测),所以 RANK_SAMPLE_MAX 与月预算是它的闸。 */
263
+ const RANK_DEPTH = 100;
264
+ /** GSC 新词回填的曝光下限(窗口内)。低于此量的词与噪声不可分——实测尾部全是
265
+ * 品牌拼写错误(clice ai / craze ai / calise ai,各 7–8 次)与泛词碎片。
266
+ * 这是个判断阈值,不是自然常量:改它只动这一行。 */
267
+ const GSC_BACKFILL_MIN_IMPRESSIONS = 10;
268
+ /** 抓够多少条才认这轮位次「测到了」。2026-08-30 dogfood 实测:同一个词连问两次,
269
+ * 一次回 97 条、一次只回 17 条 —— 上游 status 20000、无报错,就是少给。
270
+ * 若照单全收,「上游只给了 17 行」会被写成「你掉出前 100」,时序直接说谎;
271
+ * 而本设计明确接受时序有洞(SEO 是周月尺度的慢系统),**宁可留空,不可留假**。 */
272
+ const RANK_MIN_ITEMS = Math.floor(RANK_DEPTH * 0.5);
273
+ /** 位次抽查的并发度。**必须并行**:2026-08-30 实测 depth=100 的 serp live 单次要
274
+ * 8–15 秒(depth=20 只要 2.7s —— 设计时按浅抓估的时延不成立)。串行跑满 50 词
275
+ * 就是 8 分钟,「现场测一轮立刻返回」的语义直接破产,worker 与客户端也都会超时。
276
+ * 并发 10 → 50 词约 1 分钟、常见的 5–15 词约 10–20 秒。
277
+ * 上游速率上限是每分钟 2000 次,10 并发离它很远。 */
278
+ const RANK_CONCURRENCY = 10;
279
+ /** 定量并发跑一批任务,保持输入顺序。(仓库里没有现成的并发池,就地一个 12 行的。) */
280
+ async function mapLimit(items, limit, fn) {
281
+ const out = new Array(items.length);
282
+ let next = 0;
283
+ const workers = Array.from({ length: Math.min(limit, items.length) }, async () => {
284
+ for (;;) {
285
+ const i = next++;
286
+ if (i >= items.length)
287
+ return;
288
+ out[i] = await fn(items[i]);
289
+ }
290
+ });
291
+ await Promise.all(workers);
292
+ return out;
293
+ }
294
+ /** 流量与 GSC 的默认回看窗口。 */
295
+ const TRAFFIC_DAYS = 30;
296
+ const day = (ms) => new Date(ms).toISOString().slice(0, 10);
297
+ /** 位次判定:结果页上第一条属于该域(或其子域)的自然结果。 */
298
+ function positionOf(items, domain) {
299
+ const d = normalizeHost(domain);
300
+ for (const it of items) {
301
+ const h = normalizeHost(it.host);
302
+ if (h === d || h.endsWith(`.${d}`))
303
+ return it.pos;
304
+ }
305
+ return null;
306
+ }
307
+ /** 配置 upsert:带参则更新,不带则沿用已存的(首次给出后记住)。 */
308
+ async function upsertDomain(ctx, db, domain, cfg) {
309
+ const existing = await db
310
+ .prepare("SELECT domain, brand, competitors, gsc_property, budget_monthly_usd FROM seo_domains WHERE domain=? AND tenant_id=?")
311
+ .bind(domain, ctx.tenantId)
312
+ .first();
313
+ const row = {
314
+ domain,
315
+ brand: cfg.brand ?? existing?.brand ?? null,
316
+ competitors: cfg.competitors
317
+ ? JSON.stringify(cfg.competitors.map(normalizeHost))
318
+ : (existing?.competitors ?? null),
319
+ gsc_property: cfg.gsc === "none" ? null : (cfg.gsc ?? existing?.gsc_property ?? null),
320
+ budget_monthly_usd: cfg.budgetUsd ?? existing?.budget_monthly_usd ?? SEO_BUDGET_DEFAULT_USD,
321
+ };
322
+ await db
323
+ .prepare("INSERT INTO seo_domains (domain, tenant_id, brand, competitors, gsc_property, budget_monthly_usd, created_at) VALUES (?,?,?,?,?,?,?)" +
324
+ " ON CONFLICT(domain) DO UPDATE SET brand=excluded.brand, competitors=excluded.competitors," +
325
+ " gsc_property=excluded.gsc_property, budget_monthly_usd=excluded.budget_monthly_usd")
326
+ .bind(domain, ctx.tenantId, row.brand, row.competitors, row.gsc_property, row.budget_monthly_usd, new Date().toISOString())
327
+ .run();
328
+ return row;
329
+ }
330
+ /** 词表:--keywords 是**全量替换**(保留已有行的指标),不带则读已存的。 */
331
+ async function syncKeywords(db, domain, keywords, now) {
332
+ if (keywords) {
333
+ const wanted = [...new Set(keywords.map((k) => k.trim().toLowerCase()).filter(Boolean))];
334
+ // 全量替换语义:不在新表里的词删掉(含之前 gsc_backfill 进来的)。
335
+ const { results: existing } = await db
336
+ .prepare("SELECT keyword FROM seo_keywords WHERE domain=?")
337
+ .bind(domain)
338
+ .all();
339
+ for (const e of existing)
340
+ if (!wanted.includes(e.keyword))
341
+ await db.prepare("DELETE FROM seo_keywords WHERE domain=? AND keyword=?").bind(domain, e.keyword).run();
342
+ for (const k of wanted)
343
+ await db
344
+ .prepare("INSERT INTO seo_keywords (domain, keyword, locale, source, saved_at) VALUES (?,?,?,?,?)" +
345
+ " ON CONFLICT(domain, keyword, locale) DO UPDATE SET source='check'")
346
+ .bind(domain, k, DEFAULT_LOCALE, "check", now)
347
+ .run();
348
+ }
349
+ const { results } = await db
350
+ .prepare("SELECT keyword, sv, kd, source FROM seo_keywords WHERE domain=? ORDER BY id")
351
+ .bind(domain)
352
+ .all();
353
+ return results;
354
+ }
355
+ /** 测一轮。cron 若将来复活,定期跑的就是这个函数(同一条代码路径)。 */
356
+ export async function checkOne(ctx, db, domain0, cfg = {}, now = Date.now()) {
357
+ const domain = normalizeHost(domain0.replace(/^https?:\/\//, "").split("/")[0] ?? "");
358
+ if (!domain)
359
+ throw new ClizeError("no domain: pass --domain or run inside a clize project", 400);
360
+ await assertSeoBudget(ctx, db, { domain });
361
+ const ranAt = new Date(now).toISOString();
362
+ const conf = await upsertDomain(ctx, db, domain, cfg);
363
+ const competitors = conf.competitors ? JSON.parse(conf.competitors) : [];
364
+ const kwRows = await syncKeywords(db, domain, cfg.keywords, ranAt);
365
+ const notes = [];
366
+ let upstreamCost = 0;
367
+ // ── 位次抽查:每词一次 serp。**刻意不吃缓存** —— check 的语义是「去拿最新数据」,
368
+ // 读 7 天前的缓存会让时序说谎(写缓存仍照写,后续 seo serp 直接命中)。
369
+ const tracked = kwRows.filter((k) => k.source !== "gsc_backfill").slice(0, RANK_SAMPLE_MAX);
370
+ if (kwRows.filter((k) => k.source !== "gsc_backfill").length > RANK_SAMPLE_MAX)
371
+ notes.push(`rank sampling capped at ${RANK_SAMPLE_MAX} keywords; the rest stay in the list untracked`);
372
+ let rank = null;
373
+ if (tracked.length) {
374
+ const api = client(ctx);
375
+ const { codes } = resolveLocale(DEFAULT_LOCALE);
376
+ const rows = [];
377
+ const fetched = await mapLimit(tracked, RANK_CONCURRENCY, async (k) => {
378
+ let cost = 0;
379
+ try {
380
+ const first = await api.serpLiveAdvanced(k.keyword, codes, RANK_DEPTH);
381
+ cost += first.costUsd;
382
+ let page = first.result;
383
+ // 上游偶发少给行(实测:同一个词一次回 97 条、一次只回 17 条,status 正常)。
384
+ // 只在「没找到 + 结果过短」时重试一次:找到了就无所谓深浅,结果够长的 null 是真 null。
385
+ if (positionOf(page.items, domain) === null && page.items.length < RANK_MIN_ITEMS) {
386
+ try {
387
+ const retry = await api.serpLiveAdvanced(k.keyword, codes, RANK_DEPTH);
388
+ cost += retry.costUsd;
389
+ page = retry.result;
390
+ }
391
+ catch {
392
+ /* 重试也失败:按下面的「未测到」处理 */
393
+ }
394
+ }
395
+ return { keyword: k.keyword, page, cost };
396
+ }
397
+ catch (e) {
398
+ return { keyword: k.keyword, page: null, cost, error: e instanceof Error ? e.message : String(e) };
399
+ }
400
+ });
401
+ // ② 写:顺序(D1 单连接,顺序写也让 ran_at 的时序读起来干净)。
402
+ for (const f of fetched) {
403
+ upstreamCost += f.cost;
404
+ if (!f.page) {
405
+ notes.push(`rank not measured for "${f.keyword}": ${f.error}`);
406
+ continue;
407
+ }
408
+ const page = f.page;
409
+ const position = positionOf(page.items, domain);
410
+ const measured = position !== null || page.items.length >= RANK_MIN_ITEMS;
411
+ const comp = {};
412
+ for (const c of competitors)
413
+ comp[c] = positionOf(page.items, c); // 同一次抓取顺带记,零边际成本
414
+ const top3 = page.items.slice(0, 3).map((i) => i.host);
415
+ const prev = await db
416
+ .prepare("SELECT position FROM seo_rank_snapshots WHERE domain=? AND keyword=? ORDER BY ran_at DESC LIMIT 1")
417
+ .bind(domain, f.keyword)
418
+ .first();
419
+ if (measured)
420
+ await db
421
+ .prepare("INSERT INTO seo_rank_snapshots (domain, keyword, ran_at, position, serp_features, top3, competitor_positions) VALUES (?,?,?,?,?,?,?)")
422
+ .bind(domain, f.keyword, ranAt, position, JSON.stringify(page.itemTypes), JSON.stringify(top3), JSON.stringify(comp))
423
+ .run();
424
+ else
425
+ notes.push(`rank not measured for "${f.keyword}": upstream returned only ${page.items.length} results (asked for ${RANK_DEPTH}) — left out of the series rather than recorded as a drop`);
426
+ const { results: hist } = await db
427
+ .prepare("SELECT ran_at, position FROM seo_rank_snapshots WHERE domain=? AND keyword=? ORDER BY ran_at DESC LIMIT 12")
428
+ .bind(domain, f.keyword)
429
+ .all();
430
+ rows.push({
431
+ keyword: f.keyword,
432
+ position,
433
+ measured,
434
+ // 位次变小 = 前进,所以 delta = 旧 - 新(正数是好消息)。任一端未见 → null。
435
+ delta: measured && prev?.position != null && position != null ? prev.position - position : null,
436
+ series: hist.map((h) => [h.ran_at.slice(0, 10), h.position]),
437
+ competitors: comp,
438
+ serpFeatures: page.itemTypes,
439
+ top3,
440
+ });
441
+ }
442
+ const movers = rows
443
+ .filter((r) => r.delta != null && Math.abs(r.delta) >= 3)
444
+ .sort((a, b) => Math.abs(b.delta ?? 0) - Math.abs(a.delta ?? 0));
445
+ rank = { depth: RANK_DEPTH, latest: rows.sort((a, b) => (a.position ?? 999) - (b.position ?? 999)), movers };
446
+ }
447
+ else {
448
+ notes.push("no keyword list yet — pass --keywords a,b,c to start tracking positions");
449
+ }
450
+ // ── 流量分源(自有基建,不花数据钱)。AI 来源单列;全零也返回,零就是信息。
451
+ let traffic = null;
452
+ try {
453
+ traffic = await readTraffic(ctx, {
454
+ host: domain,
455
+ from: new Date(now - TRAFFIC_DAYS * 86_400_000).toISOString().replace(/\.\d{3}Z$/, "Z"),
456
+ to: new Date(now).toISOString().replace(/\.\d{3}Z$/, "Z"),
457
+ });
458
+ }
459
+ catch (e) {
460
+ notes.push(`traffic unavailable: ${e instanceof Error ? e.message : String(e)}`);
461
+ }
462
+ if (traffic)
463
+ await db
464
+ .prepare("INSERT INTO seo_traffic_snapshots (domain, period_start, period_end, pv, referrers, ai_sources) VALUES (?,?,?,?,?,?)" +
465
+ " ON CONFLICT(domain, period_start) DO UPDATE SET period_end=excluded.period_end, pv=excluded.pv," +
466
+ " referrers=excluded.referrers, ai_sources=excluded.ai_sources")
467
+ .bind(domain, traffic.from.slice(0, 10), traffic.to.slice(0, 10), traffic.pv, JSON.stringify(Object.fromEntries(traffic.topReferrers.map((r) => [r.host, r.pv]))), JSON.stringify(traffic.ai.byEngine))
468
+ .run();
469
+ // ── GSC(自有站唯一的搜索面)。拿不到不中断整轮 —— 给 null + hint。
470
+ const gsc = await gscSection(ctx, db, domain, conf, kwRows, notes, now);
471
+ const charged = await chargeSeo(ctx, "check", upstreamCost, {
472
+ domain,
473
+ keywords: tracked.length,
474
+ });
475
+ const spent = await seoSpentThisMonth(db, ctx.tenantId, domain);
476
+ const byBand = {};
477
+ for (const k of kwRows) {
478
+ const b = keywordBand(k.sv, k.kd);
479
+ byBand[b] = (byBand[b] ?? 0) + 1;
480
+ }
481
+ return {
482
+ domain,
483
+ brand: conf.brand,
484
+ ranAt,
485
+ budget: { monthlyUsd: conf.budget_monthly_usd, spentThisMonth: Number(spent.toFixed(4)) },
486
+ keywords: { total: kwRows.length, byBand, tracked: tracked.length },
487
+ rank,
488
+ traffic,
489
+ gsc,
490
+ notes,
491
+ charged,
492
+ };
493
+ }
494
+ /** GSC 段:窗口重拉替换 + 新词回填。任何失败都降级成 note,不中断 check。 */
495
+ async function gscSection(ctx, db, domain, conf, kwRows, notes, now) {
496
+ const saKey = ctx.platform.seo.gscServiceAccountKey;
497
+ if (!saKey) {
498
+ notes.push("gsc: not configured on this deployment (GSC_SA_KEY missing)");
499
+ return null;
500
+ }
501
+ const gscClient = new Gsc(saKey);
502
+ let property = conf.gsc_property;
503
+ if (!property) {
504
+ // 首跑自动探测:SA 能读哪个 property 就用哪个(域名属性覆盖全子域,通常一击即中)。
505
+ try {
506
+ const sites = await gscClient.listSites();
507
+ const want = [toGscProperty(domain), `https://${domain}/`, `https://www.${domain}/`];
508
+ property = sites.find((s) => want.includes(s.siteUrl.toLowerCase()))?.siteUrl ?? null;
509
+ }
510
+ catch (e) {
511
+ notes.push(`gsc: ${e instanceof Error ? e.message : String(e)}`);
512
+ return null;
513
+ }
514
+ if (!property) {
515
+ notes.push(`gsc: no accessible property for ${domain} — add ${gscClient.serviceAccountEmail} as a Restricted user in Search Console, then re-run (or pass --gsc none to stop asking)`);
516
+ return null;
517
+ }
518
+ await db.prepare("UPDATE seo_domains SET gsc_property=? WHERE domain=?").bind(property, domain).run();
519
+ }
520
+ // Google 会事后修正近日数据 → 固定窗口重拉 + 整窗替换(不做 append-only,
521
+ // 否则被修正掉的旧值会永久留在时序里)。
522
+ const end = day(now - 3 * 86_400_000); // 留 3 天让数据定稿(dataState:final)
523
+ const start = day(now - GSC_WINDOW_DAYS * 86_400_000);
524
+ let rows;
525
+ try {
526
+ rows = await gscClient.queryAnalytics(property, start, end);
527
+ }
528
+ catch (e) {
529
+ notes.push(`gsc: ${e instanceof Error ? e.message : String(e)}`);
530
+ return null;
531
+ }
532
+ await db.prepare("DELETE FROM seo_gsc_daily WHERE domain=? AND date>=? AND date<=?").bind(domain, start, end).run();
533
+ for (const r of rows)
534
+ await db
535
+ .prepare("INSERT INTO seo_gsc_daily (domain, date, query, page, clicks, impressions, position) VALUES (?,?,?,?,?,?,?)" +
536
+ " ON CONFLICT(domain, date, query, page) DO UPDATE SET clicks=excluded.clicks," +
537
+ " impressions=excluded.impressions, position=excluded.position")
538
+ .bind(domain, r.date, r.query, r.page, r.clicks, r.impressions, r.position)
539
+ .run();
540
+ // 按 query 聚合(明细已落库,返回只给摘要——为 context 优化)。
541
+ const agg = new Map();
542
+ for (const r of rows) {
543
+ const a = agg.get(r.query) ?? { clicks: 0, impressions: 0, posSum: 0, n: 0 };
544
+ a.clicks += r.clicks;
545
+ a.impressions += r.impressions;
546
+ a.posSum += r.position * r.impressions;
547
+ a.n += r.impressions;
548
+ agg.set(r.query, a);
549
+ }
550
+ const byQuery = [...agg.entries()]
551
+ .map(([query, a]) => ({
552
+ query,
553
+ clicks: a.clicks,
554
+ impressions: a.impressions,
555
+ position: a.n ? Number((a.posSum / a.n).toFixed(1)) : 0,
556
+ }))
557
+ .sort((x, y) => y.impressions - x.impressions);
558
+ // 新词回填:GSC 冒出的、词表里还没有的词 —— 环 4 → 环 1 的反哺回路闭合。
559
+ // 只是「发现」,不计入位次抽查(source=gsc_backfill)。
560
+ const known = new Set(kwRows.map((k) => k.keyword));
561
+ const brand = (conf.brand ?? "").toLowerCase();
562
+ const fresh = byQuery
563
+ .filter((q) => !known.has(q.query))
564
+ .filter((q) => q.impressions >= GSC_BACKFILL_MIN_IMPRESSIONS) // 低于此量与噪声不可分
565
+ .filter((q) => !q.query.startsWith("site:")) // 自查污染,不是需求信号
566
+ .filter((q) => !brand || !q.query.includes(brand)) // 品牌词不是新发现
567
+ .slice(0, GSC_BACKFILL_MAX);
568
+ const savedAt = new Date(now).toISOString();
569
+ for (const q of fresh)
570
+ await db
571
+ .prepare("INSERT INTO seo_keywords (domain, keyword, locale, source, saved_at) VALUES (?,?,?,?,?)" +
572
+ " ON CONFLICT(domain, keyword, locale) DO NOTHING")
573
+ .bind(domain, q.query, DEFAULT_LOCALE, "gsc_backfill", savedAt)
574
+ .run();
575
+ return {
576
+ property,
577
+ clicks: byQuery.reduce((s, q) => s + q.clicks, 0),
578
+ impressions: byQuery.reduce((s, q) => s + q.impressions, 0),
579
+ topQueries: byQuery.slice(0, 15),
580
+ newQueries: fresh.map((q) => ({ query: q.query, impressions: q.impressions, position: q.position })),
581
+ };
582
+ }
583
+ //# sourceMappingURL=seo.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"seo.js","sourceRoot":"","sources":["../../src/core/seo.ts"],"names":[],"mappings":"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"}