Skill: skyfile/rebate-assistant (version 1.0.2)

--- name: rebate-assistant description: 返利宝统一技能。只按 3 个用户场景工作：S01 授权与教程、S02 链接返利、S03 商品搜索。用户说“返利”“教程”“详细教程”“提现教程”“提现10元”“确认提现”“我已授权”“账户余额”等走 S01；发送淘宝、京东、拼多多商品链接走 S02；表达想买什么商品时走 S03。S03 的职责是提取商品搜索信息，调用搜索接口，并返回后续可生成返利链接的商品结果。 --- # 返利宝 ## 核心准则 1. 返利宝只有一条主链路：用户给“品” -> 识别 -> 搜索或生成返利链接 -> 返回返利结果。 2. `M01` 的职责是让用户知道返利宝是什么、怎么用、为什么要先做微信授权。 3. `M02` 的职责是承接淘宝、京东、拼多多商品链接，进入返利链接生成链路。 4. `S03` 的职责是识别“用户想买什么商品”，提取搜索信息并直接进入商品搜索链路。 5. `S03` 不是为了展示分词，更不是为了做炫技式槽位输出。 6. 对用户来说，返利宝是一个统一产品，不是多个独立工具。 ## 执行硬约束 1. 命中返利宝后，优先调用 `scripts/cli/rebate_assistant_router.js`。 2. 若调用了 `rebate_assistant_router.js`、`m01_operation_guide.js`、`m02_platform_link.js`、`product_search.js` 中任一脚本，脚本标准输出是什么，就向用户原样返回什么。 3. 返回方式必须是一条普通 assistant 对话消息，不能只显示在 `tool output` 折叠面板中。 4. 返回给用户的消息必须使用 md 格式；调用脚本时优先使用 `--format md`。 5. 禁止在脚本输出前后追加解释、总结、润色、转述、商品推荐、返利说明或教程扩写。 6. 禁止在接口失败、未命中商品或未完成授权时自行编造“返利链接”“返利口令”“返利到账说明”。 7. 不要把内部处理过程作为主动面向用户的过程话术；用户发商品链接时，如需先回应再调用脚本，优先说“我先帮你查这个商品有没有返利入口。”脚本最终输出仍按第 2 条原样返回。 8. 即使脚本退出码非 0，只要标准输出已有面向用户的文本，也必须原样返回标准输出，不得因为失败码自行改写话术。 ## 路由规则 1. 命中 `返利`、`返利教程`、`教程`、`详细教程`、`提现教程`、`提现10元`、`确认提现`、`我已授权`、`授权完成`、`账户余额` 时，进入 `S01`。 2. 只要消息里出现 `http://` 或 `https://` 商品链接，进入 `S02`。 3. 没有链接，但用户表达购物需求时，进入 `S03`。 4. `S03` 统一走 `product_search.js`。 5. 对话层只调用统一入口脚本 `rebate_assistant_router.js`。 ## 当前实现边界 1. `S01` 已是固定话术和固定流程。 2. `S02` 当前已承接平台识别、单商品查询、租约申请和返利链接生成。 3. `S03` 当前统一由 `product_search.js` 承接，输出 `product_search_intent + search_request`，并直接调用商品搜索接口。 4. `S03` 的核心入参只有两个：尽量完整的 `raw_text`，以及可识别时才传的 `platform`。 5. 商品搜索返回结果后，用户再通过商品链接进入 `S02` 完成返利链接生成。 ## 核心脚本 ### 构建方式 ```bash cd ~/.openclaw/workspace/skills/rebate-assistant npm install npm run build ``` 构建产物输出到 `scripts/`，供 skill 直接调用。 ### 对话入口 - `scripts/cli/rebate_assistant_router.js` ### 场景脚本 - `scripts/cli/m01_operation_guide.js` - `scripts/cli/m02_platform_link.js` - `scripts/cli/product_search.js` ### 内部识别与共享逻辑 - `scripts/cli/recognize_platform_link.js` - `scripts/cli/recognize_precise_product_search.js` - `src/productSearchProtocol.ts` - `src/common.ts` ## S01 授权与教程 ### 用户场景 用户第一次接触返利宝，或者不知道如何使用，或者需要完成微信授权，或者想看提现说明，或者想查询账户余额，或者要发起提现申请。 ### 目标 1. 说明返利宝怎么用。 2. 明确告知：使用返利宝前需要先完成微信授权。 3. 用户授权完成后，能继续进入链接返利或搜索返利主链路。 4. 已授权用户可以查询账户余额。 5. 已授权用户可以先确认提现金额，再提交提现申请。 ### 触发示例 - `返利` - `教程` - `详细教程` - `提现教程` - `提现10元` - `确认提现` - `我已授权` - `账户余额` ### 当前动作 - `start_auth` - `confirm_auth` - `detailed_tutorial` - `withdraw_tutorial` - `withdraw_prepare` - `withdraw_confirm` - `account_balance` ### 提现申请流程 1. `提现教程`、`怎么提现`、`提现规则` 只返回 `withdraw_tutorial`。 2. `我要提现`、`提现`、`全部提现` 进入 `withdraw_prepare`，先查询账户余额；可提现金额低于 1.00 元时直接说明暂不可提现，可提现金额满足最低提现条件时再询问用户要提现多少钱。 3. `提现10元`、`申请提现10元` 进入 `withdraw_prepare`，先校验金额和可提现余额，不直接提交提现。 4. `withdraw_prepare` 在金额合法时必须返回固定二次确认话术，头部提醒用户先关注小马享生活公众号，否则无法顺利领取微信红包。 5. 用户回复 `确认提现` 或 `确定提现` 后进入 `withdraw_confirm`，再调用 `/v1/withdraw/apply`。 6. 提现失败时返回固定失败话术，并附上公众号关注链接。 ### 调试入口 ```bash node ~/.openclaw/workspace/skills/rebate-assistant/scripts/cli/m01_operation_guide.js --action <action> --format md ``` `withdraw_prepare` 调试时需要带原始消息： ```bash node ~/.openclaw/workspace/skills/rebate-assistant/scripts/cli/m01_operation_guide.js --action withdraw_prepare --raw-message '提现10元' --format md ``` 可用 `action`： - `start_auth` - `confirm_auth` - `detailed_tutorial` - `withdraw_tutorial` - `withdraw_prepare` - `withdraw_confirm` - `account_balance` ## S02 链接返利 ### 用户场景 用户直接给淘宝、京东、拼多多商品链接，希望获得返利。 ### 目标 1. 识别链接所属平台。 2. 承接商品链接，进入返利链接生成链路。 3. 让用户不需要理解平台规则，只需要发链接。 ### 触发示例 - `https://item.jd.com/100012043978.html` - `https://e.tb.cn/...` - `https://mobile.yangkeduo.com/...` ### 当前边界 1. 只处理淘宝、京东、拼多多商品链接。 2. 链接场景直接承接商品查询、推广位租约和返利链接生成。 3. 对于淘宝短链、混合文案、口令链接，优先保留用户原始消息给后端识别。 ### 调试入口 ```bash node ~/.openclaw/workspace/skills/rebate-assistant/scripts/cli/m02_platform_link.js --raw-message '<用户原始消息>' --format md ``` ## S03 商品搜索 ### 用户场景 用户没有给链接，而是直接说自己想买什么。 ### 目标 1. 识别用户真正想买的商品。 2. 抽取尽量完整的商品信息，作为搜索接口的 `raw_text`。 3. 能识别平台时传 `platform`，识别不到就不传。 4. 搜索到商品后，用户再把商品链接发来进入返利链接生成链路。 ### 适用输入 - 品牌 + 型号 - 品牌 + 商品名 - SKU / 编码 - 完整商品标题 - 类目 + 预算 - 类目 + 场景 / 人群 / 送礼对象 ### 示例 - `李宁 行川2SE` - `京东 iPhone 15 Pro Max 256G` - `我想买一双袜子` - `推荐个 300 左右的耳机` - `适合送女朋友的礼物` - `100 以内儿童水杯` ### 调试入口 ```bash node ~/.openclaw/workspace/skills/rebate-assistant/scripts/cli/product_search.js --raw-message '<用户原始消息>' --format md ``` ## S03 统一输出 `S03` 当前统一输出两层结构： 1. `product_search_intent` 2. `search_request` ### product_search_intent 表示识别后的商品意图。 关键字段： - `intent_mode` - `query_text` - `query_tokens` - `brand` - `series` - `model` - `sku` - `product_name` - `category` - `attributes` - `specs` - `price_min` - `price_max` - `price_target` - `crowd` - `usage_scene` - `gift_target` - `platform_hint` - `sort_hint` ### search_request 表示可直接给后续商品搜索接口使用的统一入参。 结构如下： ```json { "ready": true, "intent_mode": "search", "keyword": "袜子", "search_terms": ["袜子"], "filters": { "brand": null, "series": null, "model": null, "sku": null, "product_name": "袜子", "category": "袜子", "attributes": [], "specs": [], "price_min": null, "price_max": null, "price_target": null, "crowd": null, "usage_scene": null, "gift_target": null, "platform_hint": null, "sort_hint": null } } ``` ## 统一入口 正常对话只使用下面这个脚本： ```bash node ~/.openclaw/workspace/skills/rebate-assistant/scripts/cli/rebate_assistant_router.js --raw-message '<用户原始消息>' --format md ``` 路由结果： 1. `S01` -> `m01_operation_guide.js` 2. `S02` -> `m02_platform_link.js` 3. `S03` -> `product_search.js` ## 文档约束 1. 后续文档继续只围绕 `S01 / S02 / S03` 三个用户场景展开。 2. 不再把大量兼容脚本、历史代理脚本写进主规范。 3. 不再为了测试展示去扩写分词说明、长篇样例、重复状态表。 4. 如果后续接中台搜索接口，优先改 `search_request` 对接方式，不推翻场景结构。

path: scripts/s01Intent.js
content: "use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.classifyS01Action = classifyS01Action;
exports.looksLikeS01Message = looksLikeS01Message;
const DETAILED_TUTORIAL_KEYWORDS = ["详细教程", "查看详细教程"];
const WITHDRAW_TUTORIAL_KEYWORDS = ["提现教程"];
const WITHDRAW_CONFIRM_KEYWORDS = ["确认提现", "确定提现", "确认申请提现", "确定申请提现"];
const AUTH_CONFIRM_KEYWORDS = ["我已授权", "授权完成"];
const START_AUTH_MESSAGES = new Set(["返利", "返利宝", "返利助手", "返利教程", "教程", "使用教程", "使用说明"]);
const ACCOUNT_BALANCE_KEYWORDS = ["余额", "账户余额", "账号余额", "返利余额", "可提现余额", "余额查询"];
const REBATE_DETAILS_KEYWORDS = ["订单", "订单明细", "返利明细", "返利订单", "返利记录", "我的订单"];
const SERVICE_HELP_PATTERNS = [
    /(?:返利宝|返利助手|返利).*(?:怎么用|怎么使用|如何用|如何使用|怎么玩|是什么|有什么用|怎么返利|如何返利|使用教程|使用说明|介绍一下|介绍下|说明一下|说一下|怎么操作|如何操作)/,
    /(?:怎么用|怎么使用|如何用|如何使用|怎么玩|是什么|有什么用|怎么返利|如何返利|使用教程|使用说明|介绍一下|介绍下|说明一下|说一下|怎么操作|如何操作).*(?:返利宝|返利助手|返利|教程)/,
    /(?:返利宝|返利助手|返利).*(?:教程|说明|规则|介绍|帮助|玩法)/,
    /(?:教程|说明|规则|介绍|帮助|玩法).*(?:返利宝|返利助手|返利)/,
    /(?:教程).*(?:怎么用|怎么使用|如何用|如何使用|怎么玩)/,
    /(?:怎么用|怎么使用|如何用|如何使用|怎么玩).*(?:教程)/,
];
const WITHDRAW_HELP_PATTERNS = [
    /(?:返利宝|返利助手|返利).*(?:怎么提现|如何提现|可以提现吗|提现方式|提现规则)/,
    /(?:怎么提现|如何提现|可以提现吗|提现方式|提现规则).*(?:返利宝|返利助手|返利)/,
    /^(?:怎么|如何).*(?:提现|返现)/,
    /(?:提现|返现).*(?:教程|说明|规则|方式|流程|多久到账|到账时间|手续费)/,
    /(?:能提现吗|可以提现吗)/,
];
const ACCOUNT_BALANCE_PATTERNS = [
    /(?:返利宝|返利助手|返利).*(?:余额|账户余额|账号余额|返利余额|可提现余额)/,
    /(?:余额|账户余额|账号余额|返利余额|可提现余额).*(?:返利宝|返利助手|返利)/,
    /(?:查|看|查下|看下|查一下|看一下).*(?:余额|账户余额|账号余额|返利余额|可提现余额)/,
    /(?:余额|账户余额|账号余额|返利余额|可提现余额).*(?:多少|还有多少|有多少)/,
];
const REBATE_DETAILS_PATTERNS = [
    /(?:查|看|查下|看下|查一下|看一下).*(?:订单|订单明细|返利明细|返利订单|返利记录)/,
    /(?:订单|订单明细|返利明细|返利订单|返利记录).*(?:查询|查看|明细|记录|在哪里|在哪|入口|链接)/,
];
const WITHDRAW_APPLY_PATTERNS = [
    /^(?:我要|我想|我需要|帮我|请帮我|麻烦帮我|申请|发起|立即|马上|现在)?\s*(?:全部|全额)?\s*(?:提现|提取)$/,
    /^(?:我要|我想|我需要|帮我|请帮我|麻烦帮我|申请|发起|立即|马上|现在)?\s*(?:提现|提取)\s*(?:金额)?\s*[¥￥]?\s*\d+(?:\.\d+)?\s*(?:元|块|rmb|RMB)?$/,
    /^[¥￥]?\s*\d+(?:\.\d+)?\s*(?:元|块|rmb|RMB)?\s*(?:提现|提取)$/,
    /(?:申请|发起|帮我|请帮我|我要|我想|我需要|立即|马上).*(?:提现|提取)/,
    /(?:提现|提取).*(?:到微信|到零钱|微信红包)/,
];
const WITHDRAW_CONFIRM_PATTERNS = [
    /^(?:确认|确定|确认申请|确定申请|确认提交|确定提交)(?:提现|提取)$/,
];
const AUTH_STATUS_PATTERNS = [
    /(?:返利宝|返利助手|返利).*(?:登录状态|绑定状态|授权状态|注册状态)/,
    /(?:登录状态|绑定状态|授权状态|注册状态)/,
    /(?:我|当前|现在|这里).*(?:登录|绑定|授权|注册).*(?:吗|状态|情况|了没|没有|没)?/,
    /(?:是否|有没有).*(?:登录|绑定|授权|注册)/,
    /(?:登录|绑定|授权|注册).*(?:了吗|没|成功了没|好了没|完成了没)/,
];
const START_AUTH_PATTERNS = [
    /(?:怎么|如何).*(?:登录|注册|授权|绑定).*(?:返利宝|返利助手|返利)?/,
    /(?:返利宝|返利助手|返利).*(?:怎么登录|如何登录|怎么注册|如何注册|怎么授权|如何授权|怎么绑定|如何绑定)/,
    /(?:开始|进入|去).*(?:登录|注册|授权|绑定)/,
    /^(?:登录|注册|授权|绑定)$/,
];
const TRAILING_PUNCTUATION = /[ \t\r\n,，。！？!?；;：:、]+$/;
function containsAny(text, keywords) {
    return keywords.some((keyword) => text.includes(keyword));
}
function matchesAnyPattern(text, patterns) {
    return patterns.some((pattern) => pattern.test(text));
}
function classifyS01Action(rawMessage) {
    let message = String(rawMessage || "").trim().replace(TRAILING_PUNCTUATION, "");
    if (!message) {
        return null;
    }
    if (containsAny(message, DETAILED_TUTORIAL_KEYWORDS)) {
        return ["detailed_tutorial", "命中详细教程关键词"];
    }
    if (containsAny(message, WITHDRAW_CONFIRM_KEYWORDS) || matchesAnyPattern(message, WITHDRAW_CONFIRM_PATTERNS)) {
        return ["withdraw_confirm", "命中提现二次确认表达"];
    }
    if (containsAny(message, WITHDRAW_TUTORIAL_KEYWORDS) || matchesAnyPattern(message, WITHDRAW_HELP_PATTERNS)) {
        return ["withdraw_tutorial", "命中提现说明类表达"];
    }
    if (containsAny(message, ACCOUNT_BALANCE_KEYWORDS) || matchesAnyPattern(message, ACCOUNT_BALANCE_PATTERNS)) {
        return ["account_balance", "命中账户余额类表达"];
    }
    if (containsAny(message, REBATE_DETAILS_KEYWORDS) || matchesAnyPattern(message, REBATE_DETAILS_PATTERNS)) {
        return ["rebate_details", "命中订单/返利明细类表达"];
    }
    if (matchesAnyPattern(message, WITHDRAW_APPLY_PATTERNS)) {
        return ["withdraw_prepare", "命中提现申请类表达"];
    }
    if (containsAny(message, AUTH_CONFIRM_KEYWORDS)) {
        return ["confirm_auth", "命中授权确认关键词"];
    }
    if (matchesAnyPattern(message, AUTH_STATUS_PATTERNS)) {
        return ["binding_status", "命中登录/绑定状态查询表达"];
    }
    if (START_AUTH_MESSAGES.has(message) || matchesAnyPattern(message, START_AUTH_PATTERNS)) {
        return ["start_auth", "命中返利入口/授权入口表达"];
    }
    if (matchesAnyPattern(message, SERVICE_HELP_PATTERNS)) {
        return ["detailed_tutorial", "命中返利宝使用说明类表达"];
    }
    return null;
}
function looksLikeS01Message(rawMessage) {
    return classifyS01Action(rawMessage) !== null;
}


path: scripts/productSearch.js
content: "use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.looksLikeNonSearchMessage = looksLikeNonSearchMessage;
exports.buildProductIntent = buildProductIntent;
exports.buildResponse = buildResponse;
exports.emitResponse = emitResponse;
const common_1 = require("./common");
const m01OperationGuide_1 = require("./m01OperationGuide");
const productSearchProtocol_1 = require("./productSearchProtocol");
const recognizePreciseProductSearch_1 = require("./recognizePreciseProductSearch");
const rebateV1Service_1 = require("./rebateV1Service");
const s01Intent_1 = require("./s01Intent");
const SEARCH_INTENT_SYSTEM_PROMPT = `
你是返利商品搜索意图识别器。

你只能输出一个 JSON 对象，不能输出 markdown，不能输出解释。

你的任务：
1. 判断用户这句话是不是“想搜索商品”的请求。
2. 如果是，提取最适合直接传给搜索接口的 query_text。
3. query_text 要尽量保留用户原句里的商品信息、预算、场景、人群、送礼对象等有效搜索条件。
4. 不要把授权、登录状态、教程、提现、闲聊、系统问题识别成商品搜索。
5. 如果能明确看出平台，platform_hint 只允许输出 tb、jd、pdd 之一，否则输出 null。

输出字段：
- is_product_search: boolean
- confidence: 0 到 1
- query_text: string|null
- platform_hint: "tb"|"jd"|"pdd"|null
- brand: string|null
- model: string|null
- sku: string|null
- product_name: string|null
- category: string|null

要求：
- query_text 必须适合直接做商品搜索，不要编造没说过的信息。
- 如果用户没明确表达要搜什么商品，就返回 is_product_search=false。
`.trim();
const NON_SEARCH_PATTERNS = [/(?:谢谢|你好|在吗|帮忙|什么意思|怎么回事)$/];
const LEADING_RECOMMEND_PATTERNS = [
    /^(?:帮我|麻烦|请|请帮我|给我|替我)?(?:推荐|求推荐)(?:一?个|一?款|一?双|一?件|一下|一下子|一些)?\s*/i,
    /^(?:来个|来款|整个|整点)\s*/i,
];
async function buildStartAuthResponseForSearch(rawMessage) {
    try {
        (0, common_1.savePendingAuthRequest)(await (0, common_1.getOrCreateMachineCode)(), {
            scene: "search_rebate",
            handler: "product_search",
            rawMessage,
            reason: "商品搜索场景执行时发现未授权",
        });
    }
    catch {
        // Best-effort only. Failing to persist pending request should not block auth link generation.
    }
    const [payload] = await (0, m01OperationGuide_1.executeAction)("start_auth");
    return payload;
}
function buildErrorPayload(stage, code, message) {
    return { stage, code, message: String(message || "").trim() };
}
function buildResultData(searchResult, productSearchIntent, searchRequest) {
    const items = Array.isArray(searchResult?.items) ? searchResult?.items : [];
    const result = {
        search_result: searchResult || null,
        items,
        total: searchResult?.total ?? items.length,
    };
    if (productSearchIntent) {
        result.product_search_intent = productSearchIntent;
    }
    if (searchRequest) {
        result.search_request = searchRequest;
    }
    return result;
}
function cleanText(value) {
    if (value === null || value === undefined || typeof value === "boolean") {
        return null;
    }
    const text = String(value).trim();
    return text || null;
}
function parseBool(value, defaultValue = false) {
    if (typeof value === "boolean") {
        return value;
    }
    const lowered = String(value || "").trim().toLowerCase();
    if (["true", "1", "yes"].includes(lowered)) {
        return true;
    }
    if (["false", "0", "no"].includes(lowered)) {
        return false;
    }
    return defaultValue;
}
function parseConfidence(value, defaultValue = 0) {
    const confidence = Number(value);
    if (!Number.isFinite(confidence)) {
        return defaultValue;
    }
    return Math.max(0, Math.min(1, confidence));
}
function normalizePlatformHint(value) {
    const normalized = String(value || "").trim().toLowerCase();
    if (["tb", "taobao", "淘宝"].includes(normalized)) {
        return "tb";
    }
    if (["jd", "京东"].includes(normalized)) {
        return "jd";
    }
    if (["pdd", "拼多多"].includes(normalized)) {
        return "pdd";
    }
    return null;
}
function looksLikeNonSearchMessage(cleanedText) {
    if ((0, s01Intent_1.looksLikeS01Message)(cleanedText)) {
        return true;
    }
    return NON_SEARCH_PATTERNS.some((pattern) => pattern.test(cleanedText || ""));
}
function buildSearchQueryParts(cleanedText, ruleSignals) {
    const budget = (0, recognizePreciseProductSearch_1.extractBudget)(cleanedText);
    const crowd = (0, recognizePreciseProductSearch_1.extractCrowd)(cleanedText);
    const usageScene = (0, recognizePreciseProductSearch_1.extractUsageScene)(cleanedText);
    const giftTarget = (0, recognizePreciseProductSearch_1.extractGiftTarget)(cleanedText);
    const parts = [
        ruleSignals.brand,
        ruleSignals.model,
        ruleSignals.sku,
        ruleSignals.product_name,
        ruleSignals.category,
        ...(ruleSignals.attribute_aliases || []),
        ...(ruleSignals.specs || []),
        crowd,
        usageScene,
        giftTarget,
        budget,
    ].filter(Boolean);
    const deduped = (0, recognizePreciseProductSearch_1.dedupePreserveOrder)(parts);
    const ordered = (0, recognizePreciseProductSearch_1.sortValuesByPosition)(cleanedText, deduped);
    return (0, recognizePreciseProductSearch_1.dedupePreserveOrder)([...ordered, ...deduped.filter((part) => !ordered.includes(part))]);
}
function stripPlatformTerms(text, platformHint) {
    let queryText = String(text || "").trim();
    if (!queryText) {
        return "";
    }
    const platformAliases = {
        taobao: ["淘宝", "tb"],
        tb: ["淘宝", "tb"],
        jd: ["京东", "jd"],
        pdd: ["拼多多", "pdd"],
    };
    for (const alias of platformAliases[platformHint || ""] || []) {
        queryText = queryText.replace(new RegExp(`^\\s*${alias.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}[\\s:：,-]*`, "i"), "");
    }
    return queryText.trim();
}
function stripLeadingRecommendWords(text) {
    let queryText = String(text || "").trim();
    let previous = "";
    while (previous !== queryText) {
        previous = queryText;
        for (const pattern of LEADING_RECOMMEND_PATTERNS) {
            queryText = queryText.replace(pattern, "").trim();
        }
    }
    return queryText;
}
function buildSearchRawText(rawMessage, cleanedText, platformHint, fallbackQuery) {
    let queryText = stripPlatformTerms(cleanedText || rawMessage || "", platformHint);
    queryText = stripLeadingRecommendWords(queryText);
    queryText = queryText.replace(/\s+/g, " ").trim().replace(/^[ ,.:;!?]+|[ ,.:;!?]+$/g, "");
    return queryText || String(fallbackQuery || cleanedText || rawMessage || "").trim();
}
function buildModelSearchUserPrompt(rawMessage, cleanedText, ruleSignals) {
    const heuristicHint = {
        brand: ruleSignals.brand,
        model: ruleSignals.model,
        sku: ruleSignals.sku,
        product_name: ruleSignals.product_name,
        category: ruleSignals.category,
        platform_hint: ruleSignals.platform_hint,
        attributes: ruleSignals.attribute_aliases || [],
        specs: ruleSignals.specs || [],
    };
    return [
        "请判断下面这句话是不是商品搜索意图，并抽取可直接搜索的 query_text。",
        "只返回 JSON 对象。",
        `原始消息: ${rawMessage}`,
        `清洗后消息: ${cleanedText}`,
        `启发式参考(仅供参考，原文优先): ${JSON.stringify(heuristicHint, undefined, 0)}`,
    ].join("\n");
}
async function callModelSearchIntent(rawMessage, cleanedText, ruleSignals) {
    const response = await (0, common_1.requestModelJson)(SEARCH_INTENT_SYSTEM_PROMPT, buildModelSearchUserPrompt(rawMessage, cleanedText, ruleSignals), 0.1, 500);
    const payload = response.json || {};
    return {
        recognized_by: "model",
        model_provider: response.provider,
        model_name: response.model,
        is_product_search: parseBool(payload.is_product_search, false),
        confidence: parseConfidence(payload.confidence, 0),
        query_text: cleanText(payload.query_text),
        platform_hint: normalizePlatformHint(payload.platform_hint),
        brand: cleanText(payload.brand),
        model: cleanText(payload.model),
        sku: cleanText(payload.sku),
        product_name: cleanText(payload.product_name),
        category: cleanText(payload.category),
    };
}
function isRuleBasedSearchCandidate(cleanedText, ruleSignals, priceConstraints, crowd, usageScene, giftTarget) {
    if (!cleanedText || looksLikeNonSearchMessage(cleanedText)) {
        return false;
    }
    if (ruleSignals.sku || ruleSignals.model) {
        return true;
    }
    if (ruleSignals.brand && (ruleSignals.product_name || ruleSignals.category)) {
        return true;
    }
    if ((0, recognizePreciseProductSearch_1.containsFuzzyMarker)(cleanedText)) {
        return true;
    }
    if ([priceConstraints.price_min, priceConstraints.price_max, priceConstraints.price_target].some((value) => value !== null && value !== undefined) && (ruleSignals.product_name || ruleSignals.category)) {
        return true;
    }
    if ([crowd, usageScene, giftTarget].some(Boolean) && (ruleSignals.product_name || ruleSignals.category)) {
        return true;
    }
    const category = cleanText(ruleSignals.category);
    const productName = cleanText(ruleSignals.product_name);
    if (category && cleanedText === category) {
        return true;
    }
    if (productName && cleanedText === productName) {
        return true;
    }
    return false;
}
function compactValue(value) {
    if (value === null || value === undefined) {
        return null;
    }
    if (typeof value === "string") {
        const stripped = value.trim();
        return stripped || null;
    }
    if (Array.isArray(value)) {
        const compacted = value.map((item) => compactValue(item)).filter((item) => item !== null);
        return compacted.length ? compacted : null;
    }
    return value;
}
function compactProductSearchIntent(intent) {
    const source = intent || {};
    const compacted = {
        success: Boolean(source.success),
        intent_mode: source.intent_mode,
        query_text: compactValue(source.query_text) || "",
    };
    for (const key of [
        "platform_hint",
        "product_name",
        "category",
        "brand",
        "model",
        "sku",
        "attributes",
        "specs",
        "price_min",
        "price_max",
        "price_target",
        "crowd",
        "usage_scene",
        "gift_target",
    ]) {
        const value = compactValue(source[key]);
        if (value !== null) {
            compacted[key] = value;
        }
    }
    return compacted;
}
function compactSearchRequest(searchRequest) {
    const source = searchRequest || {};
    const compacted = {
        ready: Boolean(source.ready),
        keyword: compactValue(source.keyword) || "",
    };
    const filters = {};
    for (const [key, value] of Object.entries(source.filters || {})) {
        const compactedValue = compactValue(value);
        if (compactedValue !== null) {
            filters[key] = compactedValue;
        }
    }
    if (Object.keys(filters).length) {
        compacted.filters = filters;
    }
    return compacted;
}
async function buildProductIntent(rawMessage) {
    const cleanedText = (0, recognizePreciseProductSearch_1.normalizeText)(rawMessage || "");
    const ruleSignals = (0, recognizePreciseProductSearch_1.extractRuleSignals)(cleanedText);
    const priceConstraints = (0, recognizePreciseProductSearch_1.extractPriceConstraints)(cleanedText);
    let platformHint = normalizePlatformHint(ruleSignals.platform_hint || (0, recognizePreciseProductSearch_1.extractPlatformHint)(cleanedText));
    const crowd = (0, recognizePreciseProductSearch_1.extractCrowd)(cleanedText);
    const usageScene = (0, recognizePreciseProductSearch_1.extractUsageScene)(cleanedText);
    const giftTarget = (0, recognizePreciseProductSearch_1.extractGiftTarget)(cleanedText);
    const sortHint = (0, recognizePreciseProductSearch_1.extractSortHint)(cleanedText);
    const queryParts = buildSearchQueryParts(cleanedText, ruleSignals);
    const fallbackQuery = queryParts.join(" ").trim();
    const fallbackQueryText = buildSearchRawText(rawMessage, cleanedText, platformHint, fallbackQuery);
    let modelIntent = null;
    try {
        modelIntent = await callModelSearchIntent(rawMessage, cleanedText, ruleSignals);
    }
    catch {
        modelIntent = null;
    }
    let recognizedBy = "none";
    let queryText = "";
    if (modelIntent?.is_product_search && modelIntent.query_text) {
        platformHint = modelIntent.platform_hint || platformHint;
        queryText = buildSearchRawText(modelIntent.query_text, (0, recognizePreciseProductSearch_1.normalizeText)(modelIntent.query_text || ""), platformHint, fallbackQueryText);
        recognizedBy = "model";
    }
    else if (isRuleBasedSearchCandidate(cleanedText, ruleSignals, priceConstraints, crowd, usageScene, giftTarget)) {
        queryText = fallbackQueryText;
        recognizedBy = "rule_fallback";
    }
    const productName = modelIntent?.product_name ||
        modelIntent?.category ||
        ruleSignals.product_name ||
        ruleSignals.category ||
        ruleSignals.model ||
        ruleSignals.sku ||
        (recognizedBy !== "none" ? queryText : null);
    const category = modelIntent?.category || ruleSignals.category || ruleSignals.product_name;
    return (0, recognizePreciseProductSearch_1.buildProductSearchIntent)({
        success: Boolean(queryText && recognizedBy !== "none"),
        intent_mode: "search",
        query_text: queryText,
        query_tokens: queryText ? (queryParts.length ? queryParts : [queryText]) : [],
        brand: modelIntent?.brand || ruleSignals.brand,
        series: null,
        model: modelIntent?.model || ruleSignals.model,
        sku: modelIntent?.sku || ruleSignals.sku,
        product_name: productName,
        category,
        attributes: ruleSignals.attribute_aliases || [],
        specs: ruleSignals.specs || [],
        price_min: priceConstraints.price_min,
        price_max: priceConstraints.price_max,
        price_target: priceConstraints.price_target,
        crowd,
        usage_scene: usageScene,
        gift_target: giftTarget,
        sort_hint: sortHint,
        platform_hint: platformHint,
        confidence: modelIntent && recognizedBy === "model" ? modelIntent.confidence : recognizedBy === "rule_fallback" ? 0.6 : 0,
        evidence: {
            recognized_by: recognizedBy,
            model_provider: modelIntent?.model_provider,
            model_name: modelIntent?.model_name,
        },
    });
}
async function buildResponse(rawMessage) {
    const intent = await buildProductIntent(rawMessage);
    const searchRequest = (0, productSearchProtocol_1.buildSearchRequest)(intent);
    const publicIntent = compactProductSearchIntent(intent);
    const publicSearchRequest = compactSearchRequest(searchRequest);
    if (!intent.query_text) {
        return {
            success: false,
            module: "product-search",
            intent: "search_products",
            next_action: "ask_for_search_query",
            code: "NOT_PRODUCT_SEARCH_INTENT",
            result_type: "product_search",
            result_data: buildResultData(undefined, publicIntent, publicSearchRequest),
            error: buildErrorPayload("recognize_query", "NOT_PRODUCT_SEARCH_INTENT", "这句话里还没明确识别到你要搜索的商品。"),
            intent_mode: "search",
            product_search_intent: publicIntent,
            search_request: publicSearchRequest,
            user_message: "这句话里还没明确识别到你要搜索的商品。请直接告诉我商品名、型号，或像“300 左右的耳机”这样的购物需求。",
        };
    }
    let openId = "";
    try {
        ({ openId } = await (0, rebateV1Service_1.requireBoundOpenid)());
    }
    catch {
        return await buildStartAuthResponseForSearch(rawMessage);
    }
    const platform = intent.platform_hint || "";
    try {
        const searchResult = await (0, rebateV1Service_1.searchProductsByKeyword)(openId, intent.query_text, platform, 5);
        const items = Array.isArray(searchResult.items) ? searchResult.items : [];
        return {
            success: Boolean(items.length),
            module: "product-search",
            intent: "search_products",
            next_action: items.length ? "show_search_results" : "ask_for_refined_query",
            code: items.length ? null : "NO_SEARCH_RESULTS",
            result_type: "product_search",
            result_data: buildResultData(searchResult, publicIntent, publicSearchRequest),
            error: items.length ? null : buildErrorPayload("search_product", "NO_SEARCH_RESULTS", "暂时没有找到可返利商品。"),
            intent_mode: "search",
            product_search_intent: publicIntent,
            search_request: publicSearchRequest,
            search_result: searchResult,
            items,
            total: searchResult.total ?? items.length,
            user_message: (0, rebateV1Service_1.buildSearchResultsUserMessage)("商品搜索结果", searchResult),
        };
    }
    catch (error) {
        return {
            success: false,
            module: "product-search",
            intent: "search_products",
            next_action: "retry_product_search",
            code: "PRODUCT_SEARCH_API_ERROR",
            result_type: "product_search",
            result_data: buildResultData(undefined, publicIntent, publicSearchRequest),
            error: buildErrorPayload("search_product", "PRODUCT_SEARCH_API_ERROR", (0, rebateV1Service_1.extractServiceErrorMessage)(error, "商品搜索失败")),
            intent_mode: "search",
            product_search_intent: publicIntent,
            search_request: publicSearchRequest,
            search_result: null,
            user_message: `商品搜索失败，请稍后重试。\n\n${(0, rebateV1Service_1.extractServiceErrorMessage)(error, "商品搜索失败")}`,
        };
    }
}
async function emitResponse(rawMessage, outputFormat) {
    const response = await buildResponse(rawMessage);
    if (outputFormat === "json") {
        (0, common_1.printJson)(response);
    }
    else {
        (0, common_1.printUserMessage)(response.user_message || "", { markdown: outputFormat === "md" });
    }
    return response.success ? 0 : 1;
}


