sealclaw 2026.9.23 → 2026.9.28
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/build-info.json
CHANGED
|
@@ -17,5 +17,5 @@
|
|
|
17
17
|
],
|
|
18
18
|
"channelCatalogSignature": "7a1ab9c88c6afe6defb272bfd422d06dee66ffc5",
|
|
19
19
|
"rootHelpBundleSignature": "3dfec2096c9435c4992ee4ffe46db09a4d4e99ad",
|
|
20
|
-
"rootHelpText": "\n🦞 SealClaw 2026.9.
|
|
20
|
+
"rootHelpText": "\n🦞 SealClaw 2026.9.28 (167fdb4) — Say \"stop\" and I'll stop—say \"ship\" and we'll both learn a lesson.\n\nUsage: openclaw [options] [command]\n\nOptions:\n --container <name> Run the CLI inside a running Podman/Docker container\n named <name> (default: env OPENCLAW_CONTAINER)\n --dev Dev profile: isolate state under ~/.openclaw-dev, default\n gateway port 19001, and shift derived ports\n (browser/canvas)\n -h, --help Display help for command\n --log-level <level> Global log level override for file + console\n (silent|fatal|error|warn|info|debug|trace)\n --no-color Disable ANSI colors\n --profile <name> Use a named profile (isolates\n OPENCLAW_STATE_DIR/OPENCLAW_CONFIG_PATH under\n ~/.openclaw-<name>)\n -V, --version output the version number\n\nCommands:\n Hint: commands suffixed with * have subcommands. Run <command> --help for details.\n acp * Agent Control Protocol tools\n agent Run one agent turn via the Gateway\n agents * Manage isolated agents (workspaces, auth, routing)\n approvals * Manage exec approvals (gateway or node host)\n backup * Create and verify local backup archives for OpenClaw\n state\n capability * Run provider-backed inference commands (fallback alias:\n infer)\n channels * Manage connected chat channels (Telegram, Discord, etc.)\n clawbot * Legacy clawbot command aliases\n commitments * List and manage inferred follow-up commitments\n completion Generate shell completion script\n config * Non-interactive config helpers\n (get/set/unset/file/validate). Default: starts guided\n setup.\n configure Interactive configuration for credentials, channels,\n gateway, and agent defaults\n crestodian Run a ring-zero setup or repair request\n cron * Manage cron jobs via the Gateway scheduler\n daemon * Gateway service (legacy alias)\n dashboard Open the Control UI with your current token\n devices * Device pairing + token management\n directory * Lookup contact and group IDs (self, peers, groups) for\n supported chat channels\n dns * DNS helpers for wide-area discovery (Tailscale + CoreDNS)\n docs Search the live OpenClaw docs\n doctor Health checks + quick fixes for the gateway and channels\n exec-policy * Show or synchronize requested exec policy with host\n approvals\n gateway * Run, inspect, and query the WebSocket Gateway\n health Fetch health from the running gateway\n help Display help for command\n hooks * Manage internal agent hooks\n infer * Run provider-backed inference commands\n logs Tail gateway file logs via RPC\n mcp * Manage OpenClaw MCP config and channel bridge\n memory Search, inspect, and reindex memory files\n message * Send, read, and manage messages\n migrate * Import state from another agent system\n models * Discover, scan, and configure models\n node * Run and manage the headless node host service\n nodes * Manage gateway-owned node pairing and node commands\n officecli-cli Install and run OfficeCLI\n onboard Interactive onboarding for gateway, workspace, and skills\n pairing * Secure DM pairing (approve inbound requests)\n plugins * Manage OpenClaw plugins\n proxy * Run the OpenClaw debug proxy and inspect captured traffic\n qr Generate mobile pairing QR/setup code\n reset Reset local config/state (keeps the CLI installed)\n sandbox * Manage sandbox containers for agent isolation\n secrets * Secrets runtime reload controls\n security * Security tools and local config audits\n sessions * List stored conversation sessions\n setup Initialize local config and agent workspace\n skills * List and inspect available skills\n status Show channel health and recent session recipients\n system * System events, heartbeat, and presence\n tasks * Inspect durable background task state\n uninstall Uninstall the gateway service + local data (CLI remains)\n update * Update OpenClaw and inspect update channel status\n webhooks * Webhook helpers and integrations\n\nExamples:\n openclaw models --help\n Show detailed help for the models command.\n openclaw channels login --verbose\n Link personal WhatsApp Web and show QR + connection logs.\n openclaw message send --target +15555550123 --message \"Hi\" --json\n Send via your web session and print JSON result.\n openclaw gateway --port 18789\n Run the WebSocket Gateway locally.\n openclaw --dev gateway\n Run a dev Gateway (isolated state/config) on ws://127.0.0.1:19001.\n openclaw gateway --force\n Kill anything bound to the default gateway port, then start it.\n openclaw gateway ...\n Gateway control via WebSocket.\n openclaw agent --to +15555550123 --message \"Run summary\" --deliver\n Talk directly to the agent using the Gateway; optionally send the WhatsApp reply.\n openclaw message send --channel telegram --target @mychat --message \"Hi\"\n Send via your Telegram bot.\n\nDocs: https://docs.openclaw.ai/cli\n\n"
|
|
21
21
|
}
|
|
@@ -82,7 +82,7 @@ function normalizeTabId(value) {
|
|
|
82
82
|
}
|
|
83
83
|
//#endregion
|
|
84
84
|
//#region extensions/sealseek_qushu/src/collectors/sycm/date-range.ts
|
|
85
|
-
function formatDate(date) {
|
|
85
|
+
function formatDate$2(date) {
|
|
86
86
|
return `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, "0")}-${String(date.getDate()).padStart(2, "0")}`;
|
|
87
87
|
}
|
|
88
88
|
function addDays(date, days) {
|
|
@@ -104,7 +104,7 @@ function previousWeek(now) {
|
|
|
104
104
|
function previousMonth(now) {
|
|
105
105
|
return [new Date(now.getFullYear(), now.getMonth() - 1, 1), new Date(now.getFullYear(), now.getMonth(), 0)];
|
|
106
106
|
}
|
|
107
|
-
function parseDate(value) {
|
|
107
|
+
function parseDate$2(value) {
|
|
108
108
|
const matched = /^(\d{4})-(\d{2})-(\d{2})$/.exec(value);
|
|
109
109
|
if (!matched) throw new Error("dateRange 必须使用 YYYY-MM-DD|YYYY-MM-DD 格式");
|
|
110
110
|
const year = Number(matched[1]);
|
|
@@ -114,19 +114,19 @@ function parseDate(value) {
|
|
|
114
114
|
if (date.getFullYear() !== year || date.getMonth() !== month - 1 || date.getDate() !== day) throw new Error(`dateRange 包含无效日期“${value}”`);
|
|
115
115
|
return date;
|
|
116
116
|
}
|
|
117
|
-
function parseDateRange(value) {
|
|
117
|
+
function parseDateRange$2(value) {
|
|
118
118
|
const parts = value.split("|");
|
|
119
119
|
if (parts.length !== 2) throw new Error("dateRange 必须使用 YYYY-MM-DD|YYYY-MM-DD 格式");
|
|
120
|
-
const start = parseDate(parts[0] ?? "");
|
|
121
|
-
const end = parseDate(parts[1] ?? "");
|
|
120
|
+
const start = parseDate$2(parts[0] ?? "");
|
|
121
|
+
const end = parseDate$2(parts[1] ?? "");
|
|
122
122
|
if (start.getTime() > end.getTime()) throw new Error("dateRange 开始日期不能晚于结束日期");
|
|
123
123
|
return [start, end];
|
|
124
124
|
}
|
|
125
125
|
function sameDate(left, right) {
|
|
126
|
-
return formatDate(left) === formatDate(right);
|
|
126
|
+
return formatDate$2(left) === formatDate$2(right);
|
|
127
127
|
}
|
|
128
128
|
function validateDateRange(dateType, value, now) {
|
|
129
|
-
const [start, end] = parseDateRange(value);
|
|
129
|
+
const [start, end] = parseDateRange$2(value);
|
|
130
130
|
if (dateType === "today") {
|
|
131
131
|
const today = startOfDay(now);
|
|
132
132
|
if (!sameDate(start, today) || !sameDate(end, today)) throw new Error("today 的 dateRange 必须是当天");
|
|
@@ -156,23 +156,23 @@ function validateDateRange(dateType, value, now) {
|
|
|
156
156
|
}
|
|
157
157
|
function calculateDateRange(dateType, now = /* @__PURE__ */ new Date()) {
|
|
158
158
|
if (dateType === "today") {
|
|
159
|
-
const date = formatDate(now);
|
|
159
|
+
const date = formatDate$2(now);
|
|
160
160
|
return `${date}|${date}`;
|
|
161
161
|
}
|
|
162
162
|
if (dateType === "day") {
|
|
163
163
|
const [, date] = previousWeek(now);
|
|
164
|
-
return `${formatDate(date)}|${formatDate(date)}`;
|
|
164
|
+
return `${formatDate$2(date)}|${formatDate$2(date)}`;
|
|
165
165
|
}
|
|
166
166
|
if (dateType === "week") {
|
|
167
167
|
const [start, end] = previousWeek(now);
|
|
168
|
-
return `${formatDate(start)}|${formatDate(end)}`;
|
|
168
|
+
return `${formatDate$2(start)}|${formatDate$2(end)}`;
|
|
169
169
|
}
|
|
170
170
|
if (dateType === "month") {
|
|
171
171
|
const [start, end] = previousMonth(now);
|
|
172
|
-
return `${formatDate(start)}|${formatDate(end)}`;
|
|
172
|
+
return `${formatDate$2(start)}|${formatDate$2(end)}`;
|
|
173
173
|
}
|
|
174
174
|
const end = latestAvailableDate(now);
|
|
175
|
-
return `${formatDate(addDays(end, dateType === "recent30" ? -29 : -6))}|${formatDate(end)}`;
|
|
175
|
+
return `${formatDate$2(addDays(end, dateType === "recent30" ? -29 : -6))}|${formatDate$2(end)}`;
|
|
176
176
|
}
|
|
177
177
|
function resolveSycmDateParams(params, config) {
|
|
178
178
|
const dateType = (typeof params.dateType === "string" ? params.dateType.trim() : "") || config.defaultType;
|
|
@@ -3095,8 +3095,8 @@ const SYCM_ITEM360_TRAFFIC = {
|
|
|
3095
3095
|
* 采集请求只携带 cateId + cateFlag(取行 [3]),不携带 parentCateId;
|
|
3096
3096
|
* 页面 URL query 的 cateFlag 语义不同(一级=1、非一级=2),禁止混用。
|
|
3097
3097
|
*/
|
|
3098
|
-
const MAX_CATEGORY_SUGGESTIONS = 5;
|
|
3099
|
-
function buildCategoryResolveSource() {
|
|
3098
|
+
const MAX_CATEGORY_SUGGESTIONS$1 = 5;
|
|
3099
|
+
function buildCategoryResolveSource$1() {
|
|
3100
3100
|
return `
|
|
3101
3101
|
const fetchCategoryTree = async (token) => {
|
|
3102
3102
|
const url = new URL("/mc/common/free/getCateInfo.json", location.origin);
|
|
@@ -3165,7 +3165,7 @@ function buildCategoryResolveSource() {
|
|
|
3165
3165
|
if (seen.has(key)) continue;
|
|
3166
3166
|
seen.add(key);
|
|
3167
3167
|
suggestions.push(item);
|
|
3168
|
-
if (suggestions.length >= ${MAX_CATEGORY_SUGGESTIONS}) break;
|
|
3168
|
+
if (suggestions.length >= ${MAX_CATEGORY_SUGGESTIONS$1}) break;
|
|
3169
3169
|
}
|
|
3170
3170
|
return suggestions;
|
|
3171
3171
|
};
|
|
@@ -3184,7 +3184,7 @@ function buildCategoryResolveSource() {
|
|
|
3184
3184
|
function buildMarketRankCategoryResolveScript(input) {
|
|
3185
3185
|
return `(async () => {
|
|
3186
3186
|
const input = ${JSON.stringify(input)};
|
|
3187
|
-
${buildCategoryResolveSource()}
|
|
3187
|
+
${buildCategoryResolveSource$1()}
|
|
3188
3188
|
const errorResult = (code, message, retryable = false) => ({
|
|
3189
3189
|
ok: false,
|
|
3190
3190
|
action: "sycm_market_rank_category",
|
|
@@ -3278,43 +3278,131 @@ function resolveMarketRankRequest(rankType, dateType, sortMetric) {
|
|
|
3278
3278
|
endpoint: dateType === "today" ? "/mc/mq/mkt/item/live/rank.json" : "/mc/mq/mkt/item/offline/rank/recommend.json",
|
|
3279
3279
|
indexCode: sort.indexCode,
|
|
3280
3280
|
order: "asc",
|
|
3281
|
-
orderBy: sort.orderBy
|
|
3281
|
+
orderBy: sort.orderBy,
|
|
3282
|
+
pageSize: 20
|
|
3282
3283
|
};
|
|
3283
3284
|
}
|
|
3284
3285
|
return {
|
|
3285
3286
|
gmv: {
|
|
3286
3287
|
endpoint: dateType === "today" ? "/mc/mq/mkt/item/live/rank.json" : "/mc/mq/mkt/item/offline/rank.json",
|
|
3287
|
-
indexCode: "payByrCnt,uv"
|
|
3288
|
+
indexCode: "payByrCnt,uv",
|
|
3289
|
+
pageSize: 20
|
|
3288
3290
|
},
|
|
3289
3291
|
growth: {
|
|
3290
3292
|
endpoint: dateType === "today" ? "/mc/mq/mkt/item/live/rank.json" : "/mc/mq/mkt/item/offline/rank.json",
|
|
3291
|
-
indexCode: "payByrCnt,uv"
|
|
3293
|
+
indexCode: "payByrCnt,uv",
|
|
3294
|
+
pageSize: 20
|
|
3292
3295
|
},
|
|
3293
3296
|
flow: {
|
|
3294
3297
|
endpoint: "/mc/mq/mkt/item/offline/rank/search.json",
|
|
3295
3298
|
indexCode: "uv,searchUv",
|
|
3299
|
+
pageSize: 20,
|
|
3296
3300
|
order: "",
|
|
3297
3301
|
orderBy: ""
|
|
3298
3302
|
},
|
|
3299
3303
|
growth_flow: {
|
|
3300
3304
|
endpoint: dateType === "today" ? "/mc/mq/mkt/item/live/rank.json" : "/mc/mq/mkt/item/offline/rank/rise.json",
|
|
3301
3305
|
indexCode: "ipvUvChangeRate,uv",
|
|
3306
|
+
pageSize: 20,
|
|
3302
3307
|
order: "desc",
|
|
3303
3308
|
orderBy: "ipvUvChangeRate"
|
|
3304
3309
|
},
|
|
3305
3310
|
add: {
|
|
3306
3311
|
endpoint: "/mc/mq/mkt/item/offline/rank/purpose.json",
|
|
3307
3312
|
indexCode: "cartByrCnt,cltByrCnt,uv",
|
|
3313
|
+
pageSize: 20,
|
|
3308
3314
|
order: "",
|
|
3309
3315
|
orderBy: ""
|
|
3310
3316
|
},
|
|
3311
3317
|
newitm_ipv: {
|
|
3312
3318
|
endpoint: "/mc/mq/mkt/item/offline/rank.json",
|
|
3313
|
-
indexCode: "uv,payByrCnt,cartByrCnt"
|
|
3319
|
+
indexCode: "uv,payByrCnt,cartByrCnt",
|
|
3320
|
+
pageSize: 20
|
|
3314
3321
|
}
|
|
3315
3322
|
}[rankType];
|
|
3316
3323
|
}
|
|
3317
3324
|
//#endregion
|
|
3325
|
+
//#region extensions/sealseek_qushu/src/collectors/sycm/market-rank/date-range.ts
|
|
3326
|
+
function formatDate$1(date) {
|
|
3327
|
+
return `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, "0")}-${String(date.getDate()).padStart(2, "0")}`;
|
|
3328
|
+
}
|
|
3329
|
+
function parseDate$1(value) {
|
|
3330
|
+
const matched = /^(\d{4})-(\d{2})-(\d{2})$/.exec(value);
|
|
3331
|
+
if (!matched) throw new Error("dateRange 必须使用 YYYY-MM-DD|YYYY-MM-DD 格式");
|
|
3332
|
+
const year = Number(matched[1]);
|
|
3333
|
+
const month = Number(matched[2]);
|
|
3334
|
+
const day = Number(matched[3]);
|
|
3335
|
+
const date = new Date(year, month - 1, day);
|
|
3336
|
+
if (date.getFullYear() !== year || date.getMonth() !== month - 1 || date.getDate() !== day) throw new Error(`dateRange 包含无效日期“${value}”`);
|
|
3337
|
+
return date;
|
|
3338
|
+
}
|
|
3339
|
+
function parseDateRange$1(value) {
|
|
3340
|
+
const parts = value.split("|");
|
|
3341
|
+
if (parts.length !== 2) throw new Error("dateRange 必须使用 YYYY-MM-DD|YYYY-MM-DD 格式");
|
|
3342
|
+
const start = parseDate$1(parts[0] ?? "");
|
|
3343
|
+
const end = parseDate$1(parts[1] ?? "");
|
|
3344
|
+
if (start.getTime() > end.getTime()) throw new Error("dateRange 开始日期不能晚于结束日期");
|
|
3345
|
+
return [start, end];
|
|
3346
|
+
}
|
|
3347
|
+
function resolveMarketRankDateParams(params) {
|
|
3348
|
+
if ((typeof params.dateType === "string" ? params.dateType.trim() : "") !== "day") return resolveSycmDateParams(params, {
|
|
3349
|
+
supported: [
|
|
3350
|
+
"today",
|
|
3351
|
+
"recent7",
|
|
3352
|
+
"recent30",
|
|
3353
|
+
"day",
|
|
3354
|
+
"week",
|
|
3355
|
+
"month"
|
|
3356
|
+
],
|
|
3357
|
+
defaultType: "recent7"
|
|
3358
|
+
});
|
|
3359
|
+
const now = /* @__PURE__ */ new Date();
|
|
3360
|
+
const yesterday = new Date(now.getFullYear(), now.getMonth(), now.getDate() - 1);
|
|
3361
|
+
const dateRange = (typeof params.dateRange === "string" ? params.dateRange.trim() : "") || `${formatDate$1(yesterday)}|${formatDate$1(yesterday)}`;
|
|
3362
|
+
const [start, end] = parseDateRange$1(dateRange);
|
|
3363
|
+
if (formatDate$1(start) !== formatDate$1(end)) throw new Error("day 的 dateRange 必须是同一个完整自然日");
|
|
3364
|
+
if (end.getTime() > yesterday.getTime()) throw new Error("市场排行 day 的 dateRange 最多只能到昨天");
|
|
3365
|
+
return {
|
|
3366
|
+
dateType: "day",
|
|
3367
|
+
dateRange
|
|
3368
|
+
};
|
|
3369
|
+
}
|
|
3370
|
+
//#endregion
|
|
3371
|
+
//#region extensions/sealseek_qushu/src/collectors/sycm/market-rank/price-band.ts
|
|
3372
|
+
const MARKET_RANK_PRICE_BAND_TYPES = [
|
|
3373
|
+
"gmv",
|
|
3374
|
+
"growth",
|
|
3375
|
+
"flow",
|
|
3376
|
+
"newitm_ipv"
|
|
3377
|
+
];
|
|
3378
|
+
function resolveMarketRankPriceBand(rankType, value) {
|
|
3379
|
+
if (value === void 0 || value === null) return {
|
|
3380
|
+
minPrice: "",
|
|
3381
|
+
maxPrice: ""
|
|
3382
|
+
};
|
|
3383
|
+
if (typeof value !== "string") throw new Error("价格带须为整数区间(如40-60),请重新输入范围。");
|
|
3384
|
+
const input = value.trim();
|
|
3385
|
+
if (!input || input === "all") return {
|
|
3386
|
+
minPrice: "",
|
|
3387
|
+
maxPrice: ""
|
|
3388
|
+
};
|
|
3389
|
+
if (!MARKET_RANK_PRICE_BAND_TYPES.includes(String(rankType))) throw new Error("当前榜单不支持自定义价格带。");
|
|
3390
|
+
const match = /^([0-9]+)\s*-\s*([0-9]+)$/.exec(input);
|
|
3391
|
+
if (!match) throw new Error("价格带须为整数区间(如40-60),请重新输入范围。");
|
|
3392
|
+
const minPrice = Number(match[1]);
|
|
3393
|
+
const maxPrice = Number(match[2]);
|
|
3394
|
+
for (const [label, price] of [["下界", minPrice], ["上界", maxPrice]]) {
|
|
3395
|
+
if (!Number.isInteger(price) || price < 0 || price > 1e5) throw new Error(`价格带${label}须为0~100000之间的整数,请重新输入范围。`);
|
|
3396
|
+
const step = price <= 100 ? 10 : price <= 500 ? 50 : price <= 1e3 ? 100 : price <= 1e4 ? 1e3 : 1e4;
|
|
3397
|
+
if (price % step !== 0) throw new Error(`价格带${label}${price}须为${step}的倍数,请重新输入范围。`);
|
|
3398
|
+
}
|
|
3399
|
+
if (minPrice >= maxPrice) throw new Error("价格带下界须小于上界,请重新输入范围。");
|
|
3400
|
+
return {
|
|
3401
|
+
minPrice: String(minPrice),
|
|
3402
|
+
maxPrice: String(maxPrice)
|
|
3403
|
+
};
|
|
3404
|
+
}
|
|
3405
|
+
//#endregion
|
|
3318
3406
|
//#region extensions/sealseek_qushu/src/collectors/sycm/market-rank/transform.ts
|
|
3319
3407
|
function buildMarketRankTransformSource() {
|
|
3320
3408
|
return String.raw`
|
|
@@ -3371,7 +3459,12 @@ function buildMarketRankTransformSource() {
|
|
|
3371
3459
|
if (context.rankType === "gmv" || context.rankType === "growth") {
|
|
3372
3460
|
row.payByrCnt = read(record, "payByrCnt");
|
|
3373
3461
|
row.uv = read(record, "uv");
|
|
3374
|
-
|
|
3462
|
+
const coreKeyWord = record && record.coreKeyWord;
|
|
3463
|
+
row.coreKeyWord = decodeHtmlEntities(
|
|
3464
|
+
isRecord(coreKeyWord) && Object.prototype.hasOwnProperty.call(coreKeyWord, "value")
|
|
3465
|
+
? coreKeyWord.value ?? ""
|
|
3466
|
+
: ""
|
|
3467
|
+
);
|
|
3375
3468
|
} else if (context.rankType === "flow") {
|
|
3376
3469
|
row.uv = read(record, "uv");
|
|
3377
3470
|
row.searchUv = read(record, "searchUv");
|
|
@@ -3414,10 +3507,11 @@ function buildMarketRankTransformSource() {
|
|
|
3414
3507
|
function buildMarketRankScript(context) {
|
|
3415
3508
|
return `(async () => {
|
|
3416
3509
|
const context = ${JSON.stringify(context)};
|
|
3510
|
+
const pageSize = context.pageSize ?? 10;
|
|
3417
3511
|
const resultContract = "guanshu.collect.v1";
|
|
3418
3512
|
${buildPaginationSource(context.pages)}
|
|
3419
3513
|
${buildMarketRankTransformSource()}
|
|
3420
|
-
${buildCategoryResolveSource()}
|
|
3514
|
+
${buildCategoryResolveSource$1()}
|
|
3421
3515
|
const errorResult = (code, message, retryable = false) => ({
|
|
3422
3516
|
ok: false,
|
|
3423
3517
|
action: "sycm_market_rank",
|
|
@@ -3474,14 +3568,14 @@ function buildMarketRankScript(context) {
|
|
|
3474
3568
|
const url = new URL(context.endpoint, location.origin);
|
|
3475
3569
|
url.searchParams.set("dateRange", context.dateRange);
|
|
3476
3570
|
url.searchParams.set("dateType", context.dateType);
|
|
3477
|
-
url.searchParams.set("pageSize",
|
|
3571
|
+
url.searchParams.set("pageSize", String(pageSize));
|
|
3478
3572
|
url.searchParams.set("page", String(page));
|
|
3479
3573
|
if (context.order !== undefined) url.searchParams.set("order", context.order);
|
|
3480
3574
|
if (context.orderBy !== undefined) url.searchParams.set("orderBy", context.orderBy);
|
|
3481
3575
|
url.searchParams.set("cateId", cateId);
|
|
3482
3576
|
url.searchParams.set("rankType", context.rankType);
|
|
3483
|
-
url.searchParams.set("minPrice",
|
|
3484
|
-
url.searchParams.set("maxPrice",
|
|
3577
|
+
url.searchParams.set("minPrice", context.minPrice);
|
|
3578
|
+
url.searchParams.set("maxPrice", context.maxPrice);
|
|
3485
3579
|
url.searchParams.set("priceSeg", "");
|
|
3486
3580
|
url.searchParams.set("sellerType", context.sellerType);
|
|
3487
3581
|
url.searchParams.set("keyWord", context.keyword || "");
|
|
@@ -3505,7 +3599,7 @@ function buildMarketRankScript(context) {
|
|
|
3505
3599
|
};
|
|
3506
3600
|
const firstPayload = await fetchPage(1);
|
|
3507
3601
|
const firstRows = extractRows(firstPayload);
|
|
3508
|
-
const totalPages = getPageLimit(Math.ceil(extractRecordCount(firstPayload) /
|
|
3602
|
+
const totalPages = getPageLimit(Math.ceil(extractRecordCount(firstPayload) / pageSize));
|
|
3509
3603
|
const mappingContext = { ...context, categoryName };
|
|
3510
3604
|
const rows = firstRows.map((row) => mapMarketRankRow(row, mappingContext));
|
|
3511
3605
|
const responses = [{ page: 1, rowCount: firstRows.length }];
|
|
@@ -3525,54 +3619,89 @@ function buildMarketRankScript(context) {
|
|
|
3525
3619
|
}
|
|
3526
3620
|
//#endregion
|
|
3527
3621
|
//#region extensions/sealseek_qushu/src/collectors/sycm/market-rank/index.ts
|
|
3622
|
+
const MARKET_RANK_URL = "https://sycm.taobao.com/mc/free/market_rank";
|
|
3528
3623
|
function buildMarketRankCalls(params) {
|
|
3624
|
+
const priceBand = resolveMarketRankPriceBand(params.rankType, params.priceBand);
|
|
3529
3625
|
const tabId = normalizeTabId(params.tabId);
|
|
3530
3626
|
const rankType = params.rankType;
|
|
3531
3627
|
const sellerType = String(params.sellerType);
|
|
3532
3628
|
const sortMetric = rankType === "recommend_flow" ? params.sortMetric : "deepViewCtr";
|
|
3533
|
-
const date =
|
|
3534
|
-
supported: [
|
|
3535
|
-
"today",
|
|
3536
|
-
"recent7",
|
|
3537
|
-
"recent30",
|
|
3538
|
-
"day",
|
|
3539
|
-
"week",
|
|
3540
|
-
"month"
|
|
3541
|
-
],
|
|
3542
|
-
defaultType: "recent7"
|
|
3543
|
-
});
|
|
3629
|
+
const date = resolveMarketRankDateParams(params);
|
|
3544
3630
|
const request = resolveMarketRankRequest(rankType, date.dateType, sortMetric);
|
|
3545
3631
|
const categoryId = typeof params.categoryId === "string" ? params.categoryId.trim() : "";
|
|
3546
|
-
if (categoryId && !/^[0-9]+$/.test(categoryId)) return
|
|
3547
|
-
|
|
3548
|
-
|
|
3549
|
-
|
|
3550
|
-
|
|
3551
|
-
|
|
3632
|
+
if (categoryId && !/^[0-9]+$/.test(categoryId)) return [
|
|
3633
|
+
{
|
|
3634
|
+
tool: "workspace_browser",
|
|
3635
|
+
arguments: {
|
|
3636
|
+
action: "navigate",
|
|
3637
|
+
...tabId ? { tabId } : {},
|
|
3638
|
+
url: MARKET_RANK_URL
|
|
3639
|
+
}
|
|
3640
|
+
},
|
|
3641
|
+
{
|
|
3642
|
+
tool: "workspace_browser",
|
|
3643
|
+
arguments: {
|
|
3644
|
+
action: "wait_for",
|
|
3645
|
+
...tabId ? { tabId } : {},
|
|
3646
|
+
mode: "selector",
|
|
3647
|
+
selector: ".common-picker-header",
|
|
3648
|
+
state: "visible",
|
|
3649
|
+
timeoutMs: 3e4
|
|
3650
|
+
}
|
|
3651
|
+
},
|
|
3652
|
+
{
|
|
3653
|
+
tool: "workspace_browser",
|
|
3654
|
+
arguments: {
|
|
3655
|
+
action: "evaluate",
|
|
3656
|
+
...tabId ? { tabId } : {},
|
|
3657
|
+
code: buildMarketRankCategoryResolveScript(categoryId)
|
|
3658
|
+
}
|
|
3552
3659
|
}
|
|
3553
|
-
|
|
3554
|
-
return
|
|
3555
|
-
|
|
3556
|
-
|
|
3557
|
-
|
|
3558
|
-
|
|
3559
|
-
|
|
3560
|
-
|
|
3561
|
-
|
|
3562
|
-
|
|
3563
|
-
|
|
3564
|
-
|
|
3565
|
-
|
|
3566
|
-
|
|
3567
|
-
|
|
3568
|
-
|
|
3569
|
-
|
|
3660
|
+
];
|
|
3661
|
+
return [
|
|
3662
|
+
{
|
|
3663
|
+
tool: "workspace_browser",
|
|
3664
|
+
arguments: {
|
|
3665
|
+
action: "navigate",
|
|
3666
|
+
...tabId ? { tabId } : {},
|
|
3667
|
+
url: MARKET_RANK_URL
|
|
3668
|
+
}
|
|
3669
|
+
},
|
|
3670
|
+
{
|
|
3671
|
+
tool: "workspace_browser",
|
|
3672
|
+
arguments: {
|
|
3673
|
+
action: "wait_for",
|
|
3674
|
+
...tabId ? { tabId } : {},
|
|
3675
|
+
mode: "selector",
|
|
3676
|
+
selector: ".common-picker-header",
|
|
3677
|
+
state: "visible",
|
|
3678
|
+
timeoutMs: 3e4
|
|
3679
|
+
}
|
|
3680
|
+
},
|
|
3681
|
+
{
|
|
3682
|
+
tool: "workspace_browser",
|
|
3683
|
+
arguments: {
|
|
3684
|
+
action: "evaluate",
|
|
3685
|
+
...tabId ? { tabId } : {},
|
|
3686
|
+
code: buildMarketRankScript({
|
|
3687
|
+
rankType,
|
|
3688
|
+
sellerType,
|
|
3689
|
+
sortMetric,
|
|
3690
|
+
...request,
|
|
3691
|
+
...priceBand,
|
|
3692
|
+
dateType: date.dateType,
|
|
3693
|
+
dateRange: date.dateRange,
|
|
3694
|
+
categoryId: categoryId || void 0,
|
|
3695
|
+
keyword: typeof params.keyword === "string" ? params.keyword : void 0,
|
|
3696
|
+
pages: normalizeRequestedPages(params.pages)
|
|
3697
|
+
})
|
|
3698
|
+
}
|
|
3570
3699
|
}
|
|
3571
|
-
|
|
3700
|
+
];
|
|
3572
3701
|
}
|
|
3573
3702
|
//#endregion
|
|
3574
3703
|
//#region extensions/sealseek_qushu/src/capabilities/platforms/sycm/market-rank/headers.ts
|
|
3575
|
-
const COMMON_HEADERS = {
|
|
3704
|
+
const COMMON_HEADERS$1 = {
|
|
3576
3705
|
日期: "date",
|
|
3577
3706
|
行业排名: "rank",
|
|
3578
3707
|
趋势: "trend",
|
|
@@ -3583,7 +3712,7 @@ const COMMON_HEADERS = {
|
|
|
3583
3712
|
店铺名称: "shopName",
|
|
3584
3713
|
店铺类型: "shopType"
|
|
3585
3714
|
};
|
|
3586
|
-
const METRIC_HEADERS = {
|
|
3715
|
+
const METRIC_HEADERS$1 = {
|
|
3587
3716
|
gmv: {
|
|
3588
3717
|
支付买家数: "payByrCnt",
|
|
3589
3718
|
访客数: "uv",
|
|
@@ -3620,22 +3749,22 @@ const METRIC_HEADERS = {
|
|
|
3620
3749
|
clickExpRateNew: { 猜你喜欢曝光点击率: "clickExpRateNew" }
|
|
3621
3750
|
}
|
|
3622
3751
|
};
|
|
3623
|
-
const MARKET_RANK_HEADERS = COMMON_HEADERS;
|
|
3752
|
+
const MARKET_RANK_HEADERS = COMMON_HEADERS$1;
|
|
3624
3753
|
function resolveMarketRankHeaders(params) {
|
|
3625
3754
|
const rankType = String(params.rankType);
|
|
3626
3755
|
if (rankType === "recommend_flow") {
|
|
3627
3756
|
const sortMetric = String(params.sortMetric);
|
|
3628
|
-
const metrics = METRIC_HEADERS.recommend_flow[sortMetric];
|
|
3757
|
+
const metrics = METRIC_HEADERS$1.recommend_flow[sortMetric];
|
|
3629
3758
|
return metrics ? {
|
|
3630
|
-
...COMMON_HEADERS,
|
|
3759
|
+
...COMMON_HEADERS$1,
|
|
3631
3760
|
...metrics
|
|
3632
|
-
} : COMMON_HEADERS;
|
|
3761
|
+
} : COMMON_HEADERS$1;
|
|
3633
3762
|
}
|
|
3634
|
-
const metrics = METRIC_HEADERS[rankType];
|
|
3763
|
+
const metrics = METRIC_HEADERS$1[rankType];
|
|
3635
3764
|
return metrics ? {
|
|
3636
|
-
...COMMON_HEADERS,
|
|
3765
|
+
...COMMON_HEADERS$1,
|
|
3637
3766
|
...metrics
|
|
3638
|
-
} : COMMON_HEADERS;
|
|
3767
|
+
} : COMMON_HEADERS$1;
|
|
3639
3768
|
}
|
|
3640
3769
|
//#endregion
|
|
3641
3770
|
//#region extensions/sealseek_qushu/src/capabilities/platforms/sycm/market-rank/params.ts
|
|
@@ -3666,7 +3795,7 @@ const MARKET_RANK_PARAMETERS = Type.Object({
|
|
|
3666
3795
|
Type.Literal("1")
|
|
3667
3796
|
])),
|
|
3668
3797
|
sortMetric: Type.Optional(Type.Union([Type.Literal("deepViewCtr"), Type.Literal("clickExpRateNew")])),
|
|
3669
|
-
priceBand: Type.Optional(Type.
|
|
3798
|
+
priceBand: Type.Optional(Type.String()),
|
|
3670
3799
|
keyword: Type.Optional(Type.String()),
|
|
3671
3800
|
pages: Type.Optional(Type.Integer({ minimum: 1 }))
|
|
3672
3801
|
}, { additionalProperties: false });
|
|
@@ -3707,7 +3836,7 @@ const RANK_TYPE_FIELD = {
|
|
|
3707
3836
|
}
|
|
3708
3837
|
]
|
|
3709
3838
|
};
|
|
3710
|
-
const DATE_RANGE_FIELD = {
|
|
3839
|
+
const DATE_RANGE_FIELD$1 = {
|
|
3711
3840
|
id: "date_range",
|
|
3712
3841
|
paramKey: "dateRange",
|
|
3713
3842
|
type: "text",
|
|
@@ -3715,13 +3844,13 @@ const DATE_RANGE_FIELD = {
|
|
|
3715
3844
|
required: false,
|
|
3716
3845
|
placeholder: "可选,如昨天、上周、8月"
|
|
3717
3846
|
};
|
|
3718
|
-
const CATEGORY_FIELD = {
|
|
3847
|
+
const CATEGORY_FIELD$1 = {
|
|
3719
3848
|
id: "category",
|
|
3720
3849
|
paramKey: "categoryId",
|
|
3721
3850
|
type: "text",
|
|
3722
3851
|
label: "类目",
|
|
3723
3852
|
required: false,
|
|
3724
|
-
placeholder: "可选,类目名称或类目ID
|
|
3853
|
+
placeholder: "可选,类目名称或类目ID,默认使用当前页面类目"
|
|
3725
3854
|
};
|
|
3726
3855
|
const SELLER_TYPE_FIELD = {
|
|
3727
3856
|
id: "seller_type",
|
|
@@ -3748,14 +3877,10 @@ const SELLER_TYPE_FIELD = {
|
|
|
3748
3877
|
const PRICE_BAND_FIELD = {
|
|
3749
3878
|
id: "price_band",
|
|
3750
3879
|
paramKey: "priceBand",
|
|
3751
|
-
type: "
|
|
3880
|
+
type: "text",
|
|
3752
3881
|
label: "价格带",
|
|
3753
|
-
required:
|
|
3754
|
-
|
|
3755
|
-
label: "不限",
|
|
3756
|
-
value: "all"
|
|
3757
|
-
}],
|
|
3758
|
-
defaultValue: "all"
|
|
3882
|
+
required: false,
|
|
3883
|
+
placeholder: "可选,如90-500,默认不限"
|
|
3759
3884
|
};
|
|
3760
3885
|
const SORT_METRIC_FIELD = {
|
|
3761
3886
|
id: "sort_metric",
|
|
@@ -3772,22 +3897,18 @@ const SORT_METRIC_FIELD = {
|
|
|
3772
3897
|
}]
|
|
3773
3898
|
};
|
|
3774
3899
|
const MARKET_RANK_REQUIRED_INPUTS = [RANK_TYPE_FIELD];
|
|
3775
|
-
function isMissing(value) {
|
|
3900
|
+
function isMissing$1(value) {
|
|
3776
3901
|
return value === void 0 || value === null || value === "";
|
|
3777
3902
|
}
|
|
3778
3903
|
function resolveMarketRankRequiredInputs(params) {
|
|
3779
|
-
if (isMissing(params.rankType)) return MARKET_RANK_REQUIRED_INPUTS;
|
|
3904
|
+
if (isMissing$1(params.rankType)) return MARKET_RANK_REQUIRED_INPUTS;
|
|
3905
|
+
resolveMarketRankPriceBand(params.rankType, params.priceBand);
|
|
3780
3906
|
const fields = [];
|
|
3781
|
-
if (isMissing(params.sellerType)) fields.push(SELLER_TYPE_FIELD);
|
|
3782
|
-
if (
|
|
3783
|
-
|
|
3784
|
-
|
|
3785
|
-
|
|
3786
|
-
"newitm_ipv"
|
|
3787
|
-
].includes(String(params.rankType)) && isMissing(params.priceBand)) fields.push(PRICE_BAND_FIELD);
|
|
3788
|
-
if (params.rankType === "recommend_flow" && isMissing(params.sortMetric)) fields.push(SORT_METRIC_FIELD);
|
|
3789
|
-
if (fields.length > 0) return fields;
|
|
3790
|
-
if (isMissing(params.dateType)) {
|
|
3907
|
+
if (isMissing$1(params.sellerType)) fields.push(SELLER_TYPE_FIELD);
|
|
3908
|
+
if (MARKET_RANK_PRICE_BAND_TYPES.includes(String(params.rankType)) && isMissing$1(params.priceBand)) fields.push(PRICE_BAND_FIELD);
|
|
3909
|
+
if (params.rankType === "recommend_flow" && isMissing$1(params.sortMetric)) fields.push(SORT_METRIC_FIELD);
|
|
3910
|
+
if (fields.some((field) => field.required)) return fields;
|
|
3911
|
+
if (isMissing$1(params.dateType)) {
|
|
3791
3912
|
const dateTypeField = {
|
|
3792
3913
|
id: "period",
|
|
3793
3914
|
paramKey: "dateType",
|
|
@@ -3847,12 +3968,17 @@ function resolveMarketRankRequiredInputs(params) {
|
|
|
3847
3968
|
}
|
|
3848
3969
|
]
|
|
3849
3970
|
};
|
|
3850
|
-
if (isMissing(params.dateRange)) return [
|
|
3971
|
+
if (isMissing$1(params.dateRange)) return [
|
|
3851
3972
|
dateTypeField,
|
|
3852
|
-
|
|
3853
|
-
|
|
3973
|
+
...fields,
|
|
3974
|
+
DATE_RANGE_FIELD$1,
|
|
3975
|
+
CATEGORY_FIELD$1
|
|
3976
|
+
];
|
|
3977
|
+
return [
|
|
3978
|
+
dateTypeField,
|
|
3979
|
+
...fields,
|
|
3980
|
+
CATEGORY_FIELD$1
|
|
3854
3981
|
];
|
|
3855
|
-
return [dateTypeField, CATEGORY_FIELD];
|
|
3856
3982
|
}
|
|
3857
3983
|
return fields;
|
|
3858
3984
|
}
|
|
@@ -3868,7 +3994,7 @@ const SYCM_MARKET_RANK = {
|
|
|
3868
3994
|
"行业排行",
|
|
3869
3995
|
"类目排行"
|
|
3870
3996
|
],
|
|
3871
|
-
description: "
|
|
3997
|
+
description: "按商品榜单类型、店铺类型采集生意参谋市场排行商品数据;价格带可选,不填不限;填写区间后由脚本校验,不通过则让用户重新输入,不采集;categoryId 可选,可传类目名称或类目ID,不传用当前页面类目;传类目名称时本次调用仅返回类目解析结果:唯一精确命中(found)是终态判定,直接带 cateId 作为 categoryId 重新调用采集,不打断用户;仅同名多个(ambiguous)或未命中(not_found)才与用户确认;解析调用无需 finalize;pages 可选,默认最多5页,用户明确说页数时必须传入。",
|
|
3872
3998
|
pageContext: "生意参谋市场排行商品页",
|
|
3873
3999
|
parameters: MARKET_RANK_PARAMETERS,
|
|
3874
4000
|
headers: MARKET_RANK_HEADERS,
|
|
@@ -6373,50 +6499,618 @@ const SYCM_PRODUCT_RANK = {
|
|
|
6373
6499
|
collector: buildProductRankCalls
|
|
6374
6500
|
};
|
|
6375
6501
|
//#endregion
|
|
6376
|
-
//#region extensions/sealseek_qushu/src/
|
|
6377
|
-
|
|
6378
|
-
|
|
6379
|
-
|
|
6380
|
-
|
|
6381
|
-
|
|
6382
|
-
|
|
6383
|
-
|
|
6384
|
-
|
|
6385
|
-
|
|
6386
|
-
|
|
6387
|
-
|
|
6388
|
-
|
|
6389
|
-
|
|
6390
|
-
|
|
6391
|
-
|
|
6392
|
-
|
|
6393
|
-
|
|
6394
|
-
|
|
6395
|
-
|
|
6396
|
-
|
|
6397
|
-
|
|
6398
|
-
|
|
6399
|
-
|
|
6400
|
-
|
|
6401
|
-
|
|
6402
|
-
|
|
6403
|
-
|
|
6404
|
-
|
|
6405
|
-
|
|
6406
|
-
|
|
6407
|
-
|
|
6408
|
-
|
|
6409
|
-
|
|
6410
|
-
|
|
6411
|
-
|
|
6412
|
-
|
|
6413
|
-
|
|
6414
|
-
|
|
6415
|
-
|
|
6416
|
-
|
|
6417
|
-
|
|
6418
|
-
|
|
6419
|
-
|
|
6502
|
+
//#region extensions/sealseek_qushu/src/collectors/sycm/search-rank/category.ts
|
|
6503
|
+
/**
|
|
6504
|
+
* 搜索排行类目解析,分两段执行(先拿列表返回值,再用用户输入查):
|
|
6505
|
+
* 1. buildSearchRankCategoryResolveScript:用户传类目名称时先返回的独立解析脚本,
|
|
6506
|
+
* 在页面上下文取类目列表,用名称作筛选条件反查,结果回给模型与用户确认;
|
|
6507
|
+
* 2. buildCategoryResolveSource:拼进采集脚本的片段,按已确定的类目ID反查类目名称。
|
|
6508
|
+
* 类目树接口 /mc/common/free/getCateInfo.json 返回行结构:
|
|
6509
|
+
* [parentCateId, cateId, 类目名, 采集cateFlag, marketVersion, 叶子标志, 根类目id, 根类目名]
|
|
6510
|
+
* 搜索排行请求只携带 cateId,不携带 cateFlag 或 parentCateId。
|
|
6511
|
+
*/
|
|
6512
|
+
const MAX_CATEGORY_SUGGESTIONS = 5;
|
|
6513
|
+
function buildCategoryResolveSource() {
|
|
6514
|
+
return `
|
|
6515
|
+
const fetchCategoryTree = async (token) => {
|
|
6516
|
+
const url = new URL("/mc/common/free/getCateInfo.json", location.origin);
|
|
6517
|
+
url.searchParams.set("marketVersion", "free");
|
|
6518
|
+
url.searchParams.set("_", String(Date.now()));
|
|
6519
|
+
url.searchParams.set("token", token);
|
|
6520
|
+
const response = await fetch(url.toString(), { credentials: "include", headers: { accept: "application/json, text/plain, */*", "x-requested-with": "XMLHttpRequest" } });
|
|
6521
|
+
if (!response.ok) throw new Error("类目接口请求失败:" + response.status);
|
|
6522
|
+
const payload = await response.json();
|
|
6523
|
+
if (!payload || !Array.isArray(payload.data)) throw new Error("类目接口返回异常");
|
|
6524
|
+
return payload.data;
|
|
6525
|
+
};
|
|
6526
|
+
const buildCategoryPath = (tree, row) => {
|
|
6527
|
+
const byId = new Map(tree.map((item) => [String(item[1]), item]));
|
|
6528
|
+
const parts = [row[2]];
|
|
6529
|
+
const visited = new Set([String(row[1])]);
|
|
6530
|
+
let current = row;
|
|
6531
|
+
while (true) {
|
|
6532
|
+
const parent = byId.get(String(current[0]));
|
|
6533
|
+
if (!parent || visited.has(String(parent[1]))) break;
|
|
6534
|
+
visited.add(String(parent[1]));
|
|
6535
|
+
parts.unshift(parent[2]);
|
|
6536
|
+
current = parent;
|
|
6537
|
+
}
|
|
6538
|
+
return parts.join(" > ");
|
|
6539
|
+
};
|
|
6540
|
+
const isEditDistanceAtMostOne = (a, b) => {
|
|
6541
|
+
const lengthDiff = a.length - b.length;
|
|
6542
|
+
if (Math.abs(lengthDiff) > 1) return false;
|
|
6543
|
+
if (lengthDiff === 0) {
|
|
6544
|
+
let diffCount = 0;
|
|
6545
|
+
for (let i = 0; i < a.length; i += 1) {
|
|
6546
|
+
if (a[i] !== b[i]) {
|
|
6547
|
+
diffCount += 1;
|
|
6548
|
+
if (diffCount > 1) return false;
|
|
6549
|
+
}
|
|
6550
|
+
}
|
|
6551
|
+
return true;
|
|
6552
|
+
}
|
|
6553
|
+
const shorter = lengthDiff < 0 ? a : b;
|
|
6554
|
+
const longer = lengthDiff < 0 ? b : a;
|
|
6555
|
+
let shortIndex = 0;
|
|
6556
|
+
let longIndex = 0;
|
|
6557
|
+
let skipped = false;
|
|
6558
|
+
while (shortIndex < shorter.length && longIndex < longer.length) {
|
|
6559
|
+
if (shorter[shortIndex] === longer[longIndex]) {
|
|
6560
|
+
shortIndex += 1;
|
|
6561
|
+
longIndex += 1;
|
|
6562
|
+
} else {
|
|
6563
|
+
if (skipped) return false;
|
|
6564
|
+
skipped = true;
|
|
6565
|
+
longIndex += 1;
|
|
6566
|
+
}
|
|
6567
|
+
}
|
|
6568
|
+
return true;
|
|
6569
|
+
};
|
|
6570
|
+
const findCategorySuggestions = (tree, name) => {
|
|
6571
|
+
const suggestions = [];
|
|
6572
|
+
const seen = new Set();
|
|
6573
|
+
for (const item of tree) {
|
|
6574
|
+
const candidateName = item[2];
|
|
6575
|
+
if (candidateName === name) continue;
|
|
6576
|
+
const isSubstring = name.length >= 2 && (candidateName.includes(name) || name.includes(candidateName));
|
|
6577
|
+
if (!isSubstring && !isEditDistanceAtMostOne(candidateName, name)) continue;
|
|
6578
|
+
const key = String(item[1]);
|
|
6579
|
+
if (seen.has(key)) continue;
|
|
6580
|
+
seen.add(key);
|
|
6581
|
+
suggestions.push(item);
|
|
6582
|
+
if (suggestions.length >= ${MAX_CATEGORY_SUGGESTIONS}) break;
|
|
6583
|
+
}
|
|
6584
|
+
return suggestions;
|
|
6585
|
+
};
|
|
6586
|
+
const resolveCategoryFromTree = (tree, input) => {
|
|
6587
|
+
const trimmed = String(input).trim();
|
|
6588
|
+
const isIdInput = /^[0-9]+$/.test(trimmed);
|
|
6589
|
+
const exactById = isIdInput ? tree.filter((item) => String(item[1]) === trimmed) : [];
|
|
6590
|
+
const exactByName = tree.filter((item) => item[2] === trimmed);
|
|
6591
|
+
const hits = exactById.length > 0 ? exactById : exactByName;
|
|
6592
|
+
if (hits.length === 1) return { status: "found", match: hits[0] };
|
|
6593
|
+
if (hits.length > 1) return { status: "ambiguous", candidates: hits };
|
|
6594
|
+
return { status: "not_found", suggestions: findCategorySuggestions(tree, trimmed) };
|
|
6595
|
+
};
|
|
6596
|
+
`;
|
|
6597
|
+
}
|
|
6598
|
+
function buildSearchRankCategoryResolveScript(input) {
|
|
6599
|
+
return `(async () => {
|
|
6600
|
+
const input = ${JSON.stringify(input)};
|
|
6601
|
+
${buildCategoryResolveSource()}
|
|
6602
|
+
const errorResult = (code, message, retryable = false) => ({
|
|
6603
|
+
ok: false,
|
|
6604
|
+
action: "sycm_search_rank_category",
|
|
6605
|
+
error: { code, message, retryable }
|
|
6606
|
+
});
|
|
6607
|
+
try {
|
|
6608
|
+
if (location.hostname !== "sycm.taobao.com" || !location.pathname.includes("/mc/free/search_rank")) {
|
|
6609
|
+
return errorResult("INVALID_PAGE", "当前页面不是生意参谋搜索排行页", false);
|
|
6610
|
+
}
|
|
6611
|
+
const resourceUrls = performance.getEntriesByType("resource").map((entry) => entry.name).filter((url) => typeof url === "string");
|
|
6612
|
+
const tokenUrl = resourceUrls.slice().reverse().find((url) => {
|
|
6613
|
+
try { return new URL(url).hostname === "sycm.taobao.com" && new URL(url).searchParams.get("token"); } catch { return false; }
|
|
6614
|
+
});
|
|
6615
|
+
let token = "";
|
|
6616
|
+
if (tokenUrl) token = new URL(tokenUrl).searchParams.get("token") || "";
|
|
6617
|
+
if (!token) {
|
|
6618
|
+
const microdata = document.querySelector('meta[name="microdata"]')?.getAttribute("content") || "";
|
|
6619
|
+
token = microdata.match(/(?:^|;)legalityToken=([^;]*)/)?.[1] || "";
|
|
6620
|
+
}
|
|
6621
|
+
if (!token) return errorResult("MISSING_TOKEN", "当前页面未取得生意参谋接口 token", true);
|
|
6622
|
+
let categoryTree;
|
|
6623
|
+
try {
|
|
6624
|
+
categoryTree = await fetchCategoryTree(token);
|
|
6625
|
+
} catch (error) {
|
|
6626
|
+
return errorResult("CATEGORY_TREE_FAILED", error instanceof Error ? error.message : "类目接口请求失败", true);
|
|
6627
|
+
}
|
|
6628
|
+
const resolved = resolveCategoryFromTree(categoryTree, input);
|
|
6629
|
+
if (resolved.status === "found") {
|
|
6630
|
+
return {
|
|
6631
|
+
ok: true,
|
|
6632
|
+
action: "sycm_search_rank_category",
|
|
6633
|
+
status: "found",
|
|
6634
|
+
cateId: String(resolved.match[1]),
|
|
6635
|
+
categoryPath: buildCategoryPath(categoryTree, resolved.match),
|
|
6636
|
+
next: "已唯一精确命中,直接带 cateId 作为 categoryId 重新调用搜索排行采集,无需向用户确认,无需 finalize"
|
|
6637
|
+
};
|
|
6638
|
+
}
|
|
6639
|
+
if (resolved.status === "ambiguous") {
|
|
6640
|
+
return {
|
|
6641
|
+
ok: true,
|
|
6642
|
+
action: "sycm_search_rank_category",
|
|
6643
|
+
status: "ambiguous",
|
|
6644
|
+
candidates: resolved.candidates.map((item) => ({ path: buildCategoryPath(categoryTree, item), cateId: String(item[1]) })),
|
|
6645
|
+
next: "存在同名类目,请与用户确认其一后带对应 cateId 重新调用,不猜测"
|
|
6646
|
+
};
|
|
6647
|
+
}
|
|
6648
|
+
if (resolved.suggestions.length > 0) {
|
|
6649
|
+
return {
|
|
6650
|
+
ok: true,
|
|
6651
|
+
action: "sycm_search_rank_category",
|
|
6652
|
+
status: "not_found",
|
|
6653
|
+
suggestions: resolved.suggestions.map((item) => ({ path: buildCategoryPath(categoryTree, item), cateId: String(item[1]) })),
|
|
6654
|
+
next: "未精确命中,请与用户确认是否为上述类目,确认后带 cateId 重新调用,否认则停止"
|
|
6655
|
+
};
|
|
6656
|
+
}
|
|
6657
|
+
return {
|
|
6658
|
+
ok: true,
|
|
6659
|
+
action: "sycm_search_rank_category",
|
|
6660
|
+
status: "not_found",
|
|
6661
|
+
suggestions: [],
|
|
6662
|
+
next: "当前账号类目中不存在该类目,请与用户确认,不猜测替代类目"
|
|
6663
|
+
};
|
|
6664
|
+
} catch (error) {
|
|
6665
|
+
return errorResult("CATEGORY_RESOLVE_FAILED", error instanceof Error ? error.message : "类目解析失败", true);
|
|
6666
|
+
}
|
|
6667
|
+
})()`;
|
|
6668
|
+
}
|
|
6669
|
+
//#endregion
|
|
6670
|
+
//#region extensions/sealseek_qushu/src/collectors/sycm/search-rank/config.ts
|
|
6671
|
+
function validateSearchRankCombination(kwType, rankType) {
|
|
6672
|
+
if (rankType === "rise" && [
|
|
6673
|
+
"trend",
|
|
6674
|
+
"core",
|
|
6675
|
+
"prop"
|
|
6676
|
+
].includes(String(kwType))) throw new Error("搜索增量仅支持搜索词类型,趋势词、核心词、修饰词不支持该榜单");
|
|
6677
|
+
}
|
|
6678
|
+
function resolveSearchRankRequest(kwType, rankType) {
|
|
6679
|
+
validateSearchRankCombination(kwType, rankType);
|
|
6680
|
+
return {
|
|
6681
|
+
endpoint: "/mc/mq/mkt/keyword/rank/pro.json",
|
|
6682
|
+
order: "desc",
|
|
6683
|
+
orderBy: {
|
|
6684
|
+
hot: "seIpvUvHits",
|
|
6685
|
+
soar: "seUvEx",
|
|
6686
|
+
rise: "seUvDelta"
|
|
6687
|
+
}[rankType]
|
|
6688
|
+
};
|
|
6689
|
+
}
|
|
6690
|
+
//#endregion
|
|
6691
|
+
//#region extensions/sealseek_qushu/src/collectors/sycm/search-rank/date-range.ts
|
|
6692
|
+
function formatDate(date) {
|
|
6693
|
+
return `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, "0")}-${String(date.getDate()).padStart(2, "0")}`;
|
|
6694
|
+
}
|
|
6695
|
+
function parseDate(value) {
|
|
6696
|
+
const matched = /^(\d{4})-(\d{2})-(\d{2})$/.exec(value);
|
|
6697
|
+
if (!matched) throw new Error("dateRange 必须使用 YYYY-MM-DD|YYYY-MM-DD 格式");
|
|
6698
|
+
const year = Number(matched[1]);
|
|
6699
|
+
const month = Number(matched[2]);
|
|
6700
|
+
const day = Number(matched[3]);
|
|
6701
|
+
const date = new Date(year, month - 1, day);
|
|
6702
|
+
if (date.getFullYear() !== year || date.getMonth() !== month - 1 || date.getDate() !== day) throw new Error(`dateRange 包含无效日期“${value}”`);
|
|
6703
|
+
return date;
|
|
6704
|
+
}
|
|
6705
|
+
function parseDateRange(value) {
|
|
6706
|
+
const parts = value.split("|");
|
|
6707
|
+
if (parts.length !== 2) throw new Error("dateRange 必须使用 YYYY-MM-DD|YYYY-MM-DD 格式");
|
|
6708
|
+
const start = parseDate(parts[0] ?? "");
|
|
6709
|
+
const end = parseDate(parts[1] ?? "");
|
|
6710
|
+
if (start.getTime() > end.getTime()) throw new Error("dateRange 开始日期不能晚于结束日期");
|
|
6711
|
+
return [start, end];
|
|
6712
|
+
}
|
|
6713
|
+
function resolveSearchRankDateParams(params) {
|
|
6714
|
+
if ((typeof params.dateType === "string" ? params.dateType.trim() : "") !== "day") return resolveSycmDateParams(params, {
|
|
6715
|
+
supported: [
|
|
6716
|
+
"day",
|
|
6717
|
+
"recent7",
|
|
6718
|
+
"recent30"
|
|
6719
|
+
],
|
|
6720
|
+
defaultType: "recent7"
|
|
6721
|
+
});
|
|
6722
|
+
const now = /* @__PURE__ */ new Date();
|
|
6723
|
+
const yesterday = new Date(now.getFullYear(), now.getMonth(), now.getDate() - 1);
|
|
6724
|
+
const dateRange = (typeof params.dateRange === "string" ? params.dateRange.trim() : "") || `${formatDate(yesterday)}|${formatDate(yesterday)}`;
|
|
6725
|
+
const [start, end] = parseDateRange(dateRange);
|
|
6726
|
+
if (formatDate(start) !== formatDate(end)) throw new Error("day 的 dateRange 必须是同一个完整自然日");
|
|
6727
|
+
if (end.getTime() > yesterday.getTime()) throw new Error("搜索排行 day 的 dateRange 最多只能到昨天");
|
|
6728
|
+
return {
|
|
6729
|
+
dateType: "day",
|
|
6730
|
+
dateRange
|
|
6731
|
+
};
|
|
6732
|
+
}
|
|
6733
|
+
//#endregion
|
|
6734
|
+
//#region extensions/sealseek_qushu/src/collectors/sycm/search-rank/transform.ts
|
|
6735
|
+
function buildSearchRankTransformSource() {
|
|
6736
|
+
return String.raw`
|
|
6737
|
+
const isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
6738
|
+
const unwrap = (value) => isRecord(value) && Object.prototype.hasOwnProperty.call(value, "value") ? value.value : value;
|
|
6739
|
+
const read = (record, key) => unwrap(record && record[key]);
|
|
6740
|
+
const HTML_ENTITIES = {
|
|
6741
|
+
'"': '"', '&': '&', '<': '<', '>': '>', ''': "'", ' ': ' ',
|
|
6742
|
+
'“': '"', '”': '"', '‘': '\u2018', '’': '\u2019', '‚': '\u201A', '„': '\u201E',
|
|
6743
|
+
'«': '\u00AB', '»': '\u00BB', '"': '"', ''': "'", ' ': ' ',
|
|
6744
|
+
'…': '\u2026', '—': '\u2014', '–': '\u2013', '™': '\u2122',
|
|
6745
|
+
'©': '\u00A9', '®': '\u00AE', '·': '\u00B7', '•': '\u2022',
|
|
6746
|
+
'°': '\u00B0', '±': '\u00B1', '×': '\u00D7', '÷': '\u00F7'
|
|
6747
|
+
};
|
|
6748
|
+
const decodeCodePoint = (entity, code, radix) => {
|
|
6749
|
+
const point = Number.parseInt(code, radix);
|
|
6750
|
+
if (!Number.isFinite(point) || point < 0 || point > 0x10ffff) return entity;
|
|
6751
|
+
try { return String.fromCodePoint(point); } catch { return entity; }
|
|
6752
|
+
};
|
|
6753
|
+
const decodeHtmlEntities = (text) => {
|
|
6754
|
+
if (!text) return '';
|
|
6755
|
+
let result = String(text);
|
|
6756
|
+
for (const [entity, char] of Object.entries(HTML_ENTITIES)) {
|
|
6757
|
+
result = result.split(entity).join(char);
|
|
6758
|
+
}
|
|
6759
|
+
result = result.replace(/&#(\d+);/g, (entity, code) => decodeCodePoint(entity, code, 10));
|
|
6760
|
+
result = result.replace(/&#x([0-9A-Fa-f]+);/g, (entity, code) => decodeCodePoint(entity, code, 16));
|
|
6761
|
+
return result;
|
|
6762
|
+
};
|
|
6763
|
+
// CP:保留市场排行 unwrap/read/HTML 解码;行结构替换为搜索排行指标。
|
|
6764
|
+
const mapSearchRankRow = (record, context) => {
|
|
6765
|
+
const row = {
|
|
6766
|
+
date: context.dateRange,
|
|
6767
|
+
rank: read(record, "rn"),
|
|
6768
|
+
categoryName: context.categoryName,
|
|
6769
|
+
keyword: decodeHtmlEntities(read(record, "searchWord")),
|
|
6770
|
+
keywordType: context.kwType
|
|
6771
|
+
};
|
|
6772
|
+
if (context.rankType === "hot") {
|
|
6773
|
+
row.seIpvUvHits = read(record, "seIpvUvHits");
|
|
6774
|
+
} else if (context.rankType === "soar") {
|
|
6775
|
+
row.seUvEx = read(record, "seUvEx");
|
|
6776
|
+
row.seIpvUvHits = read(record, "seIpvUvHits");
|
|
6777
|
+
} else if (context.rankType === "rise") {
|
|
6778
|
+
// seUvDelta 为已监听的排序字段,非空增量响应待用户真实验收。
|
|
6779
|
+
row.seUvDelta = read(record, "seUvDelta");
|
|
6780
|
+
}
|
|
6781
|
+
row.clickThroughRate = read(record, "clickThroughRate");
|
|
6782
|
+
row.payRate = read(record, "payRate");
|
|
6783
|
+
return row;
|
|
6784
|
+
};
|
|
6785
|
+
const extractRows = (payload) => {
|
|
6786
|
+
const data = isRecord(payload && payload.data) ? payload.data : payload;
|
|
6787
|
+
if (Array.isArray(data)) return data;
|
|
6788
|
+
if (isRecord(data) && Array.isArray(data.data)) return data.data;
|
|
6789
|
+
if (isRecord(data) && Array.isArray(data.items)) return data.items;
|
|
6790
|
+
return [];
|
|
6791
|
+
};
|
|
6792
|
+
const extractRecordCount = (payload) => {
|
|
6793
|
+
const data = isRecord(payload && payload.data) ? payload.data : payload;
|
|
6794
|
+
return Number(data && data.recordCount) || Number(data && data.total) || 0;
|
|
6795
|
+
};
|
|
6796
|
+
`;
|
|
6797
|
+
}
|
|
6798
|
+
//#endregion
|
|
6799
|
+
//#region extensions/sealseek_qushu/src/collectors/sycm/search-rank/script.ts
|
|
6800
|
+
function buildSearchRankScript(context) {
|
|
6801
|
+
return `(async () => {
|
|
6802
|
+
const context = ${JSON.stringify(context)};
|
|
6803
|
+
const resultContract = "guanshu.collect.v1";
|
|
6804
|
+
${buildPaginationSource(context.pages)}
|
|
6805
|
+
${buildSearchRankTransformSource()}
|
|
6806
|
+
${buildCategoryResolveSource()}
|
|
6807
|
+
const errorResult = (code, message, retryable = false) => ({
|
|
6808
|
+
ok: false,
|
|
6809
|
+
action: "sycm_search_rank",
|
|
6810
|
+
resultContract,
|
|
6811
|
+
error: { code, message, retryable }
|
|
6812
|
+
});
|
|
6813
|
+
try {
|
|
6814
|
+
if (location.hostname !== "sycm.taobao.com" || !location.pathname.includes("/mc/free/search_rank")) {
|
|
6815
|
+
return errorResult("INVALID_PAGE", "当前页面不是生意参谋搜索排行页");
|
|
6816
|
+
}
|
|
6817
|
+
const pageQuery = new URLSearchParams(location.search);
|
|
6818
|
+
const pageCategoryId = pageQuery.get("cateId") || "";
|
|
6819
|
+
const pageCategoryName = document.querySelector(".common-picker-header")?.getAttribute("title")?.trim() || "";
|
|
6820
|
+
const resourceUrls = performance.getEntriesByType("resource").map((entry) => entry.name).filter((url) => typeof url === "string");
|
|
6821
|
+
const tokenUrl = resourceUrls.slice().reverse().find((url) => {
|
|
6822
|
+
try { return new URL(url).hostname === "sycm.taobao.com" && new URL(url).searchParams.get("token"); } catch { return false; }
|
|
6823
|
+
});
|
|
6824
|
+
let token = "";
|
|
6825
|
+
if (tokenUrl) token = new URL(tokenUrl).searchParams.get("token") || "";
|
|
6826
|
+
if (!token) {
|
|
6827
|
+
const microdata = document.querySelector('meta[name="microdata"]')?.getAttribute("content") || "";
|
|
6828
|
+
token = microdata.match(/(?:^|;)legalityToken=([^;]*)/)?.[1] || "";
|
|
6829
|
+
}
|
|
6830
|
+
if (!token) return errorResult("MISSING_TOKEN", "当前页面未取得生意参谋接口 token", true);
|
|
6831
|
+
let cateId = "";
|
|
6832
|
+
let categoryName = pageCategoryName;
|
|
6833
|
+
if (context.categoryId) {
|
|
6834
|
+
cateId = context.categoryId;
|
|
6835
|
+
let categoryTree;
|
|
6836
|
+
try {
|
|
6837
|
+
categoryTree = await fetchCategoryTree(token);
|
|
6838
|
+
} catch (error) {
|
|
6839
|
+
return errorResult("CATEGORY_TREE_FAILED", error instanceof Error ? error.message : "类目接口请求失败", true);
|
|
6840
|
+
}
|
|
6841
|
+
const resolved = resolveCategoryFromTree(categoryTree, cateId);
|
|
6842
|
+
if (resolved.status !== "found") {
|
|
6843
|
+
return errorResult("CATEGORY_NOT_FOUND", "类目ID【" + cateId + "】在当前账号类目中不存在", false);
|
|
6844
|
+
}
|
|
6845
|
+
categoryName = buildCategoryPath(categoryTree, resolved.match);
|
|
6846
|
+
} else {
|
|
6847
|
+
if (!pageCategoryId) return errorResult("MISSING_CATEGORY", "当前搜索排行页缺少类目参数", true);
|
|
6848
|
+
cateId = pageCategoryId;
|
|
6849
|
+
}
|
|
6850
|
+
const buildUrl = (page) => {
|
|
6851
|
+
const url = new URL(context.endpoint, location.origin);
|
|
6852
|
+
url.searchParams.set("dateRange", context.dateRange);
|
|
6853
|
+
url.searchParams.set("dateType", context.dateType);
|
|
6854
|
+
url.searchParams.set("pageSize", "50");
|
|
6855
|
+
url.searchParams.set("page", String(page));
|
|
6856
|
+
url.searchParams.set("order", context.order);
|
|
6857
|
+
url.searchParams.set("orderBy", context.orderBy);
|
|
6858
|
+
url.searchParams.set("cateId", cateId);
|
|
6859
|
+
url.searchParams.set("rankType", context.rankType);
|
|
6860
|
+
url.searchParams.set("kwType", context.kwType);
|
|
6861
|
+
url.searchParams.set("keyWord", context.keyword || "");
|
|
6862
|
+
url.searchParams.set("device", "0");
|
|
6863
|
+
url.searchParams.set("marketVersion", pageQuery.get("marketVersion") || "free");
|
|
6864
|
+
url.searchParams.set("_", String(Date.now()));
|
|
6865
|
+
url.searchParams.set("token", token);
|
|
6866
|
+
return url.toString();
|
|
6867
|
+
};
|
|
6868
|
+
const fetchPage = async (page) => {
|
|
6869
|
+
const response = await fetch(buildUrl(page), { credentials: "include", headers: { accept: "application/json, text/plain, */*", "x-requested-with": "XMLHttpRequest" } });
|
|
6870
|
+
const text = await response.text();
|
|
6871
|
+
let payload;
|
|
6872
|
+
try { payload = JSON.parse(text); } catch { payload = {}; }
|
|
6873
|
+
if (!response.ok) throw new Error("搜索排行接口请求失败:" + response.status);
|
|
6874
|
+
if (payload && payload.code !== undefined && String(payload.code) !== "0" && String(payload.code) !== "200") {
|
|
6875
|
+
throw new Error(String(payload.message || payload.msg || "搜索排行接口返回错误"));
|
|
6876
|
+
}
|
|
6877
|
+
return payload;
|
|
6878
|
+
};
|
|
6879
|
+
const firstPayload = await fetchPage(1);
|
|
6880
|
+
const firstRows = extractRows(firstPayload);
|
|
6881
|
+
const totalPages = getPageLimit(Math.ceil(extractRecordCount(firstPayload) / 50));
|
|
6882
|
+
const mappingContext = { ...context, categoryName };
|
|
6883
|
+
const rows = firstRows.map((row) => mapSearchRankRow(row, mappingContext));
|
|
6884
|
+
const responses = [{ page: 1, rowCount: firstRows.length }];
|
|
6885
|
+
for (let page = 2; page <= totalPages; page += 1) {
|
|
6886
|
+
await waitBeforeNextPage();
|
|
6887
|
+
const payload = await fetchPage(page).catch(() => null);
|
|
6888
|
+
if (!payload) continue;
|
|
6889
|
+
const pageRows = extractRows(payload);
|
|
6890
|
+
rows.push(...pageRows.map((row) => mapSearchRankRow(row, mappingContext)));
|
|
6891
|
+
responses.push({ page, rowCount: pageRows.length });
|
|
6892
|
+
}
|
|
6893
|
+
return { ok: true, action: "sycm_search_rank", resultContract, data: { source: "sycm_search_rank_api", pageUrl: location.href, rankType: context.rankType, kwType: context.kwType, rows, responses } };
|
|
6894
|
+
} catch (error) {
|
|
6895
|
+
return errorResult("COLLECTION_FAILED", error instanceof Error ? error.message : "搜索排行采集失败", true);
|
|
6896
|
+
}
|
|
6897
|
+
})()`;
|
|
6898
|
+
}
|
|
6899
|
+
//#endregion
|
|
6900
|
+
//#region extensions/sealseek_qushu/src/collectors/sycm/search-rank/index.ts
|
|
6901
|
+
function buildSearchRankCalls(params) {
|
|
6902
|
+
const tabId = normalizeTabId(params.tabId);
|
|
6903
|
+
const rankType = params.rankType;
|
|
6904
|
+
const kwType = params.kwType;
|
|
6905
|
+
const date = resolveSearchRankDateParams(params);
|
|
6906
|
+
const request = resolveSearchRankRequest(kwType, rankType);
|
|
6907
|
+
const categoryId = typeof params.categoryId === "string" ? params.categoryId.trim() : "";
|
|
6908
|
+
if (categoryId && !/^[0-9]+$/.test(categoryId)) return {
|
|
6909
|
+
tool: "workspace_browser",
|
|
6910
|
+
arguments: {
|
|
6911
|
+
action: "evaluate",
|
|
6912
|
+
...tabId ? { tabId } : {},
|
|
6913
|
+
code: buildSearchRankCategoryResolveScript(categoryId)
|
|
6914
|
+
}
|
|
6915
|
+
};
|
|
6916
|
+
return {
|
|
6917
|
+
tool: "workspace_browser",
|
|
6918
|
+
arguments: {
|
|
6919
|
+
action: "evaluate",
|
|
6920
|
+
...tabId ? { tabId } : {},
|
|
6921
|
+
code: buildSearchRankScript({
|
|
6922
|
+
rankType,
|
|
6923
|
+
kwType,
|
|
6924
|
+
...request,
|
|
6925
|
+
dateType: date.dateType,
|
|
6926
|
+
dateRange: date.dateRange,
|
|
6927
|
+
categoryId: categoryId || void 0,
|
|
6928
|
+
keyword: typeof params.keyword === "string" ? params.keyword : void 0,
|
|
6929
|
+
pages: normalizeRequestedPages(params.pages)
|
|
6930
|
+
})
|
|
6931
|
+
}
|
|
6932
|
+
};
|
|
6933
|
+
}
|
|
6934
|
+
//#endregion
|
|
6935
|
+
//#region extensions/sealseek_qushu/src/capabilities/platforms/sycm/search-rank/headers.ts
|
|
6936
|
+
const COMMON_HEADERS = {
|
|
6937
|
+
日期: "date",
|
|
6938
|
+
排名: "rank",
|
|
6939
|
+
类目名称: "categoryName",
|
|
6940
|
+
关键词: "keyword",
|
|
6941
|
+
搜索词类型: "keywordType"
|
|
6942
|
+
};
|
|
6943
|
+
const METRIC_HEADERS = {
|
|
6944
|
+
hot: { 搜索人气: "seIpvUvHits" },
|
|
6945
|
+
soar: {
|
|
6946
|
+
搜索增速: "seUvEx",
|
|
6947
|
+
搜索人气: "seIpvUvHits"
|
|
6948
|
+
},
|
|
6949
|
+
rise: { 搜索增量: "seUvDelta" }
|
|
6950
|
+
};
|
|
6951
|
+
const SEARCH_RANK_HEADERS = COMMON_HEADERS;
|
|
6952
|
+
function resolveSearchRankHeaders(params) {
|
|
6953
|
+
const metrics = METRIC_HEADERS[params.rankType];
|
|
6954
|
+
return metrics ? {
|
|
6955
|
+
...COMMON_HEADERS,
|
|
6956
|
+
...metrics,
|
|
6957
|
+
点击率: "clickThroughRate",
|
|
6958
|
+
支付转化率: "payRate"
|
|
6959
|
+
} : COMMON_HEADERS;
|
|
6960
|
+
}
|
|
6961
|
+
//#endregion
|
|
6962
|
+
//#region extensions/sealseek_qushu/src/capabilities/platforms/sycm/search-rank/params.ts
|
|
6963
|
+
const SEARCH_RANK_PARAMETERS = Type.Object({
|
|
6964
|
+
tabId: TAB_ID_PARAMETER,
|
|
6965
|
+
categoryId: Type.Optional(Type.String({ minLength: 1 })),
|
|
6966
|
+
dateType: Type.Optional(Type.Union([
|
|
6967
|
+
Type.Literal("recent7"),
|
|
6968
|
+
Type.Literal("recent30"),
|
|
6969
|
+
Type.Literal("day")
|
|
6970
|
+
])),
|
|
6971
|
+
dateRange: Type.Optional(Type.String({ minLength: 1 })),
|
|
6972
|
+
kwType: Type.Optional(Type.Union([
|
|
6973
|
+
Type.Literal("search"),
|
|
6974
|
+
Type.Literal("trend"),
|
|
6975
|
+
Type.Literal("core"),
|
|
6976
|
+
Type.Literal("prop")
|
|
6977
|
+
])),
|
|
6978
|
+
rankType: Type.Optional(Type.Union([
|
|
6979
|
+
Type.Literal("hot"),
|
|
6980
|
+
Type.Literal("soar"),
|
|
6981
|
+
Type.Literal("rise")
|
|
6982
|
+
])),
|
|
6983
|
+
keyword: Type.Optional(Type.String()),
|
|
6984
|
+
pages: Type.Optional(Type.Integer({ minimum: 1 }))
|
|
6985
|
+
}, { additionalProperties: false });
|
|
6986
|
+
const KEYWORD_TYPE_FIELD = {
|
|
6987
|
+
id: "keyword_type",
|
|
6988
|
+
paramKey: "kwType",
|
|
6989
|
+
type: "single",
|
|
6990
|
+
label: "搜索词类型",
|
|
6991
|
+
required: true,
|
|
6992
|
+
options: [
|
|
6993
|
+
{
|
|
6994
|
+
label: "搜索词",
|
|
6995
|
+
value: "search"
|
|
6996
|
+
},
|
|
6997
|
+
{
|
|
6998
|
+
label: "趋势词",
|
|
6999
|
+
value: "trend"
|
|
7000
|
+
},
|
|
7001
|
+
{
|
|
7002
|
+
label: "核心词",
|
|
7003
|
+
value: "core"
|
|
7004
|
+
},
|
|
7005
|
+
{
|
|
7006
|
+
label: "修饰词",
|
|
7007
|
+
value: "prop"
|
|
7008
|
+
}
|
|
7009
|
+
]
|
|
7010
|
+
};
|
|
7011
|
+
const RANK_OPTIONS = [
|
|
7012
|
+
{
|
|
7013
|
+
label: "搜索人气",
|
|
7014
|
+
value: "hot"
|
|
7015
|
+
},
|
|
7016
|
+
{
|
|
7017
|
+
label: "搜索增速",
|
|
7018
|
+
value: "soar"
|
|
7019
|
+
},
|
|
7020
|
+
{
|
|
7021
|
+
label: "搜索增量",
|
|
7022
|
+
value: "rise"
|
|
7023
|
+
}
|
|
7024
|
+
];
|
|
7025
|
+
const DATE_TYPE_FIELD = {
|
|
7026
|
+
id: "period",
|
|
7027
|
+
paramKey: "dateType",
|
|
7028
|
+
type: "single",
|
|
7029
|
+
label: "周期",
|
|
7030
|
+
required: true,
|
|
7031
|
+
options: [
|
|
7032
|
+
{
|
|
7033
|
+
label: "最近7天",
|
|
7034
|
+
value: "recent7"
|
|
7035
|
+
},
|
|
7036
|
+
{
|
|
7037
|
+
label: "最近30天",
|
|
7038
|
+
value: "recent30"
|
|
7039
|
+
},
|
|
7040
|
+
{
|
|
7041
|
+
label: "按日",
|
|
7042
|
+
value: "day"
|
|
7043
|
+
}
|
|
7044
|
+
]
|
|
7045
|
+
};
|
|
7046
|
+
const DATE_RANGE_FIELD = {
|
|
7047
|
+
id: "date_range",
|
|
7048
|
+
paramKey: "dateRange",
|
|
7049
|
+
type: "text",
|
|
7050
|
+
label: "时间范围",
|
|
7051
|
+
required: false,
|
|
7052
|
+
placeholder: "可选,如昨天、前天"
|
|
7053
|
+
};
|
|
7054
|
+
const CATEGORY_FIELD = {
|
|
7055
|
+
id: "category",
|
|
7056
|
+
paramKey: "categoryId",
|
|
7057
|
+
type: "text",
|
|
7058
|
+
label: "类目",
|
|
7059
|
+
required: false,
|
|
7060
|
+
placeholder: "可选,类目名称或类目ID,不填用当前页面类目"
|
|
7061
|
+
};
|
|
7062
|
+
const SEARCH_RANK_REQUIRED_INPUTS = [KEYWORD_TYPE_FIELD];
|
|
7063
|
+
function isMissing(value) {
|
|
7064
|
+
return value === void 0 || value === null || value === "";
|
|
7065
|
+
}
|
|
7066
|
+
function resolveSearchRankRequiredInputs(params) {
|
|
7067
|
+
if (isMissing(params.kwType)) return SEARCH_RANK_REQUIRED_INPUTS;
|
|
7068
|
+
validateSearchRankCombination(params.kwType, params.rankType);
|
|
7069
|
+
if (isMissing(params.rankType)) return [{
|
|
7070
|
+
id: "rank_type",
|
|
7071
|
+
paramKey: "rankType",
|
|
7072
|
+
type: "single",
|
|
7073
|
+
label: "榜单类型",
|
|
7074
|
+
required: true,
|
|
7075
|
+
options: params.kwType === "search" ? RANK_OPTIONS : RANK_OPTIONS.filter((option) => option.value !== "rise")
|
|
7076
|
+
}];
|
|
7077
|
+
if (isMissing(params.dateType)) {
|
|
7078
|
+
const fields = [DATE_TYPE_FIELD];
|
|
7079
|
+
if (isMissing(params.dateRange)) fields.push(DATE_RANGE_FIELD);
|
|
7080
|
+
if (isMissing(params.categoryId)) fields.push(CATEGORY_FIELD);
|
|
7081
|
+
return fields;
|
|
7082
|
+
}
|
|
7083
|
+
return [];
|
|
7084
|
+
}
|
|
7085
|
+
//#endregion
|
|
7086
|
+
//#region extensions/sealseek_qushu/src/capabilities/platforms/sycm/index.ts
|
|
7087
|
+
const SYCM_PLATFORM = {
|
|
7088
|
+
key: "sycm",
|
|
7089
|
+
name: "生意参谋",
|
|
7090
|
+
aliases: ["生参", "sycm"],
|
|
7091
|
+
capabilities: [
|
|
7092
|
+
SYCM_DATA_OVERVIEW,
|
|
7093
|
+
SYCM_MARKET_RANK,
|
|
7094
|
+
{
|
|
7095
|
+
platformKey: "sycm",
|
|
7096
|
+
capabilityKey: "sycm_search_rank",
|
|
7097
|
+
name: "搜索排行",
|
|
7098
|
+
aliases: [
|
|
7099
|
+
"搜索排行数据",
|
|
7100
|
+
"关键词排行",
|
|
7101
|
+
"搜索词排行"
|
|
7102
|
+
],
|
|
7103
|
+
description: "按搜索词类型、榜单类型采集搜索排行数据;categoryId 可选,可传类目名称或类目ID,不传用当前页面类目;传类目名称时本次调用仅返回类目解析结果:唯一精确命中(found)是终态判定,直接带 cateId 作为 categoryId 重新调用采集,不打断用户;仅同名多个(ambiguous)或未命中(not_found)才与用户确认;解析调用无需 finalize;keyword 可选,传入时按关键词筛选;每页50条,pages 可选,默认最多5页,用户明确说页数时必须传入。",
|
|
7104
|
+
pageContext: "生意参谋搜索排行页",
|
|
7105
|
+
parameters: SEARCH_RANK_PARAMETERS,
|
|
7106
|
+
headers: SEARCH_RANK_HEADERS,
|
|
7107
|
+
resolveHeaders: resolveSearchRankHeaders,
|
|
7108
|
+
collector: buildSearchRankCalls,
|
|
7109
|
+
requiredInputs: SEARCH_RANK_REQUIRED_INPUTS,
|
|
7110
|
+
resolveRequiredInputs: resolveSearchRankRequiredInputs,
|
|
7111
|
+
combinePlatformInputs: true,
|
|
7112
|
+
platformGate: "strict"
|
|
7113
|
+
},
|
|
6420
7114
|
SYCM_PRODUCT_RANK,
|
|
6421
7115
|
SYCM_ITEM360_SALES,
|
|
6422
7116
|
SYCM_ITEM360_TRAFFIC,
|
|
@@ -10471,7 +11165,7 @@ function buildGuanshuDescription() {
|
|
|
10471
11165
|
...PLATFORM_REGISTRY.map(describePlatform).filter(Boolean),
|
|
10472
11166
|
...[describeStrictCapabilities()].filter(Boolean),
|
|
10473
11167
|
describePlatformGate(),
|
|
10474
|
-
"
|
|
11168
|
+
"工具拒绝并附表单定义时,静默按错误消息行动:不要向用户转述错误内容或解释中间过程(如先调用工具获取候选),将 form 原样传给 ask_user,保留全部字段(含可选项)及顺序。",
|
|
10475
11169
|
"平台确定后,若数据名称存在歧义或该能力参数存在缺失,只询问缺失的选择:已声明输入契约的能力按其契约字段收集(缺失必填项时工具会拒绝并附 form 定义),未声明契约的能力按各能力描述中的缺失项规则;用户已明确的信息不要重复询问。",
|
|
10476
11170
|
"用户要求商品360取数且模块、周期或具体时间范围存在缺失时,只调用一次 ask_user,并在同一个 form 中放入所有缺失项,不要先问模块再问日期。模块字段 id=modules、type=multi、至少选择一项;将每个选中模块分别展开为 targets 中的一个 target,并复用用户已提供的共同参数。",
|
|
10477
11171
|
"用户要求商品详情页取数(商品图片数据、商品SKU数据、店铺上新数据)且商品ID或商品链接缺失时,只调用一次 ask_user,form 提供 id=item_input、type=text 的输入框,说明可填写商品ID或商品链接;用户已提供的信息不要重复询问。params 传 itemId 或 url 二选一;商品链接原样放入 url,不要自行解析丢弃域名。店铺上新的页数不进表单,用户明确提到页数时必须将其作为 params.pages 传入,未提时不询问、默认最多5页。",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
2026-09-
|
|
1
|
+
2026-09-30T10:38:49.145Z
|