Add cross-border-ecommerce/miaoshou-erp
This commit is contained in:
@@ -0,0 +1,183 @@
|
|||||||
|
---
|
||||||
|
name: miaoshou-erp
|
||||||
|
description: "Miaoshou (妙手) ERP operations: cron auto-import from AtomK, product import via Excel upload, and collect box management."
|
||||||
|
version: 1.2.0
|
||||||
|
author: Hermes Agent
|
||||||
|
license: MIT
|
||||||
|
metadata:
|
||||||
|
hermes:
|
||||||
|
tags: [miaoshou, erp, cross-border, e-commerce, product-import, collect-box]
|
||||||
|
related_skills: [atomk-platform, tongtool-workflows, credential-management]
|
||||||
|
---
|
||||||
|
|
||||||
|
# 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 Python `log()` 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 to `log()`), 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):
|
||||||
|
```bash
|
||||||
|
cd /home/ubuntu && /home/ubuntu/.hermes/hermes-agent/venv/bin/python3 /home/ubuntu/auto_miaoshou_import.py
|
||||||
|
```
|
||||||
|
|
||||||
|
**Wrapper script** (same as cron):
|
||||||
|
```bash
|
||||||
|
/home/ubuntu/.hermes/scripts/miaoshou_hourly.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
**Production cron pattern** (for Linux crontab — NOT for Hermes `terminal()`):
|
||||||
|
```bash
|
||||||
|
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
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 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
|
||||||
|
|
||||||
|
1. **Empty stdout** — the script logs internally via `log()` to `miaoshou_import.log`, not to stdout. When debugging, check BOTH log files.
|
||||||
|
2. **Cookie expiry** — Miaoshou cookies in `miaoshou_cookies.json` can expire; the script uses DrissionPage which may need fresh login if cookies are stale.
|
||||||
|
3. **Chromium path** — hardcoded to `/home/ubuntu/.cache/ms-playwright/chromium-1217/chrome-linux64/chrome`; must match installed Playwright Chromium version.
|
||||||
|
4. **AtomK auth** — credentials are loaded from `~/.hermes/credentials.env` via `atomk_credentials.get_atomk_auth()`. If the script fails with `KeyError`, the credential file has `FILL_ME` placeholders that need real values. See `credential-management` skill for the full pattern.
|
||||||
|
5. **Cron log race condition** — the hourly cron and manual runs both append to `miaoshou_cron.log`. Splitting the timestamp header and script execution across separate `terminal()` 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 with `stat -c '%y'` that the file's mtime matches when the run should have completed.
|
||||||
|
|
||||||
|
6. **`{ }` compound command unreliable via `terminal()`** — the `{ }` grouping with `>>` redirect can **fail silently** when passed to Hermes `terminal()`. 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:
|
||||||
|
```bash
|
||||||
|
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), plus `EXIT_CODE=N`.
|
||||||
|
|
||||||
|
**Step 2:** Append header, captured output, and footer to cron log:
|
||||||
|
```bash
|
||||||
|
printf '\n=== %s ===\n' "$(date '+%Y-%m-%d %H:%M:%S')" >> /home/ubuntu/.hermes/miaoshou_cron.log
|
||||||
|
```
|
||||||
|
|
||||||
|
This two-step approach avoids the `{ }` + `>>` issue.
|
||||||
|
|
||||||
|
7. **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_agent` with a wrapper script for frequent recurring tasks.** See `references/cron-queue-pitfalls.md` for 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 `/welcome` if 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.earth` with `<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 by `innerText.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 (链接采集)
|
||||||
|
1. Click "链接采集" span tab
|
||||||
|
2. Fill `textarea.jx-textarea__inner` with 1688 URL
|
||||||
|
3. Acceptable formats: `URL`, `URL$$title`, `URL||title`
|
||||||
|
4. 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.
|
||||||
Reference in New Issue
Block a user