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.
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "2026.9.23",
3
- "commit": "2cb6924e57143f1bfa0e909c8d3284c0570ceb12",
4
- "builtAt": "2026-09-27T08:58:28.898Z"
2
+ "version": "2026.9.28",
3
+ "commit": "167fdb4e03b83843b5164943fa0b361662388d40",
4
+ "builtAt": "2026-09-30T10:38:49.583Z"
5
5
  }
@@ -17,5 +17,5 @@
17
17
  ],
18
18
  "channelCatalogSignature": "7a1ab9c88c6afe6defb272bfd422d06dee66ffc5",
19
19
  "rootHelpBundleSignature": "3dfec2096c9435c4992ee4ffe46db09a4d4e99ad",
20
- "rootHelpText": "\n🦞 SealClaw 2026.9.23 (2cb6924) — Greetings, Professor Falken\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"
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
- row.coreKeyWord = decodeHtmlEntities(read(record, "coreKeyWord"));
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", "10");
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) / 10));
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 = resolveSycmDateParams(params, {
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
- tool: "workspace_browser",
3548
- arguments: {
3549
- action: "evaluate",
3550
- ...tabId ? { tabId } : {},
3551
- code: buildMarketRankCategoryResolveScript(categoryId)
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
- tool: "workspace_browser",
3556
- arguments: {
3557
- action: "evaluate",
3558
- ...tabId ? { tabId } : {},
3559
- code: buildMarketRankScript({
3560
- rankType,
3561
- sellerType,
3562
- sortMetric,
3563
- ...request,
3564
- dateType: date.dateType,
3565
- dateRange: date.dateRange,
3566
- categoryId: categoryId || void 0,
3567
- keyword: typeof params.keyword === "string" ? params.keyword : void 0,
3568
- pages: normalizeRequestedPages(params.pages)
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.Literal("all")),
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: "single",
3880
+ type: "text",
3752
3881
  label: "价格带",
3753
- required: true,
3754
- options: [{
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
- "gmv",
3784
- "growth",
3785
- "flow",
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
- DATE_RANGE_FIELD,
3853
- CATEGORY_FIELD
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: "按商品榜单类型、店铺类型采集生意参谋市场排行商品数据;价格带固定为不限;categoryId 可选,可传类目名称或类目ID,不传用当前页面类目;传类目名称时本次调用仅返回类目解析结果:唯一精确命中(found)是终态判定,直接带 cateId 作为 categoryId 重新调用采集,不打断用户;仅同名多个(ambiguous)或未命中(not_found)才与用户确认;解析调用无需 finalize;pages 可选,默认最多5页,用户明确说页数时必须传入。",
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/capabilities/platforms/sycm/index.ts
6377
- const SYCM_PLATFORM = {
6378
- key: "sycm",
6379
- name: "生意参谋",
6380
- aliases: ["生参", "sycm"],
6381
- capabilities: [
6382
- SYCM_DATA_OVERVIEW,
6383
- SYCM_MARKET_RANK,
6384
- defineSycmReplayCapability({
6385
- capabilityKey: "sycm_search_rank",
6386
- name: "搜索排行",
6387
- aliases: [
6388
- "搜索排行数据",
6389
- "关键词排行",
6390
- "搜索词排行",
6391
- "搜索分析"
6392
- ],
6393
- description: "采集搜索排行或搜索分析页面已加载的关键词数据。",
6394
- pageContext: "生意参谋搜索排行或搜索分析页,目标关键词、筛选和榜单类型已选择",
6395
- platformGate: "strict",
6396
- parameters: Type.Object({
6397
- tabId: TAB_ID_PARAMETER,
6398
- maxRequests: Type.Optional(Type.Number()),
6399
- keyword: Type.Optional(Type.String()),
6400
- categoryId: Type.Optional(Type.String()),
6401
- dateType: Type.Optional(Type.String()),
6402
- dateRange: Type.Optional(Type.String()),
6403
- rankType: Type.Optional(Type.String())
6404
- }, { additionalProperties: false }),
6405
- headers: {
6406
- 日期: "date",
6407
- 关键词: "keyword",
6408
- 关键词类型: "keywordType",
6409
- 排名: "rank",
6410
- 搜索人气: "estimatedSearchPopularityValue",
6411
- 点击次数: "estimatedClickCountValue",
6412
- 点击率: "clickRateValue",
6413
- 支付人数: "estimatedPayCountValue",
6414
- 支付转化率: "estimatedPayRateValue",
6415
- 收藏人数: "collectCount",
6416
- 加购人数: "cartCount"
6417
- },
6418
- endpointFragments: ["/mc/mq/mkt/keyword/rank/pro.json", "/mc/mq/mkt/keyword/relate/analysis.json"]
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
+ '&quot;': '"', '&amp;': '&', '&lt;': '<', '&gt;': '>', '&apos;': "'", '&nbsp;': ' ',
6742
+ '&ldquo;': '"', '&rdquo;': '"', '&lsquo;': '\u2018', '&rsquo;': '\u2019', '&sbquo;': '\u201A', '&bdquo;': '\u201E',
6743
+ '&laquo;': '\u00AB', '&raquo;': '\u00BB', '&#34;': '"', '&#39;': "'", '&#160;': ' ',
6744
+ '&hellip;': '\u2026', '&mdash;': '\u2014', '&ndash;': '\u2013', '&trade;': '\u2122',
6745
+ '&copy;': '\u00A9', '&reg;': '\u00AE', '&middot;': '\u00B7', '&bull;': '\u2022',
6746
+ '&deg;': '\u00B0', '&plusmn;': '\u00B1', '&times;': '\u00D7', '&divide;': '\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-27T08:58:28.484Z
1
+ 2026-09-30T10:38:49.145Z
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sealclaw",
3
- "version": "2026.9.23",
3
+ "version": "2026.9.28",
4
4
  "description": "Multi-channel AI gateway with extensible messaging integrations",
5
5
  "keywords": [],
6
6
  "homepage": "https://github.com/openclaw/openclaw#readme",