@whyshu/argent-engine 0.6.0 → 0.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (67) hide show
  1. package/MANIFEST.json +63 -29
  2. package/engine/VERSION +1 -1
  3. package/engine/core/contracts/argent.contracts.json +1 -1
  4. package/engine/scripts/base-bootstrap.sh +2 -1
  5. package/engine/scripts/billing-mode-daily.sh +2 -1
  6. package/engine/scripts/build-engine-package.mjs +5 -2
  7. package/engine/scripts/capability-packs.lock.json +25 -25
  8. package/engine/scripts/check-billing-mode.sh +2 -1
  9. package/engine/scripts/collect-evidence.mjs +42 -0
  10. package/engine/scripts/customer-readiness.sh +2 -1
  11. package/engine/scripts/daily-report.sh +2 -1
  12. package/engine/scripts/dev-window.sh +2 -1
  13. package/engine/scripts/final-check.mjs +43 -0
  14. package/engine/scripts/flow-requirements.json +24 -9
  15. package/engine/scripts/flow-timers-plist.sh +2 -1
  16. package/engine/scripts/flow-timers.sh +3 -1
  17. package/engine/scripts/flow_config.py +2 -1
  18. package/engine/scripts/init-ml.sh +5 -2
  19. package/engine/scripts/invoice-table.sh +2 -1
  20. package/engine/scripts/lib/argent-datadir.sh +18 -0
  21. package/engine/scripts/lib/argent_datadir.py +23 -0
  22. package/engine/scripts/poll-fulfillment.sh +4 -2
  23. package/engine/scripts/poll-invoice.sh +4 -2
  24. package/engine/scripts/provider-select.sh +2 -1
  25. package/engine/scripts/release-argent.sh +1 -1
  26. package/engine/scripts/retire-inventory.mjs +40 -0
  27. package/engine/scripts/run-tests.sh +2 -1
  28. package/engine/scripts/s1-audit.mjs +85 -0
  29. package/engine/scripts/usage-anchor.sh +3 -2
  30. package/engine/scripts/wenyao-browser.sh +3 -2
  31. package/engine/scripts/wenyao-login.sh +3 -2
  32. package/engine/scripts/ziniao-keepalive.sh +2 -1
  33. package/engine/scripts/ziniao-post-upgrade-check.sh +2 -1
  34. package/engine/scripts/ziniao-windows.sh +2 -1
  35. package/engine/skills/README.md +86 -0
  36. package/engine/skills/argent-dev/SKILL.md +41 -0
  37. package/engine/skills/internal.json +37 -0
  38. package/engine/skills/lktop-pm/SKILL.md +35 -0
  39. package/engine/skills/lktopapp-dev/SKILL.md +34 -0
  40. package/engine/skills/ml-config-guide/SKILL.md +50 -0
  41. package/engine/skills/ml-fulfillment-sop/SKILL.md +178 -0
  42. package/engine/skills/ml-fulfillment-sop/references/andes-components.md +69 -0
  43. package/engine/skills/ml-fulfillment-sop/references/cdp-port-mapping.md +35 -0
  44. package/engine/skills/ml-fulfillment-sop/references/feishu-base-fulfillment-schema.md +136 -0
  45. package/engine/skills/ml-fulfillment-sop/references/multi-window-concurrency.md +69 -0
  46. package/engine/skills/ml-fulfillment-sop/references/pitfalls.md +77 -0
  47. package/engine/skills/ml-fulfillment-sop/references/selectors.md +243 -0
  48. package/engine/skills/ml-fulfillment-sop/scripts/fulfillment.js +586 -0
  49. package/engine/skills/ml-fulfillment-sop/scripts/poll-and-execute.sh +271 -0
  50. package/engine/skills/ml-fulfillment-sop/templates/report.md +55 -0
  51. package/engine/skills/ml-invoice/SKILL.md +506 -0
  52. package/engine/skills/ml-invoice/references/pitfalls.md +268 -0
  53. package/engine/skills/ml-invoice/references/portal-flow.md +360 -0
  54. package/engine/skills/ml-invoice/references/template-fill-contract.md +133 -0
  55. package/engine/skills/ml-invoice-xzh/SKILL.md +133 -0
  56. package/engine/skills/ml-invoice-xzh/references/pitfalls.md +230 -0
  57. package/engine/skills/ml-store-daily-report/SKILL.md +224 -0
  58. package/engine/skills/ml-store-daily-report/scripts/README.md +25 -0
  59. package/engine/skills/ml-store-daily-report/scripts/extract.js +691 -0
  60. package/engine/skills/ml-store-daily-report/scripts/generate.js +299 -0
  61. package/engine/skills/pet-dev/SKILL.md +42 -0
  62. package/engine/skills/whyshu-pm/SKILL.md +35 -0
  63. package/engine/skills/whyshu-web-dev/SKILL.md +50 -0
  64. package/engine/version.mjs +1 -1
  65. package/engine/verticals/mercadolibre/flows.json +37 -5
  66. package/package.json +3 -2
  67. package/requirements.txt +5 -0