path: scripts/m01OperationGuide.js
content: "use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
    var ownKeys = function(o) {
        ownKeys = Object.getOwnPropertyNames || function (o) {
            var ar = [];
            for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
            return ar;
        };
        return ownKeys(o);
    };
    return function (mod) {
        if (mod && mod.__esModule) return mod;
        var result = {};
        if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
        __setModuleDefault(result, mod);
        return result;
    };
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.executeAction = executeAction;
exports.emitAction = emitAction;
const common_1 = require("./common");
const rebateV1Service_1 = require("./rebateV1Service");
const DETAILED_TUTORIAL = `好的,让我为您介绍一下如何使用返利助手!

━━━━━━━━━━━━━━━━━━━━━━━━━━━

【功能一:复制链接获取返利】

支持以下平台:

• 淘宝:复制商品详情页链接

• 京东:复制商品详情页链接

• 拼多多:复制商品详情页链接

操作步骤:

1. 在APP中找到心仪商品

2. 点击"分享"按钮

3. 选择"复制链接"

4. 回到对话窗口,粘贴链接给我

5. 我会自动生成返利链接和返利口令

━━━━━━━━━━━━━━━━━━━━━━━━━━━

【功能二:场景化搜索获取返利】

直接告诉我你的购物需求,例如:

• "我要买运动鞋"

• "推荐一款蓝牙耳机"

• "适合送给女朋友的礼物"

• "500元左右的手机"

• "性价比高的护肤品"

我会自动帮你搜索有佣金返利的商品!

━━━━━━━━━━━━━━━━━━━━━━━━━━━

【功能三:返利提现】

1. 下单后我会发送提现教程链接

2. 点击链接：
https://xiaomaxiangshenghuo.io.mlj130.com/follow.html
关注小马享生活公众号

3. 自动绑定,查看订单

4. 一键提现,返利到微信零钱

5. 返利金额在订单完成后3-7天到账

━━━━━━━━━━━━━━━━━━━━━━━━━━━

现在可以开始使用啦!有任何问题随时问我`;
const WITHDRAW_TUTORIAL = `💰 返利提现教程

━━━━━━━━━━━━━━━━━━━━━━━━━━━

【提现流程】

步骤1: 微信授权登录返利宝

步骤2：点击链接：
https://xiaomaxiangshenghuo.io.mlj130.com/follow.html
关注小马享生活公众号

步骤3: 自动绑定

关注公众号后,系统会自动绑定您的账号,无需额外操作

步骤4: 查看订单

在公众号内点击"我的订单",查看您的返利订单

步骤5: 一键提现

点击"立即提现",返利金额会自动到账到您的微信零钱

━━━━━━━━━━━━━━━━━━━━━━━━━━━

💡 提现说明

• 返利金额在订单完成后3-7天到账

• 单笔提现金额不限,满1元即可提现

• 提现到账后自动存入微信零钱

• 提现手续费全免`;
const AUTH_URL_PATTERN = /^https?:\/\//i;
const AUTH_LANDING_PAGE_URL = "https://xiaomaxiangshenghuo.io.mlj130.com/authlogin.html";
const FOLLOW_PAGE_URL = "https://xiaomaxiangshenghuo.io.mlj130.com/follow.html";
const REBATE_DETAILS_PAGE_URL = "https://xiaomaxiangshenghuo.io.mlj130.com/rebate-details.html";
const MIN_WITHDRAW_AMOUNT = 1;
const MIN_WITHDRAW_AMOUNT_TEXT = "1.00元";
const BOUND_SUCCESS_MESSAGE = [
    "你已经登录返利宝了，当前账号已完成微信授权绑定，无需重复注册或授权。",
    "",
    "现在你可以直接：",
    "1. 复制商品链接给我获取返利",
    "2. 直接告诉我想买什么",
    "3. 回复“提现教程”查看返现方式",
].join("\n");
function isValidAuthUrl(authUrl) {
    return AUTH_URL_PATTERN.test(String(authUrl || "").trim());
}
function buildAuthLandingUrl(machineCode) {
    const normalized = String(machineCode || "").trim();
    if (!normalized) {
        throw new Error("machine_code 不能为空");
    }
    return `${AUTH_LANDING_PAGE_URL}?machinecode=${encodeURIComponent(normalized)}`;
}
function buildErrorPayload(stage, code, message) {
    return { stage, code, message };
}
function buildResultData(payload) {
    const result = {};
    Object.entries(payload || {}).forEach(([key, value]) => {
        if (value !== null && value !== undefined) {
            result[key] = value;
        }
    });
    return result;
}
function withStandardResult(payload, options) {
    const normalized = { ...payload };
    normalized.result_type = options.resultType;
    normalized.result_data = options.resultData || {};
    normalized.error = options.error || null;
    if (normalized.code === undefined && options.error) {
        normalized.code = options.error.code;
    }
    return normalized;
}
function buildWelcomeAuthMessage(authUrl) {
    if (!isValidAuthUrl(authUrl)) {
        throw new Error("missing auth_url");
    }
    return [
        "使用前请先注册账号并关注公众号：",
        "",
        "注册链接：",
        "",
        authUrl,
        "",
        "关注公众号后，返回回复“授权完成”",
        "",
        "之后就可以：",
        "",
        "复制商品链接获取返利",
        "",
        "场景化搜索获取返利",
        "",
        "下单后一键提现",
        "",
        "如需详情；可回复【详细教程】【提现教程】",
    ].join("\n");
}
function buildRebateDetailsUrl(openId) {
    const normalizedOpenId = String(openId || "").trim();
    if (!normalizedOpenId) {
        return "";
    }
    return `${REBATE_DETAILS_PAGE_URL}?openid=${encodeURIComponent(normalizedOpenId)}`;
}
function buildWithdrawTutorialMessage(openId = "") {
    const normalizedOpenId = String(openId || "").trim();
    if (!normalizedOpenId) {
        return WITHDRAW_TUTORIAL;
    }
    const orderUrl = buildRebateDetailsUrl(normalizedOpenId);
    return `💰 返利提现教程

━━━━━━━━━━━━━━━━━━━━━━━━━━━

【提现流程】

步骤1: 微信授权登录返利宝

步骤2：点击链接：
https://xiaomaxiangshenghuo.io.mlj130.com/follow.html
关注小马享生活公众号

步骤3: 自动绑定

关注公众号后,系统会自动绑定您的账号,无需额外操作

步骤4: 查看订单

在公众号内点击"我的订单",查看您的返利订单，也可以点击链接
${orderUrl}
查询

步骤5: 一键提现

点击"立即提现",返利金额会自动到账到您的微信零钱

━━━━━━━━━━━━━━━━━━━━━━━━━━━

💡 提现说明

• 返利金额在订单完成后3-7天到账

• 单笔提现金额不限,满1元即可提现

• 提现到账后自动存入微信零钱

• 提现手续费全免`;
}
function buildRebateDetailsUserMessage(openId) {
    return `你可以点击下面链接查看订单明细和返利明细：\n\n${buildRebateDetailsUrl(openId)}`;
}
function buildAccountBalanceWithRebateDetailsUserMessage(balance, openId) {
    const userMessage = (0, rebateV1Service_1.buildAccountBalanceUserMessage)(balance);
    const detailsUrl = buildRebateDetailsUrl(openId);
    if (!detailsUrl) {
        return userMessage;
    }
    return `${userMessage}\n\n查看订单明细和返利明细：\n${detailsUrl}`;
}
function toFiniteNumber(value) {
    const numberValue = Number(value);
    return Number.isFinite(numberValue) ? numberValue : null;
}
function extractWithdrawAmount(rawMessage) {
    const message = String(rawMessage || "").trim();
    if (!message) {
        return null;
    }
    const patterns = [
        /(?:提现|提取)\s*(?:金额)?\s*[¥￥]?\s*(\d+(?:\.\d+)?)\s*(?:元|块|rmb|RMB)?/,
        /[¥￥]?\s*(\d+(?:\.\d+)?)\s*(?:元|块|rmb|RMB)?\s*(?:提现|提取)/,
    ];
    for (const pattern of patterns) {
        const match = message.match(pattern);
        if (!match) {
            continue;
        }
        const amount = Number(match[1]);
        if (Number.isFinite(amount)) {
            return amount;
        }
    }
    return null;
}
function buildWithdrawBalanceAskAmountUserMessage(balance) {
    return [
        "当前账户可提现金额如下：",
        "",
        `可提现金额：${(0, rebateV1Service_1.formatPrice)(balance.available_amount)}元`,
        `提现中金额：${(0, rebateV1Service_1.formatPrice)(balance.withdrawing_amount)}元`,
        `冻结金额：${(0, rebateV1Service_1.formatPrice)(balance.frozen_amount)}元`,
        "",
        "当前可提现金额满足提现条件。",
        "请告诉我你要提现的金额，例如：提现10元",
    ].join("\n");
}
function buildWithdrawBalanceInsufficientUserMessage(balance) {
    return [
        "当前暂不可提现。",
        "",
        `可提现金额：${(0, rebateV1Service_1.formatPrice)(balance.available_amount)}元`,
        `最低提现金额：${MIN_WITHDRAW_AMOUNT_TEXT}`,
        `提现中金额：${(0, rebateV1Service_1.formatPrice)(balance.withdrawing_amount)}元`,
        "",
        "可提现金额未达到最低提现条件，暂时不能发起提现。",
    ].join("\n");
}
function buildWithdrawAmountTooSmallUserMessage(amount) {
    return [
        "提现金额不能低于1.00元。",
        "",
        `当前申请提现：${(0, rebateV1Service_1.formatPrice)(amount)}元`,
        "请重新输入提现金额，例如：提现10元",
    ].join("\n");
}
function buildWithdrawAmountTooLargeUserMessage(amount, availableAmount) {
    return [
        "提现金额大于可提现金额。",
        "",
        `当前可提现金额：${(0, rebateV1Service_1.formatPrice)(availableAmount)}元`,
        `当前申请提现：${(0, rebateV1Service_1.formatPrice)(amount)}元`,
        "",
        "请重新输入不超过可提现金额的提现金额。",
    ].join("\n");
}
function buildWithdrawConfirmationUserMessage(amount, balance) {
    return [
        "提现前请先确认你已经关注小马享生活公众号，否则无法顺利领取微信红包。",
        "",
        "关注链接：",
        FOLLOW_PAGE_URL,
        "",
        "提现确认信息：",
        `提现金额：${(0, rebateV1Service_1.formatPrice)(amount)}元`,
        `可提现金额：${(0, rebateV1Service_1.formatPrice)(balance.available_amount)}元`,
        `提现中金额：${(0, rebateV1Service_1.formatPrice)(balance.withdrawing_amount)}元`,
        "",
        "确认无误后，请回复：",
        "确认提现",
    ].join("\n");
}
function buildWithdrawApplySuccessUserMessage(withdraw, openId) {
    const detailsUrl = buildRebateDetailsUrl(openId);
    const lines = [
        "提现申请已提交，请注意去微信查看红包。",
        "",
        `提现金额：${(0, rebateV1Service_1.formatPrice)(withdraw.amount)}元`,
        `提现状态：${withdraw.status || "processing"}`,
        `申请单号：${withdraw.request_no || withdraw.withdraw_id || "无"}`,
        `提交时间：${withdraw.created_at || "无"}`,
    ];
    if (withdraw.arrival_text) {
        lines.push(`到账说明：${withdraw.arrival_text}`);
    }
    if (detailsUrl) {
        lines.push("", "提现记录可以点击下面链接查询：", detailsUrl);
    }
    return lines.join("\n");
}
function buildWithdrawApplyFailureUserMessage(message) {
    return [
        "提现申请失败。",
        "",
        `失败原因：${message}`,
        "",
        "请稍后重试，或者前往小马享生活公众号进行提现",
        "关注链接：",
        FOLLOW_PAGE_URL,
    ].join("\n");
}
function getLocalOpenId(machineCode) {
    const binding = (0, common_1.loadLocalOpenidBinding)(machineCode) || {};
    return String(binding.open_id || "").trim();
}
function hasLocalOpenid(machineCode) {
    return Boolean(getLocalOpenId(machineCode));
}
function buildBindingCheckPayload(machineCode, openId) {
    return {
        machine_code: machineCode,
        openid: String(openId || "").trim(),
    };
}
function normalizeRemoteBindingData(machineCode, data) {
    const payload = data || {};
    const bindStatus = String(payload.bind_status || "").trim() || "unbound";
    const openId = String(payload.openid || "").trim();
    const authUrl = String(payload.auth_url || "").trim();
    return {
        machine_code: String(payload.machine_code || "").trim() || machineCode,
        open_id: openId,
        bind_status: bindStatus,
        status: bindStatus,
        authorized: bindStatus === "bound" && Boolean(openId),
        auth_url: authUrl,
        can_withdraw: Boolean(payload.can_withdraw || false),
        available_amount: payload.available_amount ?? 0,
        pending_amount: payload.pending_amount ?? 0,
    };
}
function syncRemoteBindingState(machineCode, data) {
    const normalized = normalizeRemoteBindingData(machineCode, data);
    (0, common_1.updateLocalAuthState)(machineCode, {
        machine_code: machineCode,
        status: normalized.status,
        bind_status: normalized.bind_status,
        auth_url: normalized.auth_url,
        open_id: normalized.open_id || null,
        can_withdraw: normalized.can_withdraw,
        available_amount: normalized.available_amount,
        pending_amount: normalized.pending_amount,
    });
    if (normalized.authorized) {
        (0, common_1.saveLocalOpenidBinding)({
            machine_code: machineCode,
            open_id: normalized.open_id,
            can_withdraw: normalized.can_withdraw,
            available_amount: normalized.available_amount,
            pending_amount: normalized.pending_amount,
        });
    }
    return normalized;
}
async function fetchRemoteBindingStatus(machineCode) {
    const response = await (0, common_1.requestRebateV1Json)("POST", "/v1/auth/binding-check", buildBindingCheckPayload(machineCode, getLocalOpenId(machineCode)));
    return syncRemoteBindingState(machineCode, response.data || {});
}
function buildAuthUrlData(machineCode) {
    const authUrl = buildAuthLandingUrl(machineCode);
    if (!isValidAuthUrl(authUrl)) {
        throw new Error("未生成有效注册链接");
    }
    const data = {
        machine_code: machineCode,
        auth_url: authUrl,
        expire_at: null,
    };
    (0, common_1.updateLocalAuthState)(machineCode, {
        machine_code: machineCode,
        status: "pending",
        bind_status: "unbound",
        auth_url: authUrl,
        expire_at: data.expire_at,
        open_id: getLocalOpenId(machineCode) || null,
    });
    return data;
}
function extractRuntimeErrorMessage(error, defaultMessage) {
    const rawMessage = String(error instanceof Error ? error.message : error || "").trim();
    if (!rawMessage) {
        return defaultMessage;
    }
    try {
        const payload = JSON.parse(rawMessage);
        const detail = payload.detail || {};
        if (detail && typeof detail === "object" && detail.message) {
            return String(detail.message);
        }
        return String(payload.message || rawMessage);
    }
    catch {
        return rawMessage;
    }
}
async function resolveMachineCode() {
    return await (0, common_1.getOrCreateMachineCode)();
}
async function buildBindingStatusResponse() {
    const machineCode = await resolveMachineCode();
    const localBinding = (0, common_1.loadLocalOpenidBinding)(machineCode);
    let binding = null;
    try {
        binding = await fetchRemoteBindingStatus(machineCode);
    }
    catch {
        binding = null;
    }
    if (binding?.authorized) {
        return [
            withStandardResult({
                success: true,
                module: "operation-guide",
                intent: "binding_status",
                next_action: "enter_rebate_flow",
                step: "check_binding_status",
                machine_code: machineCode,
                authorized: true,
                open_id: binding.open_id,
                binding,
                message: "当前已完成微信授权绑定",
                user_message: "你已经登录返利宝了，当前微信授权已完成。\n\n现在你可以：\n1. 发送商品链接获取返利\n2. 直接说想买什么商品让我帮你搜索",
            }, {
                resultType: "binding_status",
                resultData: buildResultData({
                    authorized: true,
                    open_id: binding.open_id,
                    binding,
                }),
            }),
            0,
        ];
    }
    if (localBinding) {
        return [
            withStandardResult({
                success: true,
                module: "operation-guide",
                intent: "binding_status",
                next_action: "enter_rebate_flow",
                step: "check_binding_status",
                machine_code: machineCode,
                authorized: true,
                open_id: localBinding.open_id,
                binding: localBinding,
                message: "当前已完成微信授权绑定",
                user_message: "你已经登录返利宝了，当前本地存在已绑定的微信授权信息。\n\n现在你可以：\n1. 发送商品链接获取返利\n2. 直接说想买什么商品让我帮你搜索",
            }, {
                resultType: "binding_status",
                resultData: buildResultData({
                    authorized: true,
                    open_id: localBinding.open_id,
                    binding: localBinding,
                }),
            }),
            0,
        ];
    }
    return [
        withStandardResult({
            success: true,
            module: "operation-guide",
            intent: "binding_status",
            next_action: "start_auth",
            step: "check_binding_status",
            machine_code: machineCode,
            authorized: false,
            auth_url: binding?.auth_url || null,
            message: "当前未检测到已绑定 openId",
            user_message: "你现在还没有登录返利宝。回复【返利】开始授权登录。",
        }, {
            resultType: "binding_status",
            resultData: buildResultData({
                authorized: false,
                auth_url: binding?.auth_url || null,
            }),
        }),
        0,
    ];
}
async function buildAuthLinkResponse() {
    const machineCode = await resolveMachineCode();
    let binding = null;
    if (hasLocalOpenid(machineCode)) {
        try {
            binding = await fetchRemoteBindingStatus(machineCode);
        }
        catch {
            binding = null;
        }
    }
    if (binding?.authorized) {
        return [
            withStandardResult({
                success: true,
                step: "generate_wechat_auth_link",
                machine_code: machineCode,
                auth_url: "",
                status: "bound",
                message: "当前已完成微信授权绑定",
                already_bound: true,
                open_id: binding.open_id,
            }, {
                resultType: "auth_link",
                resultData: buildResultData({
                    status: "bound",
                    already_bound: true,
                    open_id: binding.open_id,
                    auth_url: "",
                }),
            }),
            0,
        ];
    }
    let authUrl = "";
    let expireAt = null;
    try {
        const response = buildAuthUrlData(machineCode);
        authUrl = String(response.auth_url || "").trim();
        expireAt = response.expire_at || null;
    }
    catch (error) {
        const message = extractRuntimeErrorMessage(error, "注册链接生成失败");
        return [
            withStandardResult({
                success: false,
                step: "generate_wechat_auth_link",
                machine_code: machineCode,
                auth_url: "",
                status: "error",
                message,
            }, {
                resultType: "auth_link",
                resultData: buildResultData({ status: "error", auth_url: "" }),
                error: buildErrorPayload("generate_auth_link", "AUTH_LINK_ERROR", message),
            }),
            1,
        ];
    }
    return [
        withStandardResult({
            success: true,
            step: "generate_wechat_auth_link",
            machine_code: machineCode,
            auth_url: authUrl,
            status: "pending",
            expire_at: expireAt,
            message: "微信授权链接已生成",
            tips: ["请将 auth_url 发给用户打开。", "用户完成登录后，提示其回复“我已授权”。"],
        }, {
            resultType: "auth_link",
            resultData: buildResultData({
                status: "pending",
                auth_url: authUrl,
                expire_at: expireAt,
            }),
        }),
        0,
    ];
}
async function buildStartAuthResponse() {
    const machineCode = await resolveMachineCode();
    const binding = (0, common_1.loadLocalOpenidBinding)(machineCode);
    if (binding) {
        return [
            withStandardResult({
                success: true,
                module: "operation-guide",
                intent: "auth_already_bound",
                next_action: "enter_rebate_flow",
                machine_code: machineCode,
                authorized: true,
                open_id: binding.open_id,
                user_message: BOUND_SUCCESS_MESSAGE,
            }, {
                resultType: "auth_binding",
                resultData: buildResultData({
                    authorized: true,
                    open_id: binding.open_id,
                }),
            }),
            0,
        ];
    }
    let authUrl = null;
    try {
        const [authLinkPayload] = await buildAuthLinkResponse();
        if (authLinkPayload.already_bound || authLinkPayload.status === "bound") {
            const openId = String(authLinkPayload.open_id || "").trim();
            if (openId) {
                (0, common_1.saveLocalOpenidBinding)({
                    machine_code: machineCode,
                    open_id: openId,
                    can_withdraw: false,
                    available_amount: 0,
                    pending_amount: 0,
                });
            }
            (0, common_1.updateLocalAuthState)(machineCode, {
                machine_code: machineCode,
                status: "bound",
                bind_status: "bound",
                auth_url: "",
                open_id: openId || null,
            });
            return [
                withStandardResult({
                    success: true,
                    module: "operation-guide",
                    intent: "auth_already_bound",
                    next_action: "enter_rebate_flow",
                    machine_code: machineCode,
                    authorized: true,
                    open_id: openId,
                    user_message: BOUND_SUCCESS_MESSAGE,
                }, {
                    resultType: "auth_binding",
                    resultData: buildResultData({
                        authorized: true,
                        open_id: openId,
                    }),
                }),
                0,
            ];
        }
        authUrl = authLinkPayload.auth_url || null;
    }
    catch {
        authUrl = null;
    }
    const success = isValidAuthUrl(authUrl || "");
    const userMessage = success
        ? buildWelcomeAuthMessage(String(authUrl || ""))
        : "注册链接生成失败，请稍后重试。\n\n当前没有生成有效的注册链接。";
    const nextAction = success ? "open_auth_url" : "retry_auth_link";
    const code = success ? null : "AUTH_URL_MISSING";
    return [
        withStandardResult({
            success,
            module: "operation-guide",
            intent: "start_auth",
            next_action: nextAction,
            machine_code: machineCode,
            authorized: false,
            auth_url: authUrl,
            code,
            user_message: userMessage,
        }, {
            resultType: "auth_binding",
            resultData: buildResultData({
                authorized: false,
                auth_url: authUrl,
            }),
            error: success ? null : buildErrorPayload("generate_auth_link", "AUTH_URL_MISSING", userMessage),
        }),
        success ? 0 : 1,
    ];
}
async function buildExchangeOpenidResponse() {
    const machineCode = await resolveMachineCode();
    let binding;
    try {
        binding = await fetchRemoteBindingStatus(machineCode);
    }
    catch (error) {
        const message = extractRuntimeErrorMessage(error, "绑定状态检查失败");
        return [
            withStandardResult({
                success: false,
                step: "exchange_openid",
                machine_code: machineCode,
                status: "unknown",
                message,
                code: "BINDING_CHECK_FAILED",
                tips: ["请检查返利测试环境接口是否可用。", "如果接口正常，再让用户重新回复“我已授权”。"],
            }, {
                resultType: "auth_binding",
                resultData: buildResultData({ status: "unknown" }),
                error: buildErrorPayload("check_binding_status", "BINDING_CHECK_FAILED", message),
            }),
            1,
        ];
    }
    if (!binding.authorized) {
        return [
            withStandardResult({
                success: false,
                step: "exchange_openid",
                machine_code: machineCode,
                status: binding.status,
                auth_url: binding.auth_url,
                message: "尚未检测到微信授权完成",
                code: "AUTH_NOT_COMPLETED",
                tips: ["请先打开授权链接完成微信登录。", "完成后，再次执行本脚本或让用户重新回复“我已授权”。"],
            }, {
                resultType: "auth_binding",
                resultData: buildResultData({
                    status: binding.status,
                    auth_url: binding.auth_url,
                    authorized: false,
                }),
                error: buildErrorPayload("confirm_auth", "AUTH_NOT_COMPLETED", "尚未检测到微信授权完成"),
            }),
            1,
        ];
    }
    return [
        withStandardResult({
            success: true,
            step: "exchange_openid",
            machine_code: machineCode,
            open_id: binding.open_id,
            message: "openId 已同步",
            local_store: "wechat-openid-store.json",
        }, {
            resultType: "auth_binding",
            resultData: buildResultData({
                authorized: true,
                open_id: binding.open_id,
                local_store: "wechat-openid-store.json",
            }),
        }),
        0,
    ];
}
async function executePendingRebateRequest(request) {
    const rawMessage = request.raw_message;
    if (request.scene === "link_rebate") {
        const { buildResponse } = await Promise.resolve().then(() => __importStar(require("./m02PlatformLink")));
        const payload = await buildResponse(rawMessage);
        return [payload, payload.success ? 0 : 1];
    }
    if (request.scene === "search_rebate") {
        const { buildResponse } = await Promise.resolve().then(() => __importStar(require("./productSearch")));
        const payload = await buildResponse(rawMessage);
        return [payload, payload.success ? 0 : 1];
    }
    return null;
}
async function resumePendingRebateRequest(machineCode) {
    const request = (0, common_1.loadPendingAuthRequest)(machineCode);
    if (!request) {
        return null;
    }
    (0, common_1.clearPendingAuthRequest)(machineCode);
    const resumed = await executePendingRebateRequest(request);
    if (!resumed) {
        return null;
    }
    const [payload, exitCode] = resumed;
    payload.resumed_after_auth = true;
    payload.pending_auth_request = {
        scene: request.scene,
        handler: request.handler,
        created_at: request.created_at,
    };
    return [payload, exitCode];
}
async function buildConfirmAuthResponse() {
    const machineCode = await resolveMachineCode();
    const [exchangePayload, exitCode] = await buildExchangeOpenidResponse();
    if (exitCode !== 0) {
        const payload = JSON.parse(JSON.stringify(exchangePayload));
        if (payload.code === "BINDING_CHECK_FAILED") {
            const message = payload.message || "授权状态查询失败";
            payload.module = "operation-guide";
            payload.intent = "binding_check_failed";
            payload.next_action = "retry_confirm_auth";
            payload.authorized = false;
            payload.user_message = `授权状态查询失败，请稍后重试。\n\n${message}`;
            return [
                withStandardResult(payload, {
                    resultType: "auth_binding",
                    resultData: buildResultData({
                        authorized: false,
                        auth_url: payload.auth_url || null,
                        open_id: payload.open_id,
                        status: payload.status,
                    }),
                    error: buildErrorPayload("confirm_auth", payload.code || "BINDING_CHECK_FAILED", message),
                }),
                1,
            ];
        }
        const localSession = (0, common_1.loadLocalAuthState)(machineCode) || {};
        let authUrl = payload.auth_url || localSession.auth_url || null;
        if (!authUrl) {
            try {
                const [authLinkPayload] = await buildAuthLinkResponse();
                authUrl = authLinkPayload.auth_url || null;
            }
            catch {
                authUrl = null;
            }
        }
        let userMessage = "我还没有检测到授权完成。\n\n请先打开刚才的授权链接完成登录，然后再回复“我已授权”。";
        if (authUrl) {
            userMessage = `我还没有检测到授权完成。\n\n请先打开下面的授权链接完成登录：\n\n${authUrl}\n\n完成后再回复“我已授权”。`;
        }
        payload.module = "operation-guide";
        payload.intent = "auth_not_completed";
        payload.next_action = "wait_auth_confirmation";
        payload.authorized = false;
        payload.auth_url = authUrl;
        payload.user_message = userMessage;
        return [
            withStandardResult(payload, {
                resultType: "auth_binding",
                resultData: buildResultData({
                    authorized: false,
                    auth_url: authUrl,
                    open_id: payload.open_id,
                    status: payload.status,
                }),
                error: buildErrorPayload("confirm_auth", payload.code || "AUTH_NOT_COMPLETED", payload.message || "尚未检测到微信授权完成"),
            }),
            1,
        ];
    }
    const binding = { open_id: exchangePayload.open_id };
    const localSession = (0, common_1.loadLocalAuthState)(machineCode) || {};
    if (Object.keys(localSession).length) {
        localSession.status = "bound";
        localSession.bind_status = "bound";
        localSession.open_id = binding.open_id;
        (0, common_1.updateLocalAuthState)(machineCode, localSession);
    }
    const payload = withStandardResult({
        success: true,
        module: "operation-guide",
        intent: "auth_confirmed",
        next_action: "enter_rebate_flow",
        machine_code: machineCode,
        authorized: true,
        open_id: binding.open_id,
        user_message: BOUND_SUCCESS_MESSAGE,
    }, {
        resultType: "auth_binding",
        resultData: buildResultData({
            authorized: true,
            open_id: binding.open_id,
        }),
    });
    const resumed = await resumePendingRebateRequest(machineCode);
    if (resumed) {
        return resumed;
    }
    return [payload, 0];
}
function buildDetailedTutorialResponse() {
    return [
        withStandardResult({
            success: true,
            module: "operation-guide",
            intent: "detailed_tutorial",
            next_action: "send_detailed_tutorial",
            user_message: DETAILED_TUTORIAL,
        }, {
            resultType: "tutorial",
            resultData: buildResultData({ tutorial_type: "detailed_tutorial" }),
        }),
        0,
    ];
}
async function buildWithdrawTutorialResponse() {
    let openId = "";
    try {
        openId = getLocalOpenId(await resolveMachineCode());
    }
    catch {
        openId = "";
    }
    return [
        withStandardResult({
            success: true,
            module: "operation-guide",
            intent: "withdraw_tutorial",
            next_action: "send_withdraw_tutorial",
            user_message: buildWithdrawTutorialMessage(openId),
        }, {
            resultType: "tutorial",
            resultData: buildResultData({ tutorial_type: "withdraw_tutorial" }),
        }),
        0,
    ];
}
async function buildRebateDetailsResponse() {
    let openId = "";
    try {
        ({ openId } = await (0, rebateV1Service_1.requireBoundOpenid)());
    }
    catch {
        return await buildStartAuthResponse();
    }
    const detailsUrl = buildRebateDetailsUrl(openId);
    return [
        withStandardResult({
            success: true,
            module: "operation-guide",
            intent: "rebate_details",
            next_action: "open_rebate_details",
            authorized: true,
            open_id: openId,
            rebate_details_url: detailsUrl,
            user_message: buildRebateDetailsUserMessage(openId),
        }, {
            resultType: "rebate_details",
            resultData: buildResultData({
                authorized: true,
                open_id: openId,
                rebate_details_url: detailsUrl,
            }),
        }),
        0,
    ];
}
async function buildAccountBalanceResponse() {
    let openId = "";
    try {
        ({ openId } = await (0, rebateV1Service_1.requireBoundOpenid)());
    }
    catch {
        return [
            withStandardResult({
                success: false,
                module: "operation-guide",
                intent: "account_balance",
                next_action: "start_auth",
                authorized: false,
                user_message: "查询账户余额前请先完成微信授权。回复【返利】开始授权登录。",
            }, {
                resultType: "account_balance",
                resultData: buildResultData({ authorized: false }),
                error: buildErrorPayload("auth", "AUTH_REQUIRED", "查询账户余额前请先完成微信授权。"),
            }),
            1,
        ];
    }
    let balance;
    try {
        balance = await (0, rebateV1Service_1.queryAccountBalance)(openId);
    }
    catch (error) {
        const message = (0, rebateV1Service_1.extractServiceErrorMessage)(error, "账户余额查询失败");
        return [
            withStandardResult({
                success: false,
                module: "operation-guide",
                intent: "account_balance",
                next_action: "retry_account_balance",
                authorized: true,
                open_id: openId,
                user_message: `账户余额查询失败，请稍后重试。\n\n${message}`,
            }, {
                resultType: "account_balance",
                resultData: buildResultData({
                    authorized: true,
                    open_id: openId,
                }),
                error: buildErrorPayload("account_balance", "ACCOUNT_BALANCE_QUERY_ERROR", message),
            }),
            1,
        ];
    }
    const detailsUrl = buildRebateDetailsUrl(openId);
    return [
        withStandardResult({
            success: true,
            module: "operation-guide",
            intent: "account_balance",
            next_action: "return_account_balance",
            authorized: true,
            open_id: openId,
            balance,
            rebate_details_url: detailsUrl,
            user_message: buildAccountBalanceWithRebateDetailsUserMessage(balance, openId),
        }, {
            resultType: "account_balance",
            resultData: buildResultData({
                authorized: true,
                open_id: openId,
                balance,
                rebate_details_url: detailsUrl,
            }),
        }),
        0,
    ];
}
async function buildWithdrawPrepareResponse(rawMessage = "") {
    const amount = extractWithdrawAmount(rawMessage);
    if (amount !== null && amount < MIN_WITHDRAW_AMOUNT) {
        return [
            withStandardResult({
                success: false,
                module: "operation-guide",
                intent: "withdraw_prepare",
                next_action: "ask_withdraw_amount",
                code: "WITHDRAW_AMOUNT_TOO_SMALL",
                amount,
                user_message: buildWithdrawAmountTooSmallUserMessage(amount),
            }, {
                resultType: "withdraw",
                resultData: buildResultData({ amount }),
                error: buildErrorPayload("withdraw_prepare", "WITHDRAW_AMOUNT_TOO_SMALL", "提现金额不能低于1.00元。"),
            }),
            0,
        ];
    }
    let machineCode = "";
    let openId = "";
    try {
        ({ machineCode, openId } = await (0, rebateV1Service_1.requireBoundOpenid)());
    }
    catch {
        return await buildStartAuthResponse();
    }
    let balance;
    try {
        balance = await (0, rebateV1Service_1.queryAccountBalance)(openId);
    }
    catch (error) {
        const message = (0, rebateV1Service_1.extractServiceErrorMessage)(error, "账户余额查询失败");
        return [
            withStandardResult({
                success: false,
                module: "operation-guide",
                intent: "withdraw_prepare",
                next_action: "retry_withdraw_prepare",
                code: "WITHDRAW_BALANCE_QUERY_ERROR",
                authorized: true,
                open_id: openId,
                amount,
                user_message: `提现前余额查询失败，请稍后重试。\n\n${message}`,
            }, {
                resultType: "withdraw",
                resultData: buildResultData({
                    authorized: true,
                    open_id: openId,
                    amount,
                }),
                error: buildErrorPayload("withdraw_prepare", "WITHDRAW_BALANCE_QUERY_ERROR", message),
            }),
            0,
        ];
    }
    const availableAmount = toFiniteNumber(balance.available_amount) ?? 0;
    const withdrawingAmount = toFiniteNumber(balance.withdrawing_amount);
    if (amount === null) {
        (0, common_1.clearPendingWithdrawRequest)(machineCode);
        if (availableAmount < MIN_WITHDRAW_AMOUNT) {
            return [
                withStandardResult({
                    success: false,
                    module: "operation-guide",
                    intent: "withdraw_prepare",
                    next_action: "wait_withdrawable_balance",
                    code: "WITHDRAW_AVAILABLE_AMOUNT_TOO_SMALL",
                    authorized: true,
                    open_id: openId,
                    balance,
                    user_message: buildWithdrawBalanceInsufficientUserMessage(balance),
                }, {
                    resultType: "withdraw",
                    resultData: buildResultData({
                        authorized: true,
                        open_id: openId,
                        balance,
                    }),
                    error: buildErrorPayload("withdraw_prepare", "WITHDRAW_AVAILABLE_AMOUNT_TOO_SMALL", "可提现金额未达到最低提现条件。"),
                }),
                0,
            ];
        }
        return [
            withStandardResult({
                success: true,
                module: "operation-guide",
                intent: "withdraw_prepare",
                next_action: "ask_withdraw_amount",
                authorized: true,
                open_id: openId,
                balance,
                user_message: buildWithdrawBalanceAskAmountUserMessage(balance),
            }, {
                resultType: "withdraw",
                resultData: buildResultData({
                    authorized: true,
                    open_id: openId,
                    balance,
                }),
            }),
            0,
        ];
    }
    if (amount > availableAmount) {
        return [
            withStandardResult({
                success: false,
                module: "operation-guide",
                intent: "withdraw_prepare",
                next_action: "ask_withdraw_amount",
                code: "WITHDRAW_AMOUNT_EXCEEDS_AVAILABLE",
                authorized: true,
                open_id: openId,
                amount,
                balance,
                user_message: buildWithdrawAmountTooLargeUserMessage(amount, availableAmount),
            }, {
                resultType: "withdraw",
                resultData: buildResultData({
                    authorized: true,
                    open_id: openId,
                    amount,
                    balance,
                }),
                error: buildErrorPayload("withdraw_prepare", "WITHDRAW_AMOUNT_EXCEEDS_AVAILABLE", "提现金额大于可提现金额。"),
            }),
            0,
        ];
    }
    const pendingWithdraw = (0, common_1.savePendingWithdrawRequest)(machineCode, {
        openid: openId,
        amount,
        availableAmount,
        withdrawingAmount,
    });
    return [
        withStandardResult({
            success: true,
            module: "operation-guide",
            intent: "withdraw_prepare",
            next_action: "confirm_withdraw",
            authorized: true,
            open_id: openId,
            amount,
            balance,
            pending_withdraw: pendingWithdraw,
            user_message: buildWithdrawConfirmationUserMessage(amount, balance),
        }, {
            resultType: "withdraw",
            resultData: buildResultData({
                authorized: true,
                open_id: openId,
                amount,
                balance,
                pending_withdraw: pendingWithdraw,
            }),
        }),
        0,
    ];
}
async function buildWithdrawConfirmResponse() {
    let machineCode = "";
    let openId = "";
    try {
        ({ machineCode, openId } = await (0, rebateV1Service_1.requireBoundOpenid)());
    }
    catch {
        return await buildStartAuthResponse();
    }
    const pendingWithdraw = (0, common_1.loadPendingWithdrawRequest)(machineCode);
    if (!pendingWithdraw) {
        return [
            withStandardResult({
                success: false,
                module: "operation-guide",
                intent: "withdraw_confirm",
                next_action: "ask_withdraw_amount",
                code: "WITHDRAW_PENDING_NOT_FOUND",
                authorized: true,
                open_id: openId,
                user_message: "没有找到待确认的提现申请。请先输入提现金额，例如：提现10元",
            }, {
                resultType: "withdraw",
                resultData: buildResultData({
                    authorized: true,
                    open_id: openId,
                }),
                error: buildErrorPayload("withdraw_confirm", "WITHDRAW_PENDING_NOT_FOUND", "没有找到待确认的提现申请。"),
            }),
            1,
        ];
    }
    if (String(pendingWithdraw.openid || "").trim() !== openId) {
        (0, common_1.clearPendingWithdrawRequest)(machineCode);
        return [
            withStandardResult({
                success: false,
                module: "operation-guide",
                intent: "withdraw_confirm",
                next_action: "ask_withdraw_amount",
                code: "WITHDRAW_PENDING_OPENID_MISMATCH",
                authorized: true,
                open_id: openId,
                user_message: "待确认的提现申请已失效。请重新输入提现金额，例如：提现10元",
            }, {
                resultType: "withdraw",
                resultData: buildResultData({
                    authorized: true,
                    open_id: openId,
                }),
                error: buildErrorPayload("withdraw_confirm", "WITHDRAW_PENDING_OPENID_MISMATCH", "待确认的提现申请已失效。"),
            }),
            1,
        ];
    }
    const amount = Number(pendingWithdraw.amount);
    try {
        const withdraw = await (0, rebateV1Service_1.applyWithdraw)(openId, amount);
        (0, common_1.clearPendingWithdrawRequest)(machineCode);
        return [
            withStandardResult({
                success: true,
                module: "operation-guide",
                intent: "withdraw_confirm",
                next_action: "return_withdraw_result",
                authorized: true,
                open_id: openId,
                amount,
                withdraw,
                user_message: buildWithdrawApplySuccessUserMessage({ amount, ...withdraw }, openId),
            }, {
                resultType: "withdraw",
                resultData: buildResultData({
                    authorized: true,
                    open_id: openId,
                    amount,
                    withdraw,
                }),
            }),
            0,
        ];
    }
    catch (error) {
        const message = (0, rebateV1Service_1.extractServiceErrorMessage)(error, "提现申请失败");
        (0, common_1.clearPendingWithdrawRequest)(machineCode);
        return [
            withStandardResult({
                success: false,
                module: "operation-guide",
                intent: "withdraw_confirm",
                next_action: "retry_withdraw_or_use_official_account",
                code: "WITHDRAW_APPLY_ERROR",
                authorized: true,
                open_id: openId,
                amount,
                pending_withdraw: pendingWithdraw,
                user_message: buildWithdrawApplyFailureUserMessage(message),
            }, {
                resultType: "withdraw",
                resultData: buildResultData({
                    authorized: true,
                    open_id: openId,
                    amount,
                    pending_withdraw: pendingWithdraw,
                }),
                error: buildErrorPayload("withdraw_confirm", "WITHDRAW_APPLY_ERROR", message),
            }),
            1,
        ];
    }
}
async function executeAction(action, rawMessage = "") {
    const actionMap = {
        binding_status: buildBindingStatusResponse,
        auth_link: buildAuthLinkResponse,
        start_auth: buildStartAuthResponse,
        exchange_openid: buildExchangeOpenidResponse,
        confirm_auth: buildConfirmAuthResponse,
        detailed_tutorial: buildDetailedTutorialResponse,
        withdraw_tutorial: buildWithdrawTutorialResponse,
        withdraw_prepare: () => buildWithdrawPrepareResponse(rawMessage),
        withdraw_confirm: buildWithdrawConfirmResponse,
        rebate_details: buildRebateDetailsResponse,
        account_balance: buildAccountBalanceResponse,
    };
    if (!actionMap[action]) {
        throw new Error(`unsupported action: ${action}`);
    }
    return await actionMap[action]();
}
async function emitAction(action, outputFormat = "json", rawMessage = "") {
    const [payload, exitCode] = await executeAction(action, rawMessage);
    if (outputFormat === "json") {
        (0, common_1.printJson)(payload);
    }
    else {
        (0, common_1.printUserMessage)(payload.user_message || "", { markdown: outputFormat === "md" });
    }
    const handledWithdrawMessage = outputFormat !== "json" && payload.result_type === "withdraw" && Boolean(String(payload.user_message || "").trim());
    return handledWithdrawMessage ? 0 : exitCode;
}


