
Linkfox Temu Compliance Global
- 169 installs
- 64 repo stars
- Updated August 3, 2026
- linkfox-ai/linkfox-skills
Helps with ai & agent building tasks.
About
linkfox-temu-compliance-global is a Claude Code skill in the AI & Agent Building category.
- linkfox-temu-compliance-global
- AI & Agent Building
- AI-coding skill
Linkfox Temu Compliance Global by the numbers
- 169 all-time installs (skills.sh)
- +35 installs in the week ending Aug 2, 2026 (Skillselion tracking)
- Ranked #3,135 of 16,546 AI & Agent Building skills by installs in the Skillselion catalog
- Data as of Aug 4, 2026 (Skillselion catalog sync)
npx skills add https://github.com/linkfox-ai/linkfox-skills --skill linkfox-temu-compliance-globalAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 169 |
|---|---|
| repo stars | ★ 64 |
| Last updated | August 3, 2026 |
| Repository | linkfox-ai/linkfox-skills ↗ |
What it does
Helps with ai & agent building tasks.
Files
Temu 全球站 — 电商合规(Compliance)
本 skill(linkfox-temu-compliance-global)覆盖 Partner Platform for Global 的 电商合规 / Product Compliance 相关 OpenAPI。
已接入 9 个接口,清单见 partner-global-catalog.md。
网关(本 skill 内置):
| 能力 | 方法 | 路径 |
|---|---|---|
合规 OpenAPI(global_compliance_*、temu_global_proxy) | POST | https://tool-gateway.linkfox.com/temu/proxy |
| 加签文件下载 | POST | https://tool-gateway.linkfox.com/temu/fileDownload |
相关 skill
| 场景 | skill |
|---|---|
| 电商合规(本 skill) | `linkfox-temu-compliance-global` |
| 商品列表/详情/编辑/库存 | linkfox-temu-manage-product-global |
| 价格 | linkfox-temu-price-global |
| 促销 | linkfox-temu-promotion-global |
| 网关与 Temu token | 本 skill scripts/ |
API Usage
| 文档 | 内容 |
|---|---|
| api.md | 网关、鉴权、错误码 |
| partner-global-catalog.md | 接口目录 |
| apis/README.md | 分接口文档 |
默认参数
| 字段 | 默认 | 说明 |
|---|---|---|
| site | global | Partner Global |
| managementType | semi-managed | 半托管 |
| tokenPurpose | product-inventory | 商品/合规场景 |
Scripts(按 type)
| 脚本 | type | 状态 |
|---|---|---|
global_compliance_edit.py | bg.compliance.edit | 已接入 |
global_compliance_metadata_get.py | bg.compliance.metadata.get | 已接入 |
global_compliance_goods_compliancelabel_get.py | bg.goods.compliancelabel.get | 已接入 |
global_compliance_arbok_cert_query_need_upload_items.py | bg.arbok.open.cert.queryNeedUploadItems | 已接入 |
global_compliance_arbok_cert_upload_product_cert.py | bg.arbok.open.cert.uploadProductCert | 已接入 |
global_compliance_flash_upload_recognize.py | bg.flash.open.upload.recognize | 已接入 |
global_compliance_flash_upload_real_image.py | bg.flash.open.upload.real.image | 已接入 |
global_compliance_arbok_upload_upload_file.py | bg.arbok.open.upload.uploadFile | 已接入 |
global_compliance_arbok_product_cert_query.py | bg.arbok.open.product.cert.query | 已接入 |
temu_global_proxy.py | 任意 type | 通用 |
temu_global_file_download.py | 加签文件下载 | 通用 |
示例
python scripts/global_compliance_edit.py '<JSON>' # bg.compliance.editpython scripts/global_compliance_metadata_get.py '<JSON>' # bg.compliance.metadata.getFeedback: skillName:linkfox-temu-compliance-global
网关与授权脚本
授权说明:references/access-token.md
<!-- LF_LARGE_RESPONSE_BLOCK -->
Handling Large Responses
To avoid overflowing the agent context, persist the response to disk and extract only the fields you need:
python scripts/response_io.py run --script scripts/check_linkfox_token.py --out-dir <DIR> '<params>'
python scripts/response_io.py read <file> --fields "<paths>" # or --path "<JMESPath>"Pick--out-diroutside any git working tree (e.g./tmp/...on Unix,%TEMP%/...on Windows). Persisted responses may contain PII, pricing, or auth-sensitive data — do not commit them. Files are not auto-deleted; clean up when the task is done.
This skill exposes multiple entry scripts:check_linkfox_token.py,get_temu_access_token.py,global_compliance_arbok_cert_query_need_upload_items.py,global_compliance_arbok_cert_upload_product_cert.py,global_compliance_arbok_product_cert_query.py,global_compliance_arbok_upload_upload_file.py,global_compliance_edit.py,global_compliance_flash_upload_real_image.py,global_compliance_flash_upload_recognize.py,global_compliance_goods_compliancelabel_get.py,global_compliance_metadata_get.py,list_temu_access_tokens.py,save_temu_access_token.py,temu_file_download.py,temu_global_file_download.py,temu_global_proxy.py,temu_proxy.py,temu_token_guide.py. Pass--script scripts/<name>.pyto choose the one you need.
run writes the full response to a file and emits only a schema preview + file path. read projects specific fields, with --limit/--offset for slicing and --format json|jsonl|csv|table for output.
When to prefer this pattern — apply your judgment based on the response characteristics, e.g.:
- High field count per record, or fields you don't need
- Batch/paginated results (multiple items per call)
- Long-text fields (descriptions, reviews, HTML, time series)
- Output reused across later steps rather than consumed immediately
For small, single-use responses, calling the main script directly is fine.
⚠️ The preview is a truncated schema + sample, not the full data. Any field-level decision must read from the persisted file via read. <!-- /LF_LARGE_RESPONSE_BLOCK -->
Temu accessToken 授权与获取
Temu 没有 LinkFox 侧自动 OAuth;accessToken 须在 Temu 卖家后台「服务市场 → 授权管理」手动复制。与 LINKFOXAGENT_API_KEY(LinkFox 用户鉴权)是两套令牌。
两种鉴权(勿混淆)
| 令牌 | 用途 | 获取方式 |
|---|---|---|
| LinkFox 用户 Token | 调用 /temu/proxy、/temu/fileDownload 必填 | LINKFOXAGENT_API_KEY 或请求 JSON 的 token;Header Authorization + Token(同 amazon-store-auth) |
| Temu accessToken | Temu 业务 API(经紫鸟转发) | Temu 卖家后台授权后复制 |
export LINKFOXAGENT_API_KEY="<your-key>"
python scripts/check_linkfox_token.pytokenPurpose 与场景
| tokenPurpose | 店铺类型 | 推荐 site | 授权应用 |
|---|---|---|---|
product-inventory | 半托管 | cn / partner | 酷鸟卖家助手 |
order-shipping | 半托管 | us / global / eu | Cyber-ERP酷鸟助手 |
full-managed | 全托管 | cn / partner | 酷鸟卖家助手 |
local-native | 本土(美/欧主体) | us 等 | Cyber-ERP |
1. 半托管 — 商品/库存 Token
1. 登录 seller.kuajingmaihuo.com 或 agentseller.temu.com 2. 系统管理 → 服务市场 → 授权管理 3. 获取授权 → 选择 「酷鸟卖家助手」 4. 全选常规和特殊授权 → 确认 → 复制 access_token 5. 调用 API:site=cn 或 partner,managementType=semi-managed
2. 半托管 — 订单/发货 Token
1. 登录 Temu 卖家后台 2. 右上角 Seller Central → 切换到目标区域(美区 / 欧区 / 全球) 3. 服务市场 → 授权管理 4. 获取授权 → 选择 「Cyber-ERP酷鸟助手」 5. 全选授权 → 确认 → 复制 token 6. 调用 API:site=global / global / eu,managementType=semi-managed
3. 全托管店铺
1. 登录 Temu 平台 2. 系统管理 → 服务市场 → 授权管理 3. 获取授权 → 「酷鸟卖家助手」 → 全选 → 复制 token 4. 调用 API:managementType=full-managed,site 通常 cn 或 partner
4. 本土店铺(美区、欧区主体)
1. 登录 Temu 2. Apps And Services → Manage Your Apps 3. Authorize a new app → 搜索 「Cyber-ERP」 4. 一般权限与敏感权限全选 → 确认 → 复制 token
站点与 OpenAPI 网关
| site | 说明 | Temu 网关 |
|---|---|---|
| cn | 中国站 | openapi.kuajingmaihuo.com |
| partner | Partner 网关 | openapi-b-partner.temu.com |
| us | 全球站 | openapi-b-global.temu.com |
| global | 全球区 | openapi-b-global.temu.com |
| eu | 全球站 | openapi-b-global.temu.com |
本地保存 Token(推荐)
默认存储:~/.linkfox/temu-access-tokens.json(可用 TEMU_TOKEN_STORE_PATH 覆盖)。
# 查看授权步骤
python scripts/temu_token_guide.py '{"shopType":"semi-managed","tokenPurpose":"product-inventory","site":"cn"}'
# 保存 token
python scripts/save_temu_access_token.py '{
"storeKey": "my-shop",
"label": "中国半托管",
"site": "cn",
"managementType": "semi-managed",
"tokenPurpose": "product-inventory",
"accessToken": "PASTE_TOKEN"
}'
# 列出已保存(脱敏)
python scripts/list_temu_access_tokens.py
# 用 storeKey 调 API(无需每次粘贴 token)
python scripts/temu_proxy.py '{
"storeKey": "my-shop",
"site": "cn",
"managementType": "semi-managed",
"tokenPurpose": "product-inventory",
"type": "bg.goods.category.mapping",
"params": {"goodsName": "测试", "goodsNameEn": "Test"}
}'注意事项
1. 子账号:可能无法进入服务市场,需主账号授权。 2. 多站点:全球站、英国站等需分别登录对应后台获取 Token。 3. 有效期:Token 会过期,过期后重新在后台复制并 save_temu_access_token.py。 4. IP 白名单:需联系紫鸟开放平台配置调用 IP。 5. 参数小写:site、managementType 必须小写。
脚本一览
| 脚本 | 说明 |
|---|---|
temu_token_guide.py | 按店铺类型/用途输出授权步骤 |
save_temu_access_token.py | 保存 accessToken 到本地 |
list_temu_access_tokens.py | 列出已保存 token(默认脱敏) |
get_temu_access_token.py | 读取指定 store 的 token |
linkfox-temu-compliance-global — API 参考
Temu 全球站电商合规(Partner Global Product Compliance / 商品合规),经本 skill temu_global_proxy(POST /temu/proxy)转发。Temu 的 type 写在 Body,不是 URL 路径。
网关与鉴权:本 skillscripts/(LINKFOXAGENT_API_KEY、accessToken/storeKey)。授权见references/access-token.md。
---
调用规范
| 项 | 说明 |
|---|---|
| 网关根地址 | https://tool-gateway.linkfox.com(可用 TEMU_API_BASE_URL / STORE_API_BASE_URL 覆盖) |
| 合规 OpenAPI | POST /temu/proxy |
| 加签文件下载 | POST /temu/fileDownload(temu_global_file_download.py) |
| LinkFox 鉴权 | Header `Authorization` 与 `Token`(同值);或 LINKFOXAGENT_API_KEY;或 JSON token |
| Temu 鉴权 | Body accessToken,或 storeKey + site + managementType + tokenPurpose |
| 默认 | site=global,managementType=semi-managed,tokenPurpose=product-inventory |
| 上游 OpenAPI(Global) | https://openapi-b-global.temu.com/openapi/router(网关按 site 解析) |
网关请求 Body(/temu/proxy)
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| site | string | 是 | global(本 skill 默认) |
| managementType | string | 是 | semi-managed |
| accessToken | string | 与 storeKey 二选一 | Temu 店铺令牌 |
| storeKey | string | 与 accessToken 二选一 | ~/.linkfox/temu-access-tokens.json 中的键 |
| tokenPurpose | string | 否 | 建议 product-inventory |
| type | string | 是 | Temu 接口名(以 Partner Compliance 文档为准) |
| params | object | 否 | 业务参数,见各接口文档 |
网关响应
| 字段 | 类型 | 说明 |
|---|---|---|
| body | string | Temu 原始 JSON 字符串;脚本解析为 temuBody |
| code | integer | 网关错误码:1002 参数/Token,1003 转发失败 |
解析顺序:网关 `code` → JSON.parse(body) → success / errorCode / errorMsg / result。
脚本调用
export LINKFOXAGENT_API_KEY="<key>"
python scripts/temu_global_proxy.py '{"accessToken":"TOKEN","site":"global","type":"<TEMU_API_TYPE>","params":{}}'业务字段放在 params(或各 global_compliance_*.py 脚本约定的 request 结构,接入后以接口文档为准)。
---
接口一览
完整 sub_menu_code 与 Partner 文档 URL 见 partner-global-catalog.md。
每个接口单独一份文档:apis/README.md。
---
典型合规流程
(待按 Partner Global Compliance 菜单接入后补充。)
商品详情、类目属性等辅助查询可用 `linkfox-temu-manage-product-global` 对应接口。
---
网关错误码
| code | 说明 | 处理 |
|---|---|---|
| 1002 | 参数或 LinkFox Token 无效 | 修正参数与 LINKFOXAGENT_API_KEY |
| 1003 | 转发失败 | 检查 Temu token、白名单、网络 |
---
Feedback API
- POST
https://skill-api.linkfox.com/api/v1/public/feedback skillName: `linkfox-temu-compliance-global`
待上传资质项查询 — bg.arbok.open.cert.queryNeedUploadItems
| 项 | 值 |
|---|---|
| 脚本 | scripts/global_compliance_arbok_cert_query_need_upload_items.py |
| Partner 文档 | https://partner-global.temu.com/documentation?menu_code=fb16b05f7a904765aac4af3a24b87d4a&sub_menu_code=84d3118d3a604947abf35144606cdea2 |
| 网关 | POST /temu/proxy,type=bg.arbok.open.cert.queryNeedUploadItems,业务载荷放在 Body 的 params |
Description: Query certification items that need to be uploaded for a product.
默认 `site=global`、`tokenPurpose=product-inventory`。入参来自 Partner Request 表;出参来自 Response 表 + Response Example 展开。
---
Request 结构(Partner Request 表)
params
└── request (OBJECT)
└── certType (INTEGER **必填**)
└── productId (LONG **必填**)request 内字段(Partner Request 表 + 未展开子级自 Request Example 补充)
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| certType | INTEGER | 是 | certType |
| productId | LONG | 是 | productId |
网关 params 写法
{
"accessToken": "TOKEN",
"site": "global",
"tokenPurpose": "product-inventory",
"request": {
"certType": 1,
"productId": 1
}
}Partner Request Example 为 CURL,业务字段可能在顶层;经 LinkFox 网关请放在 `params.request`(或脚本接受的顶层 request)。---
Response(Partner Response 表 + 全量展开)
response
├── success (BOOLEAN)
├── errorCode (INTEGER)
├── errorMsg (STRING)
└── result (OBJECT)
├── certNeedUploadItems[] (OBJECT[])
├── certNeedUploadItems[].aliasName (STRING)
├── certNeedUploadItems[].contentType (INTEGER)
├── certNeedUploadItems[].expireDays (INTEGER)
├── certNeedUploadItems[].expireNoticeDays (INTEGER)
├── certNeedUploadItems[].expireTime (INTEGER)
├── certNeedUploadItems[].expireType (INTEGER)
├── certNeedUploadItems[].hasExpireTime (BOOLEAN)
├── certNeedUploadItems[].needShowCustomer (BOOLEAN)Partner Response 表仅含顶层字段;result 下全部子字段按 Response 表层级 与 Response Example 合并展开。Partner Response 表(HTML 导出可见行)
| 参数 | 类型 | 说明 |
|---|---|---|
| success | BOOLEAN | success |
| errorCode | INTEGER | error code |
| errorMsg | STRING | error message |
| result | OBJECT | result |
result 及下级(全部展开)
| 参数 | 类型 | 说明 |
|---|---|---|
| certNeedUploadItems[] | OBJECT[] | Partner Response Example |
| certNeedUploadItems[].aliasName | STRING | Partner Response Example |
| certNeedUploadItems[].contentType | INTEGER | Partner Response Example |
| certNeedUploadItems[].expireDays | INTEGER | Partner Response Example |
| certNeedUploadItems[].expireNoticeDays | INTEGER | Partner Response Example |
| certNeedUploadItems[].expireTime | INTEGER | Partner Response Example |
| certNeedUploadItems[].expireType | INTEGER | Partner Response Example |
| certNeedUploadItems[].hasExpireTime | BOOLEAN | Partner Response Example |
| certNeedUploadItems[].needShowCustomer | BOOLEAN | Partner Response Example |
| errorCode | INTEGER | error code |
| errorMsg | STRING | error message |
| result | OBJECT | result |
| success | BOOLEAN | success |
---
脚本
export LINKFOXAGENT_API_KEY="<key>"
python scripts/global_compliance_arbok_cert_query_need_upload_items.py '{ "accessToken": "TOKEN", "site": "global", "tokenPurpose": "product-inventory", "request": { "certType": 1, "productId": 1 } }'上传商品资质 — bg.arbok.open.cert.uploadProductCert
| 项 | 值 |
|---|---|
| 脚本 | scripts/global_compliance_arbok_cert_upload_product_cert.py |
| Partner 文档 | https://partner-global.temu.com/documentation?menu_code=fb16b05f7a904765aac4af3a24b87d4a&sub_menu_code=56de04bcafae45509b21edeab57c9fdb |
| 网关 | POST /temu/proxy,type=bg.arbok.open.cert.uploadProductCert,业务载荷放在 Body 的 params |
Description: Upload product certification / qualification documents.
默认 `site=global`、`tokenPurpose=product-inventory`。入参来自 Partner Request 表;出参来自 Response 表 + Response Example 展开。
---
Request 结构(Partner Request 表)
params
└── request (OBJECT)
└── productCatCertReqList (OBJECT[])
└── productCatCertReqList[].authCode (STRING)
└── productCatCertReqList[].authCodes (OBJECT[])
└── productCatCertReqList[].authCodes[].authCode (STRING)
└── productCatCertReqList[].authCodes[].effectiveTime (INTEGER)
└── productCatCertReqList[].authCodes[].expireTime (INTEGER)
└── productCatCertReqList[].authCodes[].fileName (STRING)
└── productCatCertReqList[].authCodes[].fileUrl (STRING)
└── productCatCertReqList[].authCodes[].showCustomer (BOOLEAN)
└── productCatCertReqList[].certType (INTEGER)
└── productCatCertReqList[].inspectReportFiles (OBJECT[])
└── productCatCertReqList[].inspectReportFiles[].authCode (STRING)
└── productCatCertReqList[].inspectReportFiles[].effectiveTime (INTEGER)
└── productCatCertReqList[].inspectReportFiles[].expireTime (INTEGER)
└── productCatCertReqList[].inspectReportFiles[].fileName (STRING)
└── productCatCertReqList[].inspectReportFiles[].fileUrl (STRING)
└── productCatCertReqList[].inspectReportFiles[].showCustomer (BOOLEAN)
└── productCatCertReqList[].productCertFiles (OBJECT[])
└── productCatCertReqList[].productCertFiles[].authCode (STRING)
└── productCatCertReqList[].productCertFiles[].effectiveTime (INTEGER)
└── productCatCertReqList[].productCertFiles[].expireTime (INTEGER)
└── productCatCertReqList[].productCertFiles[].fileName (STRING)
└── productCatCertReqList[].productCertFiles[].fileUrl (STRING)
└── productCatCertReqList[].productCertFiles[].showCustomer (BOOLEAN)
└── productCatCertReqList[].supplementaryMaterials (OBJECT[])
└── productCatCertReqList[].supplementaryMaterials[].authCode (STRING)
└── productCatCertReqList[].supplementaryMaterials[].effectiveTime (INTEGER)
└── productCatCertReqList[].supplementaryMaterials[].expireTime (INTEGER)
└── productCatCertReqList[].supplementaryMaterials[].fileName (STRING)
└── productCatCertReqList[].supplementaryMaterials[].fileUrl (STRING)
└── productCatCertReqList[].supplementaryMaterials[].showCustomer (BOOLEAN)
└── productId (LONG **必填**)
└── realPictures (OBJECT[])
└── realPictures[].imageUrl (STRING)request 内字段(Partner Request 表 + 未展开子级自 Request Example 补充)
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| productCatCertReqList | OBJECT[] | 否 | productCatCertReqList |
| productCatCertReqList[].authCode | STRING | — | Partner Request Example(CURL -d 展开;Request 表未列出子级时补充) |
| productCatCertReqList[].authCodes | OBJECT[] | — | Partner Request Example(CURL -d 展开;Request 表未列出子级时补充) |
| productCatCertReqList[].authCodes[].authCode | STRING | — | Partner Request Example(CURL -d 展开;Request 表未列出子级时补充) |
| productCatCertReqList[].authCodes[].effectiveTime | INTEGER | — | Partner Request Example(CURL -d 展开;Request 表未列出子级时补充) |
| productCatCertReqList[].authCodes[].expireTime | INTEGER | — | Partner Request Example(CURL -d 展开;Request 表未列出子级时补充) |
| productCatCertReqList[].authCodes[].fileName | STRING | — | Partner Request Example(CURL -d 展开;Request 表未列出子级时补充) |
| productCatCertReqList[].authCodes[].fileUrl | STRING | — | Partner Request Example(CURL -d 展开;Request 表未列出子级时补充) |
| productCatCertReqList[].authCodes[].showCustomer | BOOLEAN | — | Partner Request Example(CURL -d 展开;Request 表未列出子级时补充) |
| productCatCertReqList[].certType | INTEGER | — | Partner Request Example(CURL -d 展开;Request 表未列出子级时补充) |
| productCatCertReqList[].inspectReportFiles | OBJECT[] | — | Partner Request Example(CURL -d 展开;Request 表未列出子级时补充) |
| productCatCertReqList[].inspectReportFiles[].authCode | STRING | — | Partner Request Example(CURL -d 展开;Request 表未列出子级时补充) |
| productCatCertReqList[].inspectReportFiles[].effectiveTime | INTEGER | — | Partner Request Example(CURL -d 展开;Request 表未列出子级时补充) |
| productCatCertReqList[].inspectReportFiles[].expireTime | INTEGER | — | Partner Request Example(CURL -d 展开;Request 表未列出子级时补充) |
| productCatCertReqList[].inspectReportFiles[].fileName | STRING | — | Partner Request Example(CURL -d 展开;Request 表未列出子级时补充) |
| productCatCertReqList[].inspectReportFiles[].fileUrl | STRING | — | Partner Request Example(CURL -d 展开;Request 表未列出子级时补充) |
| productCatCertReqList[].inspectReportFiles[].showCustomer | BOOLEAN | — | Partner Request Example(CURL -d 展开;Request 表未列出子级时补充) |
| productCatCertReqList[].productCertFiles | OBJECT[] | — | Partner Request Example(CURL -d 展开;Request 表未列出子级时补充) |
| productCatCertReqList[].productCertFiles[].authCode | STRING | — | Partner Request Example(CURL -d 展开;Request 表未列出子级时补充) |
| productCatCertReqList[].productCertFiles[].effectiveTime | INTEGER | — | Partner Request Example(CURL -d 展开;Request 表未列出子级时补充) |
| productCatCertReqList[].productCertFiles[].expireTime | INTEGER | — | Partner Request Example(CURL -d 展开;Request 表未列出子级时补充) |
| productCatCertReqList[].productCertFiles[].fileName | STRING | — | Partner Request Example(CURL -d 展开;Request 表未列出子级时补充) |
| productCatCertReqList[].productCertFiles[].fileUrl | STRING | — | Partner Request Example(CURL -d 展开;Request 表未列出子级时补充) |
| productCatCertReqList[].productCertFiles[].showCustomer | BOOLEAN | — | Partner Request Example(CURL -d 展开;Request 表未列出子级时补充) |
| productCatCertReqList[].supplementaryMaterials | OBJECT[] | — | Partner Request Example(CURL -d 展开;Request 表未列出子级时补充) |
| productCatCertReqList[].supplementaryMaterials[].authCode | STRING | — | Partner Request Example(CURL -d 展开;Request 表未列出子级时补充) |
| productCatCertReqList[].supplementaryMaterials[].effectiveTime | INTEGER | — | Partner Request Example(CURL -d 展开;Request 表未列出子级时补充) |
| productCatCertReqList[].supplementaryMaterials[].expireTime | INTEGER | — | Partner Request Example(CURL -d 展开;Request 表未列出子级时补充) |
| productCatCertReqList[].supplementaryMaterials[].fileName | STRING | — | Partner Request Example(CURL -d 展开;Request 表未列出子级时补充) |
| productCatCertReqList[].supplementaryMaterials[].fileUrl | STRING | — | Partner Request Example(CURL -d 展开;Request 表未列出子级时补充) |
| productCatCertReqList[].supplementaryMaterials[].showCustomer | BOOLEAN | — | Partner Request Example(CURL -d 展开;Request 表未列出子级时补充) |
| productId | LONG | 是 | productId |
| realPictures | OBJECT[] | 否 | realPictures |
| realPictures[].imageUrl | STRING | — | Partner Request Example(CURL -d 展开;Request 表未列出子级时补充) |
productCatCertReqList[].authCodes[]
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| authCode | STRING | — | Partner Request Example(CURL -d 展开;Request 表未列出子级时补充) |
| effectiveTime | INTEGER | — | Partner Request Example(CURL -d 展开;Request 表未列出子级时补充) |
| expireTime | INTEGER | — | Partner Request Example(CURL -d 展开;Request 表未列出子级时补充) |
| fileName | STRING | — | Partner Request Example(CURL -d 展开;Request 表未列出子级时补充) |
| fileUrl | STRING | — | Partner Request Example(CURL -d 展开;Request 表未列出子级时补充) |
| showCustomer | BOOLEAN | — | Partner Request Example(CURL -d 展开;Request 表未列出子级时补充) |
productCatCertReqList[].inspectReportFiles[]
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| authCode | STRING | — | Partner Request Example(CURL -d 展开;Request 表未列出子级时补充) |
| effectiveTime | INTEGER | — | Partner Request Example(CURL -d 展开;Request 表未列出子级时补充) |
| expireTime | INTEGER | — | Partner Request Example(CURL -d 展开;Request 表未列出子级时补充) |
| fileName | STRING | — | Partner Request Example(CURL -d 展开;Request 表未列出子级时补充) |
| fileUrl | STRING | — | Partner Request Example(CURL -d 展开;Request 表未列出子级时补充) |
| showCustomer | BOOLEAN | — | Partner Request Example(CURL -d 展开;Request 表未列出子级时补充) |
productCatCertReqList[].productCertFiles[]
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| authCode | STRING | — | Partner Request Example(CURL -d 展开;Request 表未列出子级时补充) |
| effectiveTime | INTEGER | — | Partner Request Example(CURL -d 展开;Request 表未列出子级时补充) |
| expireTime | INTEGER | — | Partner Request Example(CURL -d 展开;Request 表未列出子级时补充) |
| fileName | STRING | — | Partner Request Example(CURL -d 展开;Request 表未列出子级时补充) |
| fileUrl | STRING | — | Partner Request Example(CURL -d 展开;Request 表未列出子级时补充) |
| showCustomer | BOOLEAN | — | Partner Request Example(CURL -d 展开;Request 表未列出子级时补充) |
productCatCertReqList[].supplementaryMaterials[]
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| authCode | STRING | — | Partner Request Example(CURL -d 展开;Request 表未列出子级时补充) |
| effectiveTime | INTEGER | — | Partner Request Example(CURL -d 展开;Request 表未列出子级时补充) |
| expireTime | INTEGER | — | Partner Request Example(CURL -d 展开;Request 表未列出子级时补充) |
| fileName | STRING | — | Partner Request Example(CURL -d 展开;Request 表未列出子级时补充) |
| fileUrl | STRING | — | Partner Request Example(CURL -d 展开;Request 表未列出子级时补充) |
| showCustomer | BOOLEAN | — | Partner Request Example(CURL -d 展开;Request 表未列出子级时补充) |
网关 params 写法
{
"accessToken": "TOKEN",
"site": "global",
"tokenPurpose": "product-inventory",
"request": {
"productId": 1,
"productCatCertReqList": [
{
"authCodes": [
{
"fileName": "test",
"fileUrl": "test",
"showCustomer": true,
"expireTime": 1,
"authCode": "test",
"effectiveTime": 1
}
],
"productCertFiles": [
{
"fileName": "test",
"fileUrl": "test",
"showCustomer": true,
"expireTime": 1,
"authCode": "test",
"effectiveTime": 1
}
],
"supplementaryMaterials": [
{
"fileName": "test",
"fileUrl": "test",
"showCustomer": true,
"expireTime": 1,
"authCode": "test",
"effectiveTime": 1
}
],
"certType": 1,
"inspectReportFiles": [
{
"fileName": "test",
"fileUrl": "test",
"showCustomer": true,
"expireTime": 1,
"authCode": "test",
"effectiveTime": 1
}
],
"authCode": "test"
}
],
"realPictures": [
{
"imageUrl": "test"
}
]
}
}Partner Request Example 为 CURL,业务字段可能在顶层;经 LinkFox 网关请放在 `params.request`(或脚本接受的顶层 request)。---
Response(Partner Response 表 + 全量展开)
response
├── success (BOOLEAN)
├── errorCode (INTEGER)
├── errorMsg (STRING)
└── result (OBJECT)Partner Response 表仅含顶层字段;result 下全部子字段按 Response 表层级 与 Response Example 合并展开。Partner Response 表(HTML 导出可见行)
| 参数 | 类型 | 说明 |
|---|---|---|
| success | BOOLEAN | success |
| errorCode | INTEGER | error code |
| errorMsg | STRING | error message |
| result | OBJECT | result |
result 及下级(全部展开)
| 参数 | 类型 | 说明 |
|---|---|---|
| errorCode | INTEGER | error code |
| errorMsg | STRING | error message |
| result | OBJECT | result |
| success | BOOLEAN | success |
---
脚本
export LINKFOXAGENT_API_KEY="<key>"
python scripts/global_compliance_arbok_cert_upload_product_cert.py '{ "accessToken": "TOKEN", "site": "global", "tokenPurpose": "product-inventory", "request": { "productId": 1, "productCatCertReqList": [ { "authCodes": [ { "fileName": "test", "fileUrl": "test", "showCustomer": true, "expireTime": 1, "authCode": "test", "effectiveTime": 1 } ], "productCertFiles": [ { "fileName": "test", "fileUrl": "test", "showCustomer": true, "expireTime": 1, "authCode": "test", "effectiveTime": 1 } ], "supplementaryMaterials": [ { "fileName": "test", "fileUrl": "test", "showCustomer": true, "expireTime": 1, "authCode": "test", "effectiveTime": 1 } ], "certType": 1, "inspectReportFiles": [ { "fileName": "test", "fileUrl": "test", "showCustomer": true, "expireTime": 1, "authCode": "test", "effectiveTime": 1 } ], "authCode": "test" } ], "realPictures": [ { "imageUrl": "test" } ] } }'商品资质查询 — bg.arbok.open.product.cert.query
| 项 | 值 |
|---|---|
| 脚本 | scripts/global_compliance_arbok_product_cert_query.py |
| Partner 文档 | https://partner-global.temu.com/documentation?menu_code=fb16b05f7a904765aac4af3a24b87d4a&sub_menu_code=5ec78e3b36c34fcba743a75523349fb5 |
| 网关 | POST /temu/proxy,type=bg.arbok.open.product.cert.query,业务载荷放在 Body 的 params |
Description: Query product certification / qualification information.
默认 `site=global`、`tokenPurpose=product-inventory`。入参来自 Partner Request 表;出参来自 Response 表 + Response Example 展开。
---
Request 结构(Partner Request 表)
params
└── request (OBJECT)
└── certTypeList (INTEGER[])
└── language (STRING)
└── productId (LONG **必填**)request 内字段(Partner Request 表 + 未展开子级自 Request Example 补充)
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| certTypeList | INTEGER[] | 否 | certTypeList |
| language | STRING | 否 | language |
| productId | LONG | 是 | productId |
网关 params 写法
{
"accessToken": "TOKEN",
"site": "global",
"tokenPurpose": "product-inventory",
"request": {
"productId": 1,
"certTypeList": [
1,
1
],
"language": "test"
}
}Partner Request Example 为 CURL,业务字段可能在顶层;经 LinkFox 网关请放在 `params.request`(或脚本接受的顶层 request)。---
Response(Partner Response 表 + 全量展开)
response
├── success (BOOLEAN)
├── errorCode (INTEGER)
├── errorMsg (STRING)
└── result (OBJECT)
├── productCertList[] (OBJECT[])
├── productCertList[].auditStatus (INTEGER)
├── productCertList[].certName (STRING)
├── productCertList[].certType (INTEGER)
├── productCertList[].rejectReason (STRING)
├── productCertList[].updateReason (STRING)
├── productCertList[].updateStatus (INTEGER)Partner Response 表仅含顶层字段;result 下全部子字段按 Response 表层级 与 Response Example 合并展开。Partner Response 表(HTML 导出可见行)
| 参数 | 类型 | 说明 |
|---|---|---|
| success | BOOLEAN | success |
| errorCode | INTEGER | error code |
| errorMsg | STRING | error message |
| result | OBJECT | result |
result 及下级(全部展开)
| 参数 | 类型 | 说明 |
|---|---|---|
| errorCode | INTEGER | error code |
| errorMsg | STRING | error message |
| productCertList[] | OBJECT[] | Partner Response Example |
| productCertList[].auditStatus | INTEGER | Partner Response Example |
| productCertList[].certName | STRING | Partner Response Example |
| productCertList[].certType | INTEGER | Partner Response Example |
| productCertList[].rejectReason | STRING | Partner Response Example |
| productCertList[].updateReason | STRING | Partner Response Example |
| productCertList[].updateStatus | INTEGER | Partner Response Example |
| result | OBJECT | result |
| success | BOOLEAN | success |
---
脚本
export LINKFOXAGENT_API_KEY="<key>"
python scripts/global_compliance_arbok_product_cert_query.py '{ "accessToken": "TOKEN", "site": "global", "tokenPurpose": "product-inventory", "request": { "productId": 1, "certTypeList": [ 1, 1 ], "language": "test" } }'合规文件上传 — bg.arbok.open.upload.uploadFile
| 项 | 值 |
|---|---|
| 脚本 | scripts/global_compliance_arbok_upload_upload_file.py |
| Partner 文档 | https://partner-global.temu.com/documentation?menu_code=fb16b05f7a904765aac4af3a24b87d4a&sub_menu_code=01b8792d2e4a40f7b3b14f4a1f2711b6 |
| 网关 | POST /temu/proxy,type=bg.arbok.open.upload.uploadFile,业务载荷放在 Body 的 params |
Description: Upload compliance-related files (base64) via Arbok open API.
默认 `site=global`、`tokenPurpose=product-inventory`。入参来自 Partner Request 表;出参来自 Response 表 + Response Example 展开。
---
Request 结构(Partner Request 表)
params
└── request (OBJECT)
└── base64File (STRING **必填**)request 内字段(Partner Request 表 + 未展开子级自 Request Example 补充)
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| base64File | STRING | 是 | base64File |
网关 params 写法
{
"accessToken": "TOKEN",
"site": "global",
"tokenPurpose": "product-inventory",
"request": {
"base64File": "test"
}
}Partner Request Example 为 CURL,业务字段可能在顶层;经 LinkFox 网关请放在 `params.request`(或脚本接受的顶层 request)。---
Response(Partner Response 表 + 全量展开)
response
├── success (BOOLEAN)
├── errorCode (INTEGER)
├── errorMsg (STRING)
└── result (OBJECT)
├── url (STRING)Partner Response 表仅含顶层字段;result 下全部子字段按 Response 表层级 与 Response Example 合并展开。Partner Response 表(HTML 导出可见行)
| 参数 | 类型 | 说明 |
|---|---|---|
| success | BOOLEAN | success |
| errorCode | INTEGER | error code |
| errorMsg | STRING | error message |
| result | OBJECT | result |
result 及下级(全部展开)
| 参数 | 类型 | 说明 |
|---|---|---|
| errorCode | INTEGER | error code |
| errorMsg | STRING | error message |
| result | OBJECT | result |
| success | BOOLEAN | success |
| url | STRING | Partner Response Example |
---
脚本
export LINKFOXAGENT_API_KEY="<key>"
python scripts/global_compliance_arbok_upload_upload_file.py '{ "accessToken": "TOKEN", "site": "global", "tokenPurpose": "product-inventory", "request": { "base64File": "test" } }'合规编辑 — bg.compliance.edit
| 项 | 值 |
|---|---|
| 脚本 | scripts/global_compliance_edit.py |
| Partner 文档 | https://partner-global.temu.com/documentation?menu_code=fb16b05f7a904765aac4af3a24b87d4a&sub_menu_code=a8829c8ede574d9a97cd3cea7c019bc4 |
| 网关 | POST /temu/proxy,type=bg.compliance.edit,业务载荷放在 Body 的 params |
Description: Edit product compliance information (governance attributes, GPSR, certificates, etc.).
默认 `site=global`、`tokenPurpose=product-inventory`。入参来自 Partner Request 表;出参来自 Response 表 + Response Example 展开。
---
Request 结构(Partner Request 表)
params
└── request (OBJECT)
└── editReqList (OBJECT[] **必填**)
└── editReqList[].complianceType (INTEGER)
└── editReqList[].inputText (OBJECT)
└── editReqList[].inputText.$key (STRING)
└── editReqList[].inputText.$value (OBJECT)
└── editReqList[].inputText.$value.lang (OBJECT)
└── editReqList[].inputText.$value.lang.$key (STRING)
└── editReqList[].inputText.$value.lang.$value (STRING)
└── editReqList[].inputText.$value.multiLineInputs (OBJECT[])
└── editReqList[].inputText.$value.multiLineInputs[].lang (OBJECT)
└── editReqList[].inputText.$value.multiLineInputs[].lang.$key (STRING)
└── editReqList[].inputText.$value.multiLineInputs[].lang.$value (STRING)
└── editReqList[].inputText.$value.multiLineInputs[].name (STRING)
└── editReqList[].inputText.$value.name (STRING)
└── editReqList[].properties (OBJECT)
└── editReqList[].properties.$key (STRING)
└── editReqList[].properties.$value (INTEGER[])
└── editReqList[].repList (OBJECT[])
└── editReqList[].repList[].repId (INTEGER)
└── productId (LONG **必填**)request 内字段(Partner Request 表 + 未展开子级自 Request Example 补充)
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| editReqList | OBJECT[] | 是 | edit single compliance req list |
| editReqList[].complianceType | INTEGER | — | Partner Request Example(CURL -d 展开;Request 表未列出子级时补充) |
| editReqList[].inputText | OBJECT | — | Partner Request Example(CURL -d 展开;Request 表未列出子级时补充) |
| editReqList[].inputText.$key | STRING | — | Partner Request Example(CURL -d 展开;Request 表未列出子级时补充) |
| editReqList[].inputText.$value | OBJECT | — | Partner Request Example(CURL -d 展开;Request 表未列出子级时补充) |
| editReqList[].inputText.$value.lang | OBJECT | — | Partner Request Example(CURL -d 展开;Request 表未列出子级时补充) |
| editReqList[].inputText.$value.lang.$key | STRING | — | Partner Request Example(CURL -d 展开;Request 表未列出子级时补充) |
| editReqList[].inputText.$value.lang.$value | STRING | — | Partner Request Example(CURL -d 展开;Request 表未列出子级时补充) |
| editReqList[].inputText.$value.multiLineInputs | OBJECT[] | — | Partner Request Example(CURL -d 展开;Request 表未列出子级时补充) |
| editReqList[].inputText.$value.multiLineInputs[].lang | OBJECT | — | Partner Request Example(CURL -d 展开;Request 表未列出子级时补充) |
| editReqList[].inputText.$value.multiLineInputs[].lang.$key | STRING | — | Partner Request Example(CURL -d 展开;Request 表未列出子级时补充) |
| editReqList[].inputText.$value.multiLineInputs[].lang.$value | STRING | — | Partner Request Example(CURL -d 展开;Request 表未列出子级时补充) |
| editReqList[].inputText.$value.multiLineInputs[].name | STRING | — | Partner Request Example(CURL -d 展开;Request 表未列出子级时补充) |
| editReqList[].inputText.$value.name | STRING | — | Partner Request Example(CURL -d 展开;Request 表未列出子级时补充) |
| editReqList[].properties | OBJECT | — | Partner Request Example(CURL -d 展开;Request 表未列出子级时补充) |
| editReqList[].properties.$key | STRING | — | Partner Request Example(CURL -d 展开;Request 表未列出子级时补充) |
| editReqList[].properties.$value | INTEGER[] | — | Partner Request Example(CURL -d 展开;Request 表未列出子级时补充) |
| editReqList[].repList | OBJECT[] | — | Partner Request Example(CURL -d 展开;Request 表未列出子级时补充) |
| editReqList[].repList[].repId | INTEGER | — | Partner Request Example(CURL -d 展开;Request 表未列出子级时补充) |
| productId | LONG | 是 | product id |
editReqList[].inputText
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| $key | STRING | — | Partner Request Example(CURL -d 展开;Request 表未列出子级时补充) |
| $value | OBJECT | — | Partner Request Example(CURL -d 展开;Request 表未列出子级时补充) |
editReqList[].inputText.$value
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| lang | OBJECT | — | Partner Request Example(CURL -d 展开;Request 表未列出子级时补充) |
| multiLineInputs | OBJECT[] | — | Partner Request Example(CURL -d 展开;Request 表未列出子级时补充) |
| name | STRING | — | Partner Request Example(CURL -d 展开;Request 表未列出子级时补充) |
editReqList[].inputText.$value.lang
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| $key | STRING | — | Partner Request Example(CURL -d 展开;Request 表未列出子级时补充) |
| $value | STRING | — | Partner Request Example(CURL -d 展开;Request 表未列出子级时补充) |
editReqList[].inputText.$value.multiLineInputs[]
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| lang | OBJECT | — | Partner Request Example(CURL -d 展开;Request 表未列出子级时补充) |
| name | STRING | — | Partner Request Example(CURL -d 展开;Request 表未列出子级时补充) |
editReqList[].inputText.$value.multiLineInputs[].lang
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| $key | STRING | — | Partner Request Example(CURL -d 展开;Request 表未列出子级时补充) |
| $value | STRING | — | Partner Request Example(CURL -d 展开;Request 表未列出子级时补充) |
editReqList[].properties
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| $key | STRING | — | Partner Request Example(CURL -d 展开;Request 表未列出子级时补充) |
| $value | INTEGER[] | — | Partner Request Example(CURL -d 展开;Request 表未列出子级时补充) |
网关 params 写法
{
"accessToken": "TOKEN",
"site": "global",
"tokenPurpose": "product-inventory",
"request": {
"productId": 1,
"editReqList": [
{
"inputText": {
"$key": "test",
"$value": {
"name": "test",
"lang": {
"$key": "test",
"$value": "test"
},
"multiLineInputs": [
{
"name": "test",
"lang": {
"$key": "test",
"$value": "test"
}
}
]
}
},
"repList": [
{
"repId": 1
}
],
"properties": {
"$key": "test",
"$value": [
1,
1
]
},
"complianceType": 1
}
]
}
}Partner Request Example 为 CURL,业务字段可能在顶层;经 LinkFox 网关请放在 `params.request`(或脚本接受的顶层 request)。---
Response(Partner Response 表 + 全量展开)
response
├── success (BOOLEAN)
├── errorCode (INTEGER)
├── errorMsg (STRING)
└── result (OBJECT)Partner Response 表仅含顶层字段;result 下全部子字段按 Response 表层级 与 Response Example 合并展开。Partner Response 表(HTML 导出可见行)
| 参数 | 类型 | 说明 |
|---|---|---|
| success | BOOLEAN | success |
| errorCode | INTEGER | errorCode |
| errorMsg | STRING | errorMsg |
| result | OBJECT | result |
result 及下级(全部展开)
| 参数 | 类型 | 说明 |
|---|---|---|
| errorCode | INTEGER | errorCode |
| errorMsg | STRING | errorMsg |
| result | OBJECT | result |
| success | BOOLEAN | success |
---
脚本
export LINKFOXAGENT_API_KEY="<key>"
python scripts/global_compliance_edit.py '{ "accessToken": "TOKEN", "site": "global", "tokenPurpose": "product-inventory", "request": { "productId": 1, "editReqList": [ { "inputText": { "$key": "test", "$value": { "name": "test", "lang": { "$key": "test", "$value": "test" }, "multiLineInputs": [ { "name": "test", "lang": { "$key": "test", "$value": "test" } } ] } }, "repList": [ { "repId": 1 } ], "properties": { "$key": "test", "$value": [ 1, 1 ] }, "complianceType": 1 } ] } }'合规模板元数据查询 — bg.compliance.metadata.get
| 项 | 值 |
|---|---|
| 脚本 | scripts/global_compliance_metadata_get.py |
| Partner 文档 | https://partner-global.temu.com/documentation?menu_code=fb16b05f7a904765aac4af3a24b87d4a&sub_menu_code=fd12bdf5cb364366bdef85aad9cd8e48 |
| 网关 | POST /temu/proxy,type=bg.compliance.metadata.get,业务载荷放在 Body 的 params |
Description: Query compliance metadata / template definitions for a product.
默认 `site=global`、`tokenPurpose=product-inventory`。入参来自 Partner Request 表;出参来自 Response 表 + Response Example 展开。
---
Request 结构(Partner Request 表)
params
└── request (OBJECT)
└── productId (LONG **必填**)request 内字段(Partner Request 表 + 未展开子级自 Request Example 补充)
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| productId | LONG | 是 | product id |
网关 params 写法
{
"accessToken": "TOKEN",
"site": "global",
"tokenPurpose": "product-inventory",
"request": {
"productId": 1
}
}Partner Request Example 为 CURL,业务字段可能在顶层;经 LinkFox 网关请放在 `params.request`(或脚本接受的顶层 request)。---
Response(Partner Response 表 + 全量展开)
response
├── success (BOOLEAN)
├── errorCode (INTEGER)
├── errorMsg (STRING)
└── result (OBJECT)
├── complianceInfoList[] (OBJECT[])
├── complianceInfoList[].complianceType (INTEGER)
├── complianceInfoList[].desc (STRING)
├── complianceInfoList[].name (STRING)
├── complianceInfoList[].propertyList[] (OBJECT[])
├── complianceInfoList[].propertyList[].controlType (INTEGER)
├── complianceInfoList[].propertyList[].desc (STRING)
├── complianceInfoList[].propertyList[].excludeVidMap (OBJECT)
├── complianceInfoList[].propertyList[].excludeVidMap.$key (STRING)
├── complianceInfoList[].propertyList[].excludeVidMap.$value[] (INTEGER[])
├── complianceInfoList[].propertyList[].inputNum (INTEGER)
├── complianceInfoList[].propertyList[].inputType (INTEGER)
├── complianceInfoList[].propertyList[].langDescMap (OBJECT)
├── complianceInfoList[].propertyList[].langDescMap.$key (STRING)
├── complianceInfoList[].propertyList[].langDescMap.$value (STRING)
├── complianceInfoList[].propertyList[].max (INTEGER)
├── complianceInfoList[].propertyList[].min (INTEGER)
├── complianceInfoList[].propertyList[].needTransLang[] (STRING[])
├── complianceInfoList[].propertyList[].parentPid (INTEGER)
├── complianceInfoList[].propertyList[].parentVidList[] (INTEGER[])
├── complianceInfoList[].propertyList[].propertyId (INTEGER)
├── complianceInfoList[].propertyList[].propertyName (STRING)
├── complianceInfoList[].propertyList[].propertyValueList[] (OBJECT[])
├── complianceInfoList[].propertyList[].propertyValueList[].langValueNameMap (OBJECT)
├── complianceInfoList[].propertyList[].propertyValueList[].langValueNameMap.$key (STRING)
├── complianceInfoList[].propertyList[].propertyValueList[].langValueNameMap.$value (STRING)
├── complianceInfoList[].propertyList[].propertyValueList[].parentVidList[] (INTEGER[])
├── complianceInfoList[].propertyList[].propertyValueList[].valueName (STRING)
├── complianceInfoList[].propertyList[].propertyValueList[].vid (INTEGER)
├── complianceInfoList[].propertyList[].selectNum (INTEGER)
├── complianceInfoList[].propertyList[].unit (STRING)
├── complianceInfoList[].propertyList[].valuePrecision (INTEGER)
├── complianceInfoList[].repDetailList[] (OBJECT[])
├── complianceInfoList[].repDetailList[].endTimestamp (INTEGER)
├── complianceInfoList[].repDetailList[].personType (INTEGER)
├── complianceInfoList[].repDetailList[].repAddressInfo (OBJECT)
├── complianceInfoList[].repDetailList[].repAddressInfo.addressLineOne (STRING)
├── complianceInfoList[].repDetailList[].repAddressInfo.city (STRING)
├── complianceInfoList[].repDetailList[].repAddressInfo.regionName (STRING)
├── complianceInfoList[].repDetailList[].repAddressInfo.stateName (STRING)
├── complianceInfoList[].repDetailList[].repId (INTEGER)
├── complianceInfoList[].repDetailList[].repName (STRING)
├── complianceInfoList[].repDetailList[].repStatus (INTEGER)
├── complianceInfoList[].repDetailList[].startTimestamp (INTEGER)
├── complianceInfoList[].status (INTEGER)Partner Response 表仅含顶层字段;result 下全部子字段按 Response 表层级 与 Response Example 合并展开。Partner Response 表(HTML 导出可见行)
| 参数 | 类型 | 说明 |
|---|---|---|
| success | BOOLEAN | success |
| errorCode | INTEGER | errorCode |
| errorMsg | STRING | errorMsg |
| result | OBJECT | result |
result 及下级(全部展开)
| 参数 | 类型 | 说明 |
|---|---|---|
| complianceInfoList[] | OBJECT[] | Partner Response Example |
| complianceInfoList[].complianceType | INTEGER | Partner Response Example |
| complianceInfoList[].desc | STRING | Partner Response Example |
| complianceInfoList[].name | STRING | Partner Response Example |
| complianceInfoList[].propertyList[] | OBJECT[] | Partner Response Example |
| complianceInfoList[].propertyList[].controlType | INTEGER | Partner Response Example |
| complianceInfoList[].propertyList[].desc | STRING | Partner Response Example |
| complianceInfoList[].propertyList[].excludeVidMap | OBJECT | Partner Response Example |
| complianceInfoList[].propertyList[].excludeVidMap.$key | STRING | Partner Response Example |
| complianceInfoList[].propertyList[].excludeVidMap.$value[] | INTEGER[] | Partner Response Example |
| complianceInfoList[].propertyList[].inputNum | INTEGER | Partner Response Example |
| complianceInfoList[].propertyList[].inputType | INTEGER | Partner Response Example |
| complianceInfoList[].propertyList[].langDescMap | OBJECT | Partner Response Example |
| complianceInfoList[].propertyList[].langDescMap.$key | STRING | Partner Response Example |
| complianceInfoList[].propertyList[].langDescMap.$value | STRING | Partner Response Example |
| complianceInfoList[].propertyList[].max | INTEGER | Partner Response Example |
| complianceInfoList[].propertyList[].min | INTEGER | Partner Response Example |
| complianceInfoList[].propertyList[].needTransLang[] | STRING[] | Partner Response Example |
| complianceInfoList[].propertyList[].parentPid | INTEGER | Partner Response Example |
| complianceInfoList[].propertyList[].parentVidList[] | INTEGER[] | Partner Response Example |
| complianceInfoList[].propertyList[].propertyId | INTEGER | Partner Response Example |
| complianceInfoList[].propertyList[].propertyName | STRING | Partner Response Example |
| complianceInfoList[].propertyList[].propertyValueList[] | OBJECT[] | Partner Response Example |
| complianceInfoList[].propertyList[].propertyValueList[].langValueNameMap | OBJECT | Partner Response Example |
| complianceInfoList[].propertyList[].propertyValueList[].langValueNameMap.$key | STRING | Partner Response Example |
| complianceInfoList[].propertyList[].propertyValueList[].langValueNameMap.$value | STRING | Partner Response Example |
| complianceInfoList[].propertyList[].propertyValueList[].parentVidList[] | INTEGER[] | Partner Response Example |
| complianceInfoList[].propertyList[].propertyValueList[].valueName | STRING | Partner Response Example |
| complianceInfoList[].propertyList[].propertyValueList[].vid | INTEGER | Partner Response Example |
| complianceInfoList[].propertyList[].selectNum | INTEGER | Partner Response Example |
| complianceInfoList[].propertyList[].unit | STRING | Partner Response Example |
| complianceInfoList[].propertyList[].valuePrecision | INTEGER | Partner Response Example |
| complianceInfoList[].repDetailList[] | OBJECT[] | Partner Response Example |
| complianceInfoList[].repDetailList[].endTimestamp | INTEGER | Partner Response Example |
| complianceInfoList[].repDetailList[].personType | INTEGER | Partner Response Example |
| complianceInfoList[].repDetailList[].repAddressInfo | OBJECT | Partner Response Example |
| complianceInfoList[].repDetailList[].repAddressInfo.addressLineOne | STRING | Partner Response Example |
| complianceInfoList[].repDetailList[].repAddressInfo.city | STRING | Partner Response Example |
| complianceInfoList[].repDetailList[].repAddressInfo.regionName | STRING | Partner Response Example |
| complianceInfoList[].repDetailList[].repAddressInfo.stateName | STRING | Partner Response Example |
| complianceInfoList[].repDetailList[].repId | INTEGER | Partner Response Example |
| complianceInfoList[].repDetailList[].repName | STRING | Partner Response Example |
| complianceInfoList[].repDetailList[].repStatus | INTEGER | Partner Response Example |
| complianceInfoList[].repDetailList[].startTimestamp | INTEGER | Partner Response Example |
| complianceInfoList[].status | INTEGER | Partner Response Example |
| errorCode | INTEGER | errorCode |
| errorMsg | STRING | errorMsg |
| result | OBJECT | result |
| success | BOOLEAN | success |
---
脚本
export LINKFOXAGENT_API_KEY="<key>"
python scripts/global_compliance_metadata_get.py '{ "accessToken": "TOKEN", "site": "global", "tokenPurpose": "product-inventory", "request": { "productId": 1 } }'实拍图上传(Flash) — bg.flash.open.upload.real.image
| 项 | 值 |
|---|---|
| 脚本 | scripts/global_compliance_flash_upload_real_image.py |
| Partner 文档 | https://partner-global.temu.com/documentation?menu_code=fb16b05f7a904765aac4af3a24b87d4a&sub_menu_code=ef77dada37ac49569f6e7c787dd696d9 |
| 网关 | POST /temu/proxy,type=bg.flash.open.upload.real.image,业务载荷放在 Body 的 params |
Description: Upload real product images for compliance verification (Flash open API).
默认 `site=global`、`tokenPurpose=product-inventory`。入参来自 Partner Request 表;出参来自 Response 表 + Response Example 展开。
---
Request 结构(Partner Request 表)
params
└── request (OBJECT)
└── clientId (STRING)
└── image (STRING)request 内字段(Partner Request 表 + 未展开子级自 Request Example 补充)
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| clientId | STRING | 否 | Kaiping clientId |
| image | STRING | 否 | Supported formats include: jpg/jpeg, png and other image formats. Note that the input image must be transcoded into base64 encoding. |
网关 params 写法
{
"accessToken": "TOKEN",
"site": "global",
"tokenPurpose": "product-inventory",
"request": {
"image": "test",
"clientId": "test"
}
}Partner Request Example 为 CURL,业务字段可能在顶层;经 LinkFox 网关请放在 `params.request`(或脚本接受的顶层 request)。---
Response(Partner Response 表 + 全量展开)
response
├── success (BOOLEAN)
├── errorCode (INTEGER)
├── errorMsg (STRING)
└── result (OBJECT)
├── url (STRING)Partner Response 表仅含顶层字段;result 下全部子字段按 Response 表层级 与 Response Example 合并展开。Partner Response 表(HTML 导出可见行)
| 参数 | 类型 | 说明 |
|---|---|---|
| result | OBJECT | Return to detailed information |
| success | BOOLEAN | success |
| errorCode | INTEGER | error code |
| errorMsg | STRING | error message |
result 及下级(全部展开)
| 参数 | 类型 | 说明 |
|---|---|---|
| errorCode | INTEGER | error code |
| errorMsg | STRING | error message |
| result | OBJECT | Return to detailed information |
| success | BOOLEAN | success |
| url | STRING | Partner Response Example |
---
脚本
export LINKFOXAGENT_API_KEY="<key>"
python scripts/global_compliance_flash_upload_real_image.py '{ "accessToken": "TOKEN", "site": "global", "tokenPurpose": "product-inventory", "request": { "image": "test", "clientId": "test" } }'上传识别(Flash) — bg.flash.open.upload.recognize
| 项 | 值 |
|---|---|
| 脚本 | scripts/global_compliance_flash_upload_recognize.py |
| Partner 文档 | https://partner-global.temu.com/documentation?menu_code=fb16b05f7a904765aac4af3a24b87d4a&sub_menu_code=960adb7a9d1f47069cdc0a9abd686dc9 |
| 网关 | POST /temu/proxy,type=bg.flash.open.upload.recognize,业务载荷放在 Body 的 params |
Description: Upload and recognize compliance-related images (Flash open API).
默认 `site=global`、`tokenPurpose=product-inventory`。入参来自 Partner Request 表;出参来自 Response 表 + Response Example 展开。
---
Request 结构(Partner Request 表)
params
└── request (OBJECT)
└── clientId (STRING)
└── skuId (LONG)
└── skuSame (BOOLEAN)
└── spuId (LONG)
└── uploadImageList (OBJECT[])
└── uploadImageList[].image (STRING)
└── uploadImageList[].position (INTEGER)request 内字段(Partner Request 表 + 未展开子级自 Request Example 补充)
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| clientId | STRING | 否 | Open platform gateway transparently transmits parameters, not user-provided parameters |
| skuId | LONG | 否 | skuId |
| skuSame | BOOLEAN | 否 | Is the sku consistent |
| spuId | LONG | 否 | spuId |
| uploadImageList | OBJECT[] | 否 | Merchant uploads picture list |
| uploadImageList[].image | STRING | — | Partner Request Example(CURL -d 展开;Request 表未列出子级时补充) |
| uploadImageList[].position | INTEGER | — | Partner Request Example(CURL -d 展开;Request 表未列出子级时补充) |
网关 params 写法
{
"accessToken": "TOKEN",
"site": "global",
"tokenPurpose": "product-inventory",
"request": {
"clientId": "test",
"uploadImageList": [
{
"image": "test",
"position": 1
}
],
"skuSame": true,
"spuId": 1,
"skuId": 1
}
}Partner Request Example 为 CURL,业务字段可能在顶层;经 LinkFox 网关请放在 `params.request`(或脚本接受的顶层 request)。---
Response(Partner Response 表 + 全量展开)
response
├── success (BOOLEAN)
├── errorCode (INTEGER)
├── errorMsg (STRING)
└── result (OBJECT)
├── checkResult (BOOLEAN)
├── ruleCheckResult[] (OBJECT[])
├── ruleCheckResult[].ruleName (STRING)
├── ruleCheckResult[].ruleStatus (INTEGER)Partner Response 表仅含顶层字段;result 下全部子字段按 Response 表层级 与 Response Example 合并展开。Partner Response 表(HTML 导出可见行)
| 参数 | 类型 | 说明 |
|---|---|---|
| result | OBJECT | Return to detailed information |
| success | BOOLEAN | success |
| errorCode | INTEGER | error code |
| errorMsg | STRING | error message |
result 及下级(全部展开)
| 参数 | 类型 | 说明 |
|---|---|---|
| checkResult | BOOLEAN | Partner Response Example |
| errorCode | INTEGER | error code |
| errorMsg | STRING | error message |
| result | OBJECT | Return to detailed information |
| ruleCheckResult[] | OBJECT[] | Partner Response Example |
| ruleCheckResult[].ruleName | STRING | Partner Response Example |
| ruleCheckResult[].ruleStatus | INTEGER | Partner Response Example |
| success | BOOLEAN | success |
---
脚本
export LINKFOXAGENT_API_KEY="<key>"
python scripts/global_compliance_flash_upload_recognize.py '{ "accessToken": "TOKEN", "site": "global", "tokenPurpose": "product-inventory", "request": { "clientId": "test", "uploadImageList": [ { "image": "test", "position": 1 } ], "skuSame": true, "spuId": 1, "skuId": 1 } }'合规标签查询 — bg.goods.compliancelabel.get
| 项 | 值 |
|---|---|
| 脚本 | scripts/global_compliance_goods_compliancelabel_get.py |
| Partner 文档 | https://partner-global.temu.com/documentation?menu_code=fb16b05f7a904765aac4af3a24b87d4a&sub_menu_code=c49495eb93904c93b750e9798c95e7db |
| 网关 | POST /temu/proxy,type=bg.goods.compliancelabel.get,业务载荷放在 Body 的 params |
Description: Query compliance labels for goods.
默认 `site=global`、`tokenPurpose=product-inventory`。入参来自 Partner Request 表;出参来自 Response 表 + Response Example 展开。
---
Request 结构(Partner Request 表)
params
└── request (OBJECT)
└── page (INTEGER)
└── pageSize (INTEGER)
└── productIds (LONG[])request 内字段(Partner Request 表 + 未展开子级自 Request Example 补充)
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| page | INTEGER | 否 | Page Number |
| pageSize | INTEGER | 否 | Page Size |
| productIds | LONG[] | 否 | spu ids |
网关 params 写法
{
"accessToken": "TOKEN",
"site": "global",
"tokenPurpose": "product-inventory",
"request": {
"productIds": [
1,
1
],
"pageSize": 1,
"page": 1
}
}Partner Request Example 为 CURL,业务字段可能在顶层;经 LinkFox 网关请放在 `params.request`(或脚本接受的顶层 request)。---
Response(Partner Response 表 + 全量展开)
response
├── success (BOOLEAN)
├── errorCode (INTEGER)
├── errorMsg (STRING)
└── result (OBJECT)
├── pageResult (OBJECT)
├── pageResult.data[] (OBJECT[])
├── pageResult.data[].productId (INTEGER)
├── pageResult.data[].skuComplianceLabels[] (OBJECT[])
├── pageResult.data[].skuComplianceLabels[].complianceLabelPics[] (STRING[])
├── pageResult.data[].skuComplianceLabels[].complianceLabelStatus (INTEGER)
├── pageResult.data[].skuComplianceLabels[].productSkuId (INTEGER)
├── pageResult.totalCount (INTEGER)Partner Response 表仅含顶层字段;result 下全部子字段按 Response 表层级 与 Response Example 合并展开。Partner Response 表(HTML 导出可见行)
| 参数 | 类型 | 说明 |
|---|---|---|
| result | OBJECT | — |
| success | BOOLEAN | success |
| errorCode | INTEGER | error Code |
| errorMsg | STRING | error Msg |
result 及下级(全部展开)
| 参数 | 类型 | 说明 |
|---|---|---|
| errorCode | INTEGER | error Code |
| errorMsg | STRING | error Msg |
| pageResult | OBJECT | Partner Response Example |
| pageResult.data[] | OBJECT[] | Partner Response Example |
| pageResult.data[].productId | INTEGER | Partner Response Example |
| pageResult.data[].skuComplianceLabels[] | OBJECT[] | Partner Response Example |
| pageResult.data[].skuComplianceLabels[].complianceLabelPics[] | STRING[] | Partner Response Example |
| pageResult.data[].skuComplianceLabels[].complianceLabelStatus | INTEGER | Partner Response Example |
| pageResult.data[].skuComplianceLabels[].productSkuId | INTEGER | Partner Response Example |
| pageResult.totalCount | INTEGER | Partner Response Example |
| result | OBJECT | — |
| success | BOOLEAN | success |
---
脚本
export LINKFOXAGENT_API_KEY="<key>"
python scripts/global_compliance_goods_compliancelabel_get.py '{ "accessToken": "TOKEN", "site": "global", "tokenPurpose": "product-inventory", "request": { "productIds": [ 1, 1 ], "pageSize": 1, "page": 1 } }'Compliance API 文档索引(Global)
按 Temu type 分文件;入参来自 Partner Request 表,出参来自 Response 表 + Response Example 展开。
已接入(9)
| type | 文档 | 脚本 |
|---|---|---|
bg.compliance.edit | bg-compliance-edit.md | global_compliance_edit.py |
bg.compliance.metadata.get | bg-compliance-metadata-get.md | global_compliance_metadata_get.py |
bg.goods.compliancelabel.get | bg-goods-compliancelabel-get.md | global_compliance_goods_compliancelabel_get.py |
bg.arbok.open.cert.queryNeedUploadItems | bg-arbok-open-cert-query-need-upload-items.md | global_compliance_arbok_cert_query_need_upload_items.py |
bg.arbok.open.cert.uploadProductCert | bg-arbok-open-cert-upload-product-cert.md | global_compliance_arbok_cert_upload_product_cert.py |
bg.flash.open.upload.recognize | bg-flash-open-upload-recognize.md | global_compliance_flash_upload_recognize.py |
bg.flash.open.upload.real.image | bg-flash-open-upload-real-image.md | global_compliance_flash_upload_real_image.py |
bg.arbok.open.upload.uploadFile | bg-arbok-open-upload-upload-file.md | global_compliance_arbok_upload_upload_file.py |
bg.arbok.open.product.cert.query | bg-arbok-open-product-cert-query.md | global_compliance_arbok_product_cert_query.py |
Temu 授权流程
用户确认店铺类型与用途
↓
temu_token_guide.py(输出后台操作步骤)
↓
用户在 Temu 卖家后台复制 access_token
↓
save_temu_access_token.py(可选,写入本地 store)
↓
temu_proxy.py / temu_file_download.py
- 直接传 accessToken,或
- 传 storeKey + site + managementType + tokenPurpose决策表
| 用户需求 | shopType | tokenPurpose | site |
|---|---|---|---|
| 半托管查商品/库存 | semi-managed | product-inventory | cn / partner |
| 半托管查订单/发货 | semi-managed | order-shipping | us / global / eu |
| 全托管任意接口 | full-managed | full-managed | cn / partner |
| 美欧本土店 | local-native | local-native | us 等 |
与 API 调用的对应关系
紫鸟转发路径:/temu-proxy/{site}/{managementType}
- 全托管 CN:
cn+full-managed - 半托管商品:
cn/partner+semi-managed+ 商品 token - 半托管订单:
us/global/eu+semi-managed+ 订单 token
授权应用名称必须与后台一致:酷鸟卖家助手 或 Cyber-ERP酷鸟助手 / Cyber-ERP。
Partner Global — Compliance 接口目录
Partner Platform for Global 菜单:Product Compliance / 电商合规(bg.compliance.*、bg.arbok.*、bg.flash.* 等)。
文档根 menu_code:fb16b05f7a904765aac4af3a24b87d4a。
本 skill 经 temu_global_proxy(POST /temu/proxy)调用,默认 `site=global`、`tokenPurpose=product-inventory`。
已接入(9)
| sub_menu_code | type | 脚本 | 参数文档 |
|---|---|---|---|
a8829c8ede574d9a97cd3cea7c019bc4 | bg.compliance.edit | global_compliance_edit.py | apis/bg-compliance-edit.md |
fd12bdf5cb364366bdef85aad9cd8e48 | bg.compliance.metadata.get | global_compliance_metadata_get.py | apis/bg-compliance-metadata-get.md |
c49495eb93904c93b750e9798c95e7db | bg.goods.compliancelabel.get | global_compliance_goods_compliancelabel_get.py | apis/bg-goods-compliancelabel-get.md |
84d3118d3a604947abf35144606cdea2 | bg.arbok.open.cert.queryNeedUploadItems | global_compliance_arbok_cert_query_need_upload_items.py | apis/bg-arbok-open-cert-query-need-upload-items.md |
56de04bcafae45509b21edeab57c9fdb | bg.arbok.open.cert.uploadProductCert | global_compliance_arbok_cert_upload_product_cert.py | apis/bg-arbok-open-cert-upload-product-cert.md |
960adb7a9d1f47069cdc0a9abd686dc9 | bg.flash.open.upload.recognize | global_compliance_flash_upload_recognize.py | apis/bg-flash-open-upload-recognize.md |
ef77dada37ac49569f6e7c787dd696d9 | bg.flash.open.upload.real.image | global_compliance_flash_upload_real_image.py | apis/bg-flash-open-upload-real-image.md |
01b8792d2e4a40f7b3b14f4a1f2711b6 | bg.arbok.open.upload.uploadFile | global_compliance_arbok_upload_upload_file.py | apis/bg-arbok-open-upload-upload-file.md |
5ec78e3b36c34fcba743a75523349fb5 | bg.arbok.open.product.cert.query | global_compliance_arbok_product_cert_query.py | apis/bg-arbok-open-product-cert-query.md |
官方文档 URL
| type | URL |
|---|---|
bg.compliance.edit | https://partner-global.temu.com/documentation?menu_code=fb16b05f7a904765aac4af3a24b87d4a&sub_menu_code=a8829c8ede574d9a97cd3cea7c019bc4 |
bg.compliance.metadata.get | https://partner-global.temu.com/documentation?menu_code=fb16b05f7a904765aac4af3a24b87d4a&sub_menu_code=fd12bdf5cb364366bdef85aad9cd8e48 |
bg.goods.compliancelabel.get | https://partner-global.temu.com/documentation?menu_code=fb16b05f7a904765aac4af3a24b87d4a&sub_menu_code=c49495eb93904c93b750e9798c95e7db |
bg.arbok.open.cert.queryNeedUploadItems | https://partner-global.temu.com/documentation?menu_code=fb16b05f7a904765aac4af3a24b87d4a&sub_menu_code=84d3118d3a604947abf35144606cdea2 |
bg.arbok.open.cert.uploadProductCert | https://partner-global.temu.com/documentation?menu_code=fb16b05f7a904765aac4af3a24b87d4a&sub_menu_code=56de04bcafae45509b21edeab57c9fdb |
bg.flash.open.upload.recognize | https://partner-global.temu.com/documentation?menu_code=fb16b05f7a904765aac4af3a24b87d4a&sub_menu_code=960adb7a9d1f47069cdc0a9abd686dc9 |
bg.flash.open.upload.real.image | https://partner-global.temu.com/documentation?menu_code=fb16b05f7a904765aac4af3a24b87d4a&sub_menu_code=ef77dada37ac49569f6e7c787dd696d9 |
bg.arbok.open.upload.uploadFile | https://partner-global.temu.com/documentation?menu_code=fb16b05f7a904765aac4af3a24b87d4a&sub_menu_code=01b8792d2e4a40f7b3b14f4a1f2711b6 |
bg.arbok.open.product.cert.query | https://partner-global.temu.com/documentation?menu_code=fb16b05f7a904765aac4af3a24b87d4a&sub_menu_code=5ec78e3b36c34fcba743a75523349fb5 |
上游 OpenAPI(Global)
POST https://openapi-b-global.temu.com/openapi/router(经 LinkFox 网关 site=global 转发)
通用脚本
| 脚本 | 说明 |
|---|---|
temu_global_proxy.py | 任意 type + params |
temu_global_file_download.py | 加签文件下载 |
相关 skill
| 能力 | skill |
|---|---|
| 电商合规(本 skill) | `linkfox-temu-compliance-global` |
| 商品管理 | linkfox-temu-manage-product-global |
Token 说明
建议使用 `tokenPurpose=product-inventory`。详见 access-token.md。
#!/usr/bin/env python3
"""Factory for thin Global compliance API CLI scripts."""
import sys
from _temu_common import load_json_arg
from _temu_global_common import extract_business_params, run_and_print
def run_cli(api_type: str, usage: str) -> None:
if len(sys.argv) < 2:
print(f"Usage: {usage}", file=sys.stderr)
sys.exit(1)
params = load_json_arg(sys.argv)
run_and_print(params, api_type, extract_business_params(params))
#!/usr/bin/env python3
"""Authorization guide data derived from TEMU_API_README.md."""
VALID_SHOP_TYPES = frozenset({"semi-managed", "full-managed", "local-native"})
VALID_TOKEN_PURPOSES = frozenset(
{"product-inventory", "order-shipping", "full-managed", "local-native", "default"}
)
LOGIN_URLS = {
"cn": "https://seller.kuajingmaihuo.com",
"partner": "https://agentseller.temu.com",
}
APPS = {
"kuniao-seller": "酷鸟卖家助手",
"cyber-erp": "Cyber-ERP酷鸟助手",
"cyber-erp-native": "Cyber-ERP",
}
def _steps(*items: str) -> list[str]:
return list(items)
def build_guide(shop_type: str, token_purpose: str, site=None) -> dict:
shop_type = shop_type.strip().lower()
token_purpose = (token_purpose or "default").strip().lower()
if shop_type not in VALID_SHOP_TYPES:
raise ValueError(
f"Invalid shopType '{shop_type}'. Use: semi-managed, full-managed, local-native"
)
if token_purpose not in VALID_TOKEN_PURPOSES:
raise ValueError(
"Invalid tokenPurpose. Use: product-inventory, order-shipping, "
"full-managed, local-native, default"
)
guide = {
"shopType": shop_type,
"tokenPurpose": token_purpose,
"note": (
"Temu accessToken 需在卖家后台手动复制,无自动 OAuth。"
"保存后可使用 save_temu_access_token.py / storeKey 调用 API。"
),
}
if shop_type == "semi-managed" and token_purpose == "product-inventory":
site = site or "cn"
login = LOGIN_URLS.get(site, LOGIN_URLS["cn"])
guide.update(
{
"recommendedSite": site,
"recommendedManagementType": "semi-managed",
"loginUrl": login,
"appName": APPS["kuniao-seller"],
"menuPath": "系统管理 > 服务市场 > 授权管理",
"steps": _steps(
f"登录 Temu 卖家后台:{login}",
"进入:系统管理 > 服务市场 > 授权管理",
"点击「获取授权」,选择「酷鸟卖家助手」",
"全选常规和特殊授权 > 确认",
"复制 access_token,用 save_temu_access_token.py 保存",
),
"usageHint": "用于半托管商品、库存类 API;site 通常为 cn 或 partner。",
}
)
elif shop_type == "semi-managed" and token_purpose == "order-shipping":
site = site or "us"
guide.update(
{
"recommendedSite": site,
"recommendedManagementType": "semi-managed",
"loginUrl": LOGIN_URLS["cn"],
"appName": APPS["cyber-erp"],
"menuPath": "Seller Central 对应站点 > 服务市场 > 授权管理",
"steps": _steps(
"登录 Temu 卖家后台(seller.kuajingmaihuo.com 或 agentseller.temu.com)",
"右上角 Seller Central,切换到目标区域站点(美区/欧区/全球)",
"进入:服务市场 > 授权管理",
"点击「获取授权」,选择「Cyber-ERP酷鸟助手」",
"全选常规和特殊授权 > 确认",
"复制 access_token;调用 API 时 site 用 us / global / eu",
),
"usageHint": "用于半托管订单、发货类 API。",
}
)
elif shop_type == "full-managed" or token_purpose == "full-managed":
site = site or "cn"
login = LOGIN_URLS.get(site, LOGIN_URLS["cn"])
guide.update(
{
"recommendedSite": site,
"recommendedManagementType": "full-managed",
"loginUrl": login,
"appName": APPS["kuniao-seller"],
"menuPath": "系统管理 > 服务市场 > 授权管理",
"steps": _steps(
f"登录 Temu 平台:{login}",
"系统管理 > 服务市场 > 授权管理",
"获取授权 > 选择「酷鸟卖家助手」",
"全选常规和特殊授权 > 确认 > 复制 access_token",
),
"usageHint": "全托管店铺通用;site 通常为 cn 或 partner。",
}
)
elif shop_type == "local-native" or token_purpose == "local-native":
guide.update(
{
"recommendedSite": site or "us",
"recommendedManagementType": "semi-managed",
"loginUrl": LOGIN_URLS["cn"],
"appName": APPS["cyber-erp-native"],
"menuPath": "Apps And Services > Manage Your Apps",
"steps": _steps(
"登录 Temu 卖家后台",
"进入:Apps And Services(应用程序和服务)",
"Manage Your Apps(管理您的应用)",
"Authorize a new app(授权新应用)",
"搜索「Cyber-ERP」",
"一般权限和敏感权限全选 > 确认 > 复制 access_token",
),
"usageHint": "适用于美区、欧区本土主体店铺。",
}
)
else:
guide.update(
{
"steps": _steps(
"确认店铺类型:semi-managed / full-managed / local-native",
"确认用途:product-inventory(商品库存)或 order-shipping(订单发货)",
"运行 temu_token_guide.py 获取对应步骤",
"复制 token 后执行 save_temu_access_token.py",
),
"usageHint": "请指定 shopType 与 tokenPurpose,见 references/access-token.md",
}
)
guide["cautions"] = [
"子账号可能无法进入服务市场,需主账号授权",
"多站点需分别登录对应后台获取 Token",
"Token 有有效期,过期后需重新在后台复制",
"调用网关前需联系紫鸟配置 IP 白名单",
]
return guide
#!/usr/bin/env python3
"""Shared helpers for LinkFox Temu API skill scripts."""
import json
import os
import sys
from urllib.error import HTTPError, URLError
from urllib.request import Request, urlopen
from _temu_token_store import get_token
BASE_URL = os.environ.get("TEMU_API_BASE_URL") or os.environ.get(
"STORE_API_BASE_URL", "https://tool-gateway.linkfox.com"
)
BASE_URL = BASE_URL.rstrip("/")
PROXY_URL = f"{BASE_URL}/temu/proxy"
FILE_DOWNLOAD_URL = f"{BASE_URL}/temu/fileDownload"
VALID_SITES = frozenset({"cn", "partner", "us", "global", "eu"})
VALID_MANAGEMENT_TYPES = frozenset({"full-managed", "semi-managed"})
# LinkFox 用户 Token(网关鉴权),勿与 Body 中的 Temu accessToken 混淆
LINKFOX_TOKEN_PARAM_KEYS = ("token", "linkfoxToken", "linkfox_token")
def get_linkfox_token(params=None) -> str:
"""
LinkFox 用户鉴权 Token,与 linkfox-amazon-store-auth 一致。
优先级:请求 JSON 中的 token / linkfoxToken > 环境变量 LINKFOXAGENT_API_KEY。
"""
if params:
for key in LINKFOX_TOKEN_PARAM_KEYS:
value = params.get(key)
if value is not None and str(value).strip():
return str(value).strip()
key = os.environ.get("LINKFOXAGENT_API_KEY")
if not key:
print(
"LinkFox user Token not configured. Same as linkfox-amazon-store-auth:\n"
"1. Visit https://skill.linkfox.com/linkfoxskills/guide.htm to obtain your Key\n"
"2. export LINKFOXAGENT_API_KEY=your-key-here\n"
" Or pass \"token\" in the JSON parameters of proxy/fileDownload scripts.",
file=sys.stderr,
)
sys.exit(1)
return key
def build_gateway_headers(linkfox_token: str) -> dict:
"""网关鉴权:Authorization(全站通用)+ Token(TEMU_API_SPEC 约定)。"""
return {
"Authorization": linkfox_token,
"Token": linkfox_token,
"Content-Type": "application/json",
"User-Agent": "LinkFox-Skill/1.0",
}
def load_json_arg(argv: list) -> dict:
if len(argv) < 2:
return {}
try:
return json.loads(argv[1])
except json.JSONDecodeError as e:
print(f"Invalid parameter format: {e}", file=sys.stderr)
sys.exit(1)
def require_text(params: dict, key: str, label=None) -> str:
value = params.get(key)
if value is None or not str(value).strip():
print(f"Error: '{label or key}' is required.", file=sys.stderr)
sys.exit(1)
return str(value).strip()
def validate_site(site: str) -> str:
if site not in VALID_SITES:
print(
f"Error: invalid site '{site}'. Must be one of: {', '.join(sorted(VALID_SITES))}",
file=sys.stderr,
)
sys.exit(1)
return site
def validate_management_type(management_type: str) -> str:
if management_type not in VALID_MANAGEMENT_TYPES:
print(
"Error: invalid managementType. Must be: full-managed, semi-managed",
file=sys.stderr,
)
sys.exit(1)
return management_type
def call_temu_api(
url: str,
body: dict,
timeout: int = 60,
linkfox_params=None,
) -> dict:
"""调用 Temu 网关接口;必须先具备 LinkFox 用户 Token。"""
linkfox_token = get_linkfox_token(linkfox_params)
data = json.dumps(body, ensure_ascii=False).encode("utf-8")
req = Request(
url,
data=data,
headers=build_gateway_headers(linkfox_token),
method="POST",
)
try:
with urlopen(req, timeout=timeout) as response:
return json.loads(response.read().decode("utf-8"))
except HTTPError as e:
raw = e.read().decode("utf-8") if e.fp else ""
try:
return json.loads(raw)
except json.JSONDecodeError:
return {"error": f"HTTP {e.code}: {e.reason}", "details": raw}
except URLError as e:
return {"error": f"Connection failed: {e.reason}"}
def is_linkfox_auth_error(result: dict) -> bool:
msg = str(result.get("message") or result.get("error") or "")
return "无法识别当前用户" in msg or "重新登录" in msg
def resolve_access_token(params: dict) -> str:
"""Temu 店铺 accessToken:直接传入或从本地 storeKey 读取。"""
if params.get("accessToken"):
return str(params["accessToken"]).strip()
store_key = params.get("storeKey")
if not store_key:
print(
"Error: provide Temu 'accessToken' or 'storeKey' (+ site, managementType).",
file=sys.stderr,
)
sys.exit(1)
site = validate_site(require_text(params, "site"))
management_type = validate_management_type(require_text(params, "managementType"))
token_purpose = str(params.get("tokenPurpose", "default")).strip() or "default"
token = get_token(str(store_key).strip(), site, management_type, token_purpose)
if not token:
print(
f"Error: no Temu token for storeKey={store_key}, site={site}, "
f"managementType={management_type}, tokenPurpose={token_purpose}. "
"Run temu_token_guide.py and save_temu_access_token.py first.",
file=sys.stderr,
)
sys.exit(1)
return token
def parse_nested_body(result: dict) -> dict:
"""If gateway returns a JSON string in body, parse it into temuBody."""
body = result.get("body")
if isinstance(body, str) and body.strip():
try:
result["temuBody"] = json.loads(body)
except json.JSONDecodeError:
pass
return result
#!/usr/bin/env python3
"""Global / Partner compliance API helpers; local gateway helpers."""
import json
from _temu_common import (
FILE_DOWNLOAD_URL,
PROXY_URL,
call_temu_api,
load_json_arg,
parse_nested_body,
resolve_access_token,
)
# Partner Global 电商合规:默认全球站半托管 + 商品/合规场景 token
DEFAULT_SITE = "global"
DEFAULT_MANAGEMENT_TYPE = "semi-managed"
DEFAULT_TOKEN_PURPOSE = "product-inventory"
RESERVED_KEYS = frozenset(
{
"token",
"linkfoxToken",
"linkfox_token",
"accessToken",
"storeKey",
"site",
"managementType",
"tokenPurpose",
"type",
"params",
}
)
def extract_business_params(params: dict) -> dict:
if isinstance(params.get("params"), dict):
return dict(params["params"])
return {k: v for k, v in params.items() if k not in RESERVED_KEYS}
def build_global_proxy_body(params: dict, api_type: str, business=None) -> dict:
site = str(params.get("site", DEFAULT_SITE)).strip().lower() or DEFAULT_SITE
management_type = (
str(params.get("managementType", DEFAULT_MANAGEMENT_TYPE)).strip().lower()
or DEFAULT_MANAGEMENT_TYPE
)
if "tokenPurpose" not in params and params.get("storeKey") and not params.get("accessToken"):
params = dict(params)
params.setdefault("tokenPurpose", DEFAULT_TOKEN_PURPOSE)
params.setdefault("site", site)
params.setdefault("managementType", management_type)
body = {
"site": site,
"managementType": management_type,
"accessToken": resolve_access_token(params),
"type": api_type,
}
biz = business if business is not None else extract_business_params(params)
if biz:
body["params"] = biz
return body
def global_proxy_call(params: dict, api_type: str, business=None, timeout: int = 60) -> dict:
body = build_global_proxy_body(params, api_type, business)
return call_temu_api(PROXY_URL, body, timeout=timeout, linkfox_params=params)
def global_file_download_call(params: dict, timeout: int = 120) -> dict:
if "tokenPurpose" not in params and params.get("storeKey") and not params.get("accessToken"):
params = dict(params)
params.setdefault("tokenPurpose", DEFAULT_TOKEN_PURPOSE)
from _temu_common import require_text # noqa: E402
site = str(params.get("site", DEFAULT_SITE)).strip().lower() or DEFAULT_SITE
management_type = (
str(params.get("managementType", DEFAULT_MANAGEMENT_TYPE)).strip().lower()
or DEFAULT_MANAGEMENT_TYPE
)
body = {
"site": site,
"managementType": management_type,
"accessToken": resolve_access_token(params),
"url": require_text(params, "url"),
}
return call_temu_api(FILE_DOWNLOAD_URL, body, timeout=timeout, linkfox_params=params)
def run_and_print(params: dict, api_type: str, business=None) -> None:
result = parse_nested_body(global_proxy_call(params, api_type, business))
print(json.dumps(result, indent=2, ensure_ascii=False))
#!/usr/bin/env python3
"""Local Temu accessToken storage (manual authorization from seller console)."""
from __future__ import annotations
import json
import os
from datetime import datetime, timezone
DEFAULT_STORE_PATH = os.path.expanduser("~/.linkfox/temu-access-tokens.json")
def store_path() -> str:
return os.environ.get("TEMU_TOKEN_STORE_PATH", DEFAULT_STORE_PATH)
def _utc_now() -> str:
return datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ")
def _load_store() -> dict:
path = store_path()
if not os.path.isfile(path):
return {"stores": []}
with open(path, encoding="utf-8") as f:
data = json.load(f)
if "stores" not in data or not isinstance(data["stores"], list):
return {"stores": []}
return data
def _save_store(data: dict) -> None:
path = store_path()
parent = os.path.dirname(path)
if parent:
os.makedirs(parent, exist_ok=True)
with open(path, "w", encoding="utf-8") as f:
json.dump(data, f, indent=2, ensure_ascii=False)
f.write("\n")
def _find_store(data: dict, store_key: str):
for store in data["stores"]:
if store.get("storeKey") == store_key:
return store
return None
def _token_key(site: str, management_type: str, token_purpose: str) -> tuple:
return site, management_type, token_purpose or "default"
def save_token(
store_key: str,
site: str,
management_type: str,
access_token: str,
token_purpose: str = "default",
label: str | None = None,
) -> dict:
data = _load_store()
store = _find_store(data, store_key)
if store is None:
store = {"storeKey": store_key, "label": label or store_key, "tokens": []}
data["stores"].append(store)
elif label:
store["label"] = label
key = _token_key(site, management_type, token_purpose)
entry = {
"site": site,
"managementType": management_type,
"tokenPurpose": token_purpose,
"accessToken": access_token,
"updatedAt": _utc_now(),
}
replaced = False
for i, item in enumerate(store["tokens"]):
if _token_key(
item.get("site", ""),
item.get("managementType", ""),
item.get("tokenPurpose", "default"),
) == key:
store["tokens"][i] = entry
replaced = True
break
if not replaced:
store["tokens"].append(entry)
_save_store(data)
return {
"storeKey": store_key,
"site": site,
"managementType": management_type,
"tokenPurpose": token_purpose,
"storePath": store_path(),
"updatedAt": entry["updatedAt"],
"replaced": replaced,
}
def get_token(
store_key: str,
site: str,
management_type: str,
token_purpose: str = "default",
):
data = _load_store()
store = _find_store(data, store_key)
if not store:
return None
key = _token_key(site, management_type, token_purpose)
for item in store.get("tokens", []):
if _token_key(
item.get("site", ""),
item.get("managementType", ""),
item.get("tokenPurpose", "default"),
) == key:
return item.get("accessToken")
return None
def list_stores(mask: bool = True) -> dict:
data = _load_store()
stores = []
for store in data.get("stores", []):
tokens = []
for item in store.get("tokens", []):
token = item.get("accessToken", "")
if mask and token:
shown = token[:6] + "..." + token[-4:] if len(token) > 12 else "***"
else:
shown = token
tokens.append(
{
"site": item.get("site"),
"managementType": item.get("managementType"),
"tokenPurpose": item.get("tokenPurpose", "default"),
"accessToken": shown,
"updatedAt": item.get("updatedAt"),
}
)
stores.append(
{
"storeKey": store.get("storeKey"),
"label": store.get("label"),
"tokens": tokens,
}
)
return {"storePath": store_path(), "stores": stores}
#!/usr/bin/env python3
"""
Check LinkFox user Token before calling Temu gateway APIs.
Same role as LINKFOXAGENT_API_KEY in linkfox-amazon-store-auth.
Usage:
python check_linkfox_token.py
python check_linkfox_token.py '{"token": "your-linkfox-key"}'
"""
import json
import sys
from _temu_common import PROXY_URL, call_temu_api, get_linkfox_token, is_linkfox_auth_error, load_json_arg
def main():
params = load_json_arg(sys.argv) if len(sys.argv) >= 2 else {}
linkfox_token = get_linkfox_token(params)
masked = (
linkfox_token[:6] + "..." + linkfox_token[-4:]
if len(linkfox_token) > 12
else "***"
)
# 最小探测请求:LinkFox Token 有效时应返回业务/参数类 1002,而非「无法识别当前用户」
probe_body = {
"site": "cn",
"managementType": "full-managed",
"accessToken": "probe",
"type": "probe",
}
result = call_temu_api(PROXY_URL, probe_body, timeout=30, linkfox_params=params)
if is_linkfox_auth_error(result):
print(
json.dumps(
{
"ok": False,
"reason": "linkfox_token_invalid",
"message": result.get("message") or result,
"hint": "Set LINKFOXAGENT_API_KEY or pass token in JSON.",
},
indent=2,
ensure_ascii=False,
)
)
sys.exit(1)
print(
json.dumps(
{
"ok": True,
"linkfoxToken": masked,
"gatewayReachable": True,
"note": "LinkFox user Token accepted by gateway.",
},
indent=2,
ensure_ascii=False,
)
)
if __name__ == "__main__":
main()
#!/usr/bin/env python3
"""
Get Temu accessToken from local store.
Usage:
python get_temu_access_token.py '{
"storeKey": "my-shop",
"site": "cn",
"managementType": "semi-managed",
"tokenPurpose": "product-inventory"
}'
"""
import json
import sys
from _temu_common import load_json_arg, require_text, validate_management_type, validate_site
from _temu_token_store import get_token
def main():
if len(sys.argv) < 2:
print("Usage: get_temu_access_token.py '<JSON parameters>'", file=sys.stderr)
sys.exit(1)
params = load_json_arg(sys.argv)
store_key = require_text(params, "storeKey")
site = validate_site(require_text(params, "site"))
management_type = validate_management_type(require_text(params, "managementType"))
token_purpose = str(params.get("tokenPurpose", "default")).strip() or "default"
token = get_token(store_key, site, management_type, token_purpose)
if not token:
print(
json.dumps(
{
"found": False,
"storeKey": store_key,
"site": site,
"managementType": management_type,
"tokenPurpose": token_purpose,
"hint": "Run temu_token_guide.py then save_temu_access_token.py",
},
indent=2,
ensure_ascii=False,
)
)
sys.exit(1)
print(
json.dumps(
{
"found": True,
"storeKey": store_key,
"site": site,
"managementType": management_type,
"tokenPurpose": token_purpose,
"accessToken": token,
},
indent=2,
ensure_ascii=False,
)
)
if __name__ == "__main__":
main()
#!/usr/bin/env python3
"""bg.arbok.open.cert.queryNeedUploadItems — 待上传资质项查询."""
import _global_compliance_script as m
if __name__ == "__main__":
m.run_cli(
"bg.arbok.open.cert.queryNeedUploadItems",
"global_compliance_arbok_cert_query_need_upload_items.py \'<JSON>\'",
)
#!/usr/bin/env python3
"""bg.arbok.open.cert.uploadProductCert — 上传商品资质."""
import _global_compliance_script as m
if __name__ == "__main__":
m.run_cli(
"bg.arbok.open.cert.uploadProductCert",
"global_compliance_arbok_cert_upload_product_cert.py \'<JSON>\'",
)
#!/usr/bin/env python3
"""bg.arbok.open.product.cert.query — 商品资质查询."""
import _global_compliance_script as m
if __name__ == "__main__":
m.run_cli(
"bg.arbok.open.product.cert.query",
"global_compliance_arbok_product_cert_query.py \'<JSON>\'",
)
#!/usr/bin/env python3
"""bg.arbok.open.upload.uploadFile — 合规文件上传."""
import _global_compliance_script as m
if __name__ == "__main__":
m.run_cli(
"bg.arbok.open.upload.uploadFile",
"global_compliance_arbok_upload_upload_file.py \'<JSON>\'",
)
#!/usr/bin/env python3
"""bg.compliance.edit — 合规编辑."""
import _global_compliance_script as m
if __name__ == "__main__":
m.run_cli(
"bg.compliance.edit",
"global_compliance_edit.py \'<JSON>\'",
)
#!/usr/bin/env python3
"""bg.flash.open.upload.real.image — 实拍图上传(Flash)."""
import _global_compliance_script as m
if __name__ == "__main__":
m.run_cli(
"bg.flash.open.upload.real.image",
"global_compliance_flash_upload_real_image.py \'<JSON>\'",
)
#!/usr/bin/env python3
"""bg.flash.open.upload.recognize — 上传识别(Flash)."""
import _global_compliance_script as m
if __name__ == "__main__":
m.run_cli(
"bg.flash.open.upload.recognize",
"global_compliance_flash_upload_recognize.py \'<JSON>\'",
)
#!/usr/bin/env python3
"""bg.goods.compliancelabel.get — 合规标签查询."""
import _global_compliance_script as m
if __name__ == "__main__":
m.run_cli(
"bg.goods.compliancelabel.get",
"global_compliance_goods_compliancelabel_get.py \'<JSON>\'",
)
#!/usr/bin/env python3
"""bg.compliance.metadata.get — 合规模板元数据查询."""
import _global_compliance_script as m
if __name__ == "__main__":
m.run_cli(
"bg.compliance.metadata.get",
"global_compliance_metadata_get.py \'<JSON>\'",
)
#!/usr/bin/env python3
"""
List locally saved Temu accessTokens (masked).
Usage:
python list_temu_access_tokens.py
python list_temu_access_tokens.py '{"mask": false}'
"""
import json
import sys
from _temu_token_store import list_stores
def main():
mask = True
if len(sys.argv) >= 2:
try:
params = json.loads(sys.argv[1])
mask = params.get("mask", True)
except json.JSONDecodeError as e:
print(f"Invalid parameter format: {e}", file=sys.stderr)
sys.exit(1)
print(json.dumps(list_stores(mask=mask), indent=2, ensure_ascii=False))
if __name__ == "__main__":
main()
#!/usr/bin/env python3
"""
Skill response I/O helper — wraps any main script to persist large API
responses to disk, then offers a `read` subcommand to extract specific fields
from those persisted files. Generic, business-agnostic.
This script is bundled into each skill's scripts/ directory by tools/response_io/sync.py.
The agent must pass --script <path> to identify which main script to execute.
Usage:
python scripts/response_io.py run --script <PATH> --out-dir <DIR> '<json_params>' [--label NAME] [--timeout SEC]
python scripts/response_io.py read <file> (--path "<JMESPath>" | --fields "f1,f2,...") [--limit N] [--offset M] [--format json|jsonl|csv|table]
"""
from __future__ import annotations
import sys
if sys.version_info < (3, 10):
sys.exit(
"Error: Python 3.10+ required (current: "
f"{sys.version_info.major}.{sys.version_info.minor}). "
"Please upgrade Python."
)
import argparse
import csv
import io
import json
import os
import re
import secrets
import subprocess
from datetime import datetime
from pathlib import Path
from typing import Any
# Force UTF-8 stdout/stderr so non-ASCII chars in previews and API responses
# print correctly on Windows (default cp936 / gbk).
for stream in (sys.stdout, sys.stderr):
try:
stream.reconfigure(encoding="utf-8") # type: ignore[attr-defined]
except (AttributeError, OSError):
pass
try:
import jmespath # type: ignore
HAS_JMESPATH = True
except ImportError:
HAS_JMESPATH = False
MAX_STRING_LEN = 120
MAX_DEPTH = 3
SAMPLE_KEY_CAP = 15
RAW_TEXT_PEEK = 500
DEFAULT_TIMEOUT_SEC = 300
# ---------------------------------------------------------------------------
# Shared helpers
# ---------------------------------------------------------------------------
def _err(msg: str, code: int = 1) -> None:
print(msg, file=sys.stderr)
sys.exit(code)
def _resolve_script(script_arg: str) -> Path:
p = Path(script_arg).expanduser()
if not p.is_absolute():
# Resolve relative to the current working directory the agent invoked from.
p = (Path.cwd() / p).resolve()
else:
p = p.resolve()
if not p.is_file():
_err(f"--script path not found: {p}")
return p
def _resolve_skill_name(main_script: Path) -> str:
"""Best-effort skill name extraction for filename prefixing.
main_script lives at <skill_dir>/scripts/<name>.py — return <skill_dir>'s
folder name. Fall back to the script's stem if structure differs.
"""
try:
if main_script.parent.name == "scripts":
return main_script.parents[1].name
except IndexError:
pass
return main_script.stem
def _sanitize_label(label: str) -> str:
"""Allow only safe filename chars in --label to prevent path traversal."""
cleaned = re.sub(r"[^\w\-]", "_", label)
return cleaned[:64] # cap length
def _truncate_string(s: str) -> str:
if len(s) <= MAX_STRING_LEN:
return s
return s[:MAX_STRING_LEN] + f"...(truncated, total {len(s)} chars)"
def _truncate_value(value: Any, depth: int = 0) -> Any:
"""Recursively truncate strings, deep nesting, and large arrays for preview."""
if depth >= MAX_DEPTH:
if isinstance(value, dict):
return f"<truncated nested object, keys: {list(value.keys())[:10]}>"
if isinstance(value, list):
return f"<truncated nested array, length: {len(value)}>"
if isinstance(value, str):
return _truncate_string(value)
return value
if isinstance(value, str):
return _truncate_string(value)
if isinstance(value, dict):
out = {k: _truncate_value(v, depth + 1) for k, v in value.items()}
return out
if isinstance(value, list):
if not value:
return []
truncated = [_truncate_value(value[0], depth + 1)]
if len(value) > 1:
# Note total length on the parent — keep the array type-homogeneous
# so downstream consumers can iterate without special-casing strings.
truncated.append({"_omitted_items": len(value) - 1})
return truncated
return value
def _shape_of(value: Any, top: bool = False) -> Any:
"""Lightweight schema description for the preview block."""
if isinstance(value, dict):
keys = list(value.keys())
out: dict[str, Any] = {"type": "object", "top_keys" if top else "keys": keys}
if top:
for k in keys[:8]:
out[k] = _shape_of(value[k])
return out
if isinstance(value, list):
out = {"type": "array", "length": len(value)}
if value and isinstance(value[0], dict):
out["item_keys"] = list(value[0].keys())
elif value:
out["item_type"] = type(value[0]).__name__
return out
return {"type": type(value).__name__}
def _build_sample(value: Any) -> Any:
"""First-record sample with explicit truncation marker."""
if isinstance(value, list):
if not value:
return {"_truncated_record": True, "_note": "array is empty"}
first = value[0]
if isinstance(first, dict):
sample = {"_truncated_record": True, "_note": f"first of {len(value)} items"}
sample.update(_truncate_value(first, depth=1))
return sample
return {"_truncated_record": True, "_note": f"first of {len(value)} items", "value": _truncate_value(first, depth=1)}
if isinstance(value, dict):
sample = {"_truncated_record": True, "_note": "top-level object (truncated)"}
sample.update(_truncate_value(value, depth=1))
return sample
return {"_truncated_record": True, "value": _truncate_value(value, depth=1)}
def _shrink_preview(preview: dict) -> dict:
"""Cap the sample's value fields when it has many keys.
`shape.*.item_keys` is the single source of truth for the full key list
(always complete, no truncation). The sample only ever shows up to
SAMPLE_KEY_CAP fields with their concrete values, since the agent only
needs a feel for value shapes — for the full menu of available fields,
they read `shape`.
"""
sample = preview.get("sample")
if isinstance(sample, dict):
meta_keys = {"_truncated_record", "_note"}
data_keys = [k for k in sample.keys() if k not in meta_keys]
if len(data_keys) > SAMPLE_KEY_CAP:
kept = data_keys[:SAMPLE_KEY_CAP]
new_sample = {k: v for k, v in sample.items() if k in meta_keys or k in kept}
base_note = sample.get("_note", "")
extra = (
f"showing first {SAMPLE_KEY_CAP} of {len(data_keys)} fields "
f"(see `shape` for the complete key list)"
)
new_sample["_note"] = f"{base_note}; {extra}" if base_note else extra
preview["sample"] = new_sample
return preview
# ---------------------------------------------------------------------------
# `run` subcommand
# ---------------------------------------------------------------------------
def cmd_run(args: argparse.Namespace) -> int:
main_script = _resolve_script(args.script)
skill_name = _resolve_skill_name(main_script)
out_dir = Path(args.out_dir).expanduser().resolve()
try:
out_dir.mkdir(parents=True, exist_ok=True)
except OSError as e:
_err(f"Failed to create --out-dir {out_dir}: {e}")
if not os.access(out_dir, os.W_OK):
_err(f"--out-dir is not writable: {out_dir}")
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
rand = secrets.token_hex(3)
safe_label = _sanitize_label(args.label) if args.label else ""
label_part = f"__{safe_label}" if safe_label else ""
out_file = out_dir / f"{skill_name}__{timestamp}_{rand}{label_part}.json"
# Force the child process to emit UTF-8 regardless of the host console
# encoding (Windows defaults to cp936 / gbk and would otherwise corrupt
# non-ASCII bytes when we read them back).
child_env = os.environ.copy()
child_env["PYTHONIOENCODING"] = "utf-8"
timed_out = False
try:
proc = subprocess.run(
[sys.executable, str(main_script), args.params],
capture_output=True,
text=True,
encoding="utf-8",
errors="replace",
env=child_env,
timeout=args.timeout,
)
stdout_text = proc.stdout or ""
stderr_text = proc.stderr or ""
returncode = proc.returncode
except subprocess.TimeoutExpired as e:
timed_out = True
stdout_text = (e.stdout.decode("utf-8", errors="replace") if isinstance(e.stdout, bytes) else (e.stdout or "")) or ""
stderr_text = (e.stderr.decode("utf-8", errors="replace") if isinstance(e.stderr, bytes) else (e.stderr or "")) or ""
returncode = 124 # convention for timeout
# Always write the captured stdout to disk, even if not JSON.
try:
out_file.write_text(stdout_text, encoding="utf-8")
except OSError as e:
_err(f"Failed to write output file {out_file}: {e}")
if stderr_text:
sys.stderr.write(stderr_text)
# Try to parse the captured stdout as JSON for the preview.
try:
parsed = json.loads(stdout_text) if stdout_text.strip() else None
format_kind = "json"
except json.JSONDecodeError:
parsed = None
format_kind = "raw_text"
preview: dict[str, Any] = {
"_preview": {
"is_preview": True,
"warning": (
"PREVIEW ONLY — NOT FULL DATA. The full response is saved to `file`. "
"Use `python scripts/response_io.py read <file> --fields '...'` to extract "
"specific fields, or `--path '<JMESPath>'` for complex projections."
),
},
}
# Surface failures prominently so agents don't mistake a stub preview for success.
if returncode != 0 or timed_out:
stderr_snippet = stderr_text[-500:] if stderr_text else ""
preview["_error"] = {
"exit_code": returncode,
"timed_out": timed_out,
"stderr_snippet": stderr_snippet,
"hint": "The wrapped script failed or timed out. The output file may be empty or partial.",
}
preview.update({
"file": str(out_file),
"size_bytes": out_file.stat().st_size,
"skill": skill_name,
"exit_code": returncode,
"format": format_kind,
"label": safe_label or None,
"next_steps_hint": (
"use: python scripts/response_io.py read <file> --fields '...' | --path '...'"
),
})
if format_kind == "json":
preview["shape"] = _shape_of(parsed, top=True)
preview["sample"] = _build_sample(parsed)
else:
peek = stdout_text[:RAW_TEXT_PEEK]
preview["raw_text_peek"] = peek
preview["raw_text_total_chars"] = len(stdout_text)
preview["sample"] = {
"_truncated_record": True,
"_note": f"stdout was not valid JSON; first {RAW_TEXT_PEEK} chars shown above in raw_text_peek",
}
preview = _shrink_preview(preview)
print(json.dumps(preview, ensure_ascii=False, indent=2))
return returncode
# ---------------------------------------------------------------------------
# `read` subcommand
# ---------------------------------------------------------------------------
def _load_json(path: Path) -> Any:
try:
text = path.read_text(encoding="utf-8")
except OSError as e:
_err(f"Failed to read file {path}: {e}")
try:
return json.loads(text)
except json.JSONDecodeError as e:
_err(f"File is not valid JSON: {path}\n{e}")
def _basic_dot_path(data: Any, path: str) -> Any:
"""Pure-stdlib dot-path resolver. No [*] support — callers fall back here only when jmespath is unavailable AND the path has no [*]."""
cur = data
for part in path.split("."):
if isinstance(cur, dict):
cur = cur.get(part)
else:
return None
return cur
def _resolve_field(data: Any, expr: str) -> Any:
if HAS_JMESPATH:
return jmespath.search(expr, data)
if "[" in expr or "*" in expr:
_err(
f"jmespath is required for expression '{expr}'. "
f"Install with: pip install jmespath"
)
return _basic_dot_path(data, expr)
def _project_fields(data: Any, fields: list[str]) -> Any:
"""Run each field expr; if any returns a list, zip them into list-of-dicts."""
resolved: dict[str, Any] = {f: _resolve_field(data, f) for f in fields}
list_lengths = [len(v) for v in resolved.values() if isinstance(v, list)]
if not list_lengths:
return resolved
# All list values must be same length to zip cleanly.
if len(set(list_lengths)) > 1:
# Fallback: return the dict as-is so caller can inspect mismatches.
return resolved
n = list_lengths[0]
rows = []
for i in range(n):
row = {}
for f, v in resolved.items():
row[f] = v[i] if isinstance(v, list) else v
rows.append(row)
return rows
def _apply_slice(value: Any, limit: int | None, offset: int | None) -> Any:
if not isinstance(value, list):
return value
start = offset or 0
end = (start + limit) if limit is not None else None
return value[start:end]
def _format_output(value: Any, fmt: str) -> str:
if fmt == "json":
return json.dumps(value, ensure_ascii=False, indent=2)
if fmt == "jsonl":
if isinstance(value, list):
return "\n".join(json.dumps(item, ensure_ascii=False) for item in value)
return json.dumps(value, ensure_ascii=False)
if fmt in ("csv", "table"):
if not isinstance(value, list) or not value:
_err(f"--format {fmt} requires a non-empty list result")
if not all(isinstance(item, dict) for item in value):
_err(f"--format {fmt} requires list-of-objects, got list of {type(value[0]).__name__}")
keys: list[str] = []
for item in value:
for k in item.keys():
if k not in keys:
keys.append(k)
if fmt == "csv":
buf = io.StringIO()
writer = csv.DictWriter(buf, fieldnames=keys, extrasaction="ignore")
writer.writeheader()
for item in value:
writer.writerow({k: _stringify(item.get(k)) for k in keys})
return buf.getvalue().rstrip("\n")
# table: simple aligned columns
rows = [[_stringify(item.get(k)) for k in keys] for item in value]
widths = [len(k) for k in keys]
for row in rows:
for i, cell in enumerate(row):
widths[i] = max(widths[i], len(cell))
lines = [
" ".join(k.ljust(widths[i]) for i, k in enumerate(keys)),
" ".join("-" * widths[i] for i in range(len(keys))),
]
for row in rows:
lines.append(" ".join(row[i].ljust(widths[i]) for i in range(len(keys))))
return "\n".join(lines)
_err(f"Unknown --format: {fmt}")
return "" # unreachable
def _stringify(v: Any) -> str:
if v is None:
return ""
if isinstance(v, (dict, list)):
return json.dumps(v, ensure_ascii=False)
return str(v)
def cmd_read(args: argparse.Namespace) -> int:
if not args.path and not args.fields:
_err("read: either --path or --fields is required")
if args.path and args.fields:
_err("read: --path and --fields are mutually exclusive")
file_path = Path(args.file).expanduser().resolve()
data = _load_json(file_path)
if args.path:
result = _resolve_field(data, args.path)
else:
fields = [f.strip() for f in args.fields.split(",") if f.strip()]
if not fields:
_err("--fields parsed to empty list")
result = _project_fields(data, fields)
result = _apply_slice(result, args.limit, args.offset)
print(_format_output(result, args.format))
return 0
# ---------------------------------------------------------------------------
# CLI
# ---------------------------------------------------------------------------
def main() -> int:
parser = argparse.ArgumentParser(
prog="response_io.py",
description="Persist large skill API responses to disk and read fields on demand.",
)
sub = parser.add_subparsers(dest="cmd", required=True)
p_run = sub.add_parser(
"run",
help="Execute a main script and persist its stdout to a file; "
"print only a lightweight preview to stdout.",
)
p_run.add_argument("params", help="JSON params string passed verbatim to the main script (argv[1]).")
p_run.add_argument("--script", required=True, help="Path to the main script to execute, e.g. scripts/my_api.py")
p_run.add_argument("--out-dir", required=True, help="Directory to write the response file into (created if missing).")
p_run.add_argument("--label", default=None, help="Optional filename suffix; sanitized to safe filename characters.")
p_run.add_argument("--timeout", type=int, default=DEFAULT_TIMEOUT_SEC, help=f"Subprocess timeout in seconds (default: {DEFAULT_TIMEOUT_SEC}).")
p_run.set_defaults(func=cmd_run)
p_read = sub.add_parser(
"read",
help="Extract specific fields from a previously persisted response file.",
)
p_read.add_argument("file", help="Path to the persisted JSON response file.")
g = p_read.add_mutually_exclusive_group()
g.add_argument("--path", default=None, help="JMESPath expression, e.g. 'data[*].{asin: asin, title: title}'.")
g.add_argument("--fields", default=None, help="Comma-separated field paths, e.g. 'data[*].asin,data[*].title'.")
p_read.add_argument("--limit", type=int, default=None, help="Take at most N items (when result is a list).")
p_read.add_argument("--offset", type=int, default=None, help="Skip the first M items (when result is a list).")
p_read.add_argument("--format", choices=["json", "jsonl", "csv", "table"], default="json", help="Output format (default: json).")
p_read.set_defaults(func=cmd_read)
args = parser.parse_args()
return args.func(args)
if __name__ == "__main__":
sys.exit(main())
#!/usr/bin/env python3
"""
Save Temu accessToken to local store for reuse in proxy/fileDownload calls.
Usage:
python save_temu_access_token.py '{
"storeKey": "my-shop",
"site": "cn",
"managementType": "semi-managed",
"tokenPurpose": "product-inventory",
"accessToken": "PASTE_TOKEN_HERE",
"label": "中国半托管主店"
}'
"""
import json
import sys
from _temu_common import load_json_arg, require_text, validate_management_type, validate_site
from _temu_token_store import save_token
def main():
if len(sys.argv) < 2:
print("Usage: save_temu_access_token.py '<JSON parameters>'", file=sys.stderr)
sys.exit(1)
params = load_json_arg(sys.argv)
store_key = require_text(params, "storeKey")
site = validate_site(require_text(params, "site"))
management_type = validate_management_type(require_text(params, "managementType"))
access_token = require_text(params, "accessToken")
token_purpose = str(params.get("tokenPurpose", "default")).strip() or "default"
label = params.get("label")
result = save_token(
store_key=store_key,
site=site,
management_type=management_type,
access_token=access_token,
token_purpose=token_purpose,
label=label,
)
print(json.dumps(result, indent=2, ensure_ascii=False))
if __name__ == "__main__":
main()
#!/usr/bin/env python3
"""
Temu Signed File Download - LinkFox Skill
Downloads Temu signed resource files via /temu/fileDownload.
Usage:
python temu_file_download.py '{
"site": "us",
"managementType": "semi-managed",
"accessToken": "your_temu_access_token",
"url": "https://example.com/signed-file-url"
}'
"""
import json
import sys
from _temu_common import (
FILE_DOWNLOAD_URL,
call_temu_api,
load_json_arg,
require_text,
resolve_access_token,
validate_management_type,
validate_site,
)
def build_request(params: dict) -> dict:
site = validate_site(require_text(params, "site"))
management_type = validate_management_type(
require_text(params, "managementType")
)
access_token = resolve_access_token(params)
url = require_text(params, "url")
return {
"site": site,
"managementType": management_type,
"accessToken": access_token,
"url": url,
}
def main():
if len(sys.argv) < 2:
print("Usage: temu_file_download.py '<JSON parameters>'", file=sys.stderr)
print(
'Example: temu_file_download.py \'{"site":"us","managementType":"semi-managed",'
'"accessToken":"TOKEN","url":"https://example.com/signed-file-url"}\'',
file=sys.stderr,
)
sys.exit(1)
params = load_json_arg(sys.argv)
body = build_request(params)
result = call_temu_api(FILE_DOWNLOAD_URL, body, timeout=120, linkfox_params=params)
print(json.dumps(result, indent=2, ensure_ascii=False))
if __name__ == "__main__":
main()
#!/usr/bin/env python3
"""US site file download via /temu/fileDownload (default site=global)."""
import json
import sys
from _temu_common import load_json_arg
from _temu_global_common import DEFAULT_SITE, global_file_download_call
def main():
if len(sys.argv) < 2:
print(
'Usage: temu_global_file_download.py \'{"accessToken":"...","url":"https://..."}\'',
file=sys.stderr,
)
sys.exit(1)
params = load_json_arg(sys.argv)
params.setdefault("site", DEFAULT_SITE)
result = global_file_download_call(params)
print(json.dumps(result, indent=2, ensure_ascii=False))
if __name__ == "__main__":
main()
#!/usr/bin/env python3
"""
Temu Global product API generic proxy (site=global, semi-managed by default).
Usage:
python temu_global_proxy.py '{
"accessToken": "TEMU_US_TOKEN",
"type": "temu.goods.list.get",
"params": {"page": 1, "pageSize": 20}
}'
"""
import json
import sys
from _temu_common import load_json_arg, parse_nested_body, require_text
from _temu_global_common import DEFAULT_SITE, global_proxy_call
def main():
if len(sys.argv) < 2:
print("Usage: temu_global_proxy.py '<JSON>'", file=sys.stderr)
sys.exit(1)
params = load_json_arg(sys.argv)
params.setdefault("site", DEFAULT_SITE)
api_type = require_text(params, "type")
result = parse_nested_body(global_proxy_call(params, api_type))
print(json.dumps(result, indent=2, ensure_ascii=False))
if __name__ == "__main__":
main()
#!/usr/bin/env python3
"""
Temu API Proxy - LinkFox Skill
Forwards Temu platform API requests via /temu/proxy.
Usage:
export LINKFOXAGENT_API_KEY="<linkfox-user-token>"
python temu_proxy.py '{
"site": "cn",
"managementType": "full-managed",
"accessToken": "your_temu_access_token",
"type": "bg.goods.category.mapping",
"params": {"goodsName": "测试商品", "goodsNameEn": "Test Product"}
}'
"""
import json
import sys
from _temu_common import (
PROXY_URL,
call_temu_api,
load_json_arg,
parse_nested_body,
require_text,
resolve_access_token,
validate_management_type,
validate_site,
)
def build_request(params: dict) -> dict:
site = validate_site(require_text(params, "site"))
management_type = validate_management_type(
require_text(params, "managementType")
)
access_token = resolve_access_token(params)
api_type = require_text(params, "type")
body = {
"site": site,
"managementType": management_type,
"accessToken": access_token,
"type": api_type,
}
if "params" in params and params["params"] is not None:
if not isinstance(params["params"], dict):
print("Error: 'params' must be a JSON object.", file=sys.stderr)
sys.exit(1)
body["params"] = params["params"]
return body
def main():
if len(sys.argv) < 2:
print("Usage: temu_proxy.py '<JSON parameters>'", file=sys.stderr)
print(
'Example: temu_proxy.py \'{"site":"cn","managementType":"full-managed",'
'"accessToken":"TOKEN","type":"bg.goods.category.mapping",'
'"params":{"goodsName":"测试","goodsNameEn":"Test"}}\'',
file=sys.stderr,
)
sys.exit(1)
params = load_json_arg(sys.argv)
body = build_request(params)
result = call_temu_api(PROXY_URL, body, linkfox_params=params)
print(json.dumps(parse_nested_body(result), indent=2, ensure_ascii=False))
if __name__ == "__main__":
main()
#!/usr/bin/env python3
"""
Temu accessToken authorization guide - LinkFox Skill
Returns step-by-step instructions from TEMU_API_README.md by shop type.
Usage:
python temu_token_guide.py '{"shopType":"semi-managed","tokenPurpose":"product-inventory","site":"cn"}'
python temu_token_guide.py '{"shopType":"semi-managed","tokenPurpose":"order-shipping","site":"us"}'
python temu_token_guide.py '{"shopType":"full-managed"}'
python temu_token_guide.py '{"shopType":"local-native"}'
"""
import json
import sys
from _temu_auth_guide import build_guide
def main():
if len(sys.argv) < 2:
print("Usage: temu_token_guide.py '<JSON parameters>'", file=sys.stderr)
print(
'Example: temu_token_guide.py \'{"shopType":"semi-managed",'
'"tokenPurpose":"product-inventory","site":"cn"}\'',
file=sys.stderr,
)
sys.exit(1)
try:
params = json.loads(sys.argv[1])
except json.JSONDecodeError as e:
print(f"Invalid parameter format: {e}", file=sys.stderr)
sys.exit(1)
shop_type = params.get("shopType") or params.get("managementType")
if not shop_type:
print("Error: 'shopType' is required.", file=sys.stderr)
sys.exit(1)
token_purpose = params.get("tokenPurpose", "default")
site = params.get("site")
try:
guide = build_guide(shop_type, token_purpose, site)
except ValueError as e:
print(f"Error: {e}", file=sys.stderr)
sys.exit(1)
print(json.dumps(guide, indent=2, ensure_ascii=False))
if __name__ == "__main__":
main()