import type { ApiResponse, SearchResponse, ProductResponse, ReviewsResponse } from '../types/api.js'; /** * 1688 关键词 / 以图搜图慢路径(Apify actor 冷启动 + 1688 风控轮换 session)实测可达 ~110s, * 给 180s 兜底,避免慢但最终会成功的搜索被 30s 砍掉、用户体感"超时"。 */ export declare const SUPPLIER_SEARCH_TIMEOUT_MS = 180000; /** * 1688 单品详情超时。**不能借用上面那个 180s** —— 详情走的是 TMAPI 同步接口,后端 * axios 30s 就放弃了(`backend/src/services/tmapi.ts`)。借用的结果是后端早已回错、 * CLI 还在等,agent turn 白挂两分半,用户只看到光标在转。 * * 取 60s:比后端 30s 多留一倍余量给缓存查询、转换与网络抖动,又远早于「等到天荒地老」。 * ⚠️ 后端那个 30s 若上调,这个值要跟着往上抬。 */ export declare const SUPPLIER_DETAIL_TIMEOUT_MS = 60000; /** * Temu 搜索超时。取值的硬约束是**必须大于后端等 Apify 的上限**——否则会重演 * cn-prod 2026-07-28 那 4 次:服务端正常跑完、结果都进了缓存,纯粹是客户端等不及 * 先撤了(30s 默认值 vs Temu actor 实测 10–42s,耗时分布在 30s 处有一条干净的裂缝)。 * * 后端上限由 #167 的 `callActorAndWait()` 决定:`waitSecs = actor timeout + 30s`, * temu 的 actor timeout 是 120 ⇒ 后端最长等 150s。再留出缓存查询、listItems、 * 数据转换的开销,这里取 180s。 * * ⚠️ #167 若改 `WAIT_GRACE_SECS` 或 temu 的 actor timeout,这个值要跟着往上抬。 */ export declare const TEMU_SEARCH_TIMEOUT_MS = 180000; /** * TikTok 视频搜索超时(#202)。后端走 ScrapeCreators,axios 30s 就放弃 * (`backend/src/services/scrapecreators.ts` 的 `commonConfig.timeout`),再加上 * 自己的 `has_product_link` 过滤与序列化——最长会跑到「30s + 开销」,**必然 > 30s**。 * 而 CLI 原来没传超时、落到 30s 默认值 ⇒ 慢请求恒定是客户端先撤:cn-prod 2026-08-22 * 实测后端 30.219s 成功返回、CLI 在 30.000s 撤,credits 已扣、结果整份丢弃。 * * 取 60s:与 SUPPLIER_DETAIL_TIMEOUT_MS 同一形状(后端 30s 同步上游),留一倍余量。 * ⚠️ 后端那个 30s 若上调,这个值要跟着往上抬(api.test.ts 钉了余量约束)。 */ export declare const TIKTOK_VIDEO_SEARCH_TIMEOUT_MS = 60000; /** * scout HTTP 请求超时(毫秒)。原来所有 fetch 都无超时,后端/上游(跨境 tikhub 等)一慢就 * 永久挂死 → agent turn 卡住(线上事故)。统一加超时让慢请求**报错而非无限挂**。 * 优先级:`SCOUT_HTTP_TIMEOUT_MS` env(CLI 全局 `--timeout` 会设它,显式覆盖)> 调用方 fallback > 30s。 */ export declare function getHttpTimeoutMs(fallbackMs?: number): number; /** * API Client for Optima Scout Backend */ export declare class ApiClient { private baseUrl; constructor(baseUrl?: string); /** * 获取认证 headers */ private getAuthHeaders; /** * fetch 包装:统一加请求超时(AbortSignal.timeout)。原来所有 fetch 无超时,跨境/上游 * 慢就无限挂 → agent 卡死。超时由 getHttpTimeoutMs() 控制(默认 30s,--timeout 可调)。 * 若调用方已带 signal,则与超时信号合并(任一触发即中止)。超时抛明确错误供 LLM 感知。 */ private doFetch; /** * 处理 API 错误响应,提取有意义的错误信息。 * * 状态码必须留在 message 里(optima-scout#205):agent 看到的只有 `error.message` * —— 各命令文件的 `outputError()` 只打这一条。状态码丢了,agent 就分不出 * 「402 余额不足 / 403 权益不足」这类重试也没用的终态,和「429 限流 / 5xx」 * 这类退避后可重试的瞬态。cn-prod 实测:一个余额 14、单价 30 的定时任务, * agent 拿到孤零零一句 `insufficient_credits`,在一秒内连打了四次。 */ private handleErrorResponse; /** * Search for products * * `limit` 是**上限**,不传即不限制(optima-scout#164)。上游 Rainforest 没有条数 * 参数,截断由后端在出口做;不足时不会补足到 limit。 */ search(query: string, domain?: string, limit?: number): Promise>; /** * Get product details by ASIN */ getProduct(asin: string, domain?: string): Promise>; /** * Get product reviews with pagination */ getReviews(asin: string, params?: { domain?: string; pages?: number; sort?: string; star?: string; allStars?: boolean; verified?: boolean; }): Promise>; /** * Search for 1688 suppliers by keyword */ searchSuppliers(keyword: string, limit?: number): Promise; /** * Get a single 1688 offer detail by offer ID */ getSupplierOffer(offerId: string): Promise; /** * Search for 1688 suppliers by image */ searchSuppliersByImage(params: { imageUrl?: string; imageData?: string; contentType?: string; limit?: number; }): Promise; /** * Search TikTok Shop products */ searchTikTokShop(keyword: string, limit?: string | number, region?: string): Promise; /** * Get one page of a TikTok Shop's products (by numeric seller ID). * Route is single-page (billing 1:1); the CLI aggregates pages via `cursor`. */ getTikTokShopProducts(sellerId: string, options?: { cursor?: string; region?: string; }): Promise; /** * Get TikTok Shop product details */ getTikTokProduct(productId: string, region?: string): Promise; /** * Get TikTok Shop product reviews (review text, rating, buyer photos) */ getTikTokShopReviews(productId: string, params?: { region?: string; }): Promise; /** * Get popular TikTok creators * * 以下 6 个 tiktok 方法的 `limit` 收 string = 用户在 `-l` 里敲的原话,原样透传。 * 合法上限按端点各不相同(100 / 200 / 50),CLI 不复制这份知识、也不自己解析: * 命令层曾先 `parseInt`,`-l abc` 变成 NaN、`-l 0` 本就是 falsy,随后被 * `if (params.limit)` 一并丢掉——请求里没有 limit,后端回落默认值,非法值就这么 * 悄悄按 20 跑完并退出码 0,后端 parseLimit 的 400 永远够不着(#192 review)。 */ getTikTokCreators(params: { country?: string; followerCount?: string; audienceCountry?: string; sort?: string; limit?: string | number; }): Promise; /** * Get products from creator videos */ getTikTokCreatorProducts(username: string, params: { sort?: string; limit?: string | number; region?: string; }): Promise; /** * List a creator's videos by handle (#230) */ getTikTokCreatorVideos(username: string, params: { sort?: string; limit?: string | number; region?: string; /** 上一页回报的续拉游标(#234) */ cursor?: string; /** 额外查一次主页拿账号总视频数(多一次上游调用,默认关,#234) */ includeTotal?: boolean; }): Promise; /** * Get a creator's profile by handle (#230) */ getTikTokCreatorProfile(username: string): Promise; /** * Search TikTok videos by keyword */ searchTikTokVideos(keyword: string, params: { limit?: string | number; region?: string; hasProductLink?: boolean; }): Promise; /** * Get trending TikTok videos */ getTikTokTrending(params: { region?: string; timePeriod?: string; hasProductLink?: boolean; limit?: string | number; }): Promise; /** * Get video insights (details + transcript + comments) */ getTikTokVideoInsights(url: string, params: { includeTranscript?: boolean; includeComments?: boolean; }): Promise; /** * Get popular hashtags */ getTikTokHashtags(params: { region?: string; limit?: string | number; }): Promise; /** * Search Instagram Reels */ searchInstagramReels(keyword: string, params: { limit?: number; minPlays?: number; }): Promise; /** * Get creator Reels */ getInstagramCreatorReels(username: string, params: { limit?: number; }): Promise; /** * Get Reel insights (details + transcript + comments) */ getInstagramReelInsights(url: string, params: { includeTranscript?: boolean; includeComments?: boolean; }): Promise; /** * Get Instagram profile */ getInstagramProfile(username: string): Promise; /** * Search Shein products */ searchShein(keyword: string, params: { limit?: number; country?: string; currency?: string; language?: string; }): Promise; /** * Search Temu products */ searchTemu(keyword: string, params: { limit?: number; currency?: string; }): Promise; searchDouyinVideos(keyword: string, params?: { cursor?: number; sortType?: string; publishTime?: string; filterDuration?: string; }): Promise; getDouyinVideoDetail(awemeId: string): Promise; getDouyinVideoByUrl(shareUrl: string): Promise; getDouyinVideoDownloadUrl(awemeId: string): Promise; getDouyinVideoComments(awemeId: string, count?: number, cursor?: string): Promise; getDouyinVideoCommentReplies(awemeId: string, commentId: string, count?: number, cursor?: string): Promise; getDouyinVideoInsights(awemeId: string, params?: { includeComments?: boolean; }): Promise; searchDouyinUsers(keyword: string, cursor?: number): Promise; getDouyinUserProfile(secUserId: string): Promise; getDouyinUserVideos(secUserId: string, params?: { count?: number; maxCursor?: number; }): Promise; getDouyinHotSearch(): Promise; getDouyinChallenges(params?: { page?: number; pageSize?: number; }): Promise; getDouyinBillboard(params?: { page?: number; pageSize?: number; type?: string; snapshotTime?: string; startDate?: string; endDate?: string; }): Promise; searchDouyinChallenges(keyword: string, cursor?: number): Promise; getDouyinProductReviews(productId: string, shopId: string, count?: number, sortType?: number): Promise; getDouyinProduct(productId: string, shopId: string, authorId?: string): Promise; searchXhsNotes(keyword: string, params?: { page?: number; sortType?: string; noteType?: string; }): Promise; getXhsNoteDetail(noteId: string): Promise; getXhsUserProfile(userId: string): Promise; getXhsUserNotes(userId: string, params?: { cursor?: string; }): Promise; getXhsNoteComments(noteId: string, cursor?: string): Promise; getXhsNoteSubComments(noteId: string, commentId: string, cursor?: string): Promise; searchRedditPosts(query: string, params?: { sort?: string; timeframe?: string; }): Promise; getRedditSubreddit(subreddit: string, params?: { sort?: string; timeframe?: string; }): Promise; getRedditSubredditInfo(subreddit: string): Promise; getRedditPostComments(postUrl: string): Promise; searchRedditAds(query: string, params?: { industries?: string; }): Promise; discoverRedditTrends(query: string, params?: { subreddits?: string; timeframe?: string; }): Promise; searchTwitter(keyword: string, params?: { searchType?: string; }): Promise; getTwitterTrending(): Promise; getTwitterProfile(screenName: string): Promise; getTwitterTweet(tweetId: string): Promise; getTwitterComments(tweetId: string): Promise; getTwitterFollowers(screenName: string): Promise; getTwitterTopTweets(screenName: string): Promise; getTwitterTranscript(tweetUrl: string): Promise; getTwitterCommunity(communityUrl: string): Promise; getTwitterCommunityTweets(communityUrl: string): Promise; discoverTwitterTrends(keyword?: string): Promise; searchTaobao(keyword: string, params?: { page?: number; sort?: string; tmall?: boolean; startPrice?: number; endPrice?: number; }): Promise; getTaobaoDetail(itemId: string): Promise; getTaobaoComments(itemId: string, params?: { page?: number; }): Promise; getTaobaoInsights(itemId: string): Promise; getKeepaProduct(asin: string, params?: { domain?: string; stats?: number; days?: number; buybox?: boolean; rating?: boolean; offers?: number; }): Promise; keepaSearch(selection: Record, params?: { domain?: string; page?: number; perPage?: number; }): Promise; getKeepaBestsellers(category: number, params?: { domain?: string; range?: number; }): Promise; getKeepaSeller(sellerId: string, params?: { domain?: string; storefront?: boolean; }): Promise; getKeepaDeals(selection: Record, params?: { domain?: string; }): Promise; getKeepaCategory(category: number, params?: { domain?: string; parents?: boolean; }): Promise; searchKeepaCategories(term: string, params?: { domain?: string; }): Promise; getKeepaGraph(asin: string, params?: { domain?: string; width?: number; height?: number; }): Promise; getKeepaTokens(): Promise; getKeepaHistory(asin: string, type: string, params?: { domain?: string; days?: number; }): Promise; /** Generic GET helper for simple /api/* routes */ private get; getSpKeywords(asin: string, marketplace?: string): Promise; getSpKeywordsMine(keyword: string, marketplace?: string): Promise; getSpKeywordsOrder(asin: string, marketplace?: string, reverseType?: string, date?: string): Promise; getSpKeywordsResearch(keyword: string, marketplace?: string): Promise; getSpKeywordsTrends(keyword: string, marketplace?: string): Promise; getSpTrafficStats(asin: string, marketplace?: string): Promise; getSpTrafficSource(asin: string, marketplace?: string): Promise; getSpTrafficListing(asin: string, marketplace?: string): Promise; getSpTrafficRelated(asin: string, marketplace?: string): Promise; getSpTrafficExtend(asin: string, marketplace?: string): Promise; getSpPredict(asin: string, marketplace?: string): Promise; getSpCompetitors(asin: string, marketplace?: string): Promise; getSpCoupon(asin: string, marketplace?: string): Promise; getSpCategory(keyword: string, marketplace?: string): Promise; getSpMarket(nodeIdPath: string, marketplace?: string, month?: string, topN?: number): Promise; private getSpMarketRoute; getSpMarketStats(nodeIdPath: string, marketplace?: string, month?: string, topN?: number): Promise; getSpBrands(nodeIdPath: string, marketplace?: string, month?: string, topN?: number): Promise; getSpSellers(nodeIdPath: string, marketplace?: string, month?: string, topN?: number): Promise; getSpDemand(nodeIdPath: string, marketplace?: string, month?: string, topN?: number): Promise; getSpPrices(nodeIdPath: string, marketplace?: string, month?: string, topN?: number): Promise; getSpRatings(nodeIdPath: string, marketplace?: string, month?: string, topN?: number): Promise; getSpRatingValues(nodeIdPath: string, marketplace?: string, month?: string, topN?: number): Promise; getSpListingDates(nodeIdPath: string, marketplace?: string, month?: string, topN?: number): Promise; getSpListingTrends(nodeIdPath: string, marketplace?: string, month?: string, topN?: number): Promise; getSpSellerCountries(nodeIdPath: string, marketplace?: string, month?: string, topN?: number): Promise; getSpSellerTypes(nodeIdPath: string, marketplace?: string, month?: string, topN?: number): Promise; getSpEbc(nodeIdPath: string, marketplace?: string, month?: string, topN?: number): Promise; getSpProductConcentration(nodeIdPath: string, marketplace?: string, month?: string, topN?: number): Promise; getSpGoogleTrend(keyword: string, marketplace?: string): Promise; getSpAbaWeekly(keyword: string, marketplace?: string): Promise; getSpAbaMonthly(keyword: string, marketplace?: string): Promise; getSpAbaTrend(keyword: string, marketplace?: string): Promise; /** * IP infringement pre-screen via Zhihuiya patent image search */ ipCheck(params: { imageUrl?: string; imageData?: string; contentType?: string; productUrl?: string; country: string; platform: string; limit?: number; }): Promise; /** * Semantic invention-patent search via Zhihuiya (natural-language technical description) */ patentSearch(params: { text: string; market: string; limit?: number; offset?: number; }): Promise; /** * Keyword expansion (synonym/related) via Zhihuiya */ patentSuggest(params: { keywords: string[]; langs: string[]; types: string[]; }): Promise; /** * Single patent content (for plain-language translation) */ patentDetail(params: { patentId: string; patentNumber?: string; }): Promise; /** * Health check */ health(): Promise<{ status: string; timestamp: string; environment: string; }>; /** * 拿可播放视频 + metadata (完整 pipeline) */ fetchWechatChannelsVideo(opts: { url?: string; exportId?: string; }): Promise; /** * 只拿 metadata, 不下载/解密视频 (省 S3 流量) */ fetchWechatChannelsMetadata(opts: { url?: string; exportId?: string; }): Promise; /** 微信搜一搜: 视频号视频/公众号文章/账号 */ searchWechat(keyword: string, opts?: { type?: string; sort?: string; time?: string; cursor?: string; }): Promise; /** 视频号达人主页信息 */ getWechatCreatorProfile(username: string): Promise; /** 视频号达人作品列表 */ getWechatCreatorVideos(username: string, cursor?: string): Promise; /** 视频号达人直播历史 */ getWechatCreatorLives(username: string): Promise; /** 视频号内搜索达人作品 */ searchWechatChannel(username: string, keyword: string): Promise; /** 视频号作品评论 */ getWechatVideoComments(objectId: string, cursor?: string): Promise; /** 视频号作品分享链接 */ getWechatVideoShareUrl(objectId: string): Promise; /** 公众号文章详情 */ getWechatMpArticle(articleUrl: string): Promise; /** 公众号文章数据 (阅读/点赞等统计) */ getWechatMpArticleStats(articleUrl: string): Promise; /** 公众号文章评论 */ getWechatMpArticleComments(articleUrl: string, buffer?: string): Promise; /** 公众号评论的回复 (楼中楼) */ getWechatMpCommentReplies(articleUrl: string, contentId: string): Promise; /** 公众号主页信息 */ getWechatMpAccountProfile(username: string): Promise; /** 公众号历史文章列表 */ getWechatMpAccountArticles(username: string, offset?: number): Promise; } export declare const api: ApiClient; //# sourceMappingURL=api.d.ts.map