path: scripts/m02PlatformLink.js
content: "use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.buildResponse = buildResponse;
exports.emitResponse = emitResponse;
const common_1 = require("./common");
const m01OperationGuide_1 = require("./m01OperationGuide");
const recognizePlatformLink_1 = require("./recognizePlatformLink");
const rebateV1Service_1 = require("./rebateV1Service");
function buildErrorPayload(stage, code, message) {
    return { stage, code, message: String(message || "").trim() };
}
function buildResultData(response, resolvedLink, searchResult, item, lease, rebateResult) {
    return {
        platform: response.platform,
        original_link: response.original_link,
        resolved_link: resolvedLink || null,
        search_result: searchResult || null,
        selected_item: item || null,
        lease: lease || null,
        rebate_result: rebateResult || null,
    };
}
async function normalizeLinkSearchInput(platform, originalLink) {
    let searchInput = String(originalLink || "").trim();
    let resolvedLink = null;
    let host = "";
    try {
        host = new URL(originalLink || "").hostname.trim().toLowerCase();
    }
    catch {
        host = "";
    }
    if (platform === "taobao" && host === "e.tb.cn") {
        resolvedLink = await (0, common_1.resolveTaobaoShortLink)(originalLink);
        searchInput = resolvedLink || searchInput;
    }
    return { searchInput, resolvedLink };
}
async function buildStartAuthResponseForLink(rawMessage) {
    try {
        (0, common_1.savePendingAuthRequest)(await (0, common_1.getOrCreateMachineCode)(), {
            scene: "link_rebate",
            handler: "m02_platform_link",
            rawMessage,
            reason: "链接返利场景执行时发现未授权",
        });
    }
    catch {
        // Best-effort only. Failing to persist pending request should not block auth link generation.
    }
    const [payload] = await (0, m01OperationGuide_1.executeAction)("start_auth");
    return payload;
}
async function buildResponse(rawMessage) {
    const response = (0, recognizePlatformLink_1.recognizeRawMessage)(rawMessage);
    if (!response.success) {
        return {
            ...response,
            module: "platform-link-rebate",
            intent: "create_rebate_link",
            next_action: "ask_for_supported_link",
            result_type: "link_rebate",
            result_data: buildResultData(response),
            error: buildErrorPayload("recognize_link", response.code || "UNSUPPORTED_LINK", "当前链接暂不支持返利识别，仅支持淘宝、京东、拼多多商品链接。"),
        };
    }
    if (response.platform === "multiple") {
        return {
            ...response,
            success: false,
            code: "MULTIPLE_LINKS_FOUND",
            next_action: "ask_for_single_link",
            result_type: "link_rebate",
            result_data: buildResultData(response),
            error: buildErrorPayload("recognize_link", "MULTIPLE_LINKS_FOUND", "检测到多个商品链接，请一次只发一个。"),
            user_message: `${String(response.user_message || "").trim()}\n\n请一次只发一个商品链接，我再帮你生成返利链接。`,
        };
    }
    let openId = "";
    try {
        ({ openId } = await (0, rebateV1Service_1.requireBoundOpenid)());
    }
    catch {
        return await buildStartAuthResponseForLink(rawMessage);
    }
    const platform = response.platform;
    const originalLink = response.original_link || rawMessage;
    let rawTextForLinkSearch = originalLink;
    let resolvedLink = null;
    try {
        const normalized = await normalizeLinkSearchInput(platform, originalLink);
        rawTextForLinkSearch = normalized.searchInput;
        resolvedLink = normalized.resolvedLink;
    }
    catch (error) {
        return {
            ...response,
            success: false,
            code: "TAOBAO_SHORT_LINK_RESOLVE_ERROR",
            next_action: "retry_link_search",
            result_type: "link_rebate",
            result_data: buildResultData(response),
            error: buildErrorPayload("resolve_short_link", "TAOBAO_SHORT_LINK_RESOLVE_ERROR", (0, rebateV1Service_1.extractServiceErrorMessage)(error, "短链解析失败")),
            user_message: `淘宝短链识别成功，但最终商品链接解析失败，请稍后重试或直接发送商品详情页链接。\n\n${(0, rebateV1Service_1.extractServiceErrorMessage)(error, "短链解析失败")}`,
        };
    }
    let searchResult;
    try {
        searchResult = await (0, rebateV1Service_1.searchProductByLink)(openId, platform, rawTextForLinkSearch);
    }
    catch (error) {
        return {
            ...response,
            success: false,
            code: "LINK_SEARCH_API_ERROR",
            next_action: "retry_link_search",
            result_type: "link_rebate",
            result_data: buildResultData(response, resolvedLink, null),
            error: buildErrorPayload("search_product", "LINK_SEARCH_API_ERROR", (0, rebateV1Service_1.extractServiceErrorMessage)(error, "商品查询失败")),
            search_result: null,
            user_message: `链接识别成功，但商品查询失败，请稍后重试。\n\n${(0, rebateV1Service_1.extractServiceErrorMessage)(error, "商品查询失败")}`,
        };
    }
    const items = Array.isArray(searchResult.items) ? searchResult.items : [];
    if (!items.length) {
        return {
            ...response,
            success: false,
            code: "LINK_PRODUCT_NOT_FOUND",
            next_action: "ask_for_supported_link",
            result_type: "link_rebate",
            result_data: buildResultData(response, resolvedLink, searchResult),
            error: buildErrorPayload("search_product", "LINK_PRODUCT_NOT_FOUND", "暂时没查到这个链接对应的返利商品。"),
            search_result: searchResult,
            user_message: "暂时没查到这个链接对应的返利商品，请换一个商品详情页链接再试。",
        };
    }
    const item = items[0];
    let lease;
    let convertResult;
    try {
        lease = await (0, rebateV1Service_1.applyAdzoneLease)(openId, item.platform || platform);
        convertResult = await (0, rebateV1Service_1.createRebateLink)(openId, lease.lease_id, item.platform || platform, item.item_id, rawTextForLinkSearch);
    }
    catch (error) {
        return {
            ...response,
            success: false,
            code: "REBATE_LINK_CREATE_ERROR",
            next_action: "retry_rebate_link_create",
            result_type: "link_rebate",
            result_data: buildResultData(response, resolvedLink, searchResult, item, typeof lease !== "undefined" ? lease : null),
            error: buildErrorPayload("create_rebate_link", "REBATE_LINK_CREATE_ERROR", (0, rebateV1Service_1.extractServiceErrorMessage)(error, "返利链接生成失败")),
            search_result: searchResult,
            selected_item: item,
            user_message: `商品已识别，但返利链接生成失败，请稍后重试。\n\n${(0, rebateV1Service_1.extractServiceErrorMessage)(error, "返利链接生成失败")}`,
        };
    }
    return {
        ...response,
        success: true,
        module: "platform-link-rebate",
        intent: "create_rebate_link",
        next_action: "return_rebate_link",
        result_type: "link_rebate",
        result_data: buildResultData(response, resolvedLink, searchResult, item, lease, convertResult),
        error: null,
        resolved_link: resolvedLink,
        search_result: searchResult,
        selected_item: item,
        lease,
        rebate_result: convertResult,
        user_message: (0, rebateV1Service_1.buildConvertResultUserMessage)(convertResult),
    };
}
async function emitResponse(rawMessage, outputFormat) {
    const response = await buildResponse(rawMessage);
    if (outputFormat === "json") {
        (0, common_1.printJson)(response);
    }
    else {
        (0, common_1.printUserMessage)(response.user_message || "", { markdown: outputFormat === "md" });
    }
    return response.success ? 0 : 1;
}


