10 KiB
name, description, version, author, license, metadata
| name | description | version | author | license | metadata | |||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| miaoshou-erp | Miaoshou (妙手) ERP operations: cron auto-import from AtomK, product import via Excel upload, and collect box management. | 1.2.0 | Hermes Agent | MIT |
|
Miaoshou (妙手) ERP
Three workflows for Miaoshou ERP: cron auto-import from AtomK, batch product import via Excel upload, and exploring/managing the collect box (通用采集箱).
Section 1: Batch Product Import
Automate batch product import from AtomK to Miaoshou ERP via Excel upload.
When to use: Importing large numbers of products from AtomK into Miaoshou for management.
Key patterns:
- Generate Excel file from AtomK product data
- Upload to Miaoshou via UI (or API)
- Handle validation errors and duplicate detection
- Map product fields between AtomK and Miaoshou formats
See: references/miaoshou-batch-import.md for full import workflow.
Section 2: Collect Box Management
Explore and interact with Miaoshou ERP's "通用采集箱" (Common Collect Box) — URLs, collection methods, import paths, and login automation.
When to use: Adding products to the collect box, checking collected items, importing from collect box to formal listings.
Key topics:
- Collect box URL patterns and API endpoints
- Collection methods (URL import, manual add, API)
- Import from collect box to product listings
- Login automation for Miaoshou
See: references/miaoshou-collect-box.md for full collect box guide.
Section 3: Cron Auto-Import (AtomK → Miaoshou)
The production auto-import pipeline runs hourly via a no_agent Hermes cron job that invokes a wrapper shell script. The wrapper appends timestamp headers to the cron log, runs the Python import script, and appends an exit-code footer. No LLM session is spawned — the script executes directly, completing in ~3 seconds rather than 3-5 minutes.
Cron job: atomk-miaoshou-hourly (schedule: 0 * * * *, no_agent=true)
Wrapper script: /home/ubuntu/.hermes/scripts/miaoshou_hourly.sh
Main script: /home/ubuntu/auto_miaoshou_import.py
Python: /home/ubuntu/.hermes/hermes-agent/venv/bin/python3
Working dir: /home/ubuntu
Why no_agent (Not LLM-Driven)
The cron job was previously LLM-driven (Agent mode). Each run spawned a full LLM session that loaded skills, ran terminal commands, read files, and produced a report — consuming 3-5 minutes of cron queue time per hour. Since Hermes cron runs jobs sequentially in FIFO order, this caused massive queue congestion: ad-hoc cron jobs got delayed by 17+ hours (scheduled 19:16, queued to 13:08 next day).
The no_agent wrapper script eliminates the LLM overhead entirely. See references/cron-queue-pitfalls.md for the full diagnosis and fix.
Key Files
| File | Purpose |
|---|---|
/home/ubuntu/.hermes/scripts/miaoshou_hourly.sh |
Cron wrapper: timestamp header → run script → exit-code footer |
/home/ubuntu/auto_miaoshou_import.py |
Main import script (441 lines) |
/home/ubuntu/.hermes/miaoshou_import_state.json |
Tracks which AtomK products have been imported (cumulative count + IDs) |
/home/ubuntu/.hermes/cookies/miaoshou_cookies.json |
Miaoshou session cookies for browser automation |
/home/ubuntu/.hermes/miaoshou_import.log |
Script's own internal log (Python log() writes here) |
/home/ubuntu/.hermes/miaoshou_cron.log |
Cron wrapper log (stdout/stderr + timestamp headers) |
/home/ubuntu/.hermes/credentials.env |
Centralized credentials (chmod 600) — read by atomk_credentials.py |
/home/ubuntu/.hermes/scripts/atomk_credentials.py |
Python credential loader — provides get_atomk_auth(), get_miaoshou_auth(), etc. |
Two-Log Architecture
The script logs to TWO separate files — this is important when diagnosing runs:
miaoshou_import.log— the script's Pythonlog()function writes here. Contains the actual import narrative (products found, imported, skipped). This is the authoritative record.miaoshou_cron.log— the cron wrapper appends stdout/stderr plus=== TIMESTAMP ===headers and=== END (exit code: N) ===footers. If the script produces no stdout (all output goes tolog()), the cron log only contains the header/footer markers.
Normal Behavior
- BATCH_SIZE = 6 per run (hourly)
- On each run: authenticates to AtomK API → fetches pending products → filters already-imported → opens Miaoshou via DrissionPage → imports each product to collect box
- If AtomK has 0 new products: script prints "没有新产品需要导入,本次跳过" and exits 0 (this is normal, not an error)
- Cumulative import count is displayed at end:
📊 累计已导入: N 个产品
Running Manually
Direct run (for debugging — stdout visible):
cd /home/ubuntu && /home/ubuntu/.hermes/hermes-agent/venv/bin/python3 /home/ubuntu/auto_miaoshou_import.py
Wrapper script (same as cron):
/home/ubuntu/.hermes/scripts/miaoshou_hourly.sh
Production cron pattern (for Linux crontab — NOT for Hermes terminal()):
cd /home/ubuntu && { echo ""; echo "=== $(date '+%Y-%m-%d %H:%M:%S') ==="; /home/ubuntu/.hermes/hermes-agent/venv/bin/python3 /home/ubuntu/auto_miaoshou_import.py 2>&1; RC=$?; echo "=== END (exit code: $RC) ==="; } >> /home/ubuntu/.hermes/miaoshou_cron.log
Cron Job Management
# View current state
cronjob(action='list')
# The job is stored in /home/ubuntu/.hermes/cron/jobs.json
# Job ID: eee9ab11fbfc, name: atomk-miaoshou-hourly
# no_agent=true, script=miaoshou_hourly.sh
# To revert to LLM-driven (not recommended — causes queue congestion):
# cronjob(action='update', job_id='eee9ab11fbfc', no_agent=false, script='')
Pitfalls
-
Empty stdout — the script logs internally via
log()tomiaoshou_import.log, not to stdout. When debugging, check BOTH log files. -
Cookie expiry — Miaoshou cookies in
miaoshou_cookies.jsoncan expire; the script uses DrissionPage which may need fresh login if cookies are stale. -
Chromium path — hardcoded to
/home/ubuntu/.cache/ms-playwright/chromium-1217/chrome-linux64/chrome; must match installed Playwright Chromium version. -
AtomK auth — credentials are loaded from
~/.hermes/credentials.envviaatomk_credentials.get_atomk_auth(). If the script fails withKeyError, the credential file hasFILL_MEplaceholders that need real values. Seecredential-managementskill for the full pattern. -
Cron log race condition — the hourly cron and manual runs both append to
miaoshou_cron.log. Splitting the timestamp header and script execution across separateterminal()calls creates a gap where the hourly cron can interleave its write, causing the manual run's output to be silently dropped with no error. If log output appears missing, verify withstat -c '%y'that the file's mtime matches when the run should have completed. -
{ }compound command unreliable viaterminal()— the{ }grouping with>>redirect can fail silently when passed to Hermesterminal(). Observed: exit code 0 returned but no output appended to the log file, no script execution, and file mtime unchanged. For Hermes-triggered runs, use a two-step approach instead:Step 1: Run the script directly and capture output:
cd /home/ubuntu && /home/ubuntu/.hermes/hermes-agent/venv/bin/python3 /home/ubuntu/auto_miaoshou_import.py 2>&1; echo "EXIT_CODE=$?"The
terminal()response contains the script's full stdout (including timestamped log lines), plusEXIT_CODE=N.Step 2: Append header, captured output, and footer to cron log:
printf '\n=== %s ===\n' "$(date '+%Y-%m-%d %H:%M:%S')" >> /home/ubuntu/.hermes/miaoshou_cron.logThis two-step approach avoids the
{ }+>>issue. -
LLM-driven cron causes queue congestion — Hermes cron runs jobs sequentially in FIFO order. An LLM-driven job that takes 3-5 minutes blocks all subsequent jobs. When ad-hoc cron jobs are created during active sessions, they queue behind every pending hourly/scheduled job. Always use
no_agentwith a wrapper script for frequent recurring tasks. Seereferences/cron-queue-pitfalls.mdfor the full diagnosis.
Section 4: Bridge CDP Navigation & Interaction
When using Bridge CDP to control the user's desktop browser:
Login URL
- Correct:
https://erp.91miaoshou.com/— main page, auto-redirects to/welcomeif logged in - Wrong:
/auth/login— returns 404「页面未找到」. Do not use. - If not logged in, the main page will show the login form.
Navigation structure
- Top nav:
ul.nav-list.earthwith<li id="goods">for products,<li id="order">for orders - Click
#goods→ navigates to/common_collect_box/index - Left sidebar:
<span>text nodes (not<a>links). Match byinnerText.trim()
Collect box tabs
| Tab | URL | Purpose |
|---|---|---|
| 产品采集 | /common_collect_box/index |
Link import, bulk import |
| 公用采集箱 | /common_collect_box/items |
Collected products list |
| AI工作台 | AI tools | |
| 侵权检测 | Infringement check |
Link import (链接采集)
- Click "链接采集" span tab
- Fill
textarea.jx-textarea__innerwith 1688 URL - Acceptable formats:
URL,URL$$title,URL||title - Click "采集并自动认领" to collect
1688 authorization blocker
Collecting 1688 products requires 1688 account authorization first. A dialog appears: "应1688要求,请授权1688货源账号" with buttons "查看帮助教程" / "前往授权". User must complete "前往授权" manually.
"我知道了" dismiss
A tooltip with "我知道了" button sits near the textarea (x≈1200,y≈200). If collect button click has no effect, dismiss this tip first.
JS click() fallback
For Vue/Element UI components where element.click() is ignored, use
Bridge CDP Input.dispatchMouseEvent via /cdp/send to send raw mouse events.