@whyshu/argent-engine 0.0.0-stage → 0.6.0
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 +307 -0
- package/engine/VERSION +1 -0
- package/engine/agents/catalog.json +454 -0
- package/engine/configs/_aliases.proposed.json +651 -0
- package/engine/configs/_migration-plan.proposed.json +899 -0
- package/engine/configs/account.json +272 -0
- package/engine/configs/advanced.json +205 -0
- package/engine/configs/host.json +149 -0
- package/engine/configs/hr.json +27 -0
- package/engine/configs/notify.json +264 -0
- package/engine/core/cli/argent.mjs +277 -0
- package/engine/core/contracts/argent.contracts.json +1065 -0
- package/engine/core/contracts/verbs.schema.json +2539 -0
- package/engine/core/lib/argent-datadir.mjs +223 -0
- package/engine/core/lib/audit.mjs +187 -0
- package/engine/core/lib/claim.mjs +258 -0
- package/engine/core/lib/config-catalog.mjs +1172 -0
- package/engine/core/lib/doctor-customer.mjs +577 -0
- package/engine/core/lib/flow-list.mjs +138 -0
- package/engine/core/lib/flow-registry.mjs +48 -0
- package/engine/core/lib/init.mjs +247 -0
- package/engine/core/lib/ledger.mjs +124 -0
- package/engine/core/lib/license-activate.mjs +813 -0
- package/engine/core/lib/license-device.mjs +314 -0
- package/engine/core/lib/license-file.mjs +349 -0
- package/engine/core/lib/license-guard.mjs +123 -0
- package/engine/core/lib/license-ops.mjs +1645 -0
- package/engine/core/lib/license-verdict.mjs +133 -0
- package/engine/core/lib/license.mjs +577 -0
- package/engine/core/lib/notify/dedup.mjs +101 -0
- package/engine/core/lib/notify/feishu.mjs +38 -0
- package/engine/core/lib/notify/index.mjs +95 -0
- package/engine/core/lib/notify/verb.mjs +327 -0
- package/engine/core/lib/notify/webhook.mjs +135 -0
- package/engine/core/lib/notify/wecom.mjs +36 -0
- package/engine/core/lib/python.mjs +145 -0
- package/engine/core/lib/sop.mjs +116 -0
- package/engine/core/lib/versions.mjs +205 -0
- package/engine/core/mcp/client.mjs +67 -0
- package/engine/core/mcp/http.mjs +138 -0
- package/engine/core/mcp/server.mjs +702 -0
- package/engine/scripts/_engine_rules_probe.py +15 -0
- package/engine/scripts/analyze-full-day.py +568 -0
- package/engine/scripts/archive/feishu-bridge.js +246 -0
- package/engine/scripts/argent-agents.mjs +527 -0
- package/engine/scripts/argent-capabilities.mjs +776 -0
- package/engine/scripts/argent-catalog.mjs +186 -0
- package/engine/scripts/argent-datadir.py +62 -0
- package/engine/scripts/argent-doctor.mjs +624 -0
- package/engine/scripts/argent-env-write.mjs +302 -0
- package/engine/scripts/argent-schedule.mjs +620 -0
- package/engine/scripts/argent-setup-check.mjs +369 -0
- package/engine/scripts/argent-upgrade.mjs +1046 -0
- package/engine/scripts/base-bootstrap.sh +575 -0
- package/engine/scripts/billing-mode-daily.sh +12 -0
- package/engine/scripts/build-engine-package.mjs +123 -0
- package/engine/scripts/capability-packs.lock.json +484 -0
- package/engine/scripts/capability_pack.py +1242 -0
- package/engine/scripts/check-1.0-criteria.mjs +355 -0
- package/engine/scripts/check-agent-catalog.mjs +141 -0
- package/engine/scripts/check-billing-mode.sh +377 -0
- package/engine/scripts/check-child-sessions.mjs +83 -0
- package/engine/scripts/check-config-catalog.mjs +677 -0
- package/engine/scripts/check-docs-drift.mjs +522 -0
- package/engine/scripts/check-dsh-web-expose.sh +98 -0
- package/engine/scripts/check-judgment-drift.mjs +403 -0
- package/engine/scripts/check-license.mjs +199 -0
- package/engine/scripts/check-model-auth.py +443 -0
- package/engine/scripts/check-pack-closure.mjs +661 -0
- package/engine/scripts/check-portable-symbols.mjs +116 -0
- package/engine/scripts/check-preset-landing.mjs +124 -0
- package/engine/scripts/check-production-home.mjs +602 -0
- package/engine/scripts/check-role-mapping.mjs +461 -0
- package/engine/scripts/check-sample-redaction.mjs +320 -0
- package/engine/scripts/check-skill-classification.mjs +188 -0
- package/engine/scripts/check-sop-consistency.mjs +431 -0
- package/engine/scripts/check-test-env-safety.mjs +627 -0
- package/engine/scripts/check-version-consistency.mjs +452 -0
- package/engine/scripts/ci-run.sh +558 -0
- package/engine/scripts/config-explain.mjs +278 -0
- package/engine/scripts/configure-provider.rb +29 -0
- package/engine/scripts/customer-config-migrate.mjs +179 -0
- package/engine/scripts/customer-preflight.py +892 -0
- package/engine/scripts/customer-readiness.sh +134 -0
- package/engine/scripts/daily-report-inbox.py +242 -0
- package/engine/scripts/daily-report-reconcile.mjs +312 -0
- package/engine/scripts/daily-report.mjs +1310 -0
- package/engine/scripts/daily-report.sh +46 -0
- package/engine/scripts/data/lip-product-dict.json +306 -0
- package/engine/scripts/deliver-customer.mjs +932 -0
- package/engine/scripts/delivery-faces.txt +31 -0
- package/engine/scripts/desktop-trial.sh +146 -0
- package/engine/scripts/dev-window.sh +260 -0
- package/engine/scripts/drill-clean-machine.sh +315 -0
- package/engine/scripts/dsh-api-client.mjs +215 -0
- package/engine/scripts/dsh-backup-scope.sh +83 -0
- package/engine/scripts/dsh-offline-pack.mjs +77 -0
- package/engine/scripts/dsh-overlay-pin.sh +87 -0
- package/engine/scripts/dsh-plane.mjs +153 -0
- package/engine/scripts/dsh-portforward.py +223 -0
- package/engine/scripts/dsh-preflight.mjs +179 -0
- package/engine/scripts/dsh-release-watch-daily.sh +19 -0
- package/engine/scripts/dsh-release-watch.mjs +458 -0
- package/engine/scripts/dsh-remote-check.sh +99 -0
- package/engine/scripts/dsh-restart.sh +179 -0
- package/engine/scripts/dsh-restore.sh +111 -0
- package/engine/scripts/dsh-scope-rewire.mjs +803 -0
- package/engine/scripts/dsh-snapshot.mjs +116 -0
- package/engine/scripts/dsh-upgrade-readiness.mjs +165 -0
- package/engine/scripts/dsh-upgrade.mjs +612 -0
- package/engine/scripts/dsh-url.sh +86 -0
- package/engine/scripts/dsh-web-launch.mjs +155 -0
- package/engine/scripts/ensure-store-windows.py +509 -0
- package/engine/scripts/entitlements.py +458 -0
- package/engine/scripts/env_probe.py +411 -0
- package/engine/scripts/feishu-card.mjs +252 -0
- package/engine/scripts/fix-skill-frontmatter.py +209 -0
- package/engine/scripts/flow-auto-retry.py +454 -0
- package/engine/scripts/flow-requirements.json +720 -0
- package/engine/scripts/flow-scaffold.py +275 -0
- package/engine/scripts/flow-timers-plist.sh +86 -0
- package/engine/scripts/flow-timers.sh +182 -0
- package/engine/scripts/flow-trigger-wake.mjs +284 -0
- package/engine/scripts/flow-trigger.sh +571 -0
- package/engine/scripts/flow_billing.py +354 -0
- package/engine/scripts/flow_capability_stats.py +314 -0
- package/engine/scripts/flow_case.py +194 -0
- package/engine/scripts/flow_claim.py +497 -0
- package/engine/scripts/flow_config.py +1024 -0
- package/engine/scripts/flow_failure.py +588 -0
- package/engine/scripts/flow_learn.py +105 -0
- package/engine/scripts/flow_ledger.py +235 -0
- package/engine/scripts/flow_policy.py +238 -0
- package/engine/scripts/flow_preflight.py +339 -0
- package/engine/scripts/flow_registry.py +341 -0
- package/engine/scripts/flow_retry.py +304 -0
- package/engine/scripts/flow_return_to_ops.py +191 -0
- package/engine/scripts/flow_source.py +315 -0
- package/engine/scripts/flow_sources/__init__.py +53 -0
- package/engine/scripts/flow_sources/cases_json.py +106 -0
- package/engine/scripts/flow_sources/common.py +428 -0
- package/engine/scripts/flow_sources/feishu_base.py +273 -0
- package/engine/scripts/flow_stale.py +185 -0
- package/engine/scripts/flow_status_reconcile.py +344 -0
- package/engine/scripts/flow_triage.py +849 -0
- package/engine/scripts/flow_write_verify.py +496 -0
- package/engine/scripts/fulfillment.js +586 -0
- package/engine/scripts/gen-agent-bundles.mjs +276 -0
- package/engine/scripts/gen-preset-rows.mjs +259 -0
- package/engine/scripts/init-ml.sh +2275 -0
- package/engine/scripts/install-internal-role.sh +190 -0
- package/engine/scripts/install-license.mjs +641 -0
- package/engine/scripts/install-one-shot.mjs +665 -0
- package/engine/scripts/integration_contract.py +220 -0
- package/engine/scripts/integrations/wenyao/adapter.py +98 -0
- package/engine/scripts/integrations/xzh/adapter.py +75 -0
- package/engine/scripts/invoice-mapping.example.json +40 -0
- package/engine/scripts/invoice-table.sh +194 -0
- package/engine/scripts/invoice_carrier_notice.py +279 -0
- package/engine/scripts/invoice_fail_closeout.py +297 -0
- package/engine/scripts/invoice_lip_runner.py +1508 -0
- package/engine/scripts/invoice_lip_runner.py.bak-20260922-1710-freready +1090 -0
- package/engine/scripts/invoice_ready_audit.py +191 -0
- package/engine/scripts/invoice_runner.py +675 -0
- package/engine/scripts/invoice_stageb.py +516 -0
- package/engine/scripts/launchd/com.whyshu.argent-bridge.plist.example +42 -0
- package/engine/scripts/launchd/com.whyshu.fulfillment-poll.plist.example +29 -0
- package/engine/scripts/lease.py +377 -0
- package/engine/scripts/lib/agent-install.mjs +649 -0
- package/engine/scripts/lib/argent-datadir.mjs +16 -0
- package/engine/scripts/lib/argent_datadir.py +262 -0
- package/engine/scripts/lib/argent_filelock.py +507 -0
- package/engine/scripts/lib/bootcheck-wiring.mjs +510 -0
- package/engine/scripts/lib/cdp_ports.py +197 -0
- package/engine/scripts/lib/customer-env.mjs +102 -0
- package/engine/scripts/lib/daily-report-core.mjs +517 -0
- package/engine/scripts/lib/dep-declaration.mjs +81 -0
- package/engine/scripts/lib/deps-migrate.mjs +310 -0
- package/engine/scripts/lib/dsh-host-resolve.mjs +109 -0
- package/engine/scripts/lib/dsh-scope.mjs +500 -0
- package/engine/scripts/lib/dsh-snapshot-path.mjs +35 -0
- package/engine/scripts/lib/dsh-versions.mjs +96 -0
- package/engine/scripts/lib/dsh-web-probe.mjs +214 -0
- package/engine/scripts/lib/is-direct-run.mjs +61 -0
- package/engine/scripts/lib/patch-compose.mjs +241 -0
- package/engine/scripts/lib/patch-target.mjs +35 -0
- package/engine/scripts/lib/plugin-lifecycle.mjs +128 -0
- package/engine/scripts/lib/plugin-packages.mjs +332 -0
- package/engine/scripts/lib/preset-compat.mjs +127 -0
- package/engine/scripts/lib/preset-install.mjs +575 -0
- package/engine/scripts/lib/preset-mechanism.mjs +56 -0
- package/engine/scripts/lib/preset-sync.mjs +59 -0
- package/engine/scripts/lib/profile-retire.mjs +97 -0
- package/engine/scripts/lib/python-runtime.mjs +190 -0
- package/engine/scripts/lib/registry.mjs +72 -0
- package/engine/scripts/lib/repo-root.sh +56 -0
- package/engine/scripts/lib/schedule-mechanism.mjs +659 -0
- package/engine/scripts/lib/schedule-probe.mjs +835 -0
- package/engine/scripts/lib/scope-pointer.mjs +164 -0
- package/engine/scripts/lib/settings-panels.mjs +816 -0
- package/engine/scripts/lib/stdio_utf8.py +86 -0
- package/engine/scripts/lib/wake-auth.mjs +392 -0
- package/engine/scripts/lib/webdriver_ipc.py +264 -0
- package/engine/scripts/lib/zstd-frames.mjs +104 -0
- package/engine/scripts/license-e2e-drill.mjs +426 -0
- package/engine/scripts/license-gate.mjs +245 -0
- package/engine/scripts/lip_backlog.py +171 -0
- package/engine/scripts/lip_table.py +185 -0
- package/engine/scripts/local-usage.mjs +214 -0
- package/engine/scripts/migrate-providers.mjs +127 -0
- package/engine/scripts/migrate-skills.sh +65 -0
- package/engine/scripts/ml-oauth-setup.mjs +550 -0
- package/engine/scripts/ml-store-ids.json +26 -0
- package/engine/scripts/ml_artifact_attribution.py +333 -0
- package/engine/scripts/ml_capacity_probe.py +1158 -0
- package/engine/scripts/ml_hub_status.py +1556 -0
- package/engine/scripts/ml_schema_check.py +115 -0
- package/engine/scripts/ml_store_ids.py +139 -0
- package/engine/scripts/npm-publish-argent.mjs +499 -0
- package/engine/scripts/npm-publish-argent.sh +20 -0
- package/engine/scripts/orchestrator.py +13811 -0
- package/engine/scripts/pack-plugins.mjs +140 -0
- package/engine/scripts/patches/dsh-llm-deepseek--transport-diagnostics.patch +297 -0
- package/engine/scripts/patches/dsh-session--append-message-shape.patch +15 -0
- package/engine/scripts/patches/dsh-session--tool-result-block-017.patch +28 -0
- package/engine/scripts/patches/dsh-session-format-v0-to-v1--permission-preset-origin.patch +11 -0
- package/engine/scripts/patches/dsh-session-format-v3-to-v4--turn-outside-replace.patch +20 -0
- package/engine/scripts/patches/dsh-session-persistence-jsonl--worker-bundled-migrators.patch +39 -0
- package/engine/scripts/patches/dsh-tools--render-return-shape.patch +50 -0
- package/engine/scripts/patches/verify-bundled-migrators.mjs +79 -0
- package/engine/scripts/patches/verify-hardening.mjs +167 -0
- package/engine/scripts/patches/verify-session-migration.mjs +149 -0
- package/engine/scripts/patches/verify-transport-diagnostics.mjs +227 -0
- package/engine/scripts/plan-role-rename.mjs +125 -0
- package/engine/scripts/plugin-bootcheck.mjs +451 -0
- package/engine/scripts/plugin-composition.json +15 -0
- package/engine/scripts/poc-mcp-host.mjs +455 -0
- package/engine/scripts/poll-fulfillment.sh +293 -0
- package/engine/scripts/poll-invoice.sh +74 -0
- package/engine/scripts/profile-doctor.mjs +686 -0
- package/engine/scripts/provider-select.sh +70 -0
- package/engine/scripts/provider-switch.sh +146 -0
- package/engine/scripts/provider-sync-target.check.mjs +64 -0
- package/engine/scripts/python-runtime.sh +78 -0
- package/engine/scripts/query_runner.py +310 -0
- package/engine/scripts/release-argent.sh +574 -0
- package/engine/scripts/repair-unresolved-tools.mjs +178 -0
- package/engine/scripts/run-tests.sh +256 -0
- package/engine/scripts/self-sign-license.mjs +179 -0
- package/engine/scripts/session-health-scan.mjs +63 -0
- package/engine/scripts/session-repair-tool-result-content.mjs +181 -0
- package/engine/scripts/setup.sh +22 -0
- package/engine/scripts/skill-install.mjs +629 -0
- package/engine/scripts/sku_translate.py +148 -0
- package/engine/scripts/sync-plugins.sh +90 -0
- package/engine/scripts/sync-presets.sh +27 -0
- package/engine/scripts/sync-scripts.sh +445 -0
- package/engine/scripts/telemetry.py +873 -0
- package/engine/scripts/ui-copy-count.mjs +220 -0
- package/engine/scripts/usage-anchor.sh +136 -0
- package/engine/scripts/usage_report.py +531 -0
- package/engine/scripts/validate-preset-schema.mjs +208 -0
- package/engine/scripts/vertical-init.mjs +323 -0
- package/engine/scripts/vertical-lint.mjs +482 -0
- package/engine/scripts/wenyao-browser.sh +73 -0
- package/engine/scripts/wenyao-fixture-export.py +116 -0
- package/engine/scripts/wenyao-invoice-writeback.mjs +540 -0
- package/engine/scripts/wenyao-login.sh +34 -0
- package/engine/scripts/wenyao-template-map.json +292 -0
- package/engine/scripts/wenyao_attach.py +131 -0
- package/engine/scripts/wenyao_download_template.py +144 -0
- package/engine/scripts/wenyao_fill_template.py +568 -0
- package/engine/scripts/wenyao_login.py +145 -0
- package/engine/scripts/wenyao_portal.py +633 -0
- package/engine/scripts/wenyao_recon.py +230 -0
- package/engine/scripts/wenyao_run.py +503 -0
- package/engine/scripts/wenyao_shadow_diff.py +160 -0
- package/engine/scripts/wenyao_skill_contract.py +156 -0
- package/engine/scripts/wenyao_template_fetch.py +175 -0
- package/engine/scripts/win/dsh-web-autostart.ps1 +372 -0
- package/engine/scripts/ziniao-auth-probe.sh +268 -0
- package/engine/scripts/ziniao-keepalive.sh +216 -0
- package/engine/scripts/ziniao-post-upgrade-check.sh +120 -0
- package/engine/scripts/ziniao-windows.sh +153 -0
- package/engine/version.mjs +1 -0
- package/engine/verticals/README.md +103 -0
- package/engine/verticals/_engine-words.baseline.txt +237 -0
- package/engine/verticals/_engine-words.json +19 -0
- package/engine/verticals/mercadolibre/README.md +43 -0
- package/engine/verticals/mercadolibre/config.json +709 -0
- package/engine/verticals/mercadolibre/flows.json +706 -0
- package/engine/verticals/mercadolibre/manifest.json +538 -0
- package/engine/verticals/mercadolibre/migrate-machine-state.mjs +138 -0
- package/engine/verticals/mercadolibre/preflight.py +143 -0
- package/engine/verticals/mercadolibre/prompts/daily-report.failure.md +69 -0
- package/engine/verticals/mercadolibre/prompts/daily-report.md +47 -0
- package/engine/verticals/mercadolibre/prompts/daily-report.scheduled.md +79 -0
- package/engine/verticals/mercadolibre/prompts/full.failure.md +125 -0
- package/engine/verticals/mercadolibre/prompts/full.md +91 -0
- package/engine/verticals/mercadolibre/prompts/wenyao-invoice.failure.md +88 -0
- package/engine/verticals/mercadolibre/prompts/wenyao-invoice.md +64 -0
- package/engine/verticals/mercadolibre/prompts/xzh-invoice.failure.md +91 -0
- package/engine/verticals/mercadolibre/prompts/xzh-invoice.md +46 -0
- package/package.json +21 -4
- package/README.md +0 -3
|
@@ -0,0 +1,571 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# 定时触发(触发②)—— **引擎泛化版(P0 · 2026-09-16)**。
|
|
3
|
+
#
|
|
4
|
+
# 轮询是**确定性脚本**(不花模型),仅当"真有票要跑"时才唤起角色会话。
|
|
5
|
+
# 实测依据:dsh-schedule 是 agent 作用域的 ⇒ 纯 dsh 定时每轮都会花一个模型回合;
|
|
6
|
+
# 故用本脚本做分钟级廉价轮询,把模型成本压到"只在有活时"。
|
|
7
|
+
#
|
|
8
|
+
# 泛化(P0):本脚本**不认识任何具体流程** —— 暂停开关 / 日志 / 并发上限 / 唤醒方式全部
|
|
9
|
+
# 来自注册表(`scripts/flow_config.py` ← `flow-requirements.json#flows.<id>`)。
|
|
10
|
+
# 加一条流程 = 注册表加一条 + 一份提示词模板,**不改本脚本**。
|
|
11
|
+
#
|
|
12
|
+
# 用法:bash scripts/flow-trigger.sh [--flow <id>] [--max N] [--dry-run] [--no-wake]
|
|
13
|
+
# --flow 缺省 wenyao-invoice(保持既有 cron/launchd 行为不变)
|
|
14
|
+
# --max 缺省取流程配置 concurrency.max
|
|
15
|
+
# --dry-run 认领与投递都不落盘(**不写 planned、不建会话、不投递**)
|
|
16
|
+
# --no-wake **只认领 + 只写台账、不唤起角色会话**(0.6.0 P1-B):宿主**原生排程**
|
|
17
|
+
# 的计划任务是**无会话**执行的,它只能跑一条**静态**命令 ⇒ 那条命令必须
|
|
18
|
+
# `--no-wake`(认领照常、`planned` 照常落台账,只是不 spawn 会话/不写 wake 事件);
|
|
19
|
+
# 与暂停开关/环境预检/停滞回收/自动重试/认领看门狗的**相对顺序与默认模式逐字等价**
|
|
20
|
+
# —— 只有"唤醒那一步"不同。⚠️ 只为**原生排程**用;0.1.7 线的激活垫片仍走默认
|
|
21
|
+
# (它要唤醒角色会话),故 `flow-timers.sh` 生成的 plist **不带**本开关。
|
|
22
|
+
set -euo pipefail
|
|
23
|
+
REPO="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
|
24
|
+
# ── 解释器解析(2026-09-22 重启事故)─────────────────────────
|
|
25
|
+
# 事故:默认值写的是开发机 `~/.dsh/argent/venv/bin/python3`,而 **/tmp 重启即被清空** ⇒ 重启后
|
|
26
|
+
# 执行 PY 失败、脚本却 **exit 0 静默空转**:四个定时器每 5 分钟"正常"跑一遍、什么也不做,
|
|
27
|
+
# 日志/台账都看不出异常(与上午那次冻结同类:静默停摆比崩溃更贵)。
|
|
28
|
+
# 现在:多候选(ARGENT_PY → 安装目录 venv → 仓库 .venv → PATH 上的 python3),
|
|
29
|
+
# 全找不到就**响亮失败**(exit 2 + 修复指引),绝不假装跑过。
|
|
30
|
+
pick_py() {
|
|
31
|
+
local c
|
|
32
|
+
# 候选(2026-09-22):ARGENT_PY → DSH_HOME/argent/venv → ~/.dsh/argent/venv → 仓库 .venv → PATH
|
|
33
|
+
for c in "${ARGENT_PY:-}" "${DSH_HOME:-$HOME/.dsh}/argent/venv/bin/python3" \
|
|
34
|
+
"$HOME/.dsh/argent/venv/bin/python3" "${REPO:-}/.venv/bin/python3" \
|
|
35
|
+
"$(command -v python3 2>/dev/null || true)"; do
|
|
36
|
+
[ -n "$c" ] || continue
|
|
37
|
+
[ -x "$c" ] || continue
|
|
38
|
+
"$c" -c 'import openpyxl, yaml' 2>/dev/null || continue # 硬要求:配置/发票生成必需
|
|
39
|
+
"$c" -c 'import PIL, playwright' 2>/dev/null || \
|
|
40
|
+
echo "[$(date '+%F %T')] ⚠️ 解释器 $c 缺 pillow/playwright ⇒ 浏览器流程会失败(修:node install-argent.mjs --venv-only)" >&2
|
|
41
|
+
printf '%s' "$c"; return 0
|
|
42
|
+
done
|
|
43
|
+
return 1
|
|
44
|
+
}
|
|
45
|
+
if ! PY="$(pick_py)"; then
|
|
46
|
+
echo "[$(date '+%F %T')] ❌ 流程 ${FLOW:-?}:找不到可用解释器(需含 openpyxl + yaml)" >&2
|
|
47
|
+
echo " 候选: ARGENT_PY / ~/.dsh/argent/venv/bin/python3 / <repo>/.venv/bin/python3 / PATH 的 python3" >&2
|
|
48
|
+
echo " 修复: python3 -m venv ~/.dsh/argent/venv && ~/.dsh/argent/venv/bin/pip install openpyxl pyyaml" >&2
|
|
49
|
+
exit 2
|
|
50
|
+
fi
|
|
51
|
+
|
|
52
|
+
FLOW="wenyao-invoice"; MAX=""; DRY=""; NO_WAKE=""
|
|
53
|
+
while [ $# -gt 0 ]; do
|
|
54
|
+
case "$1" in
|
|
55
|
+
--flow) FLOW="$2"; shift 2;;
|
|
56
|
+
--max) MAX="$2"; shift 2;;
|
|
57
|
+
--dry-run) DRY="--dry-run"; shift;;
|
|
58
|
+
--no-wake) NO_WAKE=1; shift;;
|
|
59
|
+
-h|--help) sed -n '12,21p' "${BASH_SOURCE[0]}" | sed 's/^# \{0,1\}//'; exit 0;;
|
|
60
|
+
*) shift;;
|
|
61
|
+
esac
|
|
62
|
+
done
|
|
63
|
+
|
|
64
|
+
cfg() { "$PY" "$REPO/scripts/flow_config.py" --flow "$FLOW" --get "$1"; }
|
|
65
|
+
|
|
66
|
+
# ── 数据目录(**唯一判据,禁止在 bash 里重写三级链**)────────────────────────
|
|
67
|
+
# `ARGENT_DATA_DIR → $DSH_HOME/argent → ~/.dsh/argent` 的判据只有一份:Python 单一实现
|
|
68
|
+
# `scripts/lib/argent_datadir.py`(与 JS 内核 core/lib/argent-datadir.mjs 同语义),
|
|
69
|
+
# 这里经 CLI 包装 `scripts/argent-datadir.py` 取值。历史形态 `${ARGENT_DATA_DIR:-$HOME/.dsh/argent}`
|
|
70
|
+
# 就是"隔离实例一半写隔离目录、一半写生产"的病根(2026-10-01 桌面端事故)。
|
|
71
|
+
# 取不到 ⇒ **响亮停住**(fail-closed,绝不猜目录 —— 猜错就是往生产写)。
|
|
72
|
+
DATA_DIR="$("$PY" "$REPO/scripts/argent-datadir.py" 2>/dev/null || true)"
|
|
73
|
+
if [ -z "$DATA_DIR" ]; then
|
|
74
|
+
echo "[$(date '+%F %T')] ❌ 流程 ${FLOW}:数据目录判据不可用($REPO/scripts/argent-datadir.py 取不到)⇒ 停住,不猜目录" >&2
|
|
75
|
+
echo " 修复: 确认 scripts/lib/argent_datadir.py 存在且 python3 可用(ARGENT_PY 可指定解释器)" >&2
|
|
76
|
+
exit 2
|
|
77
|
+
fi
|
|
78
|
+
|
|
79
|
+
PAUSE="$(cfg paths.pauseSwitch)" # 路径(trigger.pauseSwitch 只是文件名)
|
|
80
|
+
# 迁移期(2026-09-29 P2a):命名空间化后开关在 verticals/<id>/PAUSE.<flow>,
|
|
81
|
+
# 机器上的旧开关(PAUSE_POLL 等)仍在 ⇒ **两条都查**,避免"改了注册表那一刻流程被静默解停"。
|
|
82
|
+
PAUSE_LEGACY="$(cfg paths.pauseSwitchLegacy 2>/dev/null || true)"
|
|
83
|
+
LOG="$(cfg paths.log)"
|
|
84
|
+
WAKE="$(cfg trigger.wake)"
|
|
85
|
+
# 本轮语义(注册表 trigger.mode;缺省 shadow):**日志必须如实播报** ——
|
|
86
|
+
# FULL 已转真实运行后,日志还在说"影子模式"会误导操作者(2026-09-18 修)。
|
|
87
|
+
MODE="$(cfg trigger.mode)"
|
|
88
|
+
case "$MODE" in
|
|
89
|
+
real) MODE_LABEL="真实写入" ;;
|
|
90
|
+
*) MODE_LABEL="影子(零副作用)" ;;
|
|
91
|
+
esac
|
|
92
|
+
[ -n "$MAX" ] || MAX="$(cfg concurrency.max)"
|
|
93
|
+
# dry-run 也播报本轮语义:开跑前就能核对"这次是影子还是真写"
|
|
94
|
+
[ -z "$DRY" ] || echo "[dry-run] 流程 ${FLOW}:本轮语义=${MODE_LABEL}(注册表 trigger.mode=${MODE})"
|
|
95
|
+
|
|
96
|
+
# 暂停开关(与既有 PAUSE_POLL / PAUSE_INVOICE 同风格):存在该文件即不唤起
|
|
97
|
+
if [ -f "$PAUSE" ]; then
|
|
98
|
+
echo "[$(date '+%F %T')] 流程 ${FLOW} 已暂停(存在 ${PAUSE})⇒ 不唤起"
|
|
99
|
+
exit 0
|
|
100
|
+
fi
|
|
101
|
+
if [ -n "$PAUSE_LEGACY" ] && [ -f "$PAUSE_LEGACY" ]; then
|
|
102
|
+
echo "[$(date '+%F %T')] 流程 ${FLOW} 已暂停(存在旧开关 ${PAUSE_LEGACY})⇒ 不唤起;⏳ 退休待办:迁移到 ${PAUSE}"
|
|
103
|
+
exit 0
|
|
104
|
+
fi
|
|
105
|
+
|
|
106
|
+
# ── 发票「放行勾」残留巡检(2026-09-18 用户发现 77447894 残留;安全网,每小时至多一次)──
|
|
107
|
+
# 根因已在源头上修掉(文鳐 P14 收口补上 --clear-ready),但历史遗留/异常中断/人工改表
|
|
108
|
+
# 都可能再留下"发票状态=已完成 却还勾着放行"的行:它在表上看着像"还等着跑",
|
|
109
|
+
# 且一旦台账那条 done 失效(身份漂移/台账轮转),开启闸门会重新放行 ⇒ 重复开票、重复上传。
|
|
110
|
+
# 已完成的行不会再跑流程 ⇒ 不能指望"顺手清掉",必须独立巡检。
|
|
111
|
+
# 只读 + 每小时一次(避免每轮全表读);有残留才发内部群卡(无残留时静默,不制造噪音)。
|
|
112
|
+
case "$FLOW" in
|
|
113
|
+
wenyao-invoice|xzh-invoice)
|
|
114
|
+
AUD_STAMP="$DATA_DIR/.invoice-ready-audit.stamp"
|
|
115
|
+
NOW_S=$(date +%s)
|
|
116
|
+
LAST_S=$(cat "$AUD_STAMP" 2>/dev/null || echo 0)
|
|
117
|
+
case "$LAST_S" in ''|*[!0-9]*) LAST_S=0;; esac
|
|
118
|
+
if [ $((NOW_S - LAST_S)) -ge 3600 ]; then
|
|
119
|
+
echo "$NOW_S" > "$AUD_STAMP" 2>/dev/null || true
|
|
120
|
+
AUD_OUT=$("$PY" "$REPO/scripts/invoice_ready_audit.py" --flow "$FLOW" 2>&1); AUD_CODE=$?
|
|
121
|
+
if [ "$AUD_CODE" -eq 1 ]; then
|
|
122
|
+
{
|
|
123
|
+
echo "[$(date '+%F %T')] ⚠️ 流程 ${FLOW}:检出**放行勾残留**(发票状态=已完成 却仍勾着「发票就绪」)"
|
|
124
|
+
printf '%s\n' "$AUD_OUT" | sed 's/^/ /'
|
|
125
|
+
echo " ⇒ 释放:${PY} ${REPO}/scripts/invoice_ready_audit.py --flow ${FLOW} --fix --yes"
|
|
126
|
+
} | tee -a "$LOG" >&2
|
|
127
|
+
# 内部群卡片(人看得见才算"暴露";缺推送身份时 feishu-card 会响亮报错)
|
|
128
|
+
node "$REPO/scripts/feishu-card.mjs" --template failure-alert --to internal --flow "$FLOW" \
|
|
129
|
+
--case "invoice-ready-leftover__${FLOW}" --level needs_human \
|
|
130
|
+
--error "放行勾残留(发票状态=已完成 ∧ 发票就绪=true)" \
|
|
131
|
+
--action "运行 invoice_ready_audit.py --fix --yes 释放" --send >>"$LOG" 2>&1 || true
|
|
132
|
+
elif [ "$AUD_CODE" -ne 0 ]; then
|
|
133
|
+
echo "[$(date '+%F %T')] ⚠️ 流程 ${FLOW}:放行勾巡检**读取失败**(exit=${AUD_CODE})——未做任何写入" | tee -a "$LOG" >&2
|
|
134
|
+
printf '%s\n' "$AUD_OUT" | sed 's/^/ /' >>"$LOG" 2>&1
|
|
135
|
+
fi
|
|
136
|
+
fi
|
|
137
|
+
;;
|
|
138
|
+
esac
|
|
139
|
+
|
|
140
|
+
# 日志目录必须**在第一次写日志之前**建好(2026-09-30 修):闸门处的 `tee -a "$LOG"` 若目录不存在,
|
|
141
|
+
# `set -o pipefail` 下 tee 非零 ⇒ 整个触发器 **exit 1**(四个定时器每 5 分钟静默失败一次,
|
|
142
|
+
# 且日志里什么都没有 —— 正是本仓库最忌讳的"静默停摆"形态)。原先这行在闸门之后,够不着它。
|
|
143
|
+
mkdir -p "$(dirname "$LOG")"
|
|
144
|
+
|
|
145
|
+
# ── 续订提醒(2026-09-22;每天至多一次)──────────────────────────────
|
|
146
|
+
# 租约快到期(< lease.remindDays)时喊人:内部群卡片 + 台账。只有**真需要续订**时才发(不制造噪音)。
|
|
147
|
+
if [ "${ARGENT_SKIP_LEASE:-0}" != "1" ]; then
|
|
148
|
+
# stamp 属于**运行时数据**,与台账/遥测同规矩:数据目录由唯一判据给出($DSH_HOME / ARGENT_DATA_DIR
|
|
149
|
+
# 隔离时它跟着隔离;否则夹具跑一次就写进真实的 ~/.dsh/argent,测试之间还会互相污染/跳过提醒)。
|
|
150
|
+
RN_STAMP="$DATA_DIR/.lease-remind.stamp"
|
|
151
|
+
RN_NOW=$(date +%s); RN_LAST=$(cat "$RN_STAMP" 2>/dev/null || echo 0)
|
|
152
|
+
case "$RN_LAST" in ''|*[!0-9]*) RN_LAST=0;; esac
|
|
153
|
+
if [ $((RN_NOW - RN_LAST)) -ge 86400 ] && [ -z "${DRY}" ]; then # dry-run 不产生任何外部动作
|
|
154
|
+
RN_OUT="$("$PY" "$REPO/scripts/lease.py" --remind 2>&1)"
|
|
155
|
+
if printf '%s' "$RN_OUT" | grep -q "⚠️\|⛔"; then
|
|
156
|
+
echo "$RN_NOW" > "$RN_STAMP" 2>/dev/null || true
|
|
157
|
+
echo "[$(date '+%F %T')] 流程 ${FLOW}:${RN_OUT}" | tee -a "$LOG" >&2
|
|
158
|
+
"$PY" "$REPO/scripts/flow_ledger.py" --case-id "lease-remind__${FLOW}" --event blocked \
|
|
159
|
+
--by flow-lease --detail "能力租约即将到期/已过期:${RN_OUT}" >/dev/null 2>&1 || true
|
|
160
|
+
node "$REPO/scripts/feishu-card.mjs" --template need-human --to internal --flow "$FLOW" \
|
|
161
|
+
--case "lease-remind__${FLOW}" --reason "能力租约续订提醒" --ask "$RN_OUT" --send >>"$LOG" 2>&1 || true
|
|
162
|
+
fi
|
|
163
|
+
fi
|
|
164
|
+
fi
|
|
165
|
+
|
|
166
|
+
# ── 「未接入货代」提示巡检(2026-09-21 用户要求;每小时至多一次)──
|
|
167
|
+
# 运营勾了「发票就绪」、但该行货代我们没接入(如允墨/商达/鑫隆…)⇒ 不会被任何流程拾取。
|
|
168
|
+
# 这是**正确行为**(不该去动没接入的货代),但运营在表上看不出来 ⇒ 把原因写进「发票错误信息」。
|
|
169
|
+
# 只写这一个字段、只写"我方确实无法承接"的行、幂等、不覆盖人工文字(见脚本头部三条纪律)。
|
|
170
|
+
case "$FLOW" in
|
|
171
|
+
wenyao-invoice|xzh-invoice)
|
|
172
|
+
CN_STAMP="$DATA_DIR/.invoice-carrier-notice.stamp"
|
|
173
|
+
CN_NOW=$(date +%s); CN_LAST=$(cat "$CN_STAMP" 2>/dev/null || echo 0)
|
|
174
|
+
case "$CN_LAST" in ''|*[!0-9]*) CN_LAST=0;; esac
|
|
175
|
+
if [ $((CN_NOW - CN_LAST)) -ge 3600 ]; then
|
|
176
|
+
echo "$CN_NOW" > "$CN_STAMP" 2>/dev/null || true
|
|
177
|
+
CN_OUT="$("$PY" "$REPO/scripts/invoice_carrier_notice.py" --yes 2>&1)"; CN_CODE=$?
|
|
178
|
+
if [ "$CN_CODE" -ne 0 ]; then
|
|
179
|
+
echo "[$(date '+%F %T')] ⚠️ 流程 ${FLOW}:「未接入货代」提示写入未全部成功(exit=${CN_CODE})——已重试,下轮会继续:"
|
|
180
|
+
printf '%s\n' "$CN_OUT" | sed 's/^/ /' | tee -a "$LOG" >&2
|
|
181
|
+
elif printf '%s' "$CN_OUT" | grep -q "✅\|⚠️"; then
|
|
182
|
+
printf '%s\n' "$CN_OUT" | sed 's/^/ /' | tee -a "$LOG" >&2
|
|
183
|
+
fi
|
|
184
|
+
fi
|
|
185
|
+
;;
|
|
186
|
+
esac
|
|
187
|
+
|
|
188
|
+
# ── 前置检查(用户要求 2026-09-17):缺 lark-cli 身份/表权限/推送身份 ⇒ **响亮停住并给人工指南** ──
|
|
189
|
+
if [ "${ARGENT_SKIP_PREFLIGHT:-0}" != "1" ]; then
|
|
190
|
+
PF_OUT="$("$PY" "$REPO/scripts/flow_preflight.py" --flow "$FLOW" 2>&1)"; PF_CODE=$?
|
|
191
|
+
if [ "$PF_CODE" -ne 0 ]; then
|
|
192
|
+
{
|
|
193
|
+
echo "[$(date '+%F %T')] 流程 ${FLOW} 前置检查未通过(exit=${PF_CODE})——本轮不派发,需人工处理:"
|
|
194
|
+
printf '%s\n' "$PF_OUT" | sed 's/^/ /'
|
|
195
|
+
} | tee -a "$LOG" >&2
|
|
196
|
+
"$PY" "$REPO/scripts/flow_ledger.py" --case-id "preflight__${FLOW}" --event blocked \
|
|
197
|
+
--by flow-preflight --detail "前置检查未通过(缺配置/权限)⇒ 已停派并输出人工指南" >/dev/null 2>&1 || true
|
|
198
|
+
# 遥测:前置检查失败也是**引擎级**事件(缺 lark-cli 身份/表权限/推送身份这类事,
|
|
199
|
+
# 客户机上没人看日志,只有上报了维护者才知道)
|
|
200
|
+
"$PY" "$REPO/scripts/telemetry.py" --emit --kind engine --flow "$FLOW" \
|
|
201
|
+
--error-class needs_human --detail "前置检查未通过 exit=${PF_CODE}" >/dev/null 2>&1 || true
|
|
202
|
+
"$PY" "$REPO/scripts/telemetry.py" --flush >/dev/null 2>&1 || true
|
|
203
|
+
exit 0
|
|
204
|
+
fi
|
|
205
|
+
fi
|
|
206
|
+
|
|
207
|
+
# 授权闸门的模式与判定入口(定义必须在租约闸门**之前**:租约 deny 分支要用它回落)。
|
|
208
|
+
# 模式(`ARGENT_LICENSE_ENFORCE`):
|
|
209
|
+
# 0|off 逃生阀:**完全跳过**许可与租约闸门(不读许可、不改派发;与 ARGENT_SKIP_LEASE=1 等效)
|
|
210
|
+
# observe **默认**:照常算"能不能用",但无论结论如何**都不停派**;必须留证据(JSONL + 日志)
|
|
211
|
+
# enforce 不可用 ⇒ 沿用租约停派分支的写法(台账 + 日志 + 不派发)
|
|
212
|
+
# 判定顺序(**租约优先**,既有逻辑一字不改):① 有可用租约 ⇒ 通过(上面已经放行);
|
|
213
|
+
# ② 租约不可用 ⇒ 问离线许可(函数在租约 deny 分支里就会被调一次,见上);
|
|
214
|
+
# ③ 租约闸门整体未启用(`ARGENT_SKIP_LEASE=1` / 注册表 lease.enforce=false 时它恒 ok)⇒ 同 ①。
|
|
215
|
+
# 证据:<data>/license-gate.jsonl(每次判定一行 JSONL,0600;>5MB 自动轮转成 .1)+ .out.log 一行人读结论。
|
|
216
|
+
# 说明:走 ① 时**不读许可**(现有租约逻辑一字不改,也不落许可证据);想让闸门**每轮都读一次许可并留证**
|
|
217
|
+
# (例如要独立核对"离线许可本身还有效吗"),设 `ARGENT_LICENSE_GATE_FORCE=1`。
|
|
218
|
+
LICENSE_MODE="${ARGENT_LICENSE_ENFORCE:-observe}"
|
|
219
|
+
case "$LICENSE_MODE" in
|
|
220
|
+
0|off|false|no) LICENSE_MODE="off" ;;
|
|
221
|
+
enforce|1|true|yes) LICENSE_MODE="enforce" ;;
|
|
222
|
+
*) LICENSE_MODE="observe" ;; # 取值不认识 ⇒ 一律按默认(**宁可只告警,不可误停**)
|
|
223
|
+
esac
|
|
224
|
+
# 判定 + 取证一次(结果在 LICENSE_OUT / LICENSE_CODE;`|| true` 防 set -e 提前退出)。
|
|
225
|
+
license_gate_decide() {
|
|
226
|
+
if [ "$LICENSE_MODE" = "off" ]; then
|
|
227
|
+
LICENSE_OUT="off"; LICENSE_CODE=0
|
|
228
|
+
echo "[$(date '+%F %T')] 流程 ${FLOW}:许可闸门已关闭(逃生阀已开:ARGENT_LICENSE_ENFORCE=off)⇒ 跳过许可与租约判定,照常派发" | tee -a "$LOG"
|
|
229
|
+
return 0
|
|
230
|
+
fi
|
|
231
|
+
if [ ! -f "$REPO/scripts/license-gate.mjs" ]; then
|
|
232
|
+
# 旧机器(还没 git pull 到许可闸门):**响亮告警但不停派** —— 与默认 observe 等价
|
|
233
|
+
LICENSE_OUT="nofile"; LICENSE_CODE=0
|
|
234
|
+
echo "[$(date '+%F %T')] 流程 ${FLOW} ⚠️ 缺少 scripts/license-gate.mjs(旧版本)⇒ 许可闸门不生效,本轮照常派发" | tee -a "$LOG" >&2
|
|
235
|
+
return 0
|
|
236
|
+
fi
|
|
237
|
+
# 带能力参数(可加业务线):`--cap` 命中才放行;业务线口径 = 许可里 verticals 为空即不限。
|
|
238
|
+
# 业务线从**注册表解析出的规范 id** 里取(P2a 起规范 id = `<业务线>.<流程>`);取不到就不传
|
|
239
|
+
# ⇒ 只看能力,不看业务线(绝不因为"解析不出业务线"把客户机停掉)。
|
|
240
|
+
LICENSE_CAP="$CAP"; [ -n "$LICENSE_CAP" ] || LICENSE_CAP="$FLOW"
|
|
241
|
+
LICENSE_CANON="$(cfg flowId 2>/dev/null || true)" # 规范 id:P2a 起 = `<业务线>.<流程>`
|
|
242
|
+
LICENSE_VERTICAL="${LICENSE_CANON%%.*}"
|
|
243
|
+
[ "$LICENSE_VERTICAL" != "$LICENSE_CANON" ] || LICENSE_VERTICAL=""
|
|
244
|
+
set +e
|
|
245
|
+
LICENSE_OUT="$(node "$REPO/scripts/license-gate.mjs" --flow "$FLOW" --cap "$LICENSE_CAP" \
|
|
246
|
+
${LICENSE_VERTICAL:+--vertical "$LICENSE_VERTICAL"} --mode "$LICENSE_MODE" 2>&1)"
|
|
247
|
+
LICENSE_CODE=$?
|
|
248
|
+
set -e
|
|
249
|
+
return 0
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
# ── 能力租约闸门(批 C4,2026-09-21):执行前查一次授权(签名本地可验,离线也拦得住)──
|
|
253
|
+
# 语义:ok=放行;grace=已过期但 ≤ 宽限期 ⇒ **放行但告警**(不突然掐死生产);deny ⇒ 停派 + 人工指南。
|
|
254
|
+
# **默认不拦人**(注册表 lease.enforce=false):服务端端点部署 + 客户端拿到租约之后才开闸,
|
|
255
|
+
# 上线顺序见注册表 lease._why。
|
|
256
|
+
if [ "${ARGENT_SKIP_LEASE:-0}" != "1" ]; then
|
|
257
|
+
# 自动续订(2026-09-22):距到期 < lease.refreshAheadHours(默认 12h)才去换,避免每 5 分钟打扰服务端;
|
|
258
|
+
# 换不到**不算错误**(离线容忍:宽限期内继续跑,由下面的检查与每日提醒负责喊人)。
|
|
259
|
+
"$PY" "$REPO/scripts/lease.py" --refresh-if-needed >/dev/null 2>&1 || true
|
|
260
|
+
CAP="$("$PY" "$REPO/scripts/flow_config.py" --flow "$FLOW" --get capability 2>/dev/null || echo "")"
|
|
261
|
+
LEASE_OUT="$("$PY" "$REPO/scripts/lease.py" --check "${CAP:-$FLOW}" --flow "$FLOW" 2>&1)" && LEASE_CODE=0 || LEASE_CODE=$?
|
|
262
|
+
# 租约**可用**(含"未启用强制"这一现状)⇒ 许可闸门整段不介入(判定顺序:租约优先)。
|
|
263
|
+
[ "$LEASE_CODE" -ne 0 ] || LEASE_OK=1
|
|
264
|
+
if [ "$LEASE_CODE" -eq 3 ]; then
|
|
265
|
+
{
|
|
266
|
+
echo "[$(date '+%F %T')] 流程 ${FLOW} ⚠️ 能力租约已过期但在宽限期内 —— 本轮仍执行,请尽快续订:"
|
|
267
|
+
printf '%s\n' "$LEASE_OUT" | sed 's/^/ /'
|
|
268
|
+
} | tee -a "$LOG" >&2
|
|
269
|
+
"$PY" "$REPO/scripts/flow_ledger.py" --case-id "lease__${FLOW}" --event blocked \
|
|
270
|
+
--by flow-lease --detail "能力租约过期(宽限期内,本轮放行)" >/dev/null 2>&1 || true
|
|
271
|
+
elif [ "$LEASE_CODE" -ne 0 ]; then
|
|
272
|
+
{
|
|
273
|
+
echo "[$(date '+%F %T')] 流程 ${FLOW} ⛔ 能力租约不可用(exit=${LEASE_CODE})——本轮不派发,需人工处理:"
|
|
274
|
+
printf '%s\n' "$LEASE_OUT" | sed 's/^/ /'
|
|
275
|
+
} | tee -a "$LOG" >&2
|
|
276
|
+
# 回落离线许可(2026-09-30)──────────────────────────────────
|
|
277
|
+
# 判据:**租约或离线许可任一通过即视为可用**。客户机是 BYO + 不登录问述账号 ⇒ 换不到租约,
|
|
278
|
+
# 授权凭据只有装机时落盘的离线许可;这里若不接手,那台机器会被**误停**(授权明明有效)。
|
|
279
|
+
# 租约这一侧的所有动作(日志/台账/遥测)一字不动,只在"本来要 exit 0"那一刻多问一句许可。
|
|
280
|
+
license_gate_decide # 函数内部已处理"跑不起来",**不要**写成 `|| true`(那会把退出码吞成 0)
|
|
281
|
+
if [ "$LICENSE_OUT" = "off" ]; then
|
|
282
|
+
# 逃生阀:不判定、**也不许**说"离线许可有效"(2026-09-30 实测踩到:off 下 LICENSE_CODE=0
|
|
283
|
+
# 会被下面当成"许可有效",日志里出现"但离线许可有效 ⇒ 照常派发"这种**假陈述**)
|
|
284
|
+
LICENSE_OK=1
|
|
285
|
+
echo " ⇒ 许可闸门已关闭(ARGENT_LICENSE_ENFORCE=off)⇒ 本轮照常派发" | tee -a "$LOG" >&2
|
|
286
|
+
elif [ "$LICENSE_OUT" = "nofile" ]; then
|
|
287
|
+
LICENSE_OK=1 # 旧机器(还没 git pull 到本闸门):与默认 observe 等价(函数据已打过响亮告警)
|
|
288
|
+
else
|
|
289
|
+
echo "[$(date '+%F %T')] 流程 ${FLOW}:${LICENSE_OUT}" | tee -a "$LOG" >&2
|
|
290
|
+
# LICENSE_CODE = 许可判定(0 可用 | 3 宽限 | 1 不可用;**observe 下也是真实判定**)。
|
|
291
|
+
# 停不停派只看 mode:enforce + 1 ⇒ 落到下面原有分支照旧停派;其余一律放行。
|
|
292
|
+
if [ "$LICENSE_CODE" -eq 0 ]; then
|
|
293
|
+
LICENSE_OK=1
|
|
294
|
+
echo "[$(date '+%F %T')] 流程 ${FLOW}:能力租约不可用,但**离线许可有效** ⇒ 本轮照常派发" | tee -a "$LOG" >&2
|
|
295
|
+
elif [ "$LICENSE_CODE" -eq 3 ]; then
|
|
296
|
+
LICENSE_OK=1
|
|
297
|
+
echo "[$(date '+%F %T')] 流程 ${FLOW}:能力租约不可用,但**离线许可在宽限期内(已过期,请尽快续期)** ⇒ 本轮照常派发" | tee -a "$LOG" >&2
|
|
298
|
+
# 宽限放行也要留痕(与租约 grace 分支同一口径):这是"快要停"的前兆,必须能被检索到
|
|
299
|
+
"$PY" "$REPO/scripts/flow_ledger.py" --case-id "lease__${FLOW}" --event blocked \
|
|
300
|
+
--by flow-license --detail "离线许可过期(宽限期内,本轮放行)" >/dev/null 2>&1 || true
|
|
301
|
+
elif [ "$LICENSE_MODE" = "observe" ]; then
|
|
302
|
+
# observe:许可不可用**也不停派**(这就是"观察模式优先"的全部含义)—— 只告警。
|
|
303
|
+
# ⚠️ 必须显式置 LICENSE_OK=1:不置的话会掉进下面的"停派"分支 —— 日志写着"仍派发"、
|
|
304
|
+
# 实际却写了停派台账并在租约分支 exit 0(**说一套做一套**,比不停派更难查)。
|
|
305
|
+
LICENSE_OK=1
|
|
306
|
+
echo "[$(date '+%F %T')] 流程 ${FLOW}:能力租约不可用,且离线许可不可用 —— observe 模式**本轮仍派发**(enforce 下会停派)" | tee -a "$LOG" >&2
|
|
307
|
+
fi
|
|
308
|
+
fi
|
|
309
|
+
if [ "${LICENSE_OK:-0}" != "1" ]; then
|
|
310
|
+
# 停派:谁说的算就记谁的名字 —— 许可被拒时写 flow-license(并给出 reason 与三条人工指引);
|
|
311
|
+
# 其余(旧机器缺脚本/判定跑不起来)沿用原有 flow-lease 那条写法,一字不改。
|
|
312
|
+
if [ "$LICENSE_CODE" -eq 1 ]; then
|
|
313
|
+
LICENSE_WHY="$(printf '%s' "$LICENSE_OUT" | sed -n 's/.*(reason=\([^)]*\)).*/\1/p')"
|
|
314
|
+
"$PY" "$REPO/scripts/flow_ledger.py" --case-id "lease__${FLOW}" --event blocked \
|
|
315
|
+
--by flow-license --detail "离线许可不可用(无租约且许可被拒${LICENSE_WHY:+:${LICENSE_WHY}})⇒ 已停派" >/dev/null 2>&1 || true
|
|
316
|
+
{
|
|
317
|
+
echo "[$(date '+%F %T')] 流程 ${FLOW} ⛔ 授权不可用(无租约且离线许可被拒)——本轮不派发,需人工处理:"
|
|
318
|
+
echo " ⇒ 看许可:node ${REPO}/scripts/check-license.mjs --cap ${LICENSE_CAP:-$FLOW}"
|
|
319
|
+
echo " ⇒ 换租约:${PY} ${REPO}/scripts/lease.py --refresh(需 WHYSHU_API_KEY)"
|
|
320
|
+
echo " ⇒ 续期/换许可:联系问述科技;临时放行可设 ARGENT_LICENSE_ENFORCE=observe(只告警不停派)"
|
|
321
|
+
} >>"$LOG" 2>&1
|
|
322
|
+
echo " 详见台账 case=lease__${FLOW} 与 ${LOG}" >&2
|
|
323
|
+
else
|
|
324
|
+
"$PY" "$REPO/scripts/flow_ledger.py" --case-id "lease__${FLOW}" --event blocked \
|
|
325
|
+
--by flow-lease --detail "能力租约不可用(无租约/超出宽限/该能力未订阅)⇒ 已停派" >/dev/null 2>&1 || true
|
|
326
|
+
fi
|
|
327
|
+
"$PY" "$REPO/scripts/telemetry.py" --emit --kind engine --flow "$FLOW" \
|
|
328
|
+
--error-class needs_human --detail "能力租约不可用 exit=${LEASE_CODE}" >/dev/null 2>&1 || true
|
|
329
|
+
"$PY" "$REPO/scripts/telemetry.py" --flush >/dev/null 2>&1 || true
|
|
330
|
+
exit 0
|
|
331
|
+
fi
|
|
332
|
+
fi
|
|
333
|
+
fi
|
|
334
|
+
|
|
335
|
+
# ── 离线许可闸门(2026-09-30):租约 / 离线许可**二选一** ────────────────
|
|
336
|
+
# 背景:客户机是 **BYO(自带 DeepSeek Key)+ 不登录问述账号** ⇒ 没有账号 JWT,换不到租约。
|
|
337
|
+
# 授权凭据是装机时落盘的**离线签名许可**(`~/.dsh/argent/license.json`)。若让租约单独说话,
|
|
338
|
+
# 这台机器的四个定时器会**全部误停**(授权明明有效,只是走的是许可这条路)。
|
|
339
|
+
#
|
|
340
|
+
if [ "${LICENSE_OK:-0}" = "1" ]; then
|
|
341
|
+
: # 租约不可用但**离线许可可用**(判定已在上面完成)⇒ 放行,且不重复判定/不重复留证
|
|
342
|
+
elif [ "${LEASE_OK:-0}" = "1" ]; then
|
|
343
|
+
: # 有可用租约 ⇒ 现有逻辑一字不改(许可闸门**根本不介入**,连证据都不写)
|
|
344
|
+
else
|
|
345
|
+
# 租约闸门整体未启用(`ARGENT_SKIP_LEASE=1`,或注册表 lease.enforce=false 时它恒 ok)
|
|
346
|
+
# ⇒ 与"有租约"同等对待:今天的行为一字不改。
|
|
347
|
+
license_gate_decide # 同上:`|| true` 会让 $? 恒为 0 ⇒ 许可无效被当成放行(实测踩到)
|
|
348
|
+
if [ "$LICENSE_OUT" = "off" ] || [ "$LICENSE_OUT" = "nofile" ]; then
|
|
349
|
+
: # 文案已在函数里打过
|
|
350
|
+
else
|
|
351
|
+
echo "[$(date '+%F %T')] 流程 ${FLOW}:${LICENSE_OUT}" | tee -a "$LOG"
|
|
352
|
+
fi
|
|
353
|
+
if [ "$LICENSE_CODE" -eq 1 ]; then
|
|
354
|
+
# enforce + 许可不可用。⚠️ 这条路径在**租约 deny 分支**里已经处理过(那里才同时握着
|
|
355
|
+
# 租约与许可两个结论,台账/遥测/人工指南都写在那一处);能走到这里只说明上面那个分支
|
|
356
|
+
# 被 `ARGENT_SKIP_LEASE=1` 整个跳过了。此时租约那一侧**本来就没在拦人**,故这里只响亮告警。
|
|
357
|
+
LICENSE_WHY="$(printf '%s' "$LICENSE_OUT" | sed -n 's/.*(reason=\([^)]*\)).*/\1/p')"
|
|
358
|
+
echo "[$(date '+%F %T')] 流程 ${FLOW} ⛔ 离线许可不可用(${LICENSE_WHY:-reason=?})——enforce 下本应停派;" >&2
|
|
359
|
+
echo " 本轮租约闸门被 ARGENT_SKIP_LEASE=1 跳过(那一侧本来也不拦人),故不在此停派。" >&2
|
|
360
|
+
echo " ⇒ 看许可:node ${REPO}/scripts/check-license.mjs --cap ${LICENSE_CAP:-$FLOW}" >&2
|
|
361
|
+
elif [ "$LICENSE_CODE" -eq 3 ]; then
|
|
362
|
+
# enforce 下的宽限:放行但记台账(与租约 grace 分支同一口径)
|
|
363
|
+
"$PY" "$REPO/scripts/flow_ledger.py" --case-id "lease__${FLOW}" --event blocked \
|
|
364
|
+
--by flow-license --detail "离线许可过期(宽限期内,本轮放行)" >/dev/null 2>&1 || true
|
|
365
|
+
fi
|
|
366
|
+
fi
|
|
367
|
+
|
|
368
|
+
# 测试钩子(许可闸门回归用):只跑到闸门就停,**绝不认领/派发**。
|
|
369
|
+
# 为什么需要:验证"observe 不停派 / enforce 停派"要么真去认领(= 抢生产行),要么只能读源码。
|
|
370
|
+
# 有了它,回归测试可以用隔离数据目录把整段闸门真跑一遍(判定顺序 + 证据落盘 + 退出码全覆盖),
|
|
371
|
+
# 而认领之后的逻辑(认领/唤醒,与许可无关)不参与 —— 两边的覆盖面反而都更清楚。
|
|
372
|
+
if [ "${ARGENT_LICENSE_GATE_ONLY:-0}" = "1" ]; then
|
|
373
|
+
echo "[$(date '+%F %T')] 流程 ${FLOW}:(测试钩子 ARGENT_LICENSE_GATE_ONLY=1)闸门后停止,不认领/不派发"
|
|
374
|
+
exit 0
|
|
375
|
+
fi
|
|
376
|
+
|
|
377
|
+
# ⚠️ 必须**同时**拿到 stdout 与退出码:`|| echo '{}'` 会把 exit=3(无票)时
|
|
378
|
+
# 已经打印出来的 JSON(含"为什么没票")覆盖成空对象 —— 2026-09-18 实测踩到。
|
|
379
|
+
# 遥测(2026-09-21):每轮**心跳一条(当天去重)+ 把队列发出去**。
|
|
380
|
+
# 刻意放在"认领之前":心跳回答的是"这台客户机今天还活着吗",
|
|
381
|
+
# · 不能挂在票上(无票时角色根本不会起);
|
|
382
|
+
# · 也不能挂在认领成功上(认领失败恰恰是最该被看见的情况——今天实测过:full 因飞书读表
|
|
383
|
+
# 瞬时错误 1204 认领失败,下一轮自愈,但此前只有一行日志,没有任何观测面)。
|
|
384
|
+
# 事件采集一律只落本地队列(见 flow_ledger 的钩子),联网只在这一处;失败不影响本轮。
|
|
385
|
+
"$PY" "$REPO/scripts/telemetry.py" --heartbeat --flush >/dev/null 2>&1 || true
|
|
386
|
+
|
|
387
|
+
# ── W2 配套(2026-09-24):紫鸟**授权心跳**(每天一次,去重靠脚本内的 stamp)──────────
|
|
388
|
+
# 常驻 WebDriver 模式的鉴权是"随请求带 company/username/password + applyAuth",不依赖 GUI 登录态;
|
|
389
|
+
# 但"到底多久要人工介入一次"供应商文档没写、我们也没实测过。故把这件事变成**可观测**:
|
|
390
|
+
# 探针每天自愈一次(失败先试 `ziniao-cli zclaw login`,再不行才叫人),结果进日志/台账/遥测。
|
|
391
|
+
# **刻意不阻塞认领**:WebDriver 授权失败时 orchestrator 会回退 bridge,未必真的跑不了票。
|
|
392
|
+
# 只在 full(唯一用紫鸟的流程)跑;ARGENT_SKIP_AUTH_PROBE=1 可跳过(测试/非紫鸟环境)
|
|
393
|
+
if [ -z "${DRY:-}" ] && [ "$FLOW" = "full" ] && [ "${ARGENT_SKIP_AUTH_PROBE:-0}" != "1" ] \
|
|
394
|
+
&& [ -f "$REPO/scripts/ziniao-auth-probe.sh" ]; then
|
|
395
|
+
if ! bash "$REPO/scripts/ziniao-auth-probe.sh" --quiet >>"$LOG" 2>&1; then
|
|
396
|
+
_ac=$?
|
|
397
|
+
if [ "$_ac" = "2" ]; then
|
|
398
|
+
echo "[$(date '+%F %T')] ⚠️ 紫鸟授权心跳**需要人工**(详见 ${REPO}/scripts/ziniao-auth-probe.sh --force)" | tee -a "$LOG"
|
|
399
|
+
fi
|
|
400
|
+
fi
|
|
401
|
+
fi
|
|
402
|
+
|
|
403
|
+
CLAIM_ERR="$(mktemp -t flow-claim-err)"
|
|
404
|
+
CLAIM_OUT="$(mktemp -t argent-claim-out)"
|
|
405
|
+
# ── B2(2026-09-24):定时器抖动 ────────────────────────────────────
|
|
406
|
+
# launchd 的 StartInterval 是**固定节拍**(每 300s 整点起),这是最容易被平台侧识别的机器特征之一;
|
|
407
|
+
# 人工操作不会每次都在同一秒发起。做法:认领前随机等 0..FLOW_TRIGGER_JITTER_SEC 秒(默认 45)。
|
|
408
|
+
# 只影响"何时开始认领",不影响并发与上限;设 0 可关闭(现场排查时用)。
|
|
409
|
+
JITTER_SEC="$(cfg trigger.jitterSec 2>/dev/null || echo 45)"
|
|
410
|
+
case "$JITTER_SEC" in ''|*[!0-9]*) JITTER_SEC=45 ;; esac
|
|
411
|
+
if [ "${FLOW_TRIGGER_NO_JITTER:-0}" != "1" ] && [ "$JITTER_SEC" -gt 0 ]; then
|
|
412
|
+
_j=$(( RANDOM % JITTER_SEC + 1 ))
|
|
413
|
+
echo "[$(date '+%F %T')] 流程 ${FLOW}:定时器抖动 ${_j}s(B2;设 FLOW_TRIGGER_NO_JITTER=1 可关闭)" | tee -a "$LOG"
|
|
414
|
+
sleep "$_j"
|
|
415
|
+
fi
|
|
416
|
+
|
|
417
|
+
# ── 认领前环境预检(2026-09-23):客户端不可用就别认领 ─────────────
|
|
418
|
+
# 事故:紫鸟"进程在 / Bridge 通 / 未登录"时,认领照常把票派出去,角色跑进去才发现 ⇒
|
|
419
|
+
# ① 白烧一轮;② 票的租约到期变「停滞」(当时还没有回收路径),还占着同店铺批次。
|
|
420
|
+
if [ -z "$DRY" ]; then
|
|
421
|
+
PROBE_OUT=$("$PY" "$REPO/scripts/env_probe.py" --flow "$FLOW" 2>&1) && PROBE_CODE=0 || PROBE_CODE=$?
|
|
422
|
+
case "$PROBE_CODE" in
|
|
423
|
+
0) echo "[$(date '+%F %T')] 流程 ${FLOW}:${PROBE_OUT# }" | tee -a "$LOG" ;;
|
|
424
|
+
3) : ;; # 未声明探针 ⇒ 不需要探,静默
|
|
425
|
+
*) { echo "[$(date '+%F %T')] 流程 ${FLOW}:环境不可用,**本轮不认领**(避免票被派出去再 blocked、租约白耗)"
|
|
426
|
+
printf '%s\n' "$PROBE_OUT" | sed 's/^/ /'; } | tee -a "$LOG" >&2
|
|
427
|
+
"$PY" "$REPO/scripts/flow_ledger.py" --case-id "env-blocked__${FLOW}" --event blocked \
|
|
428
|
+
--by flow-env-probe --detail "环境预检未通过:$(printf '%s' "$PROBE_OUT" | tail -1 | cut -c1-160)" >/dev/null 2>&1 || true
|
|
429
|
+
"$PY" "$REPO/scripts/telemetry.py" --emit --kind engine --flow "$FLOW" \
|
|
430
|
+
--error-class needs_human --detail "环境预检未通过" >/dev/null 2>&1 || true
|
|
431
|
+
"$PY" "$REPO/scripts/telemetry.py" --flush >/dev/null 2>&1 || true
|
|
432
|
+
exit 0 ;;
|
|
433
|
+
esac
|
|
434
|
+
fi
|
|
435
|
+
|
|
436
|
+
# ── 停滞票自动回收(2026-09-23):环境 OK 才回收,每轮有上限 ─────────
|
|
437
|
+
# 停滞 = 认领了但从未派发、租约已到期(triage 原本就说"可回收重派",但没人回收 ⇒ 票躺着)。
|
|
438
|
+
STALE_OUT=$("$PY" "$REPO/scripts/flow_stale.py" --flow "$FLOW" $([ -n "$DRY" ] && echo "" || echo "--requeue") 2>&1) || true
|
|
439
|
+
case "$STALE_OUT" in
|
|
440
|
+
*"无需动作"*|"") : ;;
|
|
441
|
+
*) echo "[$(date '+%F %T')] 流程 ${FLOW}:${STALE_OUT# }" | tee -a "$LOG" ;;
|
|
442
|
+
esac
|
|
443
|
+
|
|
444
|
+
# ── 失败自动重试(用户口径方案 a,2026-10-13):**先重试,再认领** ──────────────
|
|
445
|
+
# 背景(真实现场):可用性类失败(唯一分类器判 retryable)落到台账 failed→blocked 之后,
|
|
446
|
+
# 触发器只报"需人工",从此不再自动跑(两条票是人工手跑回来的)。方案 a = 这类失败自动重试。
|
|
447
|
+
#
|
|
448
|
+
# 三条纪律(都在这里、只在这里):
|
|
449
|
+
# ① 顺序:**先 auto-retry,再走既有认领**;auto-retry 失败 ⛔ **不得阻断认领**
|
|
450
|
+
# (非零退出只播报 + 留日志;见下方"不阻断"注释);
|
|
451
|
+
# ② ⛔ 本脚本**不写任何阈值/开关常量**:enabled/max/cooldownMin/perCycle 全部来自唯一读取点
|
|
452
|
+
# `scripts/flow_config.py#autoRetry`(候选筛选/分类闸门/预约闸门/次数上限/冷却/每轮上限
|
|
453
|
+
# 全在 `scripts/flow-auto-retry.py` 里,走 flow_retry 的全部守卫);
|
|
454
|
+
# ③ dry-run(本触发器 `--dry-run`)⇒ 不给 `--yes`,auto-retry 只打印"将重试谁与原因"。
|
|
455
|
+
case "$FLOW" in
|
|
456
|
+
full)
|
|
457
|
+
AR_SCRIPT="$REPO/scripts/flow-auto-retry.py"
|
|
458
|
+
if [ -f "$AR_SCRIPT" ]; then
|
|
459
|
+
# `2>/dev/null || true`:未声明该段的流程/旧配置读取点会 exit 2(探针式读法,不当作错误)
|
|
460
|
+
AR_ENABLED="$(cfg autoRetry.enabled 2>/dev/null || true)"
|
|
461
|
+
if [ "$AR_ENABLED" = "true" ]; then
|
|
462
|
+
AR_ARGS=(--flow "$FLOW")
|
|
463
|
+
[ -n "$DRY" ] || AR_ARGS+=(--yes) # 真跑才写;dry-run 轮只打印计划
|
|
464
|
+
set +e
|
|
465
|
+
AR_OUT="$("$PY" "$AR_SCRIPT" "${AR_ARGS[@]}" 2>&1)"
|
|
466
|
+
AR_CODE=$?
|
|
467
|
+
set -e
|
|
468
|
+
printf '%s\n' "$AR_OUT" | sed 's/^/ /' | tee -a "$LOG"
|
|
469
|
+
if [ "$AR_CODE" -ne 0 ]; then
|
|
470
|
+
# ⛔ **不阻断认领**:自动重试只是"把可用性类失败重置成待执行",它没跑成不影响本轮
|
|
471
|
+
# 正常的认领/派发(下一轮还会再试;票本身保持原状,绝不会被半写)。
|
|
472
|
+
{
|
|
473
|
+
echo "[$(date '+%F %T')] 流程 ${FLOW} ⚠️ 自动重试未完成(exit=${AR_CODE})——**不影响本轮认领**:"
|
|
474
|
+
echo " ⇒ 追因:${PY} ${AR_SCRIPT} --flow ${FLOW}(默认 dry-run,加 --yes 才写)|台账/审计见上方输出"
|
|
475
|
+
} | tee -a "$LOG" >&2
|
|
476
|
+
fi
|
|
477
|
+
else
|
|
478
|
+
echo "[$(date '+%F %T')] 流程 ${FLOW}:自动重试未开启(flow_config autoRetry.enabled=${AR_ENABLED:-未知})⇒ 跳过" | tee -a "$LOG"
|
|
479
|
+
fi
|
|
480
|
+
else
|
|
481
|
+
echo "[$(date '+%F %T')] 流程 ${FLOW}:缺少 scripts/flow-auto-retry.py(旧版本)⇒ 跳过自动重试(不影响认领/派发)" | tee -a "$LOG"
|
|
482
|
+
fi
|
|
483
|
+
;;
|
|
484
|
+
esac
|
|
485
|
+
|
|
486
|
+
# ⚠️ 看门狗(2026-09-22 事故):飞书 API 半开连接会让 lark-cli **永不返回**,认领锁一直被占
|
|
487
|
+
# ⇒ 定时器每轮都卡在这里,整条流程静默冻结(实测文鳐/鑫中航各冻结 4h51m:日志停在那刻、无报错)。
|
|
488
|
+
# 认领本身另有 --flow_claim 内部的 triage 超时;这里是**最后一道**兜底,任何原因挂住都能自愈。
|
|
489
|
+
CLAIM_TIMEOUT="${ARGENT_CLAIM_TIMEOUT:-240}" # 必须 < 定时器间隔(300s),否则上一轮还没退下一轮就来了
|
|
490
|
+
"$PY" "$REPO/scripts/flow_claim.py" --flow "$FLOW" --max "$MAX" --json $DRY >"$CLAIM_OUT" 2>"$CLAIM_ERR" &
|
|
491
|
+
claim_pid=$!
|
|
492
|
+
(
|
|
493
|
+
# ⚠️ 必须切断继承来的 stdin/stdout/stderr:认领**很快返回**时父进程会 kill 这个看门狗,
|
|
494
|
+
# 但它内部的 `sleep` 会变成孤儿并**继续持有调用方的管道** ⇒ 任何捕获输出的调用方
|
|
495
|
+
# (工作台工具、测试、脚本嵌套调用)都要等 sleep 结束才拿到 EOF(实测把 telemetry 测试卡死 240s)。
|
|
496
|
+
exec </dev/null >/dev/null 2>&1
|
|
497
|
+
sleep "$CLAIM_TIMEOUT"
|
|
498
|
+
if kill -0 "$claim_pid" 2>/dev/null; then
|
|
499
|
+
echo "[$(date '+%F %T')] ⚠️ 认领超时 ${CLAIM_TIMEOUT}s 未返回 ⇒ 看门狗终止(下一轮自愈)" >>"$CLAIM_ERR"
|
|
500
|
+
kill -TERM "$claim_pid" 2>/dev/null
|
|
501
|
+
sleep 5
|
|
502
|
+
kill -KILL "$claim_pid" 2>/dev/null
|
|
503
|
+
fi
|
|
504
|
+
) &
|
|
505
|
+
watchdog_pid=$!
|
|
506
|
+
wait "$claim_pid" 2>/dev/null && CLAIM_CODE=0 || CLAIM_CODE=$?
|
|
507
|
+
kill "$watchdog_pid" 2>/dev/null || true
|
|
508
|
+
wait "$watchdog_pid" 2>/dev/null || true
|
|
509
|
+
claimed=$(cat "$CLAIM_OUT" 2>/dev/null)
|
|
510
|
+
[ -n "$claimed" ] || claimed='{}'
|
|
511
|
+
if [ "$CLAIM_CODE" -ne 0 ] && [ "$CLAIM_CODE" -ne 3 ]; then
|
|
512
|
+
# 认领/分诊本身失败(缺配置、源读取失败、生成器挂了…)⇒ 响亮停住,别退化成"无票"
|
|
513
|
+
{
|
|
514
|
+
echo "[$(date '+%F %T')] 流程 ${FLOW}:认领失败(exit=${CLAIM_CODE})——本轮不派发,需人工处理:"
|
|
515
|
+
sed 's/^/ /' "$CLAIM_ERR" | head -20
|
|
516
|
+
} | tee -a "$LOG" >&2
|
|
517
|
+
"$PY" "$REPO/scripts/flow_ledger.py" --case-id "claim__${FLOW}" --event blocked \
|
|
518
|
+
--by flow-claim --detail "认领/分诊失败 exit=${CLAIM_CODE}(缺配置或源读取失败)" >/dev/null 2>&1 || true
|
|
519
|
+
# 遥测(2026-09-21):**引擎级失败**此前只有日志(2026-09-21 实测:full 因飞书读表瞬时
|
|
520
|
+
# 错误 1204 认领失败,下一轮自愈,但工作台"需关注"里看不见)。这里补一条元数据事件:
|
|
521
|
+
# 只带流程/能力/退出码性质,不带原始错误文本(脱敏由 telemetry.py 负责)。
|
|
522
|
+
"$PY" "$REPO/scripts/telemetry.py" --emit --kind engine --flow "$FLOW" \
|
|
523
|
+
--error-class needs_agent --detail "认领/分诊失败 exit=${CLAIM_CODE}" >/dev/null 2>&1 || true
|
|
524
|
+
"$PY" "$REPO/scripts/telemetry.py" --flush >/dev/null 2>&1 || true
|
|
525
|
+
rm -f "$CLAIM_ERR" "$CLAIM_OUT"
|
|
526
|
+
exit 0
|
|
527
|
+
fi
|
|
528
|
+
rm -f "$CLAIM_ERR" "$CLAIM_OUT"
|
|
529
|
+
tickets=$(printf '%s' "$claimed" | "$PY" -c 'import sys,json;print(",".join(json.load(sys.stdin).get("claimed",[])))' 2>/dev/null || echo "")
|
|
530
|
+
ts=$(date '+%Y-%m-%d %H:%M:%S')
|
|
531
|
+
if [ -z "$tickets" ]; then
|
|
532
|
+
# 2026-09-18:无票也必须说清"为什么"(源清单说明/分诊计数)——
|
|
533
|
+
# 只有"无票可认领"时,日报静默停摆一整天都没人发现。
|
|
534
|
+
REASON=$(printf '%s' "$claimed" | "$PY" -c 'import sys,json;print((json.load(sys.stdin) or {}).get("reason") or "")' 2>/dev/null || echo "")
|
|
535
|
+
echo "[$ts] 流程 ${FLOW}:无票可认领${REASON:+(${REASON})} ⇒ 不唤起" | tee -a "$LOG"
|
|
536
|
+
exit 0
|
|
537
|
+
fi
|
|
538
|
+
|
|
539
|
+
if [ "$WAKE" = "off" ]; then
|
|
540
|
+
echo "[$ts] 流程 ${FLOW}:已认领 ${tickets},但唤醒方式=off(只认领,不唤起角色)" | tee -a "$LOG"
|
|
541
|
+
exit 0
|
|
542
|
+
fi
|
|
543
|
+
|
|
544
|
+
# ── `--no-wake`(0.6.0 P1-B):**只认领 + 只写台账,不唤起角色会话** ─────────────────
|
|
545
|
+
# 为什么需要它:宿主**原生排程**的计划任务是**无会话**执行的(它只能跑一条固定命令),
|
|
546
|
+
# 而默认行为是"认领后唤醒角色会话" ⇒ 那条静态命令必须能表达"只认领"。
|
|
547
|
+
# 判据:这一步**只**替代下面的唤醒;认领/台账/暂停/预检/停滞回收/自动重试全部一字未动
|
|
548
|
+
# (`planned` 事件由上面的 `flow_claim.py` 照常写、额度/lease 语义不变)。
|
|
549
|
+
# ⛔ 不在这里 spawn 会话、⛔ 不写 wake 事件;唤醒端(`flow-trigger-wake.mjs`)**根本不启动**。
|
|
550
|
+
if [ -n "$NO_WAKE" ]; then
|
|
551
|
+
echo "[$ts] 流程 ${FLOW}:已认领 ${tickets} ⇒ --no-wake:只认领、不唤起角色会话(台账已记 planned;静态轮次指令由宿主排程承担)" | tee -a "$LOG"
|
|
552
|
+
exit 0
|
|
553
|
+
fi
|
|
554
|
+
|
|
555
|
+
echo "[$ts] 流程 ${FLOW}:已认领 ${tickets} ⇒ 唤起角色会话(本轮最多 ${MAX} 张,${MODE_LABEL})" | tee -a "$LOG"
|
|
556
|
+
# ⚠️ 唤醒失败**不许沉默**(2026-10-08 静默故障):此前这一行直接 `node … | tee`,唤醒端 exit 2
|
|
557
|
+
# 只体现在 launchd 的 last exit 上 —— 客户机上没人看,日志里也只是一行 ❌,**台账/遥测里什么都没有**。
|
|
558
|
+
# 现在:显式取唤醒端的退出码(PIPESTATUS[0],不是 tee 的),非零就响亮播报并**原样透出退出码**。
|
|
559
|
+
# 台账/遥测由唤醒端自己写(它才知道失败分类与尝试过的来源)——这里不重复写,避免同一故障两条信号。
|
|
560
|
+
set +e
|
|
561
|
+
node "$REPO/scripts/flow-trigger-wake.mjs" --flow "$FLOW" --max "$MAX" --tickets "$tickets" $DRY 2>&1 | tee -a "$LOG"
|
|
562
|
+
WAKE_CODE=${PIPESTATUS[0]}
|
|
563
|
+
set -e
|
|
564
|
+
if [ "$WAKE_CODE" -ne 0 ]; then
|
|
565
|
+
{
|
|
566
|
+
echo "[$(date '+%F %T')] 流程 ${FLOW} ❌ 唤醒失败(exit=${WAKE_CODE})——**本轮角色会话没起来,已认领的票没人跑**"
|
|
567
|
+
echo " ⇒ 追因:台账 case=wake__${FLOW}(event=blocked,by=flow-wake)|日志 $LOG"
|
|
568
|
+
echo " ⇒ 鉴权来源链的修法见唤醒端上方的 ❌ 文案(来源优先级 / 如何把 dsh-web 日志挪出 /tmp)"
|
|
569
|
+
} | tee -a "$LOG" >&2
|
|
570
|
+
exit "$WAKE_CODE"
|
|
571
|
+
fi
|