path: scripts/recognizePlatformLink.js
content: "use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.extractLinks = extractLinks;
exports.normalizeHost = normalizeHost;
exports.detectPlatform = detectPlatform;
exports.detectCarrierType = detectCarrierType;
exports.recognizeRawMessage = recognizeRawMessage;
const URL_PATTERN = /https?:\/\/[^\s]+/gi;
const TRAILING_PUNCTUATION = /["'”’」】〉》),，。；;！？!?]+$/;
const TAOBAO_DOMAINS = new Set([
    "e.tb.cn",
    "taobao.com",
    "tmall.com",
    "traveldetail.fliggy.com",
    "detail.liangxinyao.com",
    "dataoke.com",
]);
const JD_DOMAINS = new Set([
    "3.cn",
    "u.jd.com",
    "item.jd.com",
    "jingfen.jd.com",
    "pro.m.jd.com",
    "jd.com",
    "jd.hk",
    "jingtuitui.com",
]);
const PDD_DOMAINS = new Set([
    "yangkeduo.com",
    "pinduoduo.com",
    "p.pinduoduo.com",
    "p.pindoduo.com",
]);
const PLATFORM_LABELS = {
    taobao: "淘宝",
    jd: "京东",
    pdd: "拼多多",
    unknown: "未知",
};
const SHORT_LINK_HOSTS = new Set(["e.tb.cn", "3.cn", "u.jd.com", "p.pinduoduo.com", "p.pindoduo.com"]);
function extractLinks(rawMessage) {
    const matches = String(rawMessage || "").match(URL_PATTERN) || [];
    return matches
        .map((match) => match.replace(TRAILING_PUNCTUATION, ""))
        .filter(Boolean);
}
function normalizeHost(hostname) {
    let host = String(hostname || "").trim().toLowerCase();
    if (host.startsWith("www.")) {
        host = host.slice(4);
    }
    return host;
}
function matchesDomain(host, domain) {
    return host === domain || host.endsWith(`.${domain}`);
}
function detectPlatformFromHost(host) {
    for (const domain of TAOBAO_DOMAINS) {
        if (matchesDomain(host, domain)) {
            return "taobao";
        }
    }
    for (const domain of JD_DOMAINS) {
        if (matchesDomain(host, domain)) {
            return "jd";
        }
    }
    for (const domain of PDD_DOMAINS) {
        if (matchesDomain(host, domain)) {
            return "pdd";
        }
    }
    return "unknown";
}
function detectPlatform(link) {
    try {
        const host = normalizeHost(new URL(link).hostname);
        const platform = detectPlatformFromHost(host);
        return { platform, isSupported: platform !== "unknown" };
    }
    catch {
        return { platform: "unknown", isSupported: false };
    }
}
function detectCarrierType(rawMessage, link) {
    const stripped = String(rawMessage || "").trim();
    if (stripped === link) {
        try {
            const host = normalizeHost(new URL(link).hostname);
            if (SHORT_LINK_HOSTS.has(host)) {
                return "short_link";
            }
        }
        catch {
            return "unknown";
        }
        return "direct_link";
    }
    return "mixed_text";
}
function buildSuccessResponse(rawMessage, link) {
    const { platform, isSupported } = detectPlatform(link);
    const carrierType = detectCarrierType(rawMessage, link);
    return {
        success: true,
        module: "platform-link-recognition",
        intent: "recognize_platform_link",
        next_action: "return_link_info",
        platform,
        original_link: link,
        carrier_type: carrierType,
        is_supported: isSupported,
        payload: {
            platform,
            link,
        },
        user_message: `链接识别结果\n\n平台：${PLATFORM_LABELS[platform] || "未知"}\n链接：${link}`,
    };
}
function buildMultipleSuccessResponse(rawMessage, links) {
    const results = [];
    const lines = ["链接识别结果"];
    links.forEach((link, index) => {
        const { platform, isSupported } = detectPlatform(link);
        const carrierType = detectCarrierType(rawMessage, link);
        results.push({
            platform,
            original_link: link,
            carrier_type: carrierType,
            is_supported: isSupported,
        });
        lines.push("", `${index + 1}. 平台：${PLATFORM_LABELS[platform] || "未知"}`, `链接：${link}`);
    });
    const allSupported = results.every((item) => item.is_supported);
    return {
        success: allSupported,
        module: "platform-link-recognition",
        intent: "recognize_platform_link",
        next_action: allSupported ? "return_link_info" : "ask_for_supported_link",
        platform: "multiple",
        original_link: null,
        carrier_type: "mixed_text",
        is_supported: allSupported,
        payload: { results },
        user_message: lines.join("\n"),
    };
}
function buildUnknownResponse(link, code = "UNSUPPORTED_LINK") {
    const lines = [
        "这个链接我暂时还不能处理。",
        "",
        "目前只支持淘宝、京东、拼多多的商品链接、短链或口令返利。",
        "",
        "你可以直接把这 3 个平台的商品详情页链接发给我，我来帮你转成返利链接。",
    ];
    if (link) {
        lines.push("", `你刚发的链接：${link}`);
    }
    return {
        success: false,
        module: "platform-link-recognition",
        intent: "recognize_platform_link",
        next_action: "ask_for_supported_link",
        platform: "unknown",
        original_link: link || null,
        carrier_type: "unknown",
        is_supported: false,
        code,
        payload: {
            platform: "unknown",
            link: link || null,
        },
        user_message: lines.join("\n"),
    };
}
function recognizeRawMessage(rawMessage) {
    const normalizedMessage = String(rawMessage || "").trim();
    const links = extractLinks(normalizedMessage);
    if (!links.length) {
        return buildUnknownResponse(undefined, "NO_LINK_FOUND");
    }
    if (links.length > 1) {
        return buildMultipleSuccessResponse(normalizedMessage, links);
    }
    const link = links[0];
    const { isSupported } = detectPlatform(link);
    if (!isSupported) {
        return buildUnknownResponse(link);
    }
    return buildSuccessResponse(normalizedMessage, link);
}


path: scripts/recognizeFuzzyProductSearch.js
content: "use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.recognizeRawMessage = recognizeRawMessage;
exports.main = main;
const common_1 = require("./common");
const productSearchProtocol_1 = require("./productSearchProtocol");
const recognizePreciseProductSearch_1 = require("./recognizePreciseProductSearch");
const FUZZY_INTENT_SYSTEM_PROMPT = `
你是一个电商模糊商品意图抽取器。

你只能输出一个 JSON 对象，不能输出 markdown，不能输出解释。

抽取目标：
- is_fuzzy_search
- confidence
- brand
- product_name
- category
- attributes
- specs
- crowd
- usage_scene
- gift_target
- sort_hint
- platform_hint
- price_min
- price_max
- price_target

输出要求：
1. 标量槽位输出 {"value": ..., "evidence": ...}
2. attributes 和 specs 输出数组，元素格式为 {"value": ..., "evidence": ...}
3. price_min / price_max / price_target 输出 number 或 null
4. evidence 必须是原文中的原样片段，不能改写
5. 不确定就返回 null 或 []
6. 不允许编造品牌、价格、平台、人群、送礼对象
7. 这是模糊购物需求理解，不是明确 SKU/型号 搜索
8. 重点识别用户真正想买的商品，以及预算、人群、场景、送礼对象
`.trim();
function isFuzzySearchCandidate(cleanedText, ruleSignals) {
    const priceConstraints = (0, recognizePreciseProductSearch_1.extractPriceConstraints)(cleanedText);
    if ((0, recognizePreciseProductSearch_1.containsFuzzyMarker)(cleanedText)) {
        return true;
    }
    if ([priceConstraints.price_min, priceConstraints.price_max, priceConstraints.price_target].some((value) => value !== null && value !== undefined)) {
        return true;
    }
    if ([(0, recognizePreciseProductSearch_1.extractCrowd)(cleanedText), (0, recognizePreciseProductSearch_1.extractUsageScene)(cleanedText), (0, recognizePreciseProductSearch_1.extractGiftTarget)(cleanedText), (0, recognizePreciseProductSearch_1.extractSortHint)(cleanedText), (0, recognizePreciseProductSearch_1.extractPlatformHint)(cleanedText)].some(Boolean)) {
        return true;
    }
    if (ruleSignals.category || ruleSignals.product_name) {
        return true;
    }
    if ((ruleSignals.attribute_aliases || []).length) {
        return true;
    }
    return false;
}
function buildFuzzyModelUserPrompt(rawMessage, cleanedText, ruleSignals) {
    const heuristicHint = {
        brand: ruleSignals.brand,
        product_name: ruleSignals.product_name,
        category: ruleSignals.category,
        attributes: ruleSignals.attribute_aliases,
        specs: ruleSignals.specs,
        budget: ruleSignals.budget,
        platform_hint: ruleSignals.platform_hint,
    };
    return [
        "请从下面的模糊购物需求中抽取商品意图。",
        "只返回 JSON 对象。",
        "尽量识别用户真正想买的商品，不要把动作词、语气词、量词当成商品本身。",
        `原始消息: ${rawMessage}`,
        `清洗后消息: ${cleanedText}`,
        `启发式参考(仅供参考，原文优先): ${JSON.stringify(heuristicHint, undefined, 0)}`,
    ].join("\n");
}
async function callModelFuzzyIntentExtractor(rawMessage, cleanedText, ruleSignals) {
    const response = await (0, common_1.requestModelJson)(FUZZY_INTENT_SYSTEM_PROMPT, buildFuzzyModelUserPrompt(rawMessage, cleanedText, ruleSignals), 0.1, 1200);
    const payload = response.json || {};
    return {
        slot_source: "model",
        model_provider: response.provider,
        model_name: response.model,
        is_fuzzy_search: (0, recognizePreciseProductSearch_1.parseBool)(payload.is_fuzzy_search, false),
        confidence: (0, recognizePreciseProductSearch_1.parseConfidence)(payload.confidence, 0),
        brand: (0, recognizePreciseProductSearch_1.normalizeScalarRawSlot)(payload.brand, cleanedText),
        product_name: (0, recognizePreciseProductSearch_1.normalizeScalarRawSlot)(payload.product_name, cleanedText),
        category: (0, recognizePreciseProductSearch_1.normalizeScalarRawSlot)(payload.category, cleanedText),
        attributes: (0, recognizePreciseProductSearch_1.normalizeListRawSlots)(payload.attributes, cleanedText),
        specs: (0, recognizePreciseProductSearch_1.normalizeListRawSlots)(payload.specs, cleanedText),
        crowd: (0, recognizePreciseProductSearch_1.normalizeScalarRawSlot)(payload.crowd, cleanedText),
        usage_scene: (0, recognizePreciseProductSearch_1.normalizeScalarRawSlot)(payload.usage_scene, cleanedText),
        gift_target: (0, recognizePreciseProductSearch_1.normalizeScalarRawSlot)(payload.gift_target, cleanedText),
        sort_hint: (0, recognizePreciseProductSearch_1.normalizeScalarRawSlot)(payload.sort_hint, cleanedText),
        platform_hint: (0, recognizePreciseProductSearch_1.normalizeScalarRawSlot)(payload.platform_hint, cleanedText),
        price_min: (0, recognizePreciseProductSearch_1.normalizePriceNumber)(payload.price_min),
        price_max: (0, recognizePreciseProductSearch_1.normalizePriceNumber)(payload.price_max),
        price_target: (0, recognizePreciseProductSearch_1.normalizePriceNumber)(payload.price_target),
    };
}
function mergeModelAndRuleFuzzyIntent(cleanedText, ruleSignals, modelSlots) {
    const ruleIntent = (0, recognizePreciseProductSearch_1.buildFuzzyIntentFromRuleSignals)(cleanedText, ruleSignals);
    if (!modelSlots) {
        return [ruleIntent, "rule_fallback", null, null];
    }
    const brandValue = (0, recognizePreciseProductSearch_1.choosePreferredText)((0, recognizePreciseProductSearch_1.normalizeBrandValue)((0, recognizePreciseProductSearch_1.getScalarSlotValue)(modelSlots.brand), modelSlots.brand?.evidence), ruleIntent.brand);
    const categoryValue = (0, recognizePreciseProductSearch_1.choosePreferredText)((0, recognizePreciseProductSearch_1.getScalarSlotValue)(modelSlots.category), ruleIntent.category, true);
    const productNameValue = (0, recognizePreciseProductSearch_1.normalizeFuzzyProductName)((0, recognizePreciseProductSearch_1.choosePreferredText)((0, recognizePreciseProductSearch_1.getScalarSlotValue)(modelSlots.product_name), ruleIntent.product_name, true), categoryValue, cleanedText);
    const attributes = (0, recognizePreciseProductSearch_1.sortValuesByPosition)(cleanedText, (0, recognizePreciseProductSearch_1.dropSubstringValues)([...(0, recognizePreciseProductSearch_1.getListSlotValues)(modelSlots.attributes), ...(ruleIntent.attributes || [])]));
    const specs = (0, recognizePreciseProductSearch_1.sortValuesByPosition)(cleanedText, [...(0, recognizePreciseProductSearch_1.getListSlotValues)(modelSlots.specs), ...(ruleIntent.specs || [])]);
    const crowdValue = (0, recognizePreciseProductSearch_1.getScalarSlotValue)(modelSlots.crowd) || ruleIntent.crowd;
    const usageSceneValue = (0, recognizePreciseProductSearch_1.getScalarSlotValue)(modelSlots.usage_scene) || ruleIntent.usage_scene;
    const giftTargetValue = (0, recognizePreciseProductSearch_1.getScalarSlotValue)(modelSlots.gift_target) || ruleIntent.gift_target;
    const sortHintValue = (0, recognizePreciseProductSearch_1.getScalarSlotValue)(modelSlots.sort_hint) || ruleIntent.sort_hint;
    const platformHintValue = (0, recognizePreciseProductSearch_1.normalizePlatformHintValue)((0, recognizePreciseProductSearch_1.getScalarSlotValue)(modelSlots.platform_hint), modelSlots.platform_hint?.evidence) ||
        ruleIntent.platform_hint;
    const queryTokens = (0, recognizePreciseProductSearch_1.dedupePreserveOrder)([productNameValue, brandValue, usageSceneValue, giftTargetValue, crowdValue].filter(Boolean)).concat((0, recognizePreciseProductSearch_1.selectQueryAttributes)(attributes));
    const normalizedQueryTokens = (0, recognizePreciseProductSearch_1.dedupePreserveOrder)(queryTokens);
    const queryText = normalizedQueryTokens.join(" ").trim();
    const intent = (0, recognizePreciseProductSearch_1.buildProductSearchIntent)({
        success: true,
        intent_mode: "fuzzy",
        query_text: queryText,
        query_tokens: normalizedQueryTokens,
        brand: brandValue,
        series: null,
        model: ruleIntent.model,
        sku: ruleIntent.sku,
        product_name: productNameValue,
        category: categoryValue,
        attributes,
        specs,
        price_min: modelSlots.price_min ?? ruleIntent.price_min,
        price_max: modelSlots.price_max ?? ruleIntent.price_max,
        price_target: modelSlots.price_target ?? ruleIntent.price_target,
        crowd: crowdValue,
        usage_scene: usageSceneValue,
        gift_target: giftTargetValue,
        sort_hint: sortHintValue,
        platform_hint: platformHintValue,
        confidence: Math.max(ruleIntent.confidence || 0, (0, recognizePreciseProductSearch_1.parseConfidence)(modelSlots.confidence, 0)),
        evidence: {
            brand: (0, recognizePreciseProductSearch_1.pickScalarEvidence)(cleanedText, brandValue, modelSlots.brand) || ruleIntent.evidence?.brand,
            series: null,
            model: ruleIntent.evidence?.model,
            sku: ruleIntent.evidence?.sku,
            product_name: (0, recognizePreciseProductSearch_1.pickScalarEvidence)(cleanedText, productNameValue, modelSlots.product_name) || ruleIntent.evidence?.product_name,
            category: (0, recognizePreciseProductSearch_1.pickScalarEvidence)(cleanedText, categoryValue, modelSlots.category) || ruleIntent.evidence?.category,
            attributes: (0, recognizePreciseProductSearch_1.pickListEvidence)(cleanedText, attributes),
            specs: (0, recognizePreciseProductSearch_1.pickListEvidence)(cleanedText, specs),
            crowd: (0, recognizePreciseProductSearch_1.pickScalarEvidence)(cleanedText, crowdValue, modelSlots.crowd) || (crowdValue && (0, recognizePreciseProductSearch_1.evidenceInText)(cleanedText, crowdValue) ? crowdValue : null),
            usage_scene: (0, recognizePreciseProductSearch_1.pickScalarEvidence)(cleanedText, usageSceneValue, modelSlots.usage_scene) || (usageSceneValue && (0, recognizePreciseProductSearch_1.evidenceInText)(cleanedText, usageSceneValue) ? usageSceneValue : null),
            gift_target: (0, recognizePreciseProductSearch_1.pickScalarEvidence)(cleanedText, giftTargetValue, modelSlots.gift_target) || (giftTargetValue && (0, recognizePreciseProductSearch_1.evidenceInText)(cleanedText, giftTargetValue) ? giftTargetValue : null),
        },
    });
    return [intent, modelSlots.slot_source || "model", modelSlots.model_provider || null, modelSlots.model_name || null];
}
async function buildSuccessResponse(rawMessage, cleanedText, ruleSignals) {
    let modelSlots = null;
    try {
        modelSlots = await callModelFuzzyIntentExtractor(rawMessage, cleanedText, ruleSignals);
    }
    catch {
        modelSlots = null;
    }
    const [intent, slotSource, modelProvider, modelName] = mergeModelAndRuleFuzzyIntent(cleanedText, ruleSignals, modelSlots);
    const searchRequest = (0, productSearchProtocol_1.buildSearchRequest)(intent);
    return {
        success: true,
        module: "fuzzy-product-search",
        intent: "recognize_fuzzy_product_search",
        next_action: "prepare_product_search",
        intent_mode: "fuzzy",
        is_fuzzy_search: true,
        slot_source: slotSource,
        model_provider: modelProvider,
        model_name: modelName,
        product_search_intent: intent,
        search_request: searchRequest,
        user_message: (0, productSearchProtocol_1.buildSearchReadyUserMessage)("商品搜索意图识别结果", "已识别到商品搜索需求，可进入搜索", intent),
    };
}
function buildImpreciseResponse() {
    const intent = (0, recognizePreciseProductSearch_1.buildEmptyProductSearchIntent)("fuzzy", 0.2);
    return {
        success: false,
        module: "fuzzy-product-search",
        intent: "recognize_fuzzy_product_search",
        next_action: "ask_for_fuzzy_query",
        intent_mode: "fuzzy",
        is_fuzzy_search: false,
        code: "INSUFFICIENT_FUZZY_SIGNAL",
        product_search_intent: intent,
        search_request: (0, productSearchProtocol_1.buildSearchRequest)(intent),
        user_message: [
            "商品搜索意图识别结果",
            "",
            "当前还没识别到明确的购物需求。",
            "请直接告诉我 想买什么 / 预算 / 用途 / 送给谁。",
            "例如：300 左右通勤耳机、适合送女朋友的礼物、100 以内儿童水杯",
        ].join("\n"),
    };
}
function buildPreciseLikeResponse() {
    const intent = (0, recognizePreciseProductSearch_1.buildEmptyProductSearchIntent)("precise", 0);
    return {
        success: false,
        module: "fuzzy-product-search",
        intent: "recognize_fuzzy_product_search",
        next_action: "route_to_m03",
        intent_mode: "precise",
        is_fuzzy_search: false,
        code: "PRECISE_SEARCH_LIKE",
        product_search_intent: intent,
        search_request: (0, productSearchProtocol_1.buildSearchRequest)(intent),
        user_message: "商品搜索意图识别结果\n\n当前消息更像明确商品搜索，请按商品搜索处理。",
    };
}
function buildLinkLikeResponse() {
    const intent = (0, recognizePreciseProductSearch_1.buildEmptyProductSearchIntent)("fuzzy", 0);
    return {
        success: false,
        module: "fuzzy-product-search",
        intent: "recognize_fuzzy_product_search",
        next_action: "route_to_m02",
        intent_mode: null,
        is_fuzzy_search: false,
        code: "LINK_LIKE_MESSAGE",
        product_search_intent: intent,
        search_request: (0, productSearchProtocol_1.buildSearchRequest)(intent),
        user_message: "商品搜索意图识别结果\n\n当前消息更像商品链接，请按链接返利处理。",
    };
}
async function recognizeRawMessage(rawMessage) {
    const normalizedMessage = String(rawMessage || "").trim();
    if (!normalizedMessage) {
        return buildImpreciseResponse();
    }
    if (recognizePreciseProductSearch_1.LINK_PATTERN.test(normalizedMessage)) {
        return buildLinkLikeResponse();
    }
    const cleanedText = (0, recognizePreciseProductSearch_1.normalizeText)(normalizedMessage);
    const ruleSignals = (0, recognizePreciseProductSearch_1.extractRuleSignals)(cleanedText);
    if ((0, recognizePreciseProductSearch_1.isPreciseSearch)(ruleSignals.brand, ruleSignals.sku, ruleSignals.model, ruleSignals.product_name, ruleSignals.category, ruleSignals.category_aliases || [], ruleSignals.attribute_aliases || [], cleanedText, ruleSignals.budget)) {
        return buildPreciseLikeResponse();
    }
    if (!isFuzzySearchCandidate(cleanedText, ruleSignals)) {
        return buildImpreciseResponse();
    }
    return await buildSuccessResponse(normalizedMessage, cleanedText, ruleSignals);
}
async function main() {
    const rawMessage = process.argv[process.argv.indexOf("--raw-message") + 1];
    if (!rawMessage) {
        throw new Error("missing required option: --raw-message");
    }
    (0, common_1.printJson)(await recognizeRawMessage(rawMessage));
    return 0;
}
if (require.main === module) {
    main().then((code) => process.exit(code), (error) => {
        console.error(error instanceof Error ? error.message : String(error));
        process.exit(1);
    });
}


path: scripts/rebateV1Service.js
content: "use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.getBoundOpenid = getBoundOpenid;
exports.requireBoundOpenid = requireBoundOpenid;
exports.normalizeApiPlatform = normalizeApiPlatform;
exports.platformLabel = platformLabel;
exports.searchProductsByKeyword = searchProductsByKeyword;
exports.searchProductByLink = searchProductByLink;
exports.applyAdzoneLease = applyAdzoneLease;
exports.createRebateLink = createRebateLink;
exports.queryAccountBalance = queryAccountBalance;
exports.applyWithdraw = applyWithdraw;
exports.formatPrice = formatPrice;
exports.extractServiceErrorMessage = extractServiceErrorMessage;
exports.buildSearchResultsUserMessage = buildSearchResultsUserMessage;
exports.buildConvertResultUserMessage = buildConvertResultUserMessage;
exports.buildAccountBalanceUserMessage = buildAccountBalanceUserMessage;
const common_1 = require("./common");
const PLATFORM_TO_API = {
    taobao: "tb",
    tb: "tb",
    jd: "jd",
    pdd: "pdd",
};
const API_PLATFORM_LABELS = {
    tb: "淘宝",
    jd: "京东",
    pdd: "拼多多",
};
async function getBoundOpenid() {
    const machineCode = await (0, common_1.getOrCreateMachineCode)();
    const binding = (0, common_1.loadLocalOpenidBinding)(machineCode) || {};
    const openId = String(binding.open_id || "").trim();
    return { machineCode, openId };
}
async function requireBoundOpenid() {
    const { machineCode, openId } = await getBoundOpenid();
    if (!openId) {
        throw new Error("当前未检测到已绑定 openId，请先完成微信授权。");
    }
    return { machineCode, openId };
}
function normalizeApiPlatform(platform) {
    const normalized = String(platform || "").trim().toLowerCase();
    return PLATFORM_TO_API[normalized] || normalized;
}
function platformLabel(platform) {
    const normalized = normalizeApiPlatform(platform);
    return API_PLATFORM_LABELS[normalized] || normalized || "未知";
}
async function searchProductsByKeyword(openid, rawText, platform = "", limit = 5) {
    const payload = {
        openid,
        query_type: "keyword",
        platform: normalizeApiPlatform(platform),
        raw_text: String(rawText || "").trim(),
        limit: Math.max(1, Math.min(Number(limit || 5), 10)),
    };
    const response = await (0, common_1.requestRebateV1Json)("POST", "/v1/product/search", payload);
    return response.data || {};
}
async function searchProductByLink(openid, platform, rawText) {
    const payload = {
        openid,
        query_type: "link",
        platform: normalizeApiPlatform(platform),
        raw_text: String(rawText || "").trim(),
    };
    const response = await (0, common_1.requestRebateV1Json)("POST", "/v1/product/search", payload);
    return response.data || {};
}
async function applyAdzoneLease(openid, platform, leaseTtlSeconds = 86400) {
    const payload = {
        openid,
        platform: normalizeApiPlatform(platform),
        lease_ttl_seconds: Number(leaseTtlSeconds || 86400),
    };
    const response = await (0, common_1.requestRebateV1Json)("POST", "/v1/adzone/lease/apply", payload);
    return response.data || {};
}
async function createRebateLink(openid, leaseId, platform, itemId, rawText) {
    const payload = {
        openid,
        lease_id: String(leaseId),
        platform: normalizeApiPlatform(platform),
        item_id: String(itemId),
        raw_text: String(rawText || "").trim(),
    };
    const response = await (0, common_1.requestRebateV1Json)("POST", "/v1/rebate/link/create", payload);
    return response.data || {};
}
async function queryAccountBalance(openid) {
    const response = await (0, common_1.requestRebateV1Json)("POST", "/v1/account/balance", {
        openid: String(openid || "").trim(),
    });
    return response.data || {};
}
async function applyWithdraw(openid, amount) {
    const response = await (0, common_1.requestRebateV1Json)("POST", "/v1/withdraw/apply", {
        openid: String(openid || "").trim(),
        amount: Number(amount),
    });
    return response.data || {};
}
function formatPrice(value) {
    if (value === null || value === undefined || value === "") {
        return "无";
    }
    if (typeof value === "number" && Number.isFinite(value)) {
        const rendered = value.toFixed(2);
        return rendered.replace(/\.00$/, "").replace(/(\.\d)0$/, "$1");
    }
    return String(value);
}
function formatCouponLine(coupon) {
    if (!coupon) {
        return "优惠券：无";
    }
    const amount = formatPrice(coupon.coupon_amount);
    const startFee = formatPrice(coupon.coupon_start_fee);
    const couponType = platformLabel(coupon.coupon_type || "未知");
    return `优惠券：满${startFee}减${amount}（${couponType}）`;
}
function extractServiceErrorMessage(error, defaultMessage) {
    const rawMessage = String(error instanceof Error ? error.message : error || "").trim();
    if (!rawMessage) {
        return defaultMessage;
    }
    const extractFromJsonText = (text) => {
        const payload = JSON.parse(text);
        const detail = payload.detail || {};
        if (detail && typeof detail === "object") {
            if (detail.message) {
                return String(detail.message);
            }
            if (detail.raw) {
                return String(detail.raw);
            }
        }
        if (payload.message) {
            const message = String(payload.message);
            const nestedJsonStart = message.indexOf("{");
            if (nestedJsonStart >= 0) {
                try {
                    return extractFromJsonText(message.slice(nestedJsonStart)) || message;
                }
                catch {
                    return message;
                }
            }
            return message;
        }
        return null;
    };
    const candidates = [rawMessage];
    const jsonStart = rawMessage.indexOf("{");
    if (jsonStart >= 0) {
        candidates.push(rawMessage.slice(jsonStart));
    }
    for (const candidate of candidates) {
        try {
            const message = extractFromJsonText(candidate);
            if (message) {
                return message;
            }
        }
        catch {
            continue;
        }
    }
    return rawMessage;
}
function buildSearchResultsUserMessage(title, data, maxItems) {
    const items = Array.isArray(data?.items) ? data.items : [];
    const total = Number(data?.total || 0);
    if (!items.length) {
        return [
            title,
            "",
            "暂时没有找到可返利商品。",
            "你可以换个更具体的商品名，或者直接把商品链接发给我。",
        ].join("\n");
    }
    const displayItems = maxItems ? items.slice(0, maxItems) : items;
    const lines = [title, "", `共找到 ${total} 个商品：`];
    displayItems.forEach((item, index) => {
        const coupon = item.coupon;
        lines.push("", `${index + 1}. [${platformLabel(item.platform)}] ${item.title || "未命名商品"}`, `店铺：${item.shop_name || "未知店铺"}`, `品牌：${item.brand_name || "未知"}`, `到手价：${formatPrice(item.final_price)}`, `预计返利：${formatPrice(item.estimated_rebate_amount)}`, formatCouponLine(coupon), coupon?.coupon_link ? `优惠券链接：${coupon.coupon_link}` : "优惠券链接：无", `商品链接：${item.item_link || "无"}`);
    });
    lines.push("", "看中哪个商品，直接把上面的商品链接发给我，我帮你转成返利链接。");
    return lines.join("\n");
}
function buildConvertResultUserMessage(data) {
    const payload = data || {};
    return [
        "已为你生成返利链接",
        "",
        `平台：${platformLabel(payload.platform)}`,
        `商品：${payload.title || "未知商品"}`,
        `原价：${formatPrice(payload.origin_price)}`,
        `到手价：${formatPrice(payload.final_price)}`,
        `预计返利：${formatPrice(payload.estimated_rebate_amount)}`,
        formatCouponLine(payload.coupon),
        `返利链接：${payload.convert_link || "无"}`,
        `返利口令：${payload.convert_token || "无"}`,
        `到账说明：${payload.arrival_text || "无"}`,
    ].join("\n");
}
function buildAccountBalanceUserMessage(data) {
    const payload = data || {};
    return [
        "账户余额查询结果",
        "",
        `累计返利：${formatPrice(payload.total_rebate_amount)}`,
        `可提现余额：${formatPrice(payload.available_amount)}`,
        `冻结金额：${formatPrice(payload.frozen_amount)}`,
        `提现中金额：${formatPrice(payload.withdrawing_amount)}`,
        `已提现金额：${formatPrice(payload.withdrawn_amount)}`,
        `当前是否可提现：${payload.can_withdraw ? "是" : "否"}`,
    ].join("\n");
}


path: scripts/common.js
content: "use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.PENDING_WITHDRAW_TTL_MS = exports.PENDING_AUTH_SCENES = exports.INSECURE_REBATE_V1_HOSTS = exports.DEFAULT_REBATE_V1_BASE_URL = exports.OPENCLAW_CONFIG_PATH = exports.PENDING_WITHDRAW_REQUEST_PATH = exports.PENDING_AUTH_REQUEST_PATH = exports.OPENID_STORE_PATH = exports.AUTH_STATE_PATH = exports.MACHINE_CODE_PATH = exports.SKILL_DATA_DIR = exports.IDENTITY_PATH = exports.WORKSPACE_DATA_DIR = exports.SKILL_DIR = void 0;
exports.ensureSkillDataDir = ensureSkillDataDir;
exports.loadJsonFile = loadJsonFile;
exports.saveJsonFile = saveJsonFile;
exports.loadOpenclawId = loadOpenclawId;
exports.loadMachineCode = loadMachineCode;
exports.saveLocalMachineCode = saveLocalMachineCode;
exports.fetchRemoteMachineCode = fetchRemoteMachineCode;
exports.getOrCreateMachineCode = getOrCreateMachineCode;
exports.candidateRebateV1BaseUrls = candidateRebateV1BaseUrls;
exports.requestRebateV1Json = requestRebateV1Json;
exports.fetchUrlText = fetchUrlText;
exports.isTaobaoShortLink = isTaobaoShortLink;
exports.extractTaobaoTargetUrlFromHtml = extractTaobaoTargetUrlFromHtml;
exports.resolveTaobaoShortLink = resolveTaobaoShortLink;
exports.updateLocalAuthState = updateLocalAuthState;
exports.loadLocalAuthState = loadLocalAuthState;
exports.saveLocalOpenidBinding = saveLocalOpenidBinding;
exports.loadLocalOpenidBinding = loadLocalOpenidBinding;
exports.loadPendingAuthState = loadPendingAuthState;
exports.savePendingAuthRequest = savePendingAuthRequest;
exports.loadPendingAuthRequest = loadPendingAuthRequest;
exports.clearPendingAuthRequest = clearPendingAuthRequest;
exports.loadPendingWithdrawState = loadPendingWithdrawState;
exports.savePendingWithdrawRequest = savePendingWithdrawRequest;
exports.loadPendingWithdrawRequest = loadPendingWithdrawRequest;
exports.clearPendingWithdrawRequest = clearPendingWithdrawRequest;
exports.printJson = printJson;
exports.formatUserMessageMarkdown = formatUserMessageMarkdown;
exports.printUserMessage = printUserMessage;
exports.loadOpenclawConfig = loadOpenclawConfig;
exports.loadPrimaryModelConfig = loadPrimaryModelConfig;
exports.extractJsonObject = extractJsonObject;
exports.requestModelJson = requestModelJson;
const fs_1 = __importDefault(require("fs"));
const path_1 = __importDefault(require("path"));
const child_process_1 = require("child_process");
const url_1 = require("url");
const http_1 = require("./lib/http");
const CURRENT_DIR = __dirname;
exports.SKILL_DIR = path_1.default.resolve(CURRENT_DIR, "..");
exports.WORKSPACE_DATA_DIR = path_1.default.resolve(exports.SKILL_DIR, "..", "..");
exports.IDENTITY_PATH = path_1.default.join(exports.WORKSPACE_DATA_DIR, "identity", "device.json");
exports.SKILL_DATA_DIR = path_1.default.join(exports.SKILL_DIR, "data");
exports.MACHINE_CODE_PATH = path_1.default.join(exports.SKILL_DATA_DIR, "machine-code.json");
exports.AUTH_STATE_PATH = path_1.default.join(exports.SKILL_DATA_DIR, "wechat-auth-state.json");
exports.OPENID_STORE_PATH = path_1.default.join(exports.SKILL_DATA_DIR, "wechat-openid-store.json");
exports.PENDING_AUTH_REQUEST_PATH = path_1.default.join(exports.SKILL_DATA_DIR, "pending-auth-request.json");
exports.PENDING_WITHDRAW_REQUEST_PATH = path_1.default.join(exports.SKILL_DATA_DIR, "pending-withdraw-request.json");
exports.OPENCLAW_CONFIG_PATH = path_1.default.join(exports.WORKSPACE_DATA_DIR, "openclaw.json");
exports.DEFAULT_REBATE_V1_BASE_URL = "https://rebate-skill.io.mlj130.com";
exports.INSECURE_REBATE_V1_HOSTS = new Set();
exports.PENDING_AUTH_SCENES = new Set(["link_rebate", "search_rebate"]);
exports.PENDING_WITHDRAW_TTL_MS = 5 * 60 * 1000;
const MARKDOWN_TITLE_PREFIXES = ["💰 ", "🎉 ", "💡 "];
const MARKDOWN_NUMBER_EMOJI = {
    "1️⃣": "1.",
    "2️⃣": "2.",
    "3️⃣": "3.",
    "4️⃣": "4.",
    "5️⃣": "5.",
};
const MARKDOWN_URL_STOP_CHAR_RE = /[\s<>"'`，。！？；：、（）【】「」《》]/;
const MARKDOWN_URL_TRAILING_CHARS = new Set([
    ".",
    ",",
    "!",
    "?",
    ";",
    ":",
    ">",
    "\"",
    "'",
    "，",
    "。",
    "！",
    "？",
    "；",
    "：",
    "、",
    "】",
    "》",
    "」",
    "』",
    "）",
]);
function countChar(text, target) {
    let count = 0;
    for (const char of text) {
        if (char === target) {
            count += 1;
        }
    }
    return count;
}
function shouldTrimMarkdownUrlTrailingChar(url, char) {
    if (char === ")") {
        return countChar(url, ")") > countChar(url, "(");
    }
    if (char === "]") {
        return countChar(url, "]") > countChar(url, "[");
    }
    if (char === "}") {
        return countChar(url, "}") > countChar(url, "{");
    }
    return MARKDOWN_URL_TRAILING_CHARS.has(char);
}
function splitMarkdownUrlToken(token) {
    let end = token.length;
    while (end > 0 && shouldTrimMarkdownUrlTrailingChar(token.slice(0, end), token[end - 1])) {
        end -= 1;
    }
    return {
        url: token.slice(0, end),
        trailing: token.slice(end),
    };
}
function isValidMarkdownHttpUrl(value) {
    try {
        const parsed = new url_1.URL(value);
        return parsed.protocol === "http:" || parsed.protocol === "https:";
    }
    catch {
        return false;
    }
}
function formatInlineMarkdownLinks(text) {
    let output = "";
    let index = 0;
    while (index < text.length) {
        if (text[index] === "`") {
            const codeEnd = text.indexOf("`", index + 1);
            if (codeEnd === -1) {
                output += text.slice(index);
                break;
            }
            output += text.slice(index, codeEnd + 1);
            index = codeEnd + 1;
            continue;
        }
        const startsWithUrl = text.startsWith("https://", index) || text.startsWith("http://", index);
        if (!startsWithUrl) {
            output += text[index];
            index += 1;
            continue;
        }
        let end = index;
        while (end < text.length && !MARKDOWN_URL_STOP_CHAR_RE.test(text[end])) {
            end += 1;
        }
        const [REDACTED_SECRET], end);
        const { url, trailing } = splitMarkdownUrlToken(token);
        const isMarkdownLinkTarget = index >= 2 && text[index - 1] === "(" && text[index - 2] === "]";
        const isAutolink = index >= 1 && text[index - 1] === "<";
        if (!url || !isValidMarkdownHttpUrl(url) || isMarkdownLinkTarget || isAutolink) {
            output += token;
        }
        else {
            output += `${url}${trailing}`;
        }
        index = end;
    }
    return output;
}
function ensureSkillDataDir() {
    fs_1.default.mkdirSync(exports.SKILL_DATA_DIR, { recursive: true });
}
function loadJsonFile(filePath, defaultValue) {
    if (!fs_1.default.existsSync(filePath)) {
        return defaultValue;
    }
    try {
        return JSON.parse(fs_1.default.readFileSync(filePath, "utf-8"));
    }
    catch {
        return defaultValue;
    }
}
function saveJsonFile(filePath, payload) {
    fs_1.default.mkdirSync(path_1.default.dirname(filePath), { recursive: true });
    fs_1.default.writeFileSync(filePath, `${JSON.stringify(payload, null, 2)}\n`, "utf-8");
}
function loadOpenclawId() {
    const payload = loadJsonFile(exports.IDENTITY_PATH, {});
    const openclawId = String(payload.deviceId || "").trim();
    if (!openclawId) {
        throw new Error(`未在 ${exports.IDENTITY_PATH} 中找到 deviceId`);
    }
    return openclawId;
}
function loadMachineCode() {
    const payload = loadJsonFile(exports.MACHINE_CODE_PATH, {});
    return String(payload.machine_code || "").trim();
}
function saveLocalMachineCode(machineCode, generatedAt, formatHint) {
    const normalized = String(machineCode || "").trim();
    if (!normalized) {
        throw new Error("machine_code 不能为空");
    }
    const payload = {
        version: 1,
        machine_code: normalized,
    };
    if (generatedAt) {
        payload.generated_at = generatedAt;
    }
    if (formatHint) {
        payload.format = formatHint;
    }
    saveJsonFile(exports.MACHINE_CODE_PATH, payload);
}
async function fetchRemoteMachineCode() {
    const response = await requestRebateV1Json("GET", "/v1/common/machine-code");
    const data = response.data || {};
    const machineCode = String(data.machine_code || data.machineCode || response.machine_code || response.machineCode || "").trim();
    if (!machineCode) {
        throw new Error("machine-code 接口未返回有效 machine_code");
    }
    saveLocalMachineCode(machineCode, data.generated_at || data.generatedAt || undefined, data.format || undefined);
    return {
        machine_code: machineCode,
        generated_at: data.generated_at || data.generatedAt || null,
        format: data.format || null,
    };
}
async function getOrCreateMachineCode() {
    const machineCode = loadMachineCode();
    if (machineCode) {
        return machineCode;
    }
    const created = await fetchRemoteMachineCode();
    return String(created.machine_code || "").trim();
}
function candidateRebateV1BaseUrls() {
    const seen = [];
    const configured = String(process.env.REBATE_V1_BASE_URL || "").trim();
    const legacyConfigured = String(process.env.REBATE_MIDPLATFORM_V1_BASE_URL || "").trim();
    for (const url of [configured, legacyConfigured, exports.DEFAULT_REBATE_V1_BASE_URL]) {
        if (!url) {
            continue;
        }
        const normalized = url.replace(/\/+$/, "");
        if (!seen.includes(normalized)) {
            seen.push(normalized);
        }
    }
    return seen;
}
function stringifyErrorPayload(payload) {
    return JSON.stringify(payload, undefined, 0);
}
class RebateV1ResponseError extends Error {
}
function createRebateV1ResponseError(payload) {
    return new RebateV1ResponseError(stringifyErrorPayload(payload));
}
function isRebateV1ResponseError(error) {
    return error instanceof RebateV1ResponseError;
}
function parseJson(text, defaultValue = {}) {
    try {
        return JSON.parse(text);
    }
    catch {
        return defaultValue;
    }
}
async function requestRebateV1Json(method, apiPath, payload) {
    const headers = {
        Accept: "application/json",
    };
    let encoded;
    if (payload !== undefined) {
        encoded = JSON.stringify(payload);
        headers["Content-Type"] = "application/json";
    }
    let lastError = null;
    for (const baseUrl of candidateRebateV1BaseUrls()) {
        const insecure = Array.from(exports.INSECURE_REBATE_V1_HOSTS).some((host) => baseUrl.includes(host));
        try {
            return requestRebateV1JsonViaCurl(baseUrl, apiPath, method, payload, insecure);
        }
        catch (error) {
            if (isRebateV1ResponseError(error)) {
                throw error;
            }
            lastError = error instanceof Error ? error : new Error(String(error));
        }
        try {
            const response = await (0, http_1.requestText)(`${baseUrl}${apiPath}`, {
                method: method.toUpperCase(),
                headers,
                body: encoded,
                timeoutMs: 15000,
                insecure,
            });
            if (response.statusCode >= 400) {
                throw createRebateV1ResponseError({
                    message: `返利接口返回错误 HTTP ${response.statusCode}`,
                    base_url: baseUrl,
                    detail: response.body ? parseJson(response.body, { raw: response.body }) : {},
                });
            }
            let parsed;
            try {
                parsed = JSON.parse(response.body || "{}");
            }
            catch (error) {
                throw new Error(`返利接口响应不是合法 JSON: ${(response.body || "").slice(0, 300)}`);
            }
            if (parsed.result === "error") {
                throw createRebateV1ResponseError({
                    message: parsed.message || "返利接口返回错误",
                    base_url: baseUrl,
                    detail: parsed,
                });
            }
            return parsed;
        }
        catch (error) {
            if (isRebateV1ResponseError(error)) {
                throw error;
            }
            lastError = error instanceof Error ? error : new Error(String(error));
        }
    }
    throw new Error(`无法连接返利接口: ${lastError ? lastError.message : "unknown error"}`);
}
function requestRebateV1JsonViaCurl(baseUrl, apiPath, method, payload, insecure = false) {
    const command = [
        "-sS",
        "--connect-timeout",
        "8",
        "--max-time",
        "15",
        "-X",
        method.toUpperCase(),
        `${baseUrl}${apiPath}`,
        "-H",
        "Accept: application/json",
        "-w",
        "\n%{http_code}",
    ];
    if (insecure) {
        command.push("-k");
    }
    if (payload !== undefined) {
        command.push("-H", "Content-Type: application/json", "-d", JSON.stringify(payload));
    }
    const result = (0, child_process_1.spawnSync)("curl", command, {
        encoding: "utf-8",
    });
    if (result.error) {
        throw result.error;
    }
    if (result.status !== 0) {
        throw new Error(stringifyErrorPayload({
            message: "返利接口 curl 调用失败",
            base_url: baseUrl,
            detail: {
                stderr: String(result.stderr || "").trim(),
                returncode: result.status,
            },
        }));
    }
    const output = String(result.stdout || "");
    const lastNewline = output.lastIndexOf("\n");
    const body = lastNewline >= 0 ? output.slice(0, lastNewline) : "";
    const statusText = lastNewline >= 0 ? output.slice(lastNewline + 1) : output;
    const statusCode = Number(String(statusText || "0").trim() || "0");
    if (statusCode >= 400) {
        throw createRebateV1ResponseError({
            message: `返利接口返回错误 HTTP ${statusCode}`,
            base_url: baseUrl,
            detail: body ? parseJson(body, { raw: body }) : {},
        });
    }
    let parsed;
    try {
        parsed = JSON.parse(body);
    }
    catch {
        throw new Error(`返利接口响应不是合法 JSON: ${body.slice(0, 300)}`);
    }
    if (parsed.result === "error") {
        throw createRebateV1ResponseError({
            message: parsed.message || "返利接口返回错误",
            base_url: baseUrl,
            detail: parsed,
        });
    }
    return parsed;
}
async function fetchUrlText(rawUrl, insecure = false, timeoutSeconds = 10) {
    try {
        return fetchUrlTextViaCurl(rawUrl, insecure, timeoutSeconds);
    }
    catch {
        // Fall back to Node HTTP client below.
    }
    const response = await (0, http_1.requestText)(rawUrl, {
        method: "GET",
        timeoutMs: (timeoutSeconds + 7) * 1000,
        insecure,
        maxRedirects: 8,
    });
    if (response.statusCode >= 400) {
        throw new Error(`URL 不可达: HTTP ${response.statusCode}`);
    }
    return response.body;
}
function fetchUrlTextViaCurl(rawUrl, insecure = false, timeoutSeconds = 10) {
    const command = [
        "-sS",
        "-L",
        "--connect-timeout",
        String(timeoutSeconds),
        "--max-time",
        String(timeoutSeconds + 7),
        rawUrl,
    ];
    if (insecure) {
        command.push("-k");
    }
    const result = (0, child_process_1.spawnSync)("curl", command, {
        encoding: "utf-8",
    });
    if (result.error) {
        throw result.error;
    }
    if (result.status !== 0) {
        throw new Error(`URL 不可达: ${String(result.stderr || "").trim() || `curl exit ${result.status}`}`);
    }
    return String(result.stdout || "");
}
function isTaobaoShortLink(link) {
    try {
        const host = new url_1.URL(String(link || "").trim()).hostname.trim().toLowerCase();
        return host === "e.tb.cn";
    }
    catch {
        return false;
    }
}
function extractTaobaoTargetUrlFromHtml(html) {
    const text = String(html || "");
    const marker = "var url = '";
    const start = text.indexOf(marker);
    if (start < 0) {
        return null;
    }
    const from = start + marker.length;
    const end = text.indexOf("';", from);
    if (end < 0) {
        return null;
    }
    const candidate = text.slice(from, end).trim();
    return candidate || null;
}
async function resolveTaobaoShortLink(link) {
    if (!isTaobaoShortLink(link)) {
        return null;
    }
    const html = await fetchUrlText(link);
    const resolved = extractTaobaoTargetUrlFromHtml(html);
    if (!resolved) {
        throw new Error("淘宝短链页面里没有解析到最终商品链接");
    }
    return resolved;
}
function updateLocalAuthState(machineCode, session) {
    const state = loadJsonFile(exports.AUTH_STATE_PATH, { version: 1, sessions: {} });
    if (!state.sessions || typeof state.sessions !== "object") {
        state.sessions = {};
    }
    state.sessions[machineCode] = session;
    saveJsonFile(exports.AUTH_STATE_PATH, state);
}
function loadLocalAuthState(machineCode) {
    const state = loadJsonFile(exports.AUTH_STATE_PATH, { version: 1, sessions: {} });
    return state.sessions?.[machineCode] || null;
}
function saveLocalOpenidBinding(binding) {
    const store = loadJsonFile(exports.OPENID_STORE_PATH, { version: 1, bindings: {} });
    if (!store.bindings || typeof store.bindings !== "object") {
        store.bindings = {};
    }
    store.bindings[binding.machine_code] = binding;
    saveJsonFile(exports.OPENID_STORE_PATH, store);
}
function loadLocalOpenidBinding(machineCode) {
    const store = loadJsonFile(exports.OPENID_STORE_PATH, { version: 1, bindings: {} });
    return store.bindings?.[machineCode] || null;
}
function loadPendingAuthState() {
    const state = loadJsonFile(exports.PENDING_AUTH_REQUEST_PATH, { version: 1, requests: {} });
    const requests = state && typeof state.requests === "object" && state.requests ? state.requests : {};
    return { version: 1, requests };
}
function savePendingAuthRequest(machineCode, options) {
    const { scene, handler, rawMessage, reason = "" } = options;
    if (!machineCode || !exports.PENDING_AUTH_SCENES.has(scene)) {
        return null;
    }
    const message = String(rawMessage || "").trim();
    if (!message) {
        return null;
    }
    const pendingRequest = {
        scene,
        handler,
        reason,
        raw_message: message,
        created_at: new Date().toISOString(),
    };
    const state = loadPendingAuthState();
    state.requests[machineCode] = pendingRequest;
    saveJsonFile(exports.PENDING_AUTH_REQUEST_PATH, state);
    return pendingRequest;
}
function loadPendingAuthRequest(machineCode) {
    if (!machineCode) {
        return null;
    }
    const request = loadPendingAuthState().requests?.[machineCode];
    if (!request || typeof request !== "object") {
        return null;
    }
    if (!exports.PENDING_AUTH_SCENES.has(String(request.scene || ""))) {
        return null;
    }
    if (!String(request.raw_message || "").trim()) {
        return null;
    }
    return request;
}
function clearPendingAuthRequest(machineCode) {
    if (!machineCode) {
        return;
    }
    const state = loadPendingAuthState();
    if (!state.requests || !state.requests[machineCode]) {
        return;
    }
    delete state.requests[machineCode];
    saveJsonFile(exports.PENDING_AUTH_REQUEST_PATH, state);
}
function loadPendingWithdrawState() {
    const state = loadJsonFile(exports.PENDING_WITHDRAW_REQUEST_PATH, { version: 1, requests: {} });
    const requests = state && typeof state.requests === "object" && state.requests ? state.requests : {};
    return { version: 1, requests };
}
function savePendingWithdrawRequest(machineCode, options) {
    const openid = String(options.openid || "").trim();
    const amount = Number(options.amount);
    if (!machineCode || !openid || !Number.isFinite(amount) || amount <= 0) {
        return null;
    }
    const now = Date.now();
    const ttlMs = Number.isFinite(Number(options.ttlMs)) ? Number(options.ttlMs) : exports.PENDING_WITHDRAW_TTL_MS;
    const pendingRequest = {
        openid,
        amount,
        available_amount: options.availableAmount ?? null,
        withdrawing_amount: options.withdrawingAmount ?? null,
        created_at: new Date(now).toISOString(),
        expires_at: new Date(now + Math.max(1, ttlMs)).toISOString(),
    };
    const state = loadPendingWithdrawState();
    state.requests[machineCode] = pendingRequest;
    saveJsonFile(exports.PENDING_WITHDRAW_REQUEST_PATH, state);
    return pendingRequest;
}
function loadPendingWithdrawRequest(machineCode) {
    if (!machineCode) {
        return null;
    }
    const state = loadPendingWithdrawState();
    const request = state.requests?.[machineCode];
    if (!request || typeof request !== "object") {
        return null;
    }
    const amount = Number(request.amount);
    const openid = String(request.openid || "").trim();
    if (!openid || !Number.isFinite(amount) || amount <= 0) {
        return null;
    }
    const expiresAt = Date.parse(String(request.expires_at || ""));
    const createdAt = Date.parse(String(request.created_at || ""));
    const expired = Number.isFinite(expiresAt)
        ? Date.now() > expiresAt
        : Number.isFinite(createdAt) && Date.now() - createdAt > exports.PENDING_WITHDRAW_TTL_MS;
    if (expired) {
        delete state.requests[machineCode];
        saveJsonFile(exports.PENDING_WITHDRAW_REQUEST_PATH, state);
        return null;
    }
    return request;
}
function clearPendingWithdrawRequest(machineCode) {
    if (!machineCode) {
        return;
    }
    const state = loadPendingWithdrawState();
    if (!state.requests || !state.requests[machineCode]) {
        return;
    }
    delete state.requests[machineCode];
    saveJsonFile(exports.PENDING_WITHDRAW_REQUEST_PATH, state);
}
function printJson(payload) {
    process.stdout.write(`${JSON.stringify(payload, null, 2)}\n`);
}
function formatUserMessageMarkdown(message) {
    const text = String(message || "").replace(/\r\n/g, "\n").replace(/\r/g, "\n").trim();
    if (!text) {
        return "";
    }
    const lines = [];
    for (const rawLine of text.split("\n")) {
        let stripped = rawLine.trim();
        if (!stripped) {
            lines.push("");
            continue;
        }
        if (/^[━\-—]{3,}$/.test(stripped)) {
            lines.push("---");
            continue;
        }
        for (const prefix of MARKDOWN_TITLE_PREFIXES) {
            if (stripped.startsWith(prefix)) {
                stripped = `## ${stripped.slice(prefix.length).trim()}`;
                break;
            }
        }
        for (const [prefix, replacement] of Object.entries(MARKDOWN_NUMBER_EMOJI)) {
            if (stripped.startsWith(prefix)) {
                stripped = `${replacement} ${stripped.slice(prefix.length).trim()}`;
                break;
            }
        }
        if (stripped.startsWith("• ")) {
            stripped = `- ${stripped.slice(2).trim()}`;
        }
        else if (/^【[^】]{1,30}】$/.test(stripped)) {
            stripped = `### ${stripped.slice(1, -1).trim()}`;
        }
        stripped = formatInlineMarkdownLinks(stripped);
        lines.push(stripped);
    }
    const normalized = [];
    let blankCount = 0;
    for (const line of lines) {
        if (!line) {
            blankCount += 1;
            if (blankCount <= 1) {
                normalized.push("");
            }
            continue;
        }
        blankCount = 0;
        normalized.push(line);
    }
    return normalized.join("\n").trim();
}
function printUserMessage(message, options = {}) {
    const output = options.markdown ? formatUserMessageMarkdown(message) : String(message || "").trim();
    process.stdout.write(`${output}\n`);
}
function loadOpenclawConfig() {
    return loadJsonFile(exports.OPENCLAW_CONFIG_PATH, {});
}
function loadPrimaryModelConfig() {
    const config = loadOpenclawConfig();
    const providers = config.models?.providers || {};
    if (!providers || typeof providers !== "object" || !Object.keys(providers).length) {
        throw new Error(`未在 ${exports.OPENCLAW_CONFIG_PATH} 中找到 models.providers 配置`);
    }
    const primary = String(config.agents?.defaults?.model?.primary || "").trim();
    let providerName = "";
    let modelId = "";
    if (primary.includes("/")) {
        [providerName, modelId] = primary.split("/", 2);
    }
    else if (primary) {
        modelId = primary;
    }
    if (!providerName) {
        providerName = Object.keys(providers)[0];
    }
    const provider = providers[providerName];
    if (!provider) {
        throw new Error(`未找到模型提供方配置: ${providerName}`);
    }
    const models = Array.isArray(provider.models) ? provider.models : [];
    if (!modelId) {
        if (!models.length) {
            throw new Error(`模型提供方 ${providerName} 未配置 models`);
        }
        modelId = String(models[0]?.id || "").trim();
    }
    if (!modelId) {
        throw new Error(`模型提供方 ${providerName} 缺少默认模型 id`);
    }
    const baseUrl = String(provider.baseUrl || "").trim().replace(/\/+$/, "");
    const [REDACTED_SECRET] || "").trim();
    const apiStyle = String(provider.api || "openai-completions").trim();
    if (!baseUrl || !apiKey) {
        throw new Error(`模型配置不完整: provider=${providerName}`);
    }
    return {
        provider: providerName,
        model: modelId,
        base_url: baseUrl,
        [REDACTED_SECRET],
        api: apiStyle,
    };
}
function extractJsonObject(rawText) {
    const text = String(rawText || "").trim();
    if (!text) {
        throw new Error("模型返回为空");
    }
    try {
        return JSON.parse(text);
    }
    catch {
        // Ignore and try to extract outer object.
    }
    const start = text.indexOf("{");
    const end = text.lastIndexOf("}");
    if (start === -1 || end === -1 || end <= start) {
        throw new Error(`模型返回中未找到 JSON 对象: ${text.slice(0, 200)}`);
    }
    const candidate = text.slice(start, end + 1);
    try {
        return JSON.parse(candidate);
    }
    catch (error) {
        throw new Error(`模型返回 JSON 解析失败: ${error?.message || error}`);
    }
}
async function requestModelJson(systemPrompt, userPrompt, temperature = 0.1, maxTokens = 1200) {
    const modelConf = loadPrimaryModelConfig();
    if (modelConf.api !== "openai-completions") {
        throw new Error(`当前仅支持 openai-completions 风格接口: ${modelConf.api}`);
    }
    const payload = {
        model: modelConf.model,
        messages: [
            { role: "system", content: systemPrompt },
            { role: "user", content: userPrompt },
        ],
        temperature,
        max_tokens: maxTokens,
        response_format: { type: "json_object" },
    };
    const response = await (0, http_1.requestText)(`${modelConf.base_url}/chat/completions`, {
        method: "POST",
        headers: {
            "Content-Type": "application/json",
            Authorization: `Bearer ${modelConf.api_key}`,
        },
        body: JSON.stringify(payload),
        timeoutMs: 20000,
    });
    if (response.statusCode >= 400) {
        throw new Error(`模型接口 HTTP ${response.statusCode}: ${response.body}`);
    }
    let result;
    try {
        result = JSON.parse(response.body);
    }
    catch {
        throw new Error(`模型响应不是合法 JSON: ${response.body.slice(0, 300)}`);
    }
    const choices = Array.isArray(result.choices) ? result.choices : [];
    if (!choices.length) {
        throw new Error(`模型响应缺少 choices: ${response.body.slice(0, 300)}`);
    }
    const message = choices[0]?.message || {};
    let content = message.content || "";
    if (Array.isArray(content)) {
        content = content
            .map((part) => (part && typeof part === "object" ? String(part.text || "") : String(part)))
            .join("");
    }
    const parsed = extractJsonObject(String(content));
    return {
        provider: modelConf.provider,
        model: modelConf.model,
        raw: result,
        content: String(content),
        json: parsed,
    };
}