@@ -0,0 +1,243 @@
1
+ # MercadoLibre FULL 页面 CSS 选择器映射
2
+
3
+ ## 维护说明
4
+ - 美客多后台使用 **Andes** 设计系统(`andes-table`、`andes-button`、`andes-card` 等)
5
+ - 原生 `querySelectorAll` **不支持** `:contains()` / `:has-text()` 伪类——这些是 jQuery/Sizzle 扩展
6
+ - 数据提取策略:CSS 选择器定位结构容器 → JavaScript 遍历 textContent 匹配内容
7
+ - 每次 UI 变更后更新「最后验证日期」
8
+ - 关键页面:`https://myaccount.mercadolibre.com.mx/shipping/inbounds`
9
+
10
+ ## 当前有效选择器(Playwright CDP,2026-08-05)
11
+
12
+ | 元素 | 选择器 / 提取方式 | 说明 |
13
+ |------|------------------|------|
14
+ | 数量输入框 | `input[id^="_r_"], input[id^="_R_"]` | ⚠️ 不是 `type="number"`,用 ID 前缀匹配 |
15
+ | Checkbox | 限定到 `main` 容器 | 排除 `#nav-header-user-switch`,防导航栏误触 |
16
+ | 产品标 PDF | 文件名模式 `Envio-*-Etiquetas-de-productos.pdf` | `Envio-` 前缀容易漏 |
17
+ | 货件号 | 从 DOM 标题元素提取 | 不是 URL inbound 编号 |
18
+ | ML 码 | `match(/[A-Z]{4}[0-9]+/)` 搜子串 | 用正则扫文本 |
19
+ | 预约确认按钮 | 双 Confirmar | 日历确认 top<700 + 主确认 top≈800 |
20
+ | 教程蒙层 | `aria-label=Close` + `andes-coach-marks` | 关闭新手引导浮层 |
21
+ | Enviar productos | `<a>` 标签 + force=True | 链接型按钮需强制点击 |
22
+ | 大货件地址 MXCD05 | card footer Continuar force=True | 特殊地址流程的确认按钮 |
23
+
24
+ ## 已验证信息(2026-07-27)
25
+
26
+ | 项目 | 值 |
27
+ |------|-----|
28
+ | 表格类名 | `andes-table` |
29
+ | 表格列(th) | Envío / Declaradas-Aptas / Cita reservada / Cargo aplicado / Estado / Acciones |
30
+ | 按钮组件 | `andes-button`(A 标签或 BUTTON 标签) |
31
+ | 状态文本容器 | `TD.andes-table__column` → `DIV.andes-typography` |
32
+ | 操作列容器 | `TD.andes-table__column` → `DIV.table-actions-column` |
33
+ | 卡片容器 | `andes-card` |
34
+ | 已验证货件状态值 | Vencido, En preparación, Pendiente de recepción, Procesamiento finalizado |
35
+
36
+ ---
37
+
38
+ ## 步骤 1:前期准备 [已验证 ✅]
39
+
40
+ ### 选择器(CSS 结构定位)
41
+
42
+ | 元素 | CSS 选择器 | 验证状态 | 说明 |
43
+ |------|-----------|----------|------|
44
+ | 货件列表表格 | `table.andes-table` | ✅ 已验证 | 页面唯一表格 |
45
+ | 货件行 | `table.andes-table tbody tr` | ✅ 已验证 | 每行一个货件 |
46
+ | 状态列单元格 | `td.andes-table__column` (第 5 列, 0-indexed) | ✅ 已验证 | 表头第 5 列 = "Estado" |
47
+ | 操作列单元格 | `td.andes-table__column` (第 6 列) | ✅ 已验证 | 表头第 6 列 = "Acciones" |
48
+ | 库容警告容器 | `.andes-card`(含 "liberar espacio" 文本) | ✅ 已验证 | 出现在页面顶部 |
49
+
50
+ ### 库容警告的**严格阻断形态**(2026-10-12 判据收紧,⛔ 别只看关键词)
51
+
52
+ ⚠️ 库容警告的**关键词本身不是判据**:`ml_capacity_probe` 要求「关键词 ∧ 阻断形态」同时成立才算仓容满
53
+ (只出现提示文案、主按钮仍可点 ⇒ 不算)。判据是**纯函数**,可离线回归。
54
+
55
+ ### 数据提取方式(JavaScript)
56
+
57
+ 由于 Andes 组件 class 名不包含业务语义,数据提取**必须用 textContent 匹配**:
58
+
59
+ ```javascript
60
+ // 检查过期货件
61
+ const rows = document.querySelectorAll('table.andes-table tbody tr');
62
+ const vencidos = Array.from(rows).filter(row =>
63
+ row.textContent.includes('Vencido')
64
+ );
65
+
66
+ // 统计各状态数量
67
+ const statuses = {};
68
+ rows.forEach(row => {
69
+ const cells = row.querySelectorAll('td');
70
+ const status = cells[4]?.textContent?.trim(); // 第 5 列 = Estado
71
+ if (status) statuses[status] = (statuses[status] || 0) + 1;
72
+ });
73
+
74
+ // 检测库容警告
75
+ const warning = Array.from(document.querySelectorAll('.andes-card'))
76
+ .find(card => card.textContent.includes('liberar espacio'));
77
+ ```
78
+
79
+ ---
80
+
81
+ ## 步骤 2:进入创建入口 [已验证 ✅]
82
+
83
+ | 元素 | 定位方式 | 验证状态 | 说明 |
84
+ |------|----------|----------|------|
85
+ | "Enviar productos" 按钮 | `a.andes-button` 且 textContent === "Enviar productos" | ✅ 已确认组件类型 | ⚠️ 库容满时此按钮不显示/禁用,页面显示"Para enviar productos, primero deberás liberar espacio..." |
86
+
87
+ ```javascript
88
+ const enviarBtn = Array.from(document.querySelectorAll('a.andes-button'))
89
+ .find(btn => btn.textContent.trim() === 'Enviar productos');
90
+ ```
91
+
92
+ ---
93
+
94
+ ## 步骤 3-5:创建货件表单
95
+
96
+ ### 步骤 3:选择产品与数量 [✅ 已部分验证]
97
+
98
+ > 已验证:配送计划页面的 SKU 搜索、数量填写、继续按钮。
99
+
100
+ | 元素 | 定位方式 | 验证状态 |
101
+ |------|----------|----------|
102
+ | SKU 搜索框 | `input[placeholder*="搜索产品"]` | ✅ 已验证 |
103
+ | 数量输入 | `input.andes-form-control__field`(搜索结果行的可见 input) | ✅ 已验证 |
104
+ | "继续" 按钮 | `button.andes-button` + textContent === "继续" | ✅ 已验证 |
105
+ | 明星产品弹窗 "维持现有方案" | textContent 匹配 → click 关闭 | ✅ 已验证 |
106
+
107
+ ### 步骤 4:预约时间 [✅ 已验证]
108
+
109
+ > 已验证:配送方式下拉、focus-ui-calendar 日期选择、时间槽、确认按钮。
110
+ > 页面:`/shipping/inbounds/{id}/appointment-v2`
111
+
112
+ | 子步骤 | 元素 | 定位方式 | 验证状态 |
113
+ |--------|------|----------|----------|
114
+ | 配送方式下拉 | `[id*=shipment-type-selection]` | id 前缀匹配 | ✅ |
115
+ | "Vehículo particular" | 叶子 SPAN,textContent === "Vehículo particular" | textContent 精确匹配 | ✅ |
116
+ | 日期输入框 | `input[readonly][id^="_R_"]` | 只读,动态 ID 以 `_R_` 开头 | ✅ |
117
+ | 当前月份 | `.focus-ui-datepicker__content__month-year` | 如 "julio 2026" | ✅ |
118
+ | 下月按钮 | `.focus-ui-datepicker__content__next_month_selector` | aria-label="next month" | ✅ |
119
+ | 上月按钮 | `.focus-ui-datepicker__content__back_month_selector` | aria-label="back month" | ✅ |
120
+ | 可选日期 | `div.day:not(.day--disabled)` | 排除 `day--disabled` 的格子 | ✅ |
121
+ | 已选日期 | `div.day--selected` | 已选日期(可能同时 disabled) | ✅ |
122
+ | 时间槽 | `.date-time-card-content` 内 textContent 匹配 `/\d{1,2}:\d{2}/` 的叶子元素 | 动态渲染 | ✅ |
123
+ | 确认按钮 | `button:not([disabled])` + textContent === "Confirmar" | 需等时间槽选定后才启用 | ✅ |
124
+
125
+ **自动化算法(⚠️ 2026-07-27 更新:robustClick 模式)**:
126
+ ```javascript
127
+ // robustClick: 攻克 Andes React 组件的鼠标事件序列
128
+ function rc(el){ el.scrollIntoView();el.focus();
129
+ el.dispatchEvent(new MouseEvent("mousedown",{bubbles:true}));
130
+ el.dispatchEvent(new MouseEvent("mouseup",{bubbles:true}));
131
+ el.dispatchEvent(new MouseEvent("click",{bubbles:true,cancelable:true,view:window})); }
132
+
133
+ // 1. 选配送方式
134
+ rc(document.querySelector("[role=combobox]")); // 打开下拉
135
+ // 在 [role=option] 中找 textContent 含 "Vehículo particular" → rc()
136
+
137
+ // 2. 选日期
138
+ rc(document.getElementById("_r_j_")); // 打开日历
139
+ // 翻月: rc(document.querySelector("[aria-label=\"next month\"]"));
140
+ // ⚠️ 日历格子点击: rc() 无法触发 React onChange
141
+ // → **此步目前需人工点击**,其余 7 步全自动
142
+
143
+ // 3. 选时间
144
+ // 在 date-time-card-content 内找 textContent 匹配 /\d{1,2}:\d{2}/ → rc()
145
+
146
+ // 4. 确认
147
+ // 先点日历 Confirmar (top<700 的), 再点主 Confirmar (_r_k_)
148
+ ```
149
+
150
+ ### 步骤 5:包装确认 [⚠️ 待验证]
151
+
152
+ | 元素 | 定位方式 | 验证状态 |
153
+ |------|----------|----------|
154
+ | 包装确认复选框 | 待确认 | ⚠️ 待验证 |
155
+
156
+ ---
157
+
158
+ ## 步骤 6-8:标签/箱唛/取消
159
+
160
+ ### 步骤 6:标签下载 [✅ 已验证]
161
+
162
+ > 已验证:通过 Hub 页「Revisar」链接进入标签页、复选框(Andes 组件需 MouseEvent)、下载弹窗、确认。
163
+
164
+ | 元素 | 定位方式 | 验证状态 | 备注 |
165
+ |------|----------|----------|------|
166
+ | Hub 页入口(初始) | `[data-testid="link-identifiers"]`(卡片本身 `role="button"`) | ✅ | 首次进入,右侧 SVG 箭头是卡片视觉元素,整个卡片可点击 |
167
+ | Hub 页入口(二次) | `a[href*="labeling"]` + textContent === "Revisar" | ✅ | 进入过一次后出现 |
168
+ | 产品展开按钮 | `button` + textContent === "Producto" | ✅ | 展开产品列表 |
169
+ | 复选框 | `input[type=checkbox]` 在 `.andes-checkbox__checkbox` 内 | ✅ | **必须用 MouseEvent 而非 .click()** |
170
+ | Descargar etiquetas | `button` + textContent === "Descargar etiquetas" + `!disabled` | ✅ | 复选框勾选后启用 |
171
+ | 弹窗 PDF 选项 | `button` + textContent === "PDF" + `inModal` | ✅ | 默认已选中 |
172
+ | 弹窗 Descargar | `button` + textContent === "Descargar" + `inModal` | ✅ | 触发下载 |
173
+ | Confirmar | `button` + textContent === "Confirmar" | ✅ | 完成标签步骤 |
174
+
175
+ **关键 Pitfall**:Andes 复选框是 React 受控组件,`cb.click()` **不会触发 React onChange**——必须用:
176
+ ```javascript
177
+ cb.dispatchEvent(new MouseEvent("click", {bubbles: true, cancelable: true, view: window}));
178
+ ```
179
+ 复选框 DOM `checked` 属性不会更新,但 React 内部状态会正确更新。
180
+
181
+ ---
182
+
183
+ ## 从页面内容提取数据的完整模式
184
+
185
+ `ziniao-cli page content --content-format text` 返回的纯文本已包含所有业务数据,正则提取比 CSS 选择器更可靠:
186
+
187
+ ```javascript
188
+ // 从 page content 文本中提取货件列表
189
+ const text = pageContent; // ziniao-cli page content 返回的纯文本
190
+
191
+ // 匹配货件行:货件号 + 声明数/合格数 + 日期(可选) + 状态 + 操作
192
+ const shipmentPattern = /#(\d{8})\s+(\d+)\s*\/\s*(\S+)\s+([\d\/]+\s+\S+(?:\s+\d+:\d+)?\s*(?:hs)?\s+[^VEPC]*)?\s+(Vencido|En preparación|Pendiente de recepción|Procesamiento finalizado)\s+(Revisar detalle|Completar envío)/g;
193
+
194
+ let match;
195
+ while ((match = shipmentPattern.exec(text)) !== null) {
196
+ // match[1] = 货件号, match[2] = 声明数, match[3] = 合格数
197
+ // match[5] = 状态, match[6] = 操作
198
+ }
199
+ ```
200
+
201
+ **优势**:纯文本提取不依赖 CSS 选择器,UI 改版后仍可工作(只要文本内容不变)。
202
+
203
+ ---
204
+
205
+ ## 注意
206
+ - `:has-text()` 和 `:contains()` **不可用**——这是 jQuery 扩展,标准 CSS / `querySelectorAll` 不支持
207
+ - Andes 组件 class 名(`andes-button__content`、`andes-typography`)是内部实现细节,可能随版本变更
208
+ - **推荐策略**:结构定位用 CSS(`table.andes-table`),内容匹配用 textContent
209
+ - 纯文本正则提取(`page content`)是最稳定的数据采集方式,优先使用
210
+
211
+ ---
212
+
213
+ ## 商品尺寸页(units)与「⛔ 尺寸分级阻塞」(2026-10-12 实测登记)
214
+
215
+ ### 两个页面的分工(⛔ 别只看 hub)
216
+
217
+ | 页面 | URL | 能看到什么 |
218
+ |------|-----|-----------|
219
+ | hub-v2 | `/shipping/inbounds/<货件号>/hub-v2` | 逐项任务卡(`done` 状态)+「Tienes N producto para revisar」+「Debes completar las tareas anteriores para preparar e identificar.」|**看不到商品尺寸分级** |
220
+ | units | `/shipping/inbounds/<货件号>/units?from=hub` | 商品尺寸分级 / 体积 / 空间口径 / 库存输入框渲染态|= hub 上「Tienes N producto para revisar」右侧【**Editar**】的 href |
221
+
222
+ ### units 页新观察点(2026-10-12 现场,货件 79301358 / SKU HW-MX-162 / 1 店)
223
+
224
+ | 元素 | 定位方式 | 现场实测值 | 说明 |
225
+ |------|----------|-----------|------|
226
+ | 尺寸分级标签 | `[data-testid="size-dimensions-tag"]` | **`EXTRAGRANDE`** | ⚠️ 取值是**西语原文**(`PEQUEÑO`/`MEDIANO`/`GRANDE`/`EXTRAGRANDE`…),大小写/重音需归一化后再比 |
227
+ | 体积胶囊 | `[data-testid="volume-pill"]` | **`11,712 cm 3`** | 原文带千分位与空格,**照抄原文**留痕(别自己格式化) |
228
+ | 空间口径原文 | 正文文本 | 「**Pequeños y medianos** — Este espacio incluye a los productos Pequeños y medianos. Revisa el tamaño de cada uno junto a su nombre.」 | 描述的是**空间**(这个空间只收中小件),⛔ **不是**"这个商品有问题"的判据 |
229
+ | 尺寸规则入口 | 链接文本 | 「Revisar dimensiones permitidas」|「Cómo clasifican los productos por tamaño」 | 同上,只作佐证 |
230
+ | 库存输入框 | `[aria-label="Stock a enviar"]`(`[data-testid^="input-textfield"]`) | 值 `200`,**被平台渲染为 `disabled`**:外层 `andes-form-control--disabled` + `data-andes-state="disabled"`,位于 `.andes-tooltip__trigger` 内 | 运营实测:**强制启用 + 重输 + 点 Guardar 没有任何效果**(hub 项不变)⇒ ⛔ 别写"自动启用它"的补丁 |
231
+
232
+ ### ⛔ 尺寸分级阻塞 = **运营侧**,脚本侧无解
233
+
234
+ **判据(唯一实现 `scripts/ml_hub_status.py#step7_block_verdict`,纯函数、可离线回归):**
235
+ 出现 `size-dimensions-tag` **且值 ∉ 中小件允许集**(`PEQUEÑO`/`MEDIANO` 族)⇒
236
+ **该商品被平台判为超大件,而本货件空间只收中小件** ⇒ 步骤7 **进不了箱数页**(hub 第 5 项
237
+ 「Prepara bultos y/o pallets…」恒 `done=False`)。
238
+
239
+ - **正确动作(运营)**:修正商品包装尺寸(改小到该空间接受的分级)**或**改走其他渠道/货件。
240
+ - **⛔ 不是门户改版、⛔ 不是选择器缺陷、⛔ 无需 ml-dev 改脚本** —— 页面元素一个都没变,
241
+ 是平台按尺寸**合规性**把库存输入框锁成 disabled。
242
+ - ⚠️ **判别力**:空间文案「Pequeños y medianos…」在**任何一个**中小件货件的 units 页都会出现
243
+ ⇒ 单凭它判"尺寸阻塞"会把正常票全判错。判据必须是**商品自己的尺寸标签**。