| name | qianniu-shared |
| description | Common Taobao/Tmall 千牛 (Qianniu) seller mechanics — chrome-backend login (QR scan or slider-captcha + SMS is a HUMAN step; NO auto-captcha), wrong-account guard (verify the logged-in 店铺名 matches the bound store, else ask), promo/公告/安全提示 popup detect-and-close on every page load, the 千牛 / 生意参谋 (sycm) / 万相台无界 (one.alimama.com) page map + SSO, per-store download dir, and GB18030 CSV caveat. Prerequisite: every other qianniu-* skill (qianniu-listing, qianniu-ads) expects this loaded for auth + navigation. Load before any browser-use action on a Taobao/Tmall (千牛) store. |
Qianniu (Taobao/Tmall) — Shared (auth, navigation, common patterns)
What every 千牛 seller task needs: the workbench login (chrome backend,
human-in-the-loop), the 生意参谋 / 万相台无界 page map + SSO, the per-store
download directory, and the CSV encoding caveat. Operation-specific skills
(qianniu-listing, qianniu-ads) load this first.
These stores run on the chrome browser backend (not Ziniao) — check
stores.browser_backend. There is no auto-captcha here: the slider
captcha and SMS are HUMAN steps (§ 2).
browser-use 0.13 CLI (heredoc). Drive the wrapper by piping a Python
snippet: ~/.vibe-seller/bin/<slug>/browser-use <<'PY' … PY. Pre-imported
helpers: new_tab(url) (first navigation, not goto), page_info(),
js("<javascript>") (runs JS in the page, returns the value; pierce open
shadow roots via .shadowRoot), click_at_xy(x, y), capture_screenshot(path),
wait_for_load(), cdp("Domain.method", ...) (raw CDP). See the
browser-harness skill. Gotcha: cdp(...) / click_at_xy(...) are
Python helpers in the heredoc scope — do NOT call them inside a
js("…") string (that runs in the page, where they're undefined).
First-run on a fresh box: the chrome backend needs Playwright's headed
Chromium ("Chrome for Testing"). If browser/start fails with
"Executable doesn't exist at …/chromium-/…Google Chrome for Testing",
install it once: .venv/bin/python3 -m playwright install chromium.
1. Surfaces & URLs
| Surface | URL | Notes |
|---|
| 千牛工作台 (workbench home) | https://myseller.taobao.com/ | redirects to loginmyseller.taobao.com when logged out |
| 商品管理 (listings) | https://myseller.taobao.com/home.htm/SellManage/on_sale | tabs: 全部 / 出售中 / 仓库中 / 预售 / 已售空 / 违规 |
| 生意参谋 (Business Advisor) | https://sycm.taobao.com/ | retail big-data; product/traffic/transaction reports |
| 万相台无界 (Alimama, ad platform) | https://one.alimama.com/index.html | hash-route SPA #!/...; ad plans + 报表 live here |
生意参谋 and 万相台无界 are separate apps but share the 千牛 login
session (SSO). Once 千牛 is logged in, opening sycm.taobao.com or
one.alimama.com does not need a second login. If the session is dead
they redirect to the Taobao login — that is a human step (§ 2).
2. Login (chrome backend — human-in-the-loop)
There is no auto-captcha. Two human paths; QR is simplest:
QR scan (preferred). Open https://myseller.taobao.com/, run the
popup-close loop (§ 4), switch the login card to 扫码登录 (click the QR
toggle if it opens on 密码登录), then ask the human to scan with the
手机淘宝/千牛 app and wait until the workbench home renders.
~/.vibe-seller/bin/<slug>/browser-use <<'PY'
import time
new_tab("https://myseller.taobao.com/")
time.sleep(6)
print("url:", js("return location.href"))
capture_screenshot("/tmp/qn_login.png")
PY
Then hand off with AskUserQuestion ("scan the QR / complete slider+SMS in
the visible window, then continue"). Never drag the slider
programmatically — Taobao flags automated drags and burns attempts.
Password login (fallback) fills the havanalogin.taobao.com iframe
(#fm-login-id, #fm-login-password, button[type=submit]) via js(...),
but still lands on the slider + SMS human step. Credentials, when used, come
from the store profile (stores/<slug>/STORE.md), format 店铺名:子账号名.
The chrome profile persists at ~/.vibe-seller/browser_profiles/<slug>/,
so login survives restarts — re-login is only needed when the session expires.
2.1 Wrong-account guard ⚠️ HARD GATE
The chrome profile persists, and a human may have left the window logged into
the wrong seller account. Data pulled from the wrong account is silently
wrong. Before any read/export, confirm the active 店铺名 matches the bound
store. The workbench header renders the sub-account as 店铺名:子账号名.
~/.vibe-seller/bin/<slug>/browser-use <<'PY'
print(js(r"""
var t=(document.body.innerText||'').replace(/ /g,' ');
var m=t.match(/([一-龥A-Za-z0-9]{2,40})[::]([一-龥A-Za-z0-9_]{1,24})/g)||[];
return JSON.stringify(m.slice(0,12));
"""))
PY
Compare the extracted 店铺名 (before the :/:) to the bound store's
店铺名. If it does not match, do NOT proceed — surface via
AskUserQuestion (human decision; never auto-continue). Never write the
detected 店铺名 into code, commits, or PRs — it is store-identifying data.
3. Headed vs headless (so a human can log in / scan)
Wrapper-managed — NEVER pass --headed/--headless, never close+reopen
to "switch to headed". Window visibility is decided by the backend:
chrome (mac/Linux Playwright) is visible when a display exists; if it's
invisible, an admin flips browser_headless=false and restarts the store
browser (see debug-store). Just open the login page and have the human scan.
4. Promo / 公告 / 安全提示 popups — close on sight ⚠️
千牛, the login pages, 生意参谋, and 万相台 pop ad / 活动 / 公告 / 安全提示
modals on load that eat clicks (an element that won't respond usually
has an invisible overlay on top). Standing rule: after every page load or
route change on a Taobao/千牛 domain, run the detect-and-close loop before
any other interaction, and re-run whenever a click doesn't register.
Close via X / 关闭 / 知道了 / 我知道了 / 稍后 only — NEVER click a promo CTA
(开通 / 立即开通 / 升级 / 立即参加 / 领取 / 报名 / 马上抢 / 一键…): those
subscribe the store to a paid service or join an activity (a write — out
of scope unless the user asked).
~/.vibe-seller/bin/<slug>/browser-use <<'PY'
print(js(r"""
var closed=0;
document.querySelectorAll('.baxia-dialog-close,.next-dialog-close,.next-overlay-wrapper .next-icon-close').forEach(function(e){if(e.offsetParent){e.click();closed++}});
var BAD=/开通|升级|参加|报名|抢|领取|去逛|立即|马上|一键|体验|试用/;
var OK=/^(知道了|我知道了|关闭|稍后再说|稍后|以后再说|不再提示|跳过|取消|×|✕|✖|x|X)$/;
document.querySelectorAll('[class*=dialog] *,[class*=modal] *,[class*=popup] *,[class*=Dialog] *,[class*=Modal] *').forEach(function(e){
if(!e.offsetParent||e.children.length)return;
var t=(e.innerText||e.getAttribute('aria-label')||'').trim();
if(OK.test(t)&&!BAD.test(t)){e.click();closed++}
});
return 'closed '+closed;
"""))
PY
- Loop until clean (re-run until it returns
closed 0, cap ~4 rounds) —
closing one modal can reveal another.
- Verify by re-detecting, not by screenshot (a background tab can show a
stale frame). If a synthetic
.click() won't dismiss it, dispatch a trusted
CDP click at the X's center (cdp('Input.dispatchMouseEvent', ...)). If a
blocking dialog exposes no close affordance, js("location.reload()") and
re-run.
5. Per-store download dir & CSV encoding
Exports (Excel/CSV) the browser downloads land in the vibe-seller-monitored
per-store dir ~/.vibe-seller/downloads/<slug>/ (list newest first with
ls -lt). Save any live capture (exports, screenshots, scraped rows) under
/tmp/<task>/ — never under ~/.vibe-seller/knowledge/ (curated facts
only) or stores/ (per the capture rule).
CSV encoding: Taobao/Tmall CSV exports are GB18030, not UTF-8.
Decode with encoding='gb18030' (pandas/open) or headers/values render
as mojibake. Excel (.xlsx) exports are fine as-is.
6. Common "always load X first" pattern
Every qianniu-* operation skill lists requires: [qianniu-shared] and opens
by loading this for login + the page map + the popup-close rule. Load the
operation skill for the actual task (listing round trip, ads audit, reports).
See also
qianniu-listing — CRUD listings via the Excel bulk round trip.
qianniu-ads — 万相台 report export (analysis) + plan CRUD (review-first).
browser-harness — the browser-use 0.13 heredoc CLI + helper reference.