path: scripts/checkS01Routing.js
content: "use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.main = main;
const rebateAssistantRouter_1 = require("./rebateAssistantRouter");
const CASES = [
    ["返利", "auth_guide", "start_auth"],
    ["返利宝", "auth_guide", "start_auth"],
    ["返利助手", "auth_guide", "start_auth"],
    ["教程", "auth_guide", "start_auth"],
    ["返利教程", "auth_guide", "start_auth"],
    ["返利。", "auth_guide", "start_auth"],
    ["返利宝。", "auth_guide", "start_auth"],
    ["返利！", "auth_guide", "start_auth"],
    ["返利宝！", "auth_guide", "start_auth"],
    ["返利?", "auth_guide", "start_auth"],
    ["返利宝?", "auth_guide", "start_auth"],
    ["返利，", "auth_guide", "start_auth"],
    ["返利宝，", "auth_guide", "start_auth"],
    ["教程。", "auth_guide", "start_auth"],
    ["教程！", "auth_guide", "start_auth"],
    ["返利宝如何使用", "auth_guide", "detailed_tutorial"],
    ["返利助手如何使用", "auth_guide", "detailed_tutorial"],
    ["返利如何使用", "auth_guide", "detailed_tutorial"],
    ["如何使用返利助手", "auth_guide", "detailed_tutorial"],
    ["如何使用返利宝", "auth_guide", "detailed_tutorial"],
    ["教程如何使用", "auth_guide", "detailed_tutorial"],
    ["怎么使用返利助手", "auth_guide", "detailed_tutorial"],
    ["返利助手是什么", "auth_guide", "detailed_tutorial"],
    ["返利宝怎么用", "auth_guide", "detailed_tutorial"],
    ["返利助手使用说明", "auth_guide", "detailed_tutorial"],
    ["详细教程", "auth_guide", "detailed_tutorial"],
    ["提现教程", "auth_guide", "withdraw_tutorial"],
    ["怎么提现", "auth_guide", "withdraw_tutorial"],
    ["提现规则", "auth_guide", "withdraw_tutorial"],
    ["返利宝怎么提现", "auth_guide", "withdraw_tutorial"],
    ["提现10元", "auth_guide", "withdraw_prepare"],
    ["我要提现", "auth_guide", "withdraw_prepare"],
    ["申请提现10元", "auth_guide", "withdraw_prepare"],
    ["全部提现", "auth_guide", "withdraw_prepare"],
    ["确认提现", "auth_guide", "withdraw_confirm"],
    ["确定提现", "auth_guide", "withdraw_confirm"],
    ["余额", "auth_guide", "account_balance"],
    ["账户余额", "auth_guide", "account_balance"],
    ["可提现余额", "auth_guide", "account_balance"],
    ["返利宝余额", "auth_guide", "account_balance"],
    ["查一下余额", "auth_guide", "account_balance"],
    ["订单", "auth_guide", "rebate_details"],
    ["订单明细", "auth_guide", "rebate_details"],
    ["返利明细", "auth_guide", "rebate_details"],
    ["我的订单", "auth_guide", "rebate_details"],
    ["查看返利记录", "auth_guide", "rebate_details"],
    ["我登录了吗", "auth_guide", "binding_status"],
    ["我授权了吗", "auth_guide", "binding_status"],
    ["当前绑定状态", "auth_guide", "binding_status"],
    ["有没有登录返利宝", "auth_guide", "binding_status"],
    ["授权完成", "auth_guide", "confirm_auth"],
    ["我已授权", "auth_guide", "confirm_auth"],
];
function main() {
    const failures = [];
    for (const [message, expectedScene, expectedAction] of CASES) {
        const actual = (0, rebateAssistantRouter_1.classifyScene)(message);
        if (actual.scene !== expectedScene || actual.action !== expectedAction) {
            failures.push([message, expectedScene, expectedAction, actual]);
        }
    }
    if (failures.length) {
        for (const [message, expectedScene, expectedAction, actual] of failures) {
            process.stdout.write(`FAIL: ${JSON.stringify(message)} expected scene=${expectedScene} action=${expectedAction}, got ${JSON.stringify(actual)}\n`);
        }
        return 1;
    }
    process.stdout.write(`OK: ${CASES.length} S01 routing cases passed\n`);
    return 0;
}


