@whyshu/argent-engine 0.7.5 → 0.7.7
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.
- package/MANIFEST.json +39 -35
- package/engine/VERSION +1 -1
- package/engine/core/contracts/argent.contracts.json +12 -4
- package/engine/scripts/appointment-create.mjs +37 -2
- package/engine/scripts/appointment-worker.mjs +72 -15
- package/engine/scripts/appointment_grab.py +141 -20
- package/engine/scripts/argent-machine-setup.mjs +118 -0
- package/engine/scripts/argent-runner.mjs +63 -10
- package/engine/scripts/capability-packs.lock.json +54 -51
- package/engine/scripts/ci-run.sh +14 -2
- package/engine/scripts/collect-evidence.mjs +1 -1
- package/engine/scripts/customer-preflight.py +33 -0
- package/engine/scripts/flow-auto-retry.py +3 -0
- package/engine/scripts/flow-requirements.json +23 -13
- package/engine/scripts/flow-session-retention.mjs +88 -0
- package/engine/scripts/flow-trigger-wake.mjs +13 -1
- package/engine/scripts/flow-trigger.sh +2 -1
- package/engine/scripts/flow_config.py +4 -0
- package/engine/scripts/flow_failure.py +9 -4
- package/engine/scripts/flow_preflight.py +30 -0
- package/engine/scripts/flow_retry.py +22 -7
- package/engine/scripts/gen-agent-bundles.mjs +1 -1
- package/engine/scripts/gen-preset-rows.mjs +2 -2
- package/engine/scripts/invoice_lip_runner.py +30 -2
- package/engine/scripts/invoice_stageb.py +101 -0
- package/engine/scripts/login-readiness.py +220 -0
- package/engine/scripts/ml-oauth-setup.mjs +1 -1
- package/engine/scripts/orchestrator.py +416 -30
- package/engine/scripts/probe_ml_site.py +103 -0
- package/engine/scripts/release-argent.sh +26 -2
- package/engine/skills/argent-machine-onboarding/SKILL.md +143 -0
- package/engine/skills/internal.json +4 -0
- package/engine/skills/ml-full-appointment/SKILL.md +32 -13
- package/engine/skills/ml-invoice/SKILL.md +1 -1
- package/engine/skills/ml-invoice-xzh/SKILL.md +2 -2
- package/engine/version.mjs +1 -1
- package/engine/verticals/_engine-words.baseline.txt +42 -23
- package/engine/verticals/mercadolibre/flows.json +9 -5
- package/engine/verticals/mercadolibre/manifest.json +5 -4
- package/engine/verticals/mercadolibre/prompts/daily-report.failure.md +1 -1
- package/engine/verticals/mercadolibre/prompts/daily-report.md +7 -10
- package/package.json +1 -1
- package/engine/scripts/ml-ads.mjs +0 -181
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"id": "mercadolibre",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"title": "美客多(MercadoLibre)业务线",
|
|
5
5
|
"_note": "P2a 落位:flows/prompts/暂停开关/日志/会话文件已命名空间化;角色/preset/技能仍是旧名(legacyNaming,P2b/P5 在有重启窗口时改名);能力与计费单位仍在 base(随重装窗口改 SKU id);适配器仍在 scripts/integrations(P4 端口化时搬)。",
|
|
6
|
-
"_versionNote": "0.1.1(2026-10-06):`requires.argent` 由 `>=0.2 <0.3` 抬到 `>=0.3 <0.4`(平台现役 VERSION=0.3.0)—— 修前「Argent 能力 → 概览」把这唯一的业务线标成 `⚠️ 版本不满足`,而它实际能跑(口径与事实相反)。同批 `version` +1(P0-5,见 docs/Argent-工作台口径审计-2026-10-06.md)。|0.1.2(2026-10-09):**平台抬到 0.4.0** ⇒ `requires.argent` 同步抬到 `>=0.4 <0.5`(不改则会重演 P0-5:兼容区间把现役平台挡在门外)。判据已固化为门禁 `node scripts/check-version-consistency.mjs`(**平台版本必须落在每条业务线声明的兼容区间内**),不再靠人记得复核。⚠️ `verifiedOn.argent` **刻意维持 `0.3.0`**:该字段记的是\"**真装真跑过**\",本批只抬版本号、内核无行为改动,未在 0.4.0 上跑过生产 ⇒ 落地后补登,⛔
|
|
6
|
+
"_versionNote": "0.1.1(2026-10-06):`requires.argent` 由 `>=0.2 <0.3` 抬到 `>=0.3 <0.4`(平台现役 VERSION=0.3.0)—— 修前「Argent 能力 → 概览」把这唯一的业务线标成 `⚠️ 版本不满足`,而它实际能跑(口径与事实相反)。同批 `version` +1(P0-5,见 docs/Argent-工作台口径审计-2026-10-06.md)。|0.1.2(2026-10-09):**平台抬到 0.4.0** ⇒ `requires.argent` 同步抬到 `>=0.4 <0.5`(不改则会重演 P0-5:兼容区间把现役平台挡在门外)。判据已固化为门禁 `node scripts/check-version-consistency.mjs`(**平台版本必须落在每条业务线声明的兼容区间内**),不再靠人记得复核。⚠️ `verifiedOn.argent` **刻意维持 `0.3.0`**:该字段记的是\"**真装真跑过**\",本批只抬版本号、内核无行为改动,未在 0.4.0 上跑过生产 ⇒ 落地后补登,⛔ 不预填。|0.1.3(2026-10-21):平台版本线已到 0.7.6 ⇒ `requires.argent` 由 `>=0.4 <0.5` 抬到 `>=0.7 <0.8`(原区间把现役平台挡在门外;`check-version-consistency` R1-LOCKSTEP 实测报红)。",
|
|
7
7
|
"requires": {
|
|
8
|
-
"argent": ">=0.
|
|
8
|
+
"argent": ">=0.7 <0.8",
|
|
9
9
|
"dsh": ">=0.1.5 <0.2"
|
|
10
10
|
},
|
|
11
11
|
"verifiedOn": {
|
|
@@ -50,7 +50,8 @@
|
|
|
50
50
|
"ml-invoice",
|
|
51
51
|
"ml-invoice-xzh",
|
|
52
52
|
"ml-store-daily-report",
|
|
53
|
-
"ml-config-guide"
|
|
53
|
+
"ml-config-guide",
|
|
54
|
+
"ml-full-appointment"
|
|
54
55
|
],
|
|
55
56
|
"flows": [
|
|
56
57
|
"mercadolibre.full",
|
|
@@ -43,7 +43,7 @@
|
|
|
43
43
|
|
|
44
44
|
```bash
|
|
45
45
|
node {{cloneScripts}}/feishu-card.mjs --template need-human --flow {{flow}} --case '{{case}}' \
|
|
46
|
-
--reason '<卡在哪/缺什么>' --ask '<要用户定什么>' --to
|
|
46
|
+
--reason '<卡在哪/缺什么>' --ask '<要用户定什么>' --to daily --send
|
|
47
47
|
```
|
|
48
48
|
|
|
49
49
|
`needs_agent` 类由引擎在唤醒时自动发 failure-alert 卡片(与你重复也没关系,卡片是给人看的)。
|
|
@@ -27,21 +27,18 @@
|
|
|
27
27
|
失败才退回影子取证(见步骤②)并把问题暴露出来;
|
|
28
28
|
- 认领最多 {{max}} 张;台账:`{{ledger}}`。
|
|
29
29
|
|
|
30
|
-
##
|
|
30
|
+
## 收尾:日报结果通知(2026-10-20 修订)
|
|
31
31
|
|
|
32
|
-
|
|
32
|
+
- **日报成功时不要再发 flow-result 汇总卡**:`daily-report.mjs` 已经把日报表卡发到
|
|
33
|
+
日报群(`ML_DAILY_CHAT_ID`),再发一张汇总卡只会重复刷屏。
|
|
34
|
+
- **只有失败 / needs_human / blocked** 才发卡片;落点必须是**日报群**,不是内部群:
|
|
33
35
|
|
|
34
36
|
```bash
|
|
35
|
-
# 流程结果汇报(一票一行:<票号>=<状态>|<细节>;状态用 done / failed / blocked 等)
|
|
36
|
-
node {{cloneScripts}}/feishu-card.mjs --template flow-result \
|
|
37
|
-
--flow {{flow}} --flow-title '{{flowTitle}}' \
|
|
38
|
-
--result '<票号>=done|<做了什么>' --note '本轮一句话小结' --to internal --send
|
|
39
|
-
|
|
40
|
-
# 需要人确认时(needs_human / blocked):用需人工卡,写清「卡在哪」与「要你定什么」
|
|
41
37
|
node {{cloneScripts}}/feishu-card.mjs --template need-human \
|
|
42
|
-
--flow {{flow}} --case '
|
|
38
|
+
--flow {{flow}} --case '{{case}}' \
|
|
39
|
+
--reason '<卡在哪/缺什么>' --ask '<要用户定什么>' --to daily --send
|
|
43
40
|
```
|
|
44
41
|
|
|
45
42
|
- 先 `--dry-run`(不带 `--send`)看一眼卡片 JSON 再发;发送失败**不要**反复重试,改把结论写进台账;
|
|
46
|
-
-
|
|
43
|
+
- ⛔ 日报流程不要用 `--to internal`;内部群只接收 FULL/发票等内部流程卡片。
|
|
47
44
|
- 台账照旧记(卡片是给人看的,台账是给引擎看的,两者都要)。
|
package/package.json
CHANGED
|
@@ -1,181 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
3
|
-
* ml-ads.mjs —— Mercado Ads(Product Ads)**只读**效果报告 CLI。
|
|
4
|
-
*
|
|
5
|
-
* ## 干什么
|
|
6
|
-
* 用与日报同一条授权链(`~/.dsh/argent/ml-api-stores.json`)逐店拉广告数据,输出:
|
|
7
|
-
* 花费 / 广告销售额 / ROAS / ACoS / 点击 / CTR / CPC / 广告单量,
|
|
8
|
-
* 外加**告警**(ACoS 超标、烧钱零转化、ROAS 过低)和活动级下钻。
|
|
9
|
-
*
|
|
10
|
-
* ## 用法
|
|
11
|
-
* node scripts/ml-ads.mjs # 全部已授权店铺 · 近 7 个完整自然日
|
|
12
|
-
* node scripts/ml-ads.mjs --store 1店 # 店铺子串过滤
|
|
13
|
-
* node scripts/ml-ads.mjs --days 30 # 回溯天数(MELI 上限 90)
|
|
14
|
-
* node scripts/ml-ads.mjs --to 2026-10-16 # 指定结束日(站点本地)
|
|
15
|
-
* node scripts/ml-ads.mjs --detail # 附「花钱最多 / ACoS 超标」活动
|
|
16
|
-
* node scripts/ml-ads.mjs --json # 结构化输出(stdout 纯 JSON)
|
|
17
|
-
* node scripts/ml-ads.mjs --lines # 只输出中文卡片行(给日报/角色回复用)
|
|
18
|
-
* node scripts/ml-ads.mjs --list # 只列每店广告主,不拉活动
|
|
19
|
-
* node scripts/ml-ads.mjs --acos-bad 30 --min-spend 200 # 告警阈值
|
|
20
|
-
*
|
|
21
|
-
* ## 只读纪律
|
|
22
|
-
* · 只发 GET(`/advertising/...`);⛔ 不改预算/暂停/调价,也不写任何客户数据;
|
|
23
|
-
* · 唯一写盘 = token 轮换回写(MELI refresh_token 是轮换制,不写回下次必 429):
|
|
24
|
-
* 原子写(tmp + rename)、保持 600;
|
|
25
|
-
* · API 基址:`ML_API_BASE` → 默认平台地址(引擎 lib 里⛔不含域名,只在本 CLI 注入)。
|
|
26
|
-
*/
|
|
27
|
-
import { readFileSync, writeFileSync, renameSync, existsSync, mkdirSync, chmodSync } from 'node:fs'
|
|
28
|
-
import { join, dirname } from 'node:path'
|
|
29
|
-
import { fileURLToPath } from 'node:url'
|
|
30
|
-
import { resolveDataDir } from './lib/argent-datadir.mjs'
|
|
31
|
-
import {
|
|
32
|
-
loadEnvFile, refreshStoreToken, apiGet as coreApiGet,
|
|
33
|
-
SITE_TZ, SITE_CURRENCY, fallbackSite, startOfLocalDayUTC, localDateStr, matchStores,
|
|
34
|
-
} from './lib/daily-report-core.mjs'
|
|
35
|
-
import {
|
|
36
|
-
fetchAdsReport, classifyAdsAlerts, adsCardLines, fmtMoney, ADS_ALERT_DEFAULTS,
|
|
37
|
-
} from './lib/ml-ads-core.mjs'
|
|
38
|
-
|
|
39
|
-
const API_BASE = () => process.env.ML_API_BASE || 'https://api.mercadolibre.com'
|
|
40
|
-
|
|
41
|
-
const args = process.argv.slice(2)
|
|
42
|
-
const argVal = (n) => { const i = args.indexOf(n); return i >= 0 && args[i + 1] ? args[i + 1] : null }
|
|
43
|
-
const has = (n) => args.includes(n)
|
|
44
|
-
|
|
45
|
-
if (has('-h') || has('--help')) {
|
|
46
|
-
console.log(`Mercado Ads 只读报告
|
|
47
|
-
|
|
48
|
-
用法: node scripts/ml-ads.mjs [选项]
|
|
49
|
-
--store <名或uid> 店铺过滤(名称子串 / uid 全等,与日报同一判据)
|
|
50
|
-
--days <n> 回溯天数,默认 7(MELI 上限 90)
|
|
51
|
-
--to <YYYY-MM-DD> 结束日(站点本地),默认「昨天」
|
|
52
|
-
--detail 附活动级下钻(花钱最多 + ACoS 超标)
|
|
53
|
-
--list 只列出每店广告主(1 请求/店)
|
|
54
|
-
--json 结构化 JSON(stdout)
|
|
55
|
-
--lines 中文卡片行(给日报分区/角色回复复用)
|
|
56
|
-
--acos-warn <n> ACoS 黄色阈值,默认 ${ADS_ALERT_DEFAULTS.acosWarn}
|
|
57
|
-
--acos-bad <n> ACoS 红色阈值,默认 ${ADS_ALERT_DEFAULTS.acosBad}
|
|
58
|
-
--min-spend <n> 告警最小花费(本币),默认 ${ADS_ALERT_DEFAULTS.minSpend}
|
|
59
|
-
--env <path> daily-report.env 路径
|
|
60
|
-
退出码: 0 正常 | 1 用法/取数失败 | 2 全部店铺失败`)
|
|
61
|
-
process.exit(0)
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
const DATA_DIR = resolveDataDir({ env: process.env }).dir
|
|
65
|
-
const ENV_PATH = argVal('--env') || join(DATA_DIR, 'daily-report.env')
|
|
66
|
-
const STORES_PATH = process.env.ML_API_STORES_JSON || join(DATA_DIR, 'ml-api-stores.json')
|
|
67
|
-
const env = loadEnvFile(ENV_PATH)
|
|
68
|
-
const doc = existsSync(STORES_PATH) ? JSON.parse(readFileSync(STORES_PATH, 'utf8')) : { stores: [] }
|
|
69
|
-
const stores = doc.stores || []
|
|
70
|
-
|
|
71
|
-
const persistStores = () => {
|
|
72
|
-
try {
|
|
73
|
-
mkdirSync(dirname(STORES_PATH), { recursive: true })
|
|
74
|
-
const tmp = `${STORES_PATH}.tmp`
|
|
75
|
-
writeFileSync(tmp, JSON.stringify({ stores }, null, 2) + '\n', { mode: 0o600 })
|
|
76
|
-
renameSync(tmp, STORES_PATH)
|
|
77
|
-
chmodSync(STORES_PATH, 0o600)
|
|
78
|
-
} catch (e) { console.error(`✗ token 写回失败:${e.message}`) }
|
|
79
|
-
}
|
|
80
|
-
const apiGet = (path, token) => coreApiGet(path, token, { base: API_BASE() })
|
|
81
|
-
|
|
82
|
-
const alertOpts = {
|
|
83
|
-
acosWarn: Number(argVal('--acos-warn') || ADS_ALERT_DEFAULTS.acosWarn),
|
|
84
|
-
acosBad: Number(argVal('--acos-bad') || ADS_ALERT_DEFAULTS.acosBad),
|
|
85
|
-
minSpend: Number(argVal('--min-spend') || ADS_ALERT_DEFAULTS.minSpend),
|
|
86
|
-
minRoas: ADS_ALERT_DEFAULTS.minRoas,
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
const FLAG_JSON = has('--json')
|
|
90
|
-
const FLAG_LINES = has('--lines')
|
|
91
|
-
const FLAG_DETAIL = has('--detail')
|
|
92
|
-
const FLAG_LIST = has('--list')
|
|
93
|
-
const days = Math.max(1, Math.min(90, Number(argVal('--days') || 7)))
|
|
94
|
-
const toArg = argVal('--to')
|
|
95
|
-
|
|
96
|
-
const now = new Date()
|
|
97
|
-
const picked = matchStores(stores, (argVal('--store') || '').split(',').map((x) => x.trim()).filter(Boolean))
|
|
98
|
-
if (!picked.length) {
|
|
99
|
-
console.error(`✗ 没有匹配到店铺(已授权 ${stores.length} 家)`)
|
|
100
|
-
process.exit(1)
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
const results = []
|
|
104
|
-
for (const store of picked) {
|
|
105
|
-
const site = fallbackSite(store.site)
|
|
106
|
-
const tz = SITE_TZ[site]
|
|
107
|
-
const todayStart = startOfLocalDayUTC(tz, now)
|
|
108
|
-
const toMs = toArg ? startOfLocalDayUTC(tz, new Date(`${toArg}T12:00:00Z`)) : todayStart - 86400000
|
|
109
|
-
const fromMs = toMs - (days - 1) * 86400000
|
|
110
|
-
const dateFrom = localDateStr(tz, new Date(fromMs))
|
|
111
|
-
const dateTo = localDateStr(tz, new Date(toMs))
|
|
112
|
-
const row = { store: store.name, site, uid: store.user_id, currency: SITE_CURRENCY[site] || '', dateFrom, dateTo }
|
|
113
|
-
try {
|
|
114
|
-
const token = await refreshStoreToken(env, store, { onRotate: persistStores })
|
|
115
|
-
if (FLAG_LIST) {
|
|
116
|
-
const adv = await apiGet('/advertising/advertisers?product_id=PADS', token)
|
|
117
|
-
row.advertisers = (adv.advertisers || []).map((a) => ({ advertiser_id: a.advertiser_id, site: a.site_id, name: a.advertiser_name }))
|
|
118
|
-
results.push(row)
|
|
119
|
-
continue
|
|
120
|
-
}
|
|
121
|
-
const report = await fetchAdsReport({ apiGet, token, site, dateFrom, dateTo })
|
|
122
|
-
row.report = report
|
|
123
|
-
row.alerts = classifyAdsAlerts(report.totals, alertOpts)
|
|
124
|
-
results.push(row)
|
|
125
|
-
} catch (e) {
|
|
126
|
-
row.error = String(e.message || e)
|
|
127
|
-
results.push(row)
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
// ── 输出 ───────────────────────────────────────────────────
|
|
132
|
-
const ok = results.filter((r) => !r.error)
|
|
133
|
-
if (!ok.length) {
|
|
134
|
-
if (FLAG_JSON) console.log(JSON.stringify({ results }, null, 2))
|
|
135
|
-
else for (const r of results) console.error(`✗ ${r.store}:${r.error}`)
|
|
136
|
-
process.exit(2)
|
|
137
|
-
}
|
|
138
|
-
if (FLAG_JSON) { console.log(JSON.stringify({ generatedAt: now.toISOString(), days, results }, null, 2)); process.exit(0) }
|
|
139
|
-
|
|
140
|
-
if (FLAG_LINES) {
|
|
141
|
-
for (const r of ok) {
|
|
142
|
-
const lines = adsCardLines(r.report, { currency: r.currency, alertOpts })
|
|
143
|
-
console.log(`### ${r.store}`)
|
|
144
|
-
console.log(lines.join('\n'))
|
|
145
|
-
}
|
|
146
|
-
process.exit(0)
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
if (FLAG_LIST) {
|
|
150
|
-
for (const r of results) {
|
|
151
|
-
if (r.error) { console.log(`${r.store}: ✗ ${r.error}`); continue }
|
|
152
|
-
console.log(`${r.store}(${r.site}):` + (r.advertisers.length ? r.advertisers.map((a) => `${a.advertiser_id}(${a.site})`).join(', ') : '无广告主'))
|
|
153
|
-
}
|
|
154
|
-
process.exit(0)
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
const hdr = ['店铺', '站点', '花费', '销售额', 'ROAS', 'ACoS', '点击', 'CTR', '广告单量', '活动']
|
|
158
|
-
let text = `## 📣 Mercado Ads · ${ok[0].dateFrom} ~ ${ok[0].dateTo}(近 ${days} 天)\n\n`
|
|
159
|
-
text += '| ' + hdr.join(' | ') + ' |\n|' + hdr.map(() => '---').join('|') + '|\n'
|
|
160
|
-
const allAlerts = []
|
|
161
|
-
for (const r of results) {
|
|
162
|
-
if (r.error) { text += `| ${r.store} | ${r.site} | — | — | — | — | — | — | — | ✗ ${r.error.slice(0, 40)} |\n`; continue }
|
|
163
|
-
const t = r.report.totals
|
|
164
|
-
const c = r.currency ? r.currency + ' ' : ''
|
|
165
|
-
text += `| ${r.store} | ${r.site} | ${c}${fmtMoney(t.cost)} | ${c}${fmtMoney(t.amount)} | ${t.roas} | ${t.acos}% | ${t.clicks} | ${t.ctr}% | ${t.units} | ${t.campaigns} |\n`
|
|
166
|
-
for (const a of r.alerts) allAlerts.push({ store: r.store, ...a })
|
|
167
|
-
if (FLAG_DETAIL) {
|
|
168
|
-
for (const a of r.report.advertisers) {
|
|
169
|
-
const top = [...(a.topCampaigns.top || [])]
|
|
170
|
-
for (const cp of top) text += `| ↳ ${r.store}·${cp.name.slice(0, 24)} | ${a.advertiserId} | ${c}${fmtMoney(cp.cost)} | ${c}${fmtMoney(cp.amount)} | ${cp.roas} | ${cp.acos}% | ${cp.clicks} | — | ${cp.units} | — |\n`
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
text += '\n'
|
|
175
|
-
if (allAlerts.length) {
|
|
176
|
-
text += '### ⚠️ 告警\n'
|
|
177
|
-
for (const a of allAlerts) text += `- ${a.level === 'red' ? '🔴' : '🟡'} **${a.store}**:${a.text}\n`
|
|
178
|
-
} else {
|
|
179
|
-
text += '✅ 无告警(ACoS 与 ROAS 均在阈值内)\n'
|
|
180
|
-
}
|
|
181
|
-
console.log(text)
|