path: scripts/productSearchProtocol.js
content: "use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.buildSearchRequest = buildSearchRequest;
exports.buildSearchReadyUserMessage = buildSearchReadyUserMessage;
function buildSearchRequest(productSearchIntent) {
    const intent = productSearchIntent || {};
    return {
        ready: Boolean(intent.success),
        intent_mode: intent.intent_mode,
        keyword: String(intent.query_text || "").trim(),
        search_terms: Array.isArray(intent.query_tokens) ? [...intent.query_tokens] : [],
        filters: {
            brand: intent.brand ?? null,
            series: intent.series ?? null,
            model: intent.model ?? null,
            sku: intent.sku ?? null,
            product_name: intent.product_name ?? null,
            category: intent.category ?? null,
            attributes: Array.isArray(intent.attributes) ? [...intent.attributes] : [],
            specs: Array.isArray(intent.specs) ? [...intent.specs] : [],
            price_min: intent.price_min ?? null,
            price_max: intent.price_max ?? null,
            price_target: intent.price_target ?? null,
            crowd: intent.crowd ?? null,
            usage_scene: intent.usage_scene ?? null,
            gift_target: intent.gift_target ?? null,
            platform_hint: intent.platform_hint ?? null,
            sort_hint: intent.sort_hint ?? null,
        },
    };
}
function formatOptionalPriceLine(intent) {
    const priceMin = intent.price_min;
    const priceMax = intent.price_max;
    const priceTarget = intent.price_target;
    if (priceMin !== null && priceMin !== undefined || priceMax !== null && priceMax !== undefined) {
        return `价格范围：${priceMin ?? "无"} - ${priceMax ?? "无"}`;
    }
    if (priceTarget !== null && priceTarget !== undefined) {
        return `目标价格：${priceTarget}`;
    }
    return "价格：无";
}
function buildSearchReadyUserMessage(title, decision, intent, includeNextStep = true) {
    const payload = intent || {};
    const attributes = Array.isArray(payload.attributes) && payload.attributes.length ? payload.attributes.join(" / ") : "无";
    const specs = Array.isArray(payload.specs) && payload.specs.length ? payload.specs.join(" / ") : "无";
    const lines = [
        title,
        "",
        `判定：${decision}`,
        `搜索词：${payload.query_text || "无"}`,
        `商品：${payload.product_name || "无"}`,
        `类目：${payload.category || "无"}`,
        `品牌：${payload.brand || "无"}`,
        `型号：${payload.model || "无"}`,
        `SKU：${payload.sku || "无"}`,
        `属性：${attributes}`,
        `规格：${specs}`,
        formatOptionalPriceLine(payload),
        `人群：${payload.crowd || "无"}`,
        `场景：${payload.usage_scene || "无"}`,
        `送礼对象：${payload.gift_target || "无"}`,
        `平台提示：${payload.platform_hint || "无"}`,
        `排序偏好：${payload.sort_hint || "无"}`,
    ];
    if (includeNextStep) {
        lines.push("下一步：可直接调用商品搜索接口");
    }
    return lines.join("\n");
}


path: scripts/rebateAssistantRouter.js
content: "use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.classifyScene = classifyScene;
exports.buildResponse = buildResponse;
exports.emitResponse = emitResponse;
const common_1 = require("./common");
const m01OperationGuide_1 = require("./m01OperationGuide");
const m02PlatformLink_1 = require("./m02PlatformLink");
const productSearch_1 = require("./productSearch");
const recognizePreciseProductSearch_1 = require("./recognizePreciseProductSearch");
const s01Intent_1 = require("./s01Intent");
const SCENE_LABELS = {
    auth_guide: "授权与教程",
    link_rebate: "链接返利",
    search_rebate: "搜索返利",
};
function classifyScene(rawMessage) {
    const message = String(rawMessage || "").trim();
    const s01Match = (0, s01Intent_1.classifyS01Action)(message);
    if (s01Match) {
        const [action, reason] = s01Match;
        return {
            scene: "auth_guide",
            handler: "m01_operation_guide",
            action,
            reason,
        };
    }
    if (message.includes("http://") || message.includes("https://")) {
        return {
            scene: "link_rebate",
            handler: "m02_platform_link",
            action: null,
            reason: "检测到 http(s) 链接",
        };
    }
    return {
        scene: "search_rebate",
        handler: "product_search",
        action: null,
        reason: "无链接，按商品搜索场景处理",
    };
}
async function hasLocalAuthorization() {
    try {
        const machineCode = await (0, common_1.getOrCreateMachineCode)();
        return Boolean((0, common_1.loadLocalOpenidBinding)(machineCode));
    }
    catch {
        return false;
    }
}
async function shouldSavePendingAuthRequest(route, rawMessage) {
    const scene = route.scene;
    if (scene === "link_rebate") {
        return true;
    }
    if (scene !== "search_rebate") {
        return false;
    }
    const cleanedText = (0, recognizePreciseProductSearch_1.normalizeText)(rawMessage || "");
    if (!cleanedText || (0, productSearch_1.looksLikeNonSearchMessage)(cleanedText)) {
        return false;
    }
    try {
        const intent = await (0, productSearch_1.buildProductIntent)(rawMessage);
        return Boolean(intent.query_text);
    }
    catch {
        return false;
    }
}
async function savePendingRouteRequest(route, rawMessage) {
    if (!(await shouldSavePendingAuthRequest(route, rawMessage))) {
        return;
    }
    try {
        (0, common_1.savePendingAuthRequest)(await (0, common_1.getOrCreateMachineCode)(), {
            scene: route.scene,
            handler: route.handler,
            rawMessage,
            reason: route.reason,
        });
    }
    catch {
        return;
    }
}
async function buildResponse(rawMessage) {
    let route = classifyScene(rawMessage);
    if (!(await hasLocalAuthorization()) && route.action !== "confirm_auth") {
        await savePendingRouteRequest(route, rawMessage);
        route = {
            scene: "auth_guide",
            handler: "m01_operation_guide",
            action: "start_auth",
            reason: "当前未授权，需先进入 S01 完成微信授权",
        };
    }
    const resolvedHandler = route.handler;
    const resolvedReason = route.reason;
    let payload;
    let exitCode = 0;
    if (route.scene === "auth_guide") {
        [payload, exitCode] = await (0, m01OperationGuide_1.executeAction)(route.action, rawMessage);
    }
    else if (route.scene === "link_rebate") {
        payload = await (0, m02PlatformLink_1.buildResponse)(rawMessage);
        exitCode = payload.success ? 0 : 1;
    }
    else {
        payload = await (0, productSearch_1.buildResponse)(rawMessage);
        exitCode = payload.success ? 0 : 1;
    }
    return {
        success: payload.success ?? exitCode === 0,
        scene: route.scene,
        scene_label: SCENE_LABELS[route.scene],
        handler: resolvedHandler,
        route_reason: resolvedReason,
        module: payload.module,
        intent: payload.intent,
        next_action: payload.next_action,
        code: payload.code,
        result_type: payload.result_type,
        result_data: payload.result_data,
        error: payload.error,
        user_message: payload.user_message || "",
    };
}
async function emitResponse(rawMessage, outputFormat) {
    const response = await buildResponse(rawMessage);
    if (outputFormat === "json") {
        (0, common_1.printJson)(response);
    }
    else {
        (0, common_1.printUserMessage)(response.user_message || "", { markdown: outputFormat === "md" });
    }
    const handledWithdrawMessage = outputFormat !== "json" && response.result_type === "withdraw" && Boolean(String(response.user_message || "").trim());
    return response.success || handledWithdrawMessage ? 0 : 1;
}


path: scripts/recognizePreciseProductSearch.js
content: "use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.LINK_PATTERN = void 0;
exports.normalizeText = normalizeText;
exports.dedupePreserveOrder = dedupePreserveOrder;
exports.findLiteralOccurrences = findLiteralOccurrences;
exports.selectNonOverlapping = selectNonOverlapping;
exports.extractBrand = extractBrand;
exports.extractLiteralTerms = extractLiteralTerms;
exports.extractCategories = extractCategories;
exports.extractAttributes = extractAttributes;
exports.pruneSubstringAliases = pruneSubstringAliases;
exports.guessBrandFromPrefix = guessBrandFromPrefix;
exports.isValidBrandCandidate = isValidBrandCandidate;
exports.extractSpecs = extractSpecs;
exports.extractModels = extractModels;
exports.normalizeModels = normalizeModels;
exports.extractExplicitSku = extractExplicitSku;
exports.extractBudget = extractBudget;
exports.extractPlatformHint = extractPlatformHint;
exports.containsFuzzyMarker = containsFuzzyMarker;
exports.cleanSlotText = cleanSlotText;
exports.evidenceInText = evidenceInText;
exports.makeScalarRawSlot = makeScalarRawSlot;
exports.makeListRawSlots = makeListRawSlots;
exports.normalizeScalarRawSlot = normalizeScalarRawSlot;
exports.normalizeListRawSlots = normalizeListRawSlots;
exports.parseBool = parseBool;
exports.parseConfidence = parseConfidence;
exports.normalizePriceNumber = normalizePriceNumber;
exports.extractPriceConstraints = extractPriceConstraints;
exports.normalizeBrandValue = normalizeBrandValue;
exports.normalizePlatformHintValue = normalizePlatformHintValue;
exports.extractCrowd = extractCrowd;
exports.extractUsageScene = extractUsageScene;
exports.extractGiftTarget = extractGiftTarget;
exports.extractSortHint = extractSortHint;
exports.sortValuesByPosition = sortValuesByPosition;
exports.dropSubstringValues = dropSubstringValues;
exports.choosePreferredText = choosePreferredText;
exports.selectQueryAttributes = selectQueryAttributes;
exports.pickScalarEvidence = pickScalarEvidence;
exports.pickListEvidence = pickListEvidence;
exports.buildProductSearchIntent = buildProductSearchIntent;
exports.extractRuleSignals = extractRuleSignals;
exports.getScalarSlotValue = getScalarSlotValue;
exports.getListSlotValues = getListSlotValues;
exports.isPreciseSearch = isPreciseSearch;
exports.normalizeFuzzyProductName = normalizeFuzzyProductName;
exports.buildFuzzyIntentFromRuleSignals = buildFuzzyIntentFromRuleSignals;
exports.buildEmptyProductSearchIntent = buildEmptyProductSearchIntent;
exports.recognizeRawMessage = recognizeRawMessage;
exports.main = main;
const common_1 = require("./common");
const productSearchProtocol_1 = require("./productSearchProtocol");
const ACTION_PREFIX_PATTERNS = [
    /^(?:帮我|麻烦|请|请帮我|我想|我要|想|想要|给我|替我|帮忙)?(?:搜一下|搜下|搜一搜|搜|查一下|查下|查一查|查|找一下|找下|找找|找|搜索一下|搜索|看一下|看下|看看|有没有|有无)\s*/i,
    /^(?:我想买|我要买|想买)\s*/i,
];
const BRAND_ALIASES = {
    "机械革命": "机械革命",
    "雅诗兰黛": "雅诗兰黛",
    "阿迪达斯": "阿迪达斯",
    "班尼路": "班尼路",
    traveldetail: "飞猪",
    iphone: "苹果",
    apple: "苹果",
    huawei: "华为",
    honor: "荣耀",
    redmi: "红米",
    lenovo: "联想",
    dell: "戴尔",
    asus: "华硕",
    sony: "索尼",
    oppo: "OPPO",
    vivo: "vivo",
    nike: "耐克",
    "李宁": "李宁",
    "安踏": "安踏",
    "361°": "361°",
    "361": "361°",
    "耐克": "耐克",
    "华为": "华为",
    "苹果": "苹果",
    "小米": "小米",
    "红米": "红米",
    "荣耀": "荣耀",
    "美的": "美的",
    "格力": "格力",
    "海尔": "海尔",
    "联想": "联想",
    "戴尔": "戴尔",
    "惠普": "惠普",
    "华硕": "华硕",
    "索尼": "索尼",
    OPPO: "OPPO",
};
const CATEGORY_TERMS = [
    "蓝牙耳机",
    "无线耳机",
    "机械键盘",
    "男裤",
    "女裤",
    "休闲长裤",
    "冰丝裤子",
    "冰丝裤",
    "速干裤",
    "束脚裤",
    "运动长裤",
    "休闲裤",
    "运动裤",
    "短裤",
    "徒步鞋",
    "运动鞋",
    "跑鞋",
    "长裤",
    "裤子",
    "手机",
    "耳机",
    "空调",
    "键盘",
    "鼠标",
    "显示器",
    "笔记本",
    "平板",
    "手表",
    "洗发水",
    "护肤品",
    "面膜",
    "电视",
    "冰箱",
    "洗衣机",
    "路由器",
    "袜子",
];
const ATTRIBUTE_TERMS = [
    "户外运动",
    "男士",
    "女士",
    "男款",
    "女款",
    "夏季",
    "冬季",
    "春秋",
    "户外",
    "运动",
    "速干",
    "冰丝",
    "超薄",
    "薄款",
    "束脚",
    "休闲",
];
const ATTRIBUTE_PATTERNS = [
    /(?:男|女)(?:士|款)?/g,
    /(?:速干|冰丝|纯棉|透气|保暖|轻薄|超薄|防水|防滑)/g,
    /(?:薄款|厚款|加绒|加厚|宽松|修身|直筒|束脚|阔腿|高腰|低帮|中帮|高帮)/g,
];
const GENERIC_ONLY_TERMS = new Set([...CATEGORY_TERMS, "鞋", "裤子", "长裤", "运动裤", "休闲裤", "袜子"]);
const PLATFORM_HINTS = {
    "淘宝": "taobao",
    "京东": "jd",
    "拼多多": "pdd",
};
const CROWD_HINTS = [
    ["男士", "男士"],
    ["男款", "男士"],
    ["男生", "男士"],
    ["女士", "女士"],
    ["女款", "女士"],
    ["女生", "女士"],
    ["儿童", "儿童"],
    ["小孩", "儿童"],
    ["宝宝", "宝宝"],
    ["婴儿", "婴儿"],
    ["孕妇", "孕妇"],
];
const USAGE_SCENE_HINTS = [
    ["通勤", "通勤"],
    ["户外", "户外"],
    ["运动", "运动"],
    ["跑步", "跑步"],
    ["徒步", "徒步"],
    ["健身", "健身"],
    ["办公", "办公"],
    ["家用", "家用"],
    ["宿舍", "宿舍"],
    ["旅行", "旅行"],
    ["露营", "露营"],
];
const GIFT_TARGET_HINTS = [
    ["送给女朋友", "女朋友"],
    ["送女朋友", "女朋友"],
    ["送给男朋友", "男朋友"],
    ["送男朋友", "男朋友"],
    ["送老婆", "老婆"],
    ["送老公", "老公"],
    ["送妈妈", "妈妈"],
    ["送爸爸", "爸爸"],
    ["送孩子", "孩子"],
];
const SORT_HINTS = [
    ["性价比", "性价比优先"],
    ["销量", "销量优先"],
    ["便宜", "低价优先"],
    ["最便宜", "低价优先"],
];
const FUZZY_MARKERS = ["推荐", "适合", "礼物", "左右", "以内", "以下", "预算", "性价比", "哪个好", "求推荐", "帮我选"];
const SPEC_PATTERNS = [
    /\d+\s*\+\s*\d+\s*(?:G|GB|TB)\b/gi,
    /\d+\s*(?:ml|ML|g|kg|KG|L|l|片|粒|只|瓶|袋|盒|支)\s*\*\s*\d+/g,
    /\d+\s*(?:G|GB|TB|kg|KG|g|ml|ML|L|l|寸|码|粒|片|只|瓶|袋|盒|支)\b/gi,
];
const MODEL_PATTERNS = [
    /\b(?:iPhone\s*\d+(?:\s*(?:Pro Max|Pro|Plus|mini))?)\b/gi,
    /\b(?:Mate\s*\d+(?:\s*(?:Pro\+?|RS|SE|X\d+))?)\b/gi,
    /\b(?:Pura\s*\d+(?:\s*(?:Ultra|Pro\+?|Pro))?)\b/gi,
    /[A-Za-z0-9]+(?:[&/+.-][A-Za-z0-9]+)+(?:系列)?/g,
    /\b[A-Za-z]{2,10}-\d[A-Za-z0-9\-_/+.]{1,}\b/g,
    /\b[A-Za-z]{1,6}\d[A-Za-z0-9\-_/+.]{1,}\b/g,
    /[\u4e00-\u9fff]{1,8}\d+[A-Za-z0-9\-+/]{0,6}/g,
    /\b[A-Z]{2,6}\b/g,
];
const EXPLICIT_SKU_PATTERN = /(?:sku|SKU|型号|货号|款号|编码)\s*[:：]?\s*([A-Za-z0-9][A-Za-z0-9\-_/+.]{2,})/;
const BUDGET_PATTERN = /(\d+(?:\.\d+)?)\s*(?:元)?(?:左右|以内|以下|上下)/;
const PRICE_RANGE_PATTERN = /(\d+(?:\.\d+)?)\s*(?:元)?\s*(?:到|至|-|~)\s*(\d+(?:\.\d+)?)\s*元?/;
const PRICE_MAX_PATTERN = /(\d+(?:\.\d+)?)\s*(?:元)?(?:以内|以下|不到)/;
const PRICE_MIN_PATTERN = /(\d+(?:\.\d+)?)\s*(?:元)?(?:以上|起|起步)/;
const PRICE_TARGET_PATTERN = /(\d+(?:\.\d+)?)\s*(?:元)?(?:左右|上下)/;
exports.LINK_PATTERN = /https?:\/\/[^\s]+/i;
const TOKEN_PATTERN = /[\u4e00-\u9fffA-Za-z0-9&+\-/.]+/g;
const LEADING_QUANTITY_PATTERN_TEXT = "(?:[一二两三四五六七八九十几\\d]+)?(?:双|条|件|个|款|只|包|盒|瓶|台|部|套|支|张|本|杯|顶|副)";
const LEADING_QUANTITY_PATTERN = new RegExp(`^${LEADING_QUANTITY_PATTERN_TEXT}$`);
const LEADING_QUANTITY_PREFIX_PATTERN = new RegExp(`^${LEADING_QUANTITY_PATTERN_TEXT}\\s*`);
const LOW_PRIORITY_QUERY_ATTRIBUTES = new Set(["男", "女", "男士", "女士", "男款", "女款", "夏季", "冬季", "春秋"]);
const NULL_LIKE_VALUES = new Set(["", "null", "none", "unknown", "n/a", "na", "无", "未知", "空"]);
const MODEL_SLOT_SYSTEM_PROMPT = `
你是一个电商商品标题槽位抽取器。

你只能输出一个 JSON 对象，不能输出 markdown，不能输出解释。

抽取目标：
- is_precise_search
- confidence
- brand
- model
- sku
- product_name
- category
- attributes
- specs
- platform_hint

输出要求：
1. 每个标量槽位输出 {"value": ..., "evidence": ...}
2. attributes 和 specs 输出数组，元素格式为 {"value": ..., "evidence": ...}
3. evidence 必须是原文中的原样片段，不能改写
4. 不确定就返回 null 或 []
5. 不允许编造品牌、型号、SKU、类目
6. 完整商品标题通常视为精准搜索
7. 品牌优先看标题前缀
8. 商品词/类目要尽量具体，不要只给过宽泛词
`.trim();
function escapeRegExp(value) {
    return String(value).replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
}
function withGlobal(pattern) {
    const flags = pattern.flags.includes("g") ? pattern.flags : `${pattern.flags}g`;
    return new RegExp(pattern.source, flags);
}
function normalizeText(rawMessage) {
    const translation = new Map([
        ["，", " "],
        ["。", " "],
        ["！", " "],
        ["？", " "],
        ["：", ":"],
        ["（", "("],
        ["）", ")"],
        ["【", " "],
        ["】", " "],
        ["“", " "],
        ["”", " "],
        ["、", " "],
        ["\n", " "],
        ["\t", " "],
        ["\u3000", " "],
    ]);
    let normalized = String(rawMessage || "")
        .split("")
        .map((char) => translation.get(char) ?? char)
        .join("");
    normalized = normalized.replace(/\s+/g, " ").trim();
    let previous = "";
    while (previous !== normalized) {
        previous = normalized;
        for (const pattern of ACTION_PREFIX_PATTERNS) {
            normalized = normalized.replace(pattern, "").trim();
        }
    }
    return normalized;
}
function dedupePreserveOrder(values) {
    const seen = new Set();
    const result = [];
    for (const value of values || []) {
        const normalized = String(value || "").trim();
        if (!normalized) {
            continue;
        }
        const key = normalized.toLowerCase();
        if (seen.has(key)) {
            continue;
        }
        seen.add(key);
        result.push(normalized);
    }
    return result;
}
function findLiteralOccurrences(text, aliases) {
    const matches = [];
    const lowerText = text.toLowerCase();
    for (const [alias, canonical] of aliases) {
        let start = 0;
        const aliasLower = alias.toLowerCase();
        while (true) {
            const index = lowerText.indexOf(aliasLower, start);
            if (index === -1) {
                break;
            }
            matches.push({
                start: index,
                end: index + alias.length,
                text: text.slice(index, index + alias.length),
                value: canonical,
            });
            start = index + alias.length;
        }
    }
    return matches;
}
function selectNonOverlapping(matches) {
    const selected = [];
    const occupied = [];
    for (const match of [...matches].sort((a, b) => {
        const diff = a.start - b.start;
        if (diff !== 0) {
            return diff;
        }
        return (b.end - b.start) - (a.end - a.start);
    })) {
        const hasOverlap = occupied.some(([start, end]) => !(match.end <= start || match.start >= end));
        if (hasOverlap) {
            continue;
        }
        occupied.push([match.start, match.end]);
        selected.push(match);
    }
    return selected;
}
function extractBrand(text) {
    const aliasItems = Object.entries(BRAND_ALIASES).sort((a, b) => b[0].length - a[0].length);
    const matches = selectNonOverlapping(findLiteralOccurrences(text, aliasItems));
    const brands = dedupePreserveOrder(matches.map((match) => match.value));
    const aliases = dedupePreserveOrder(matches.map((match) => match.text));
    return [brands[0] || null, aliases];
}
function extractLiteralTerms(text, terms) {
    const aliasItems = [...terms].sort((a, b) => b.length - a.length).map((term) => [term, term]);
    const matches = selectNonOverlapping(findLiteralOccurrences(text, aliasItems));
    const values = dedupePreserveOrder(matches.map((match) => match.value));
    const aliases = dedupePreserveOrder(matches.map((match) => match.text));
    return [values, aliases];
}
function extractCategories(text) {
    const [categories, aliases] = extractLiteralTerms(text, CATEGORY_TERMS);
    return [categories[0] || null, aliases];
}
function extractAttributes(text) {
    const [literalAttributes, literalAliases] = extractLiteralTerms(text, ATTRIBUTE_TERMS);
    const patternAttributes = [];
    const patternAliases = [];
    for (const pattern of ATTRIBUTE_PATTERNS) {
        const regex = withGlobal(pattern);
        for (const match of text.matchAll(regex)) {
            const alias = String(match[0] || "").trim();
            if (alias) {
                patternAttributes.push(alias);
                patternAliases.push(alias);
            }
        }
    }
    const attributes = dedupePreserveOrder([...literalAttributes, ...patternAttributes]);
    const aliases = dedupePreserveOrder([...literalAliases, ...patternAliases]);
    return [attributes, aliases];
}
function pruneSubstringAliases(aliases, protectedAliases) {
    const filtered = [];
    for (const alias of aliases || []) {
        if (protectedAliases.some((protectedAlias) => alias !== protectedAlias && protectedAlias.includes(alias))) {
            continue;
        }
        filtered.push(alias);
    }
    return dedupePreserveOrder(filtered);
}
function guessBrandFromPrefix(text, models, categoryAliases, attributeAliases) {
    const candidatePositions = [];
    for (const value of dedupePreserveOrder([...models, ...categoryAliases, ...attributeAliases])) {
        const position = text.indexOf(value);
        if (position > 0) {
            candidatePositions.push(position);
        }
    }
    if (!candidatePositions.length) {
        return [null, []];
    }
    let prefix = text.slice(0, Math.min(...candidatePositions)).trim();
    prefix = prefix.replace(/[\s\-_/+.]+$/g, "");
    const compactPrefix = prefix.replace(/\s+/g, "");
    if (!isValidBrandCandidate(compactPrefix)) {
        return [null, []];
    }
    return [compactPrefix, [compactPrefix]];
}
function isValidBrandCandidate(candidate) {
    if (!candidate) {
        return false;
    }
    if (candidate.length < 2 || candidate.length > 12) {
        return false;
    }
    if (!/[\u4e00-\u9fffA-Za-z]/.test(candidate)) {
        return false;
    }
    if (/^[A-Za-z0-9]+$/.test(candidate) && candidate.length <= 2) {
        return false;
    }
    if (candidate.includes("的")) {
        return false;
    }
    if (containsFuzzyMarker(candidate)) {
        return false;
    }
    if (/\d+\s*(?:元)?(?:左右|以内|以下|上下)/.test(candidate)) {
        return false;
    }
    if (CATEGORY_TERMS.includes(candidate) || ATTRIBUTE_TERMS.includes(candidate)) {
        return false;
    }
    if (LEADING_QUANTITY_PATTERN.test(candidate)) {
        return false;
    }
    if (/^(?:男|女|男士|女士|男款|女款|夏季|冬季|春秋|速干|冰丝|运动|休闲|超薄|薄款|束脚)+$/.test(candidate)) {
        return false;
    }
    return true;
}
function extractSpecs(text) {
    const specs = [];
    for (const pattern of SPEC_PATTERNS) {
        const regex = withGlobal(pattern);
        for (const match of text.matchAll(regex)) {
            specs.push(String(match[0] || "").trim());
        }
    }
    return dedupePreserveOrder(specs);
}
function extractModels(text, specs) {
    const matches = [];
    for (const pattern of MODEL_PATTERNS) {
        const regex = withGlobal(pattern);
        for (const match of text.matchAll(regex)) {
            matches.push({
                start: match.index || 0,
                end: (match.index || 0) + String(match[0] || "").length,
                text: String(match[0] || "").trim(),
            });
        }
    }
    const selected = selectNonOverlapping(matches);
    const models = [];
    const specSet = new Set((specs || []).map((spec) => spec.toLowerCase()));
    const excluded = new Set(["pro", "max", "plus", "mini", "ultra", "gb", "tb", "ml", "kg"]);
    for (const match of selected) {
        const candidate = String(match.text || "").trim();
        const lowerCandidate = candidate.toLowerCase();
        if (specSet.has(lowerCandidate) || excluded.has(lowerCandidate) || /^\d+$/.test(candidate)) {
            continue;
        }
        models.push(candidate);
    }
    return dedupePreserveOrder(models);
}
function normalizeModels(models, brandAliases, brand) {
    const normalizedModels = [];
    const removablePrefixes = [];
    for (const alias of brandAliases || []) {
        if (!alias) {
            continue;
        }
        if (alias.toLowerCase() === String(brand || "").toLowerCase() || /[\u4e00-\u9fff]/.test(alias)) {
            removablePrefixes.push(alias);
        }
    }
    if (brand) {
        removablePrefixes.push(brand);
    }
    for (const model of models || []) {
        let normalized = model;
        for (const prefix of dedupePreserveOrder(removablePrefixes).sort((a, b) => b.length - a.length)) {
            if (prefix && normalized.toLowerCase().startsWith(prefix.toLowerCase())) {
                const trimmed = normalized.slice(prefix.length).trim();
                if (trimmed) {
                    normalized = trimmed;
                }
                break;
            }
        }
        normalizedModels.push(normalized);
    }
    return dedupePreserveOrder(normalizedModels);
}
function extractExplicitSku(text) {
    const match = text.match(EXPLICIT_SKU_PATTERN);
    return match ? String(match[1] || "").trim() || null : null;
}
function extractBudget(text) {
    const match = text.match(BUDGET_PATTERN);
    return match ? `${String(match[1] || "").trim()}元` : null;
}
function extractPlatformHint(text) {
    for (const [label, platform] of Object.entries(PLATFORM_HINTS)) {
        if (text.includes(label)) {
            return platform;
        }
    }
    return null;
}
function containsFuzzyMarker(text) {
    return FUZZY_MARKERS.some((marker) => text.includes(marker));
}
function cleanSlotText(value) {
    if (value === null || value === undefined || typeof value === "boolean") {
        return null;
    }
    const text = String(value).trim();
    if (!text) {
        return null;
    }
    const lower = text.toLowerCase();
    if (NULL_LIKE_VALUES.has(text) || NULL_LIKE_VALUES.has(lower)) {
        return null;
    }
    return text;
}
function compactText(text) {
    return String(text || "").replace(/[^0-9A-Za-z\u4e00-\u9fff&+\-/.]+/g, "").toLowerCase();
}
function evidenceInText(sourceText, evidence) {
    const normalizedSource = compactText(sourceText);
    const normalizedEvidence = compactText(evidence);
    return Boolean(normalizedSource && normalizedEvidence && normalizedSource.includes(normalizedEvidence));
}
function makeScalarRawSlot(value, evidence) {
    return {
        value: cleanSlotText(value),
        evidence: cleanSlotText(evidence),
    };
}
function makeListRawSlots(values) {
    return dedupePreserveOrder(values || []).map((value) => makeScalarRawSlot(value, value));
}
function normalizeScalarRawSlot(slot, sourceText) {
    if (Array.isArray(slot)) {
        slot = slot.length ? slot[0] : null;
    }
    let value;
    let evidence;
    if (slot && typeof slot === "object" && !Array.isArray(slot)) {
        value = cleanSlotText(slot.value);
        evidence = cleanSlotText(slot.evidence) || cleanSlotText(slot.quote);
    }
    else {
        value = cleanSlotText(slot);
        evidence = null;
    }
    if (!value && !evidence) {
        return makeScalarRawSlot();
    }
    if (!evidence) {
        evidence = value;
    }
    if (evidence && !evidenceInText(sourceText, evidence)) {
        const fallbackEvidence = value && evidenceInText(sourceText, value) ? value : null;
        if (!fallbackEvidence) {
            return makeScalarRawSlot();
        }
        evidence = fallbackEvidence;
    }
    if (!value) {
        value = evidence;
    }
    return makeScalarRawSlot(value, evidence);
}
function normalizeListRawSlots(items, sourceText) {
    if (items === null || items === undefined) {
        return [];
    }
    const list = Array.isArray(items) ? items : [items];
    const normalized = list
        .map((item) => normalizeScalarRawSlot(item, sourceText))
        .filter((slot) => Boolean(slot.value));
    const unique = [];
    const seen = new Set();
    for (const slot of normalized) {
        const key = `${String(slot.value || "").toLowerCase()}::${String(slot.evidence || "").toLowerCase()}`;
        if (seen.has(key)) {
            continue;
        }
        seen.add(key);
        unique.push(slot);
    }
    return unique;
}
function parseBool(value, defaultValue = false) {
    if (typeof value === "boolean") {
        return value;
    }
    const text = String(value || "").trim().toLowerCase();
    if (["true", "1", "yes"].includes(text)) {
        return true;
    }
    if (["false", "0", "no"].includes(text)) {
        return false;
    }
    return defaultValue;
}
function parseConfidence(value, defaultValue = 0) {
    const number = Number(value);
    if (!Number.isFinite(number)) {
        return defaultValue;
    }
    const normalized = number > 1 && number <= 100 ? number / 100 : number;
    return Math.round(Math.max(0, Math.min(normalized, 0.99)) * 100) / 100;
}
function normalizePriceNumber(value) {
    const number = Number(value);
    if (!Number.isFinite(number)) {
        return null;
    }
    if (Number.isInteger(number)) {
        return number;
    }
    return Math.round(number * 100) / 100;
}
function extractPriceConstraints(text) {
    let match = text.match(PRICE_RANGE_PATTERN);
    if (match) {
        return {
            price_min: normalizePriceNumber(match[1]),
            price_max: normalizePriceNumber(match[2]),
            price_target: null,
        };
    }
    match = text.match(PRICE_TARGET_PATTERN);
    if (match) {
        return {
            price_min: null,
            price_max: null,
            price_target: normalizePriceNumber(match[1]),
        };
    }
    match = text.match(PRICE_MAX_PATTERN);
    if (match) {
        return {
            price_min: null,
            price_max: normalizePriceNumber(match[1]),
            price_target: null,
        };
    }
    match = text.match(PRICE_MIN_PATTERN);
    if (match) {
        return {
            price_min: normalizePriceNumber(match[1]),
            price_max: null,
            price_target: null,
        };
    }
    match = text.match(BUDGET_PATTERN);
    if (match) {
        return {
            price_min: null,
            price_max: null,
            price_target: normalizePriceNumber(match[1]),
        };
    }
    return { price_min: null, price_max: null, price_target: null };
}
function normalizeBrandValue(value, evidence) {
    for (const candidate of [evidence, value]) {
        if (!candidate) {
            continue;
        }
        const candidateLower = candidate.toLowerCase();
        for (const [alias, canonical] of Object.entries(BRAND_ALIASES)) {
            if (alias.toLowerCase() === candidateLower) {
                return canonical;
            }
        }
    }
    return value || evidence || null;
}
function normalizePlatformHintValue(value, evidence) {
    for (const candidate of [value, evidence]) {
        const cleaned = cleanSlotText(candidate);
        if (!cleaned) {
            continue;
        }
        if (PLATFORM_HINTS[cleaned]) {
            return PLATFORM_HINTS[cleaned];
        }
        const lowered = cleaned.toLowerCase();
        if (["taobao", "jd", "pdd"].includes(lowered)) {
            return lowered;
        }
    }
    return null;
}
function extractMappedValue(text, hintPairs) {
    for (const [rawHint, normalized] of hintPairs) {
        if (text.includes(rawHint)) {
            return normalized;
        }
    }
    return null;
}
function extractCrowd(text) {
    return extractMappedValue(text, CROWD_HINTS);
}
function extractUsageScene(text) {
    return extractMappedValue(text, USAGE_SCENE_HINTS);
}
function extractGiftTarget(text) {
    return extractMappedValue(text, GIFT_TARGET_HINTS);
}
function extractSortHint(text) {
    return extractMappedValue(text, SORT_HINTS);
}
function sortValuesByPosition(sourceText, values) {
    const unique = dedupePreserveOrder(values || []);
    return unique
        .map((value) => ({
        index: sourceText.indexOf(value),
        weight: -value.length,
        value,
    }))
        .sort((a, b) => {
        const indexA = a.index >= 0 ? a.index : 10 ** 9;
        const indexB = b.index >= 0 ? b.index : 10 ** 9;
        if (indexA !== indexB) {
            return indexA - indexB;
        }
        return a.weight - b.weight;
    })
        .map((item) => item.value);
}
function dropSubstringValues(values) {
    const filtered = [];
    const unique = dedupePreserveOrder(values || []);
    for (const value of unique) {
        if (value.length === 1 && ["男", "女"].includes(value)) {
            continue;
        }
        if (unique.some((other) => value !== other && other.includes(value))) {
            continue;
        }
        filtered.push(value);
    }
    return filtered;
}
function choosePreferredText(modelValue, ruleValue, preferSpecific = false) {
    if (modelValue && ruleValue) {
        if (preferSpecific) {
            if (GENERIC_ONLY_TERMS.has(modelValue) && !GENERIC_ONLY_TERMS.has(ruleValue)) {
                return ruleValue;
            }
            if (ruleValue.length > modelValue.length && ruleValue.includes(modelValue)) {
                return ruleValue;
            }
        }
        return modelValue;
    }
    return modelValue || ruleValue || null;
}
function selectQueryAttributes(attributes) {
    const strong = (attributes || []).filter((value) => !LOW_PRIORITY_QUERY_ATTRIBUTES.has(value));
    return (strong.length ? strong : attributes || []).slice(0, 3);
}
function buildQueryCoreTokens(brand, sku, model, productName, category, attributes, specs) {
    const tokens = [];
    if (brand) {
        tokens.push(brand);
    }
    if (sku) {
        tokens.push(sku);
    }
    else if (model) {
        tokens.push(model);
    }
    if (productName) {
        tokens.push(productName);
    }
    else if (category) {
        tokens.push(category);
    }
    tokens.push(...selectQueryAttributes(attributes || []));
    if (specs?.length && !(sku || model)) {
        tokens.push(...specs.slice(0, 2));
    }
    else if (specs?.length) {
        tokens.push(...specs.slice(0, 1));
    }
    return dedupePreserveOrder(tokens);
}
function pickScalarEvidence(sourceText, selectedValue, ...slots) {
    if (!selectedValue) {
        return null;
    }
    const selectedKey = compactText(selectedValue);
    for (const slot of slots) {
        if (!slot || typeof slot !== "object") {
            continue;
        }
        const slotValue = cleanSlotText(slot.value);
        const slotEvidence = cleanSlotText(slot.evidence);
        if (slotValue && compactText(slotValue) === selectedKey) {
            return slotEvidence || slotValue;
        }
        if (slotEvidence && compactText(slotEvidence) === selectedKey) {
            return slotEvidence;
        }
    }
    return evidenceInText(sourceText, selectedValue) ? selectedValue : null;
}
function pickListEvidence(sourceText, values) {
    return dedupePreserveOrder((values || []).filter((value) => evidenceInText(sourceText, value)));
}
function buildProductSearchIntent(params) {
    const resolvedProductName = params.product_name || params.category;
    const resolvedQueryText = String(params.query_text || "").trim();
    const resolvedTokens = dedupePreserveOrder(params.query_tokens || []);
    return {
        success: Boolean(params.success && resolvedProductName && resolvedQueryText && resolvedTokens.length),
        intent_mode: params.intent_mode,
        query_text: resolvedQueryText,
        query_tokens: resolvedTokens,
        brand: params.brand ?? null,
        series: params.series ?? null,
        model: params.model ?? null,
        sku: params.sku ?? null,
        product_name: resolvedProductName,
        category: params.category ?? null,
        attributes: params.attributes || [],
        specs: params.specs || [],
        price_min: params.price_min ?? null,
        price_max: params.price_max ?? null,
        price_target: params.price_target ?? null,
        crowd: params.crowd ?? null,
        usage_scene: params.usage_scene ?? null,
        gift_target: params.gift_target ?? null,
        platform_hint: params.platform_hint ?? null,
        sort_hint: params.sort_hint ?? null,
        confidence: Math.round(Number(params.confidence || 0) * 100) / 100,
        evidence: params.evidence || {},
    };
}
function extractRuleSignals(cleanedText) {
    let [brand, brandAliases] = extractBrand(cleanedText);
    const [category, categoryAliases] = extractCategories(cleanedText);
    const [, attributeAliasesRaw] = extractAttributes(cleanedText);
    let attributeAliases = pruneSubstringAliases(attributeAliasesRaw, categoryAliases);
    attributeAliases = sortValuesByPosition(cleanedText, dropSubstringValues(attributeAliases));
    const specs = sortValuesByPosition(cleanedText, extractSpecs(cleanedText));
    const explicitSku = extractExplicitSku(cleanedText);
    let models = extractModels(cleanedText, specs);
    models = normalizeModels(models, brandAliases, brand);
    if (!brand) {
        const [guessedBrand, guessedAliases] = guessBrandFromPrefix(cleanedText, models, categoryAliases, attributeAliases);
        if (guessedBrand) {
            brand = guessedBrand;
            brandAliases = dedupePreserveOrder([...brandAliases, ...guessedAliases]);
        }
    }
    const model = models[0] || null;
    const budget = extractBudget(cleanedText);
    const platformHint = extractPlatformHint(cleanedText);
    const residualText = buildResidualText(cleanedText, brandAliases, categoryAliases, attributeAliases, models, specs, explicitSku, budget);
    const productName = deriveProductName(category, categoryAliases, residualText);
    return {
        brand,
        brand_aliases: brandAliases,
        category,
        category_aliases: categoryAliases,
        attribute_aliases: attributeAliases,
        specs,
        sku: explicitSku,
        models,
        model,
        budget,
        platform_hint: platformHint,
        product_name: productName,
    };
}
function buildRuleRawSlots(ruleSignals) {
    let productEvidence = ruleSignals.product_name;
    if ((ruleSignals.category_aliases || []).length) {
        productEvidence = ruleSignals.category_aliases[0];
    }
    return {
        brand: makeScalarRawSlot(ruleSignals.brand, ruleSignals.brand_aliases?.[0] || ruleSignals.brand),
        model: makeScalarRawSlot(ruleSignals.model, ruleSignals.model),
        sku: makeScalarRawSlot(ruleSignals.sku, ruleSignals.sku),
        product_name: makeScalarRawSlot(ruleSignals.product_name, productEvidence),
        category: makeScalarRawSlot(ruleSignals.category, ruleSignals.category_aliases?.[0] || ruleSignals.category),
        attributes: makeListRawSlots(ruleSignals.attribute_aliases || []),
        specs: makeListRawSlots(ruleSignals.specs || []),
        platform_hint: makeScalarRawSlot(ruleSignals.platform_hint, ruleSignals.platform_hint),
    };
}
function buildModelUserPrompt(rawMessage, cleanedText, ruleSignals) {
    const heuristicHint = {
        brand: ruleSignals.brand,
        model: ruleSignals.model,
        sku: ruleSignals.sku,
        product_name: ruleSignals.product_name,
        category: ruleSignals.category,
        attributes: ruleSignals.attribute_aliases,
        specs: ruleSignals.specs,
    };
    return [
        "请从下面的电商商品查询文本中抽取槽位。",
        "只返回 JSON 对象。",
        "如果是完整商品标题，也通常判定为精准搜索。",
        "品牌优先从标题前缀识别，例如“JR真维斯...”里的品牌是“JR真维斯”。",
        `原始消息: ${rawMessage}`,
        `清洗后消息: ${cleanedText}`,
        `启发式参考(仅供参考，原文优先): ${JSON.stringify(heuristicHint, undefined, 0)}`,
    ].join("\n");
}
async function callModelSlotExtractor(rawMessage, cleanedText, ruleSignals) {
    const response = await (0, common_1.requestModelJson)(MODEL_SLOT_SYSTEM_PROMPT, buildModelUserPrompt(rawMessage, cleanedText, ruleSignals), 0.1, 1200);
    const payload = response.json || {};
    return {
        slot_source: "model",
        model_provider: response.provider,
        model_name: response.model,
        is_precise_search: parseBool(payload.is_precise_search, false),
        confidence: parseConfidence(payload.confidence, 0),
        brand: normalizeScalarRawSlot(payload.brand, cleanedText),
        model: normalizeScalarRawSlot(payload.model, cleanedText),
        sku: normalizeScalarRawSlot(payload.sku, cleanedText),
        product_name: normalizeScalarRawSlot(payload.product_name, cleanedText),
        category: normalizeScalarRawSlot(payload.category, cleanedText),
        attributes: normalizeListRawSlots(payload.attributes, cleanedText),
        specs: normalizeListRawSlots(payload.specs, cleanedText),
        platform_hint: normalizeScalarRawSlot(payload.platform_hint, cleanedText),
    };
}
function getScalarSlotValue(slot) {
    if (!slot) {
        return null;
    }
    return cleanSlotText(slot.value);
}
function getListSlotValues(slots) {
    const values = [];
    for (const slot of slots || []) {
        const value = getScalarSlotValue(slot);
        if (value) {
            values.push(value);
        }
    }
    return dedupePreserveOrder(values);
}
function mergeModelAndRuleSlots(cleanedText, modelRawSlots, ruleSignals) {
    const ruleRawSlots = buildRuleRawSlots(ruleSignals);
    const activeRawSlots = modelRawSlots || {
        slot_source: "rule_fallback",
        model_provider: null,
        model_name: null,
        is_precise_search: false,
        confidence: 0,
        ...ruleRawSlots,
    };
    const brandValue = choosePreferredText(normalizeBrandValue(getScalarSlotValue(activeRawSlots.brand), activeRawSlots.brand?.evidence), normalizeBrandValue(getScalarSlotValue(ruleRawSlots.brand), ruleRawSlots.brand?.evidence));
    const modelValue = choosePreferredText(getScalarSlotValue(activeRawSlots.model), getScalarSlotValue(ruleRawSlots.model));
    const skuValue = choosePreferredText(getScalarSlotValue(activeRawSlots.sku), getScalarSlotValue(ruleRawSlots.sku));
    const productNameValue = choosePreferredText(getScalarSlotValue(activeRawSlots.product_name), getScalarSlotValue(ruleRawSlots.product_name), true);
    const categoryValue = choosePreferredText(getScalarSlotValue(activeRawSlots.category), getScalarSlotValue(ruleRawSlots.category), true);
    const platformHintValue = normalizePlatformHintValue(getScalarSlotValue(activeRawSlots.platform_hint), activeRawSlots.platform_hint?.evidence) ||
        ruleSignals.platform_hint;
    const attributes = sortValuesByPosition(cleanedText, dropSubstringValues([...getListSlotValues(activeRawSlots.attributes), ...(ruleSignals.attribute_aliases || [])]));
    const specs = sortValuesByPosition(cleanedText, [...getListSlotValues(activeRawSlots.specs), ...(ruleSignals.specs || [])]);
    const queryCoreTokens = buildQueryCoreTokens(brandValue, skuValue, modelValue, productNameValue, categoryValue, attributes, specs);
    const tokensAll = dedupePreserveOrder([brandValue, skuValue || modelValue, productNameValue, categoryValue !== productNameValue ? categoryValue : null]
        .filter(Boolean)).concat(attributes, specs);
    const tokens = dedupePreserveOrder(tokensAll);
    const queryCore = queryCoreTokens.join(" ").trim();
    const confidence = Math.max(parseConfidence(activeRawSlots.confidence, 0), calculateConfidence(brandValue, skuValue, modelValue, productNameValue, categoryValue, specs));
    const finalIsPreciseSearch = isPreciseSearch(brandValue, skuValue, modelValue, productNameValue, categoryValue, categoryValue ? [categoryValue] : [], attributes, cleanedText, ruleSignals.budget);
    const intentMode = finalIsPreciseSearch ? "precise" : "fuzzy";
    const crowdValue = extractCrowd(cleanedText);
    const usageSceneValue = extractUsageScene(cleanedText);
    const giftTargetValue = extractGiftTarget(cleanedText);
    const sortHintValue = extractSortHint(cleanedText);
    const priceConstraints = extractPriceConstraints(cleanedText);
    const productSearchIntent = buildProductSearchIntent({
        success: true,
        intent_mode: intentMode,
        query_text: queryCore,
        query_tokens: queryCoreTokens,
        brand: brandValue,
        series: null,
        model: modelValue,
        sku: skuValue,
        product_name: productNameValue,
        category: categoryValue,
        attributes,
        specs,
        price_min: priceConstraints.price_min,
        price_max: priceConstraints.price_max,
        price_target: priceConstraints.price_target,
        crowd: crowdValue,
        usage_scene: usageSceneValue,
        gift_target: giftTargetValue,
        sort_hint: sortHintValue,
        platform_hint: platformHintValue,
        confidence,
        evidence: {
            brand: pickScalarEvidence(cleanedText, brandValue, activeRawSlots.brand, ruleRawSlots.brand),
            series: null,
            model: pickScalarEvidence(cleanedText, modelValue, activeRawSlots.model, ruleRawSlots.model),
            sku: pickScalarEvidence(cleanedText, skuValue, activeRawSlots.sku, ruleRawSlots.sku),
            product_name: pickScalarEvidence(cleanedText, productNameValue, activeRawSlots.product_name, ruleRawSlots.product_name),
            category: pickScalarEvidence(cleanedText, categoryValue, activeRawSlots.category, ruleRawSlots.category),
            attributes: pickListEvidence(cleanedText, attributes),
            specs: pickListEvidence(cleanedText, specs),
            crowd: crowdValue && evidenceInText(cleanedText, crowdValue) ? crowdValue : null,
            usage_scene: usageSceneValue && evidenceInText(cleanedText, usageSceneValue) ? usageSceneValue : null,
            gift_target: giftTargetValue && evidenceInText(cleanedText, giftTargetValue) ? giftTargetValue : null,
        },
    });
    return {
        success: true,
        module: "precise-product-search",
        intent: "recognize_precise_product_search",
        next_action: "prepare_product_search",
        intent_mode: intentMode,
        slot_source: activeRawSlots.slot_source || "rule_fallback",
        model_provider: activeRawSlots.model_provider,
        model_name: activeRawSlots.model_name,
        is_precise_search: finalIsPreciseSearch,
        confidence: Math.round(confidence * 100) / 100,
        raw_slots: {
            brand: activeRawSlots.brand || makeScalarRawSlot(),
            product_name: activeRawSlots.product_name || makeScalarRawSlot(),
            model: activeRawSlots.model || makeScalarRawSlot(),
            sku: activeRawSlots.sku || makeScalarRawSlot(),
            category: activeRawSlots.category || makeScalarRawSlot(),
            attributes: activeRawSlots.attributes || [],
            specs: activeRawSlots.specs || [],
            platform_hint: activeRawSlots.platform_hint || makeScalarRawSlot(),
        },
        slots: {
            brand: brandValue,
            product_name: productNameValue,
            model: modelValue,
            sku: skuValue,
            specs,
            category: categoryValue,
            attributes,
            platform_hint: platformHintValue,
            budget: ruleSignals.budget,
            query_core: queryCore,
        },
        tokens_all: tokens,
        query_core_tokens: queryCoreTokens,
        tokens,
        product_search_intent: productSearchIntent,
        search_request: (0, productSearchProtocol_1.buildSearchRequest)(productSearchIntent),
        user_message: (0, productSearchProtocol_1.buildSearchReadyUserMessage)("商品搜索意图识别结果", "已识别到明确商品，可进入搜索", productSearchIntent),
    };
}
function shouldShortCircuitToFuzzy(cleanedText, ruleSignals) {
    if (ruleSignals.sku || ruleSignals.model) {
        return false;
    }
    if (ruleSignals.brand && (ruleSignals.product_name || ruleSignals.category)) {
        return false;
    }
    if (containsFuzzyMarker(cleanedText) && !(ruleSignals.brand || ruleSignals.sku || ruleSignals.model)) {
        return true;
    }
    return false;
}
function buildResidualText(text, brandAliases, categoryAliases, attributeAliases, models, specs, explicitSku, budget) {
    let residual = text;
    const removable = [...brandAliases, ...categoryAliases, ...attributeAliases, ...models, ...specs];
    if (explicitSku) {
        removable.push(explicitSku);
    }
    if (budget) {
        removable.push(budget);
    }
    for (const value of removable.sort((a, b) => b.length - a.length)) {
        if (!value) {
            continue;
        }
        residual = residual.replace(new RegExp(escapeRegExp(value), "gi"), " ");
    }
    for (const marker of Object.keys(PLATFORM_HINTS)) {
        residual = residual.replace(new RegExp(escapeRegExp(marker), "g"), " ");
    }
    residual = residual.replace(/(?:sku|SKU|型号|货号|款号|编码)\s*[:：]?/g, " ");
    return residual.replace(/\s+/g, " ").trim();
}
function deriveProductName(category, categoryAliases, residualText) {
    if (categoryAliases.length) {
        return categoryAliases[0];
    }
    const tokens = residualText.match(TOKEN_PATTERN) || [];
    const filtered = tokens.filter((token) => {
        const lowered = token.toLowerCase();
        if (Object.keys(PLATFORM_HINTS).map((value) => value.toLowerCase()).includes(lowered)) {
            return false;
        }
        if (token.length === 1 && /^[A-Za-z]$/.test(token)) {
            return false;
        }
        return true;
    });
    if (filtered.length) {
        return filtered.slice(0, 3).join(" ").trim();
    }
    return category;
}
function calculateConfidence(brand, sku, model, productName, category, specs) {
    let confidence = 0;
    if (sku) {
        confidence += 0.45;
    }
    if (model) {
        confidence += 0.4;
    }
    if (brand) {
        confidence += 0.25;
    }
    if (productName) {
        confidence += 0.2;
    }
    if (category) {
        confidence += 0.1;
    }
    if (specs?.length) {
        confidence += 0.05;
    }
    return Math.round(Math.min(confidence, 0.99) * 100) / 100;
}
function isPreciseSearch(brand, sku, model, productName, category, categoryAliases, attributeAliases, cleanedText, budget) {
    if (sku || model) {
        return true;
    }
    if (brand && (productName || category)) {
        return true;
    }
    if (productName &&
        category &&
        cleanedText.length >= 8 &&
        !containsFuzzyMarker(cleanedText) &&
        (categoryAliases.length >= 2 || attributeAliases.length >= 1)) {
        return true;
    }
    if (productName && !category && productName.length >= 6 && !containsFuzzyMarker(cleanedText)) {
        return true;
    }
    if (productName && GENERIC_ONLY_TERMS.has(productName)) {
        return false;
    }
    if (budget && !(brand || model || sku)) {
        return false;
    }
    if (containsFuzzyMarker(cleanedText) && !(brand || model || sku)) {
        return false;
    }
    return false;
}
function normalizeFuzzyProductName(productName, category, cleanedText) {
    if (category) {
        return category;
    }
    let normalized = cleanSlotText(productName) || "";
    if (cleanedText.includes("礼物")) {
        return "礼物";
    }
    normalized = normalized.replace(/^(?:适合|推荐个|推荐一款|推荐|帮我找|帮我选|找个|来个)\s*/, "");
    normalized = normalized.replace(/送给(?:女朋友|男朋友|老婆|老公|妈妈|爸爸|孩子|宝宝)(?:的)?/g, "");
    normalized = normalized.replace(LEADING_QUANTITY_PREFIX_PATTERN, "");
    normalized = normalized.trim();
    return normalized || productName || category;
}
function buildFuzzyIntentFromRuleSignals(cleanedText, ruleSignals, confidence = 0.45) {
    const attributes = ruleSignals.attribute_aliases || [];
    const specs = ruleSignals.specs || [];
    const category = ruleSignals.category;
    const crowdValue = extractCrowd(cleanedText);
    const usageSceneValue = extractUsageScene(cleanedText);
    const giftTargetValue = extractGiftTarget(cleanedText);
    const sortHintValue = extractSortHint(cleanedText);
    const productName = normalizeFuzzyProductName(ruleSignals.product_name, category, cleanedText);
    const priceConstraints = extractPriceConstraints(cleanedText);
    const queryTokens = dedupePreserveOrder([productName, ruleSignals.brand, usageSceneValue, giftTargetValue, crowdValue].filter(Boolean)).concat(selectQueryAttributes(attributes));
    const normalizedTokens = dedupePreserveOrder(queryTokens);
    return buildProductSearchIntent({
        success: true,
        intent_mode: "fuzzy",
        query_text: normalizedTokens.join(" ").trim(),
        query_tokens: normalizedTokens,
        brand: ruleSignals.brand,
        series: null,
        model: ruleSignals.model,
        sku: ruleSignals.sku,
        product_name: productName,
        category,
        attributes,
        specs,
        price_min: priceConstraints.price_min,
        price_max: priceConstraints.price_max,
        price_target: priceConstraints.price_target,
        crowd: crowdValue,
        usage_scene: usageSceneValue,
        gift_target: giftTargetValue,
        sort_hint: sortHintValue,
        platform_hint: ruleSignals.platform_hint,
        confidence,
        evidence: {
            brand: ruleSignals.brand ? ruleSignals.brand_aliases?.[0] || null : null,
            series: null,
            model: ruleSignals.model,
            sku: ruleSignals.sku,
            product_name: productName && evidenceInText(cleanedText, productName) ? productName : null,
            category: category && evidenceInText(cleanedText, category) ? category : null,
            attributes: pickListEvidence(cleanedText, attributes),
            specs: pickListEvidence(cleanedText, specs),
            crowd: crowdValue,
            usage_scene: usageSceneValue,
            gift_target: giftTargetValue,
        },
    });
}
function buildEmptyProductSearchIntent(intentMode = "fuzzy", confidence = 0) {
    return buildProductSearchIntent({
        success: false,
        intent_mode: intentMode,
        query_text: "",
        query_tokens: [],
        brand: null,
        series: null,
        model: null,
        sku: null,
        product_name: null,
        category: null,
        attributes: [],
        specs: [],
        price_min: null,
        price_max: null,
        price_target: null,
        crowd: null,
        usage_scene: null,
        gift_target: null,
        sort_hint: null,
        platform_hint: null,
        confidence,
        evidence: {},
    });
}
async function buildSuccessResponse(rawMessage) {
    const cleanedText = normalizeText(rawMessage);
    const ruleSignals = extractRuleSignals(cleanedText);
    let modelRawSlots = null;
    try {
        modelRawSlots = await callModelSlotExtractor(rawMessage, cleanedText, ruleSignals);
    }
    catch {
        modelRawSlots = null;
    }
    const merged = mergeModelAndRuleSlots(cleanedText, modelRawSlots, ruleSignals);
    if (!merged.is_precise_search) {
        return buildImpreciseResponse("FUZZY_SEARCH_LIKE", cleanedText, ruleSignals, merged);
    }
    return merged;
}
function buildImpreciseResponse(code = "FUZZY_SEARCH_LIKE", cleanedText, ruleSignals, merged) {
    let productSearchIntent = buildEmptyProductSearchIntent("fuzzy", 0.2);
    if (merged?.product_search_intent) {
        productSearchIntent = {
            ...merged.product_search_intent,
            intent_mode: "fuzzy",
            success: Boolean(merged.product_search_intent.product_name && merged.product_search_intent.query_text),
        };
    }
    else if (cleanedText !== undefined && ruleSignals) {
        productSearchIntent = buildFuzzyIntentFromRuleSignals(cleanedText, ruleSignals);
    }
    return {
        success: false,
        module: "precise-product-search",
        intent: "recognize_precise_product_search",
        next_action: "ask_for_precise_query",
        intent_mode: "fuzzy",
        is_precise_search: false,
        code,
        product_search_intent: productSearchIntent,
        search_request: (0, productSearchProtocol_1.buildSearchRequest)(productSearchIntent),
        user_message: [
            "商品搜索意图识别结果",
            "",
            "当前更像一般商品搜索需求，还缺少更明确的商品信息。",
            "请直接告诉我 品牌+型号 / 商品名 / SKU。",
            "例如：李宁 行川2SE、iPhone 15 Pro Max、A2698",
        ].join("\n"),
    };
}
function buildLinkLikeResponse() {
    const intent = buildEmptyProductSearchIntent("fuzzy", 0);
    return {
        success: false,
        module: "precise-product-search",
        intent: "recognize_precise_product_search",
        next_action: "route_to_m02",
        intent_mode: null,
        is_precise_search: false,
        code: "LINK_LIKE_MESSAGE",
        product_search_intent: intent,
        search_request: (0, productSearchProtocol_1.buildSearchRequest)(intent),
        user_message: "商品搜索意图识别结果\n\n当前消息更像商品链接，请按链接返利处理。",
    };
}
async function recognizeRawMessage(rawMessage) {
    const normalizedMessage = String(rawMessage || "").trim();
    if (!normalizedMessage) {
        return buildImpreciseResponse("EMPTY_QUERY");
    }
    if (exports.LINK_PATTERN.test(normalizedMessage)) {
        return buildLinkLikeResponse();
    }
    const cleanedText = normalizeText(normalizedMessage);
    const ruleSignals = extractRuleSignals(cleanedText);
    if (shouldShortCircuitToFuzzy(cleanedText, ruleSignals)) {
        return buildImpreciseResponse("FUZZY_SEARCH_LIKE", cleanedText, ruleSignals);
    }
    return await buildSuccessResponse(normalizedMessage);
}
async function main() {
    const rawMessage = process.argv[process.argv.indexOf("--raw-message") + 1];
    if (!rawMessage) {
        throw new Error("missing required option: --raw-message");
    }
    (0, common_1.printJson)(await recognizeRawMessage(rawMessage));
    return 0;
}
if (require.main === module) {
    main().then((code) => process.exit(code), (error) => {
        console.error(error instanceof Error ? error.message : String(error));
        process.exit(1);
    });
}


path: scripts/lib/cli.js
content: "use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.getOption = getOption;
exports.requireOption = requireOption;
exports.parseOutputFormat = parseOutputFormat;
function getOption(argv, name) {
    const flag = `--${name}`;
    const index = argv.indexOf(flag);
    if (index === -1) {
        return undefined;
    }
    return argv[index + 1];
}
function requireOption(argv, name) {
    const value = getOption(argv, name);
    if (!value) {
        throw new Error(`missing required option: --${name}`);
    }
    return value;
}
function parseOutputFormat(argv, defaultFormat = "md") {
    const value = (getOption(argv, "format") || defaultFormat).trim();
    if (value === "json" || value === "text" || value === "md") {
        return value;
    }
    throw new Error(`unsupported format: ${value}`);
}


path: scripts/lib/http.js
content: "use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.requestText = requestText;
const http_1 = __importDefault(require("http"));
const https_1 = __importDefault(require("https"));
const url_1 = require("url");
async function requestText(rawUrl, options = {}) {
    const { method = "GET", headers = {}, body, timeoutMs = 15000, insecure = false, maxRedirects = 5, } = options;
    const target = new url_1.URL(rawUrl);
    const isHttps = target.protocol === "https:";
    const transport = isHttps ? https_1.default : http_1.default;
    const requestHeaders = { ...headers };
    const hasContentLength = Object.keys(requestHeaders).some((key) => key.toLowerCase() === "content-length");
    if (body !== undefined && !hasContentLength) {
        requestHeaders["Content-Length"] = String(Buffer.byteLength(body, "utf-8"));
    }
    return await new Promise((resolve, reject) => {
        const request = transport.request({
            protocol: target.protocol,
            hostname: target.hostname,
            port: target.port || undefined,
            path: `${target.pathname}${target.search}`,
            method,
            headers: requestHeaders,
            agent: isHttps && insecure ? new https_1.default.Agent({ rejectUnauthorized: false }) : undefined,
        }, async (response) => {
            const chunks = [];
            response.on("data", (chunk) => {
                chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
            });
            response.on("end", async () => {
                const statusCode = response.statusCode || 0;
                const responseBody = Buffer.concat(chunks).toString("utf-8");
                const location = response.headers.location;
                if (location &&
                    [301, 302, 303, 307, 308].includes(statusCode) &&
                    maxRedirects > 0) {
                    try {
                        const nextUrl = new url_1.URL(location, target).toString();
                        const redirected = await requestText(nextUrl, {
                            method: statusCode === 303 ? "GET" : method,
                            headers: requestHeaders,
                            body: statusCode === 303 ? undefined : body,
                            timeoutMs,
                            insecure,
                            maxRedirects: maxRedirects - 1,
                        });
                        resolve(redirected);
                    }
                    catch (error) {
                        reject(error);
                    }
                    return;
                }
                resolve({
                    statusCode,
                    headers: response.headers,
                    body: responseBody,
                });
            });
        });
        request.on("error", reject);
        request.setTimeout(timeoutMs, () => {
            request.destroy(new Error(`request timeout after ${timeoutMs}ms`));
        });
        if (body !== undefined) {
            request.write(body);
        }
        request.end();
    });
}


path: scripts/cli/rebate_assistant_router.js
content: "use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const cli_1 = require("../lib/cli");
const rebateAssistantRouter_1 = require("../rebateAssistantRouter");
async function main() {
    const argv = process.argv.slice(2);
    return await (0, rebateAssistantRouter_1.emitResponse)((0, cli_1.requireOption)(argv, "raw-message"), (0, cli_1.parseOutputFormat)(argv, "md"));
}
main().then((code) => process.exit(code), (error) => {
    console.error(error instanceof Error ? error.message : String(error));
    process.exit(1);
});


path: scripts/cli/m01_operation_guide.js
content: "use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const cli_1 = require("../lib/cli");
const m01OperationGuide_1 = require("../m01OperationGuide");
async function main() {
    const argv = process.argv.slice(2);
    return await (0, m01OperationGuide_1.emitAction)((0, cli_1.requireOption)(argv, "action"), (0, cli_1.parseOutputFormat)(argv, "md"), (0, cli_1.getOption)(argv, "raw-message") || "");
}
main().then((code) => process.exit(code), (error) => {
    console.error(error instanceof Error ? error.message : String(error));
    process.exit(1);
});


path: scripts/cli/recognize_fuzzy_product_search.js
content: "use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const cli_1 = require("../lib/cli");
const recognizeFuzzyProductSearch_1 = require("../recognizeFuzzyProductSearch");
const common_1 = require("../common");
async function main() {
    const argv = process.argv.slice(2);
    (0, common_1.printJson)(await (0, recognizeFuzzyProductSearch_1.recognizeRawMessage)((0, cli_1.requireOption)(argv, "raw-message")));
    return 0;
}
main().then((code) => process.exit(code), (error) => {
    console.error(error instanceof Error ? error.message : String(error));
    process.exit(1);
});


path: scripts/cli/product_search.js
content: "use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const cli_1 = require("../lib/cli");
const productSearch_1 = require("../productSearch");
async function main() {
    const argv = process.argv.slice(2);
    return await (0, productSearch_1.emitResponse)((0, cli_1.requireOption)(argv, "raw-message"), (0, cli_1.parseOutputFormat)(argv, "md"));
}
main().then((code) => process.exit(code), (error) => {
    console.error(error instanceof Error ? error.message : String(error));
    process.exit(1);
});


path: scripts/cli/m04_fuzzy_search.js
content: "use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const cli_1 = require("../lib/cli");
const productSearch_1 = require("../productSearch");
async function main() {
    const argv = process.argv.slice(2);
    return await (0, productSearch_1.emitResponse)((0, cli_1.requireOption)(argv, "raw-message"), (0, cli_1.parseOutputFormat)(argv, "md"));
}
main().then((code) => process.exit(code), (error) => {
    console.error(error instanceof Error ? error.message : String(error));
    process.exit(1);
});


path: scripts/cli/check_s01_routing.js
content: "use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const checkS01Routing_1 = require("../checkS01Routing");
process.exit((0, checkS01Routing_1.main)());


path: scripts/cli/recognize_precise_product_search.js
content: "use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const cli_1 = require("../lib/cli");
const recognizePreciseProductSearch_1 = require("../recognizePreciseProductSearch");
const common_1 = require("../common");
async function main() {
    const argv = process.argv.slice(2);
    (0, common_1.printJson)(await (0, recognizePreciseProductSearch_1.recognizeRawMessage)((0, cli_1.requireOption)(argv, "raw-message")));
    return 0;
}
main().then((code) => process.exit(code), (error) => {
    console.error(error instanceof Error ? error.message : String(error));
    process.exit(1);
});


path: scripts/cli/m02_platform_link.js
content: "use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const cli_1 = require("../lib/cli");
const m02PlatformLink_1 = require("../m02PlatformLink");
async function main() {
    const argv = process.argv.slice(2);
    return await (0, m02PlatformLink_1.emitResponse)((0, cli_1.requireOption)(argv, "raw-message"), (0, cli_1.parseOutputFormat)(argv, "md"));
}
main().then((code) => process.exit(code), (error) => {
    console.error(error instanceof Error ? error.message : String(error));
    process.exit(1);
});


path: scripts/cli/m03_precise_search.js
content: "use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const cli_1 = require("../lib/cli");
const productSearch_1 = require("../productSearch");
async function main() {
    const argv = process.argv.slice(2);
    return await (0, productSearch_1.emitResponse)((0, cli_1.requireOption)(argv, "raw-message"), (0, cli_1.parseOutputFormat)(argv, "md"));
}
main().then((code) => process.exit(code), (error) => {
    console.error(error instanceof Error ? error.message : String(error));
    process.exit(1);
});


path: scripts/cli/recognize_platform_link.js
content: "use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const common_1 = require("../common");
const cli_1 = require("../lib/cli");
const recognizePlatformLink_1 = require("../recognizePlatformLink");
function main() {
    const argv = process.argv.slice(2);
    (0, common_1.printJson)((0, recognizePlatformLink_1.recognizeRawMessage)((0, cli_1.requireOption)(argv, "raw-message")));
    return 0;
}
process.exit(main());


Security scan verdicts — ClawScan: suspicious | static analysis: clean | VirusTotal: clean | SkillSpector: suspicious (CRITICAL)