@whyshu/argent-engine 0.0.0-stage → 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.
- package/MANIFEST.json +341 -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 +576 -0
- package/engine/scripts/billing-mode-daily.sh +13 -0
- package/engine/scripts/build-engine-package.mjs +126 -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 +378 -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/collect-evidence.mjs +42 -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 +135 -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 +47 -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 +261 -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/final-check.mjs +43 -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 +735 -0
- package/engine/scripts/flow-scaffold.py +275 -0
- package/engine/scripts/flow-timers-plist.sh +87 -0
- package/engine/scripts/flow-timers.sh +184 -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 +1025 -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 +2278 -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 +195 -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.sh +18 -0
- package/engine/scripts/lib/argent_datadir.py +285 -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 +295 -0
- package/engine/scripts/poll-invoice.sh +76 -0
- package/engine/scripts/profile-doctor.mjs +686 -0
- package/engine/scripts/provider-select.sh +71 -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/retire-inventory.mjs +40 -0
- package/engine/scripts/run-tests.sh +257 -0
- package/engine/scripts/s1-audit.mjs +85 -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 +137 -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 +74 -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 +35 -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 +217 -0
- package/engine/scripts/ziniao-post-upgrade-check.sh +121 -0
- package/engine/scripts/ziniao-windows.sh +154 -0
- package/engine/skills/README.md +86 -0
- package/engine/skills/argent-dev/SKILL.md +41 -0
- package/engine/skills/internal.json +37 -0
- package/engine/skills/lktop-pm/SKILL.md +35 -0
- package/engine/skills/lktopapp-dev/SKILL.md +34 -0
- package/engine/skills/ml-config-guide/SKILL.md +50 -0
- package/engine/skills/ml-fulfillment-sop/SKILL.md +178 -0
- package/engine/skills/ml-fulfillment-sop/references/andes-components.md +69 -0
- package/engine/skills/ml-fulfillment-sop/references/cdp-port-mapping.md +35 -0
- package/engine/skills/ml-fulfillment-sop/references/feishu-base-fulfillment-schema.md +136 -0
- package/engine/skills/ml-fulfillment-sop/references/multi-window-concurrency.md +69 -0
- package/engine/skills/ml-fulfillment-sop/references/pitfalls.md +77 -0
- package/engine/skills/ml-fulfillment-sop/references/selectors.md +243 -0
- package/engine/skills/ml-fulfillment-sop/scripts/fulfillment.js +586 -0
- package/engine/skills/ml-fulfillment-sop/scripts/poll-and-execute.sh +271 -0
- package/engine/skills/ml-fulfillment-sop/templates/report.md +55 -0
- package/engine/skills/ml-invoice/SKILL.md +506 -0
- package/engine/skills/ml-invoice/references/pitfalls.md +268 -0
- package/engine/skills/ml-invoice/references/portal-flow.md +360 -0
- package/engine/skills/ml-invoice/references/template-fill-contract.md +133 -0
- package/engine/skills/ml-invoice-xzh/SKILL.md +133 -0
- package/engine/skills/ml-invoice-xzh/references/pitfalls.md +230 -0
- package/engine/skills/ml-store-daily-report/SKILL.md +224 -0
- package/engine/skills/ml-store-daily-report/scripts/README.md +25 -0
- package/engine/skills/ml-store-daily-report/scripts/extract.js +691 -0
- package/engine/skills/ml-store-daily-report/scripts/generate.js +299 -0
- package/engine/skills/pet-dev/SKILL.md +42 -0
- package/engine/skills/whyshu-pm/SKILL.md +35 -0
- package/engine/skills/whyshu-web-dev/SKILL.md +50 -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 +738 -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 +22 -4
- package/requirements.txt +5 -0
- package/README.md +0 -3
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `argent flow list` —— **注册表只读清单**(P1,2026-10-03)。
|
|
3
|
+
*
|
|
4
|
+
* 薄包:全部内容来自 `scripts/flow_registry.py --json`(**唯一真源**:引擎级 base + 各业务线的
|
|
5
|
+
* 合并/冲突检查/别名解析都由它做),本文件**不实现任何合并**,只做三件事:
|
|
6
|
+
* ① 用**已选定的解释器**去调它(`core/lib/python.mjs`,D3:不赌 PATH);
|
|
7
|
+
* ② 把合并结果整理成宿主好用的形状(流程 → 业务线/能力/角色/技能/数据面/触发器);
|
|
8
|
+
* ③ **如实回显**:读不到注册表 ⇒ `2 + python_unavailable|missing_declaration`,**绝不**报"没有流程"。
|
|
9
|
+
*
|
|
10
|
+
* ## 暂停开关:只做**存在性探测**,不重算路径
|
|
11
|
+
* 每条流程的 `trigger.pauseSwitch`(相对数据目录)与 `pauseSwitchLegacy`(数据目录根下的裸名)
|
|
12
|
+
* 都是**注册表声明**;本 verb 只在声明的路径上探一次"在不在"。⚠️ 权威判据仍是
|
|
13
|
+
* `scripts/flow_config.py` / `scripts/flow-trigger.sh`(迁移期两条都查)—— 输出里用
|
|
14
|
+
* `authoritative` 字段点名,免得被当成第二份判据。
|
|
15
|
+
*
|
|
16
|
+
* ## A1/A2
|
|
17
|
+
* · `0` 读到(流程 0 条 ⇒ `empty:true + no_match`,不是错误);
|
|
18
|
+
* · `2` 解释器不可用 / 注册表读不到 / 未知参数。
|
|
19
|
+
*/
|
|
20
|
+
import { existsSync } from 'node:fs'
|
|
21
|
+
import { join } from 'node:path'
|
|
22
|
+
import { resolveDataDir } from './argent-datadir.mjs'
|
|
23
|
+
import { pickPython } from './python.mjs'
|
|
24
|
+
import { loadRegistry, resolveFlow } from './flow-registry.mjs'
|
|
25
|
+
|
|
26
|
+
/** 注册表文档 → 宿主友好的流程清单(纯函数,便于夹具)。 */
|
|
27
|
+
export function summarizeFlows(doc, { dataDir = '', exists = existsSync } = {}) {
|
|
28
|
+
const flows = (doc && doc.flows) || {}
|
|
29
|
+
const aliases = (doc && doc.aliases) || {}
|
|
30
|
+
const aliasTo = {}
|
|
31
|
+
for (const [from, to] of Object.entries(aliases)) (aliasTo[to] ||= []).push(from)
|
|
32
|
+
const verticals = {}
|
|
33
|
+
const rows = []
|
|
34
|
+
for (const [id, f] of Object.entries(flows)) {
|
|
35
|
+
const vertical = id.includes('.') ? id.split('.')[0] : null
|
|
36
|
+
const local = id.includes('.') ? id.split('.').slice(1).join('.') : id
|
|
37
|
+
const t = f.trigger || {}
|
|
38
|
+
const declared = t.pauseSwitch ? join(String(dataDir), String(t.pauseSwitch)) : null
|
|
39
|
+
const legacy = t.pauseSwitchLegacy ? join(String(dataDir), String(t.pauseSwitchLegacy)) : null
|
|
40
|
+
const paused = Boolean((declared && exists(declared)) || (legacy && exists(legacy)))
|
|
41
|
+
rows.push({
|
|
42
|
+
id, localId: local, vertical,
|
|
43
|
+
title: f.title || null,
|
|
44
|
+
role: f.role || null,
|
|
45
|
+
capability: f.capability || null,
|
|
46
|
+
skill: f.skill || null,
|
|
47
|
+
sourceKind: (f.source && f.source.kind) || null,
|
|
48
|
+
sourceIdentity: (f.source && f.source.identity) || null,
|
|
49
|
+
trigger: {
|
|
50
|
+
wake: t.wake ?? null, mode: t.mode ?? null, intervalSec: t.intervalSec ?? null,
|
|
51
|
+
pauseSwitch: t.pauseSwitch ?? null, pauseSwitchLegacy: t.pauseSwitchLegacy ?? null,
|
|
52
|
+
promptTemplate: t.promptTemplate ?? null,
|
|
53
|
+
log: t.log ?? null,
|
|
54
|
+
},
|
|
55
|
+
pause: {
|
|
56
|
+
paused, declaredPath: declared, declaredExists: Boolean(declared && exists(declared)),
|
|
57
|
+
legacyPath: legacy, legacyExists: Boolean(legacy && exists(legacy)),
|
|
58
|
+
authoritative: 'scripts/flow_config.py / scripts/flow-trigger.sh(本 verb 只在注册表声明的路径上探存在性,不重算路径)',
|
|
59
|
+
},
|
|
60
|
+
concurrency: f.concurrency || null,
|
|
61
|
+
guards: f.guards || null,
|
|
62
|
+
failuresEnabled: Boolean(f.failures && f.failures.enabled),
|
|
63
|
+
aliases: aliasTo[id] || [],
|
|
64
|
+
requiredFields: Array.isArray(f.required) ? f.required.length : 0,
|
|
65
|
+
})
|
|
66
|
+
if (vertical) verticals[vertical] = true
|
|
67
|
+
}
|
|
68
|
+
rows.sort((a, b) => (a.id < b.id ? -1 : a.id > b.id ? 1 : 0))
|
|
69
|
+
return { rows, verticalIds: Object.keys(verticals).sort(), aliases }
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* verb 入口:`argent flow list [--json]`。
|
|
74
|
+
* @param {string[]} argv 子命令之后的参数(`list` 已由组 verb 消费)
|
|
75
|
+
* @param {{repo?:string, env?:object, py?:string, pyFailure?:string|null, doc?:object}} [deps]
|
|
76
|
+
*/
|
|
77
|
+
export function runFlowListVerb(argv = [], deps = {}) {
|
|
78
|
+
for (const t of argv) {
|
|
79
|
+
if (t === '--json') continue
|
|
80
|
+
if (t === '--help' || t === '-h') return { json: { ok: true, empty: false, code: 0, reason: null, verb: 'flow', subcommand: 'list', usage: FLOW_LIST_USAGE }, human: FLOW_LIST_USAGE, stderr: '', exit: 0 }
|
|
81
|
+
if (t.startsWith('--')) return listFail(2, 'usage_error', `未知参数:${t}(用法:argent flow list [--json])`)
|
|
82
|
+
return listFail(2, 'usage_error', `多余的位置参数:${t}(用法:argent flow list [--json])`)
|
|
83
|
+
}
|
|
84
|
+
const repo = deps.repo || ''
|
|
85
|
+
const env = deps.env || process.env
|
|
86
|
+
const dataDir = deps.dataDir || resolveDataDir({ env }).dir
|
|
87
|
+
// 解释器:显式给了就用(夹具/已解析),否则探一次;探不到 ⇒ 下面的 loadRegistry 会走 py='' 并响亮失败
|
|
88
|
+
let py = deps.py !== undefined ? deps.py : null
|
|
89
|
+
let pyFailure = deps.pyFailure !== undefined ? deps.pyFailure : null
|
|
90
|
+
if (deps.doc === undefined && py === null) {
|
|
91
|
+
const p = pickPython({ env, dataDir, repo })
|
|
92
|
+
py = p.ok ? p.path : ''
|
|
93
|
+
pyFailure = p.ok ? null : p.diagnostics
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
let doc = deps.doc || null
|
|
97
|
+
if (!doc) {
|
|
98
|
+
if (pyFailure) return listFail(2, 'python_unavailable', pyFailure, { dataDir })
|
|
99
|
+
const reg = loadRegistry({ repo, py })
|
|
100
|
+
if (!reg.ok) {
|
|
101
|
+
return listFail(2, reg.reason === 'registry_script_missing' ? 'missing_declaration' : 'python_unavailable', reg.error, { dataDir })
|
|
102
|
+
}
|
|
103
|
+
doc = reg.doc
|
|
104
|
+
}
|
|
105
|
+
const { rows, verticalIds, aliases } = summarizeFlows(doc, { dataDir })
|
|
106
|
+
const json = {
|
|
107
|
+
ok: true, empty: rows.length === 0, code: 0, reason: rows.length ? null : 'no_match', verb: 'flow', subcommand: 'list',
|
|
108
|
+
source: 'scripts/flow_registry.py --json(合并 + 别名解析的唯一真源)',
|
|
109
|
+
dataDir,
|
|
110
|
+
count: rows.length,
|
|
111
|
+
verticals: verticalIds,
|
|
112
|
+
aliases,
|
|
113
|
+
flows: rows,
|
|
114
|
+
note: 'pause 只在注册表声明的路径上做存在性探测(权威判据见 pause.authoritative);'
|
|
115
|
+
+ 'guards/concurrency 为注册表原文照录,判据仍在引擎脚本里。',
|
|
116
|
+
}
|
|
117
|
+
const human = [
|
|
118
|
+
`📋 流程清单(${rows.length} 条 | 业务线 ${verticalIds.join('、') || '(无)'})`,
|
|
119
|
+
...rows.map((r) => ` · ${r.id.padEnd(34)} ${(r.title || '').padEnd(18)} 角色=${r.role || '—'} 能力=${r.capability || '—'}`
|
|
120
|
+
+ ` 唤醒=${r.trigger.wake || '—'}${r.pause.paused ? ' ⏸ 已暂停开关' : ''}`),
|
|
121
|
+
rows.length ? null : ' (注册表里 0 条流程 —— 这不是错误,但要确认注册表是否读对了)',
|
|
122
|
+
].filter(Boolean).join('\n')
|
|
123
|
+
return { json, human, stderr: '', exit: 0 }
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function listFail(code, reason, error, echo = {}) {
|
|
127
|
+
return { json: { ok: false, empty: false, code, reason, error, verb: 'flow', subcommand: 'list', ...echo }, human: `❌ ${reason}:${error}`, stderr: '', exit: code }
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/** 供组 verb 复用:别名解析(不复制 `resolveFlow` 的实现)。 */
|
|
131
|
+
export { resolveFlow }
|
|
132
|
+
|
|
133
|
+
export const FLOW_LIST_USAGE = [
|
|
134
|
+
'用法:argent flow list [--json]',
|
|
135
|
+
'',
|
|
136
|
+
'只读清单:流程(规范 id / 标题 / 角色 / 能力 / 技能 / 数据面 / 触发器 / 并发 / 守卫 / 别名)。',
|
|
137
|
+
'真源 = scripts/flow_registry.py --json(本 verb 不实现合并);暂停开关只做存在性探测。',
|
|
138
|
+
].join('\n')
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 注册表读取(core 侧薄包装)—— **唯一真源仍是 `scripts/flow_registry.py`**
|
|
3
|
+
* (base + 业务线的合并、冲突即 fail、别名解析都由它做;Node 侧入口 `scripts/lib/registry.mjs`)。
|
|
4
|
+
*
|
|
5
|
+
* 本模块只加两件内核需要的事,**不复制任何合并逻辑**:
|
|
6
|
+
* ① 用**已选定的解释器**去调它(不赌 PATH 上恰好有 python3);
|
|
7
|
+
* ② 把失败**分类**(脚本缺失 / 合并或解析失败)—— 2026-10-03 修 D3:此前 `core/lib/sop.mjs`
|
|
8
|
+
* 用空 `catch {}` 吞掉一切失败 ⇒ 注册表读不到时 `sop` 报"未命中"且 exit 0(假象)。
|
|
9
|
+
*/
|
|
10
|
+
import { existsSync } from 'node:fs'
|
|
11
|
+
import { join } from 'node:path'
|
|
12
|
+
import { loadRegistryDoc, resolveFlowId } from '../../scripts/lib/registry.mjs'
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* 读合并后的注册表(永不抛)。
|
|
16
|
+
* @returns {{ok: true, doc: object} | {ok: false, reason: string, error: string}}
|
|
17
|
+
* reason:`registry_script_missing`(仓库不完整)/ `registry_failed`(合并或解析失败)
|
|
18
|
+
*/
|
|
19
|
+
export function loadRegistry({ repo, py }) {
|
|
20
|
+
const script = join(repo, 'scripts', 'flow_registry.py')
|
|
21
|
+
if (!existsSync(script)) {
|
|
22
|
+
return { ok: false, reason: 'registry_script_missing', error: `注册表加载器不存在:${script}(仓库不完整?)` }
|
|
23
|
+
}
|
|
24
|
+
try {
|
|
25
|
+
return { ok: true, doc: loadRegistryDoc({ root: repo, py }) }
|
|
26
|
+
} catch (e) {
|
|
27
|
+
const stderr = String((e && e.stderr) || '').trim()
|
|
28
|
+
const tail = stderr.split('\n').map((l) => l.trim()).filter(Boolean).slice(-3).join(' / ')
|
|
29
|
+
return {
|
|
30
|
+
ok: false,
|
|
31
|
+
reason: 'registry_failed',
|
|
32
|
+
error: `注册表合并/解析失败:${(tail || String((e && e.message) || e)).slice(0, 400)}`,
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* 别名 → 规范 id,并给出**局部 id**(台账 caseId 的前缀约定是 `<flow>__<票号>`,用的是局部 id)。
|
|
39
|
+
* 别名解析走 `resolveFlowId`(加载器的唯一实现),本函数不自己维护任何映射。
|
|
40
|
+
*/
|
|
41
|
+
export function resolveFlow({ input, doc }) {
|
|
42
|
+
const raw = String(input || '')
|
|
43
|
+
const canonical = resolveFlowId(raw, doc)
|
|
44
|
+
const flows = (doc && doc.flows) || {}
|
|
45
|
+
const known = Object.prototype.hasOwnProperty.call(flows, canonical)
|
|
46
|
+
const local = canonical.includes('.') ? canonical.split('.').slice(1).join('.') : canonical
|
|
47
|
+
return { input: raw, canonical, local, known }
|
|
48
|
+
}
|
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `argent init` —— **数据目录画像**(P1,2026-10-03)。
|
|
3
|
+
*
|
|
4
|
+
* 客户 agent 装完 CLI 后的第一个问题永远是"我该写哪儿、我这台机器现在能跑什么"。
|
|
5
|
+
* 本 verb 把这件事**一次答完**,且**只读**(除 `--apply` 建目录那一步)。
|
|
6
|
+
*
|
|
7
|
+
* ## 它答什么(每一项都指向唯一判据,本文件不复制判据)
|
|
8
|
+
* | 画像块 | 唯一判据 |
|
|
9
|
+
* |---|---|
|
|
10
|
+
* | 数据目录(含来源/是否隔离/存在性/可写性) | `core/lib/argent-datadir.mjs#resolveDataDir`(判断⑧) |
|
|
11
|
+
* | 解释器候选 | `core/lib/python.mjs#pickPython`(D3 的多候选 + 响亮失败,与 `flow-trigger.sh` 的 `pick_py()` 同源) |
|
|
12
|
+
* | 已装技能 | 技能根的**存在性计数**(`<数据目录>/skills`、`<dsh home>/skills`、仓库 `skills/`);格式判据在 `scripts/fix-skill-frontmatter.py` |
|
|
13
|
+
* | 许可状态 | `core/lib/license-verdict.mjs#licenseVerdict`(判定点唯一在 CLI 进程内,§3.5①) |
|
|
14
|
+
* | 通道可用性 | `core/lib/notify/index.mjs#listChannels`(P2 端口;预留位如实标 `implemented:false`) |
|
|
15
|
+
* | 宿主档事实(dsh 在不在 / 定时器 / 补丁) | **只探测、如实上报**;缺宿主是**设计**不是 FAIL(§4.2 M1 判据③) |
|
|
16
|
+
*
|
|
17
|
+
* ## A1/A2/A3
|
|
18
|
+
* · A1:`0` 画像成功(**缺 dsh / 缺解释器 / 无许可都不算失败** —— 它们是"事实",画像的职责是如实报出来);
|
|
19
|
+
* `2` 用法错误(未知参数 / `--apply` 时目录建不出来 `${datadir_unwritable}`)。
|
|
20
|
+
* · A2:`ok/empty/code/reason` 恒在;回显**实际生效**的数据目录、来源、以及 `--apply` 的 `applied`。
|
|
21
|
+
* · A3:**默认 dry-run**(只画像,**不建目录**);`--apply` 才 `mkdir -p`。dry-run 零副作用。
|
|
22
|
+
*
|
|
23
|
+
* ## ⛔ 它绝不做什么(写进代码与输出,两头都声明)
|
|
24
|
+
* ① 不写 `~/.dsh/settings.yaml`(宿主配置归宿主;改它会让 dsh 自身 schema 重写时丢段);
|
|
25
|
+
* ② 不写/不碰 credentials(`~/.dsh/.credentials.yaml`);
|
|
26
|
+
* ③ **不发明数据目录** —— 只落 `resolveDataDir()` 给出的那一个(另建目录 = 第二份判据,
|
|
27
|
+
* 那正是 `core/lib/argent-datadir.mjs:1-23` 记录过的事故形态:隔离实例写进生产)。
|
|
28
|
+
*
|
|
29
|
+
* ## 默认目录已定(真源在 `core/lib/argent-datadir.mjs`,本文件只**如实呈现**)
|
|
30
|
+
* 实施方案 §P1③ / §6.2 **Q6 选项 B**(2026-10-03 批准、**已落地(两步)**):
|
|
31
|
+
* · **存在即沿用**:`~/.dsh/argent` 已存在 ⇒ 它就是默认目录(老机器**不**被误判隔离);
|
|
32
|
+
* · 否则(新装、无宿主)⇒ `~/.argent`。
|
|
33
|
+
* 本 verb 把两个候选路径 + 谁在用 + 老位置在不在**一次打全**(`dataDir.legacyDir` /
|
|
34
|
+
* `legacyDirExists` / `noHostDefault`)—— 因为判据是"老位置在不在",所以**先在 `~/.argent`
|
|
35
|
+
* 落过数据、之后才出现 `~/.dsh/argent`** 的机器会切回老位置(可能读到空台账);
|
|
36
|
+
* 要钉死目录就用 `ARGENT_DATA_DIR`(或 `DSH_HOME`)显式指定。判据**不在这里**,只在核心里。
|
|
37
|
+
*/
|
|
38
|
+
import { existsSync, mkdirSync, readdirSync, statSync } from 'node:fs'
|
|
39
|
+
import { join } from 'node:path'
|
|
40
|
+
import { NO_HOST_DATA_DIR_SEGMENTS, defaultDataDir, describeDataDir, dshHomeOf, legacyDataDir, legacyDataDirExists, osHome, resolveDataDir } from './argent-datadir.mjs'
|
|
41
|
+
import { pickPython } from './python.mjs'
|
|
42
|
+
import { licenseVerdict } from './license-verdict.mjs'
|
|
43
|
+
import { listChannels } from './notify/index.mjs'
|
|
44
|
+
|
|
45
|
+
/** 可写性探测:dry-run **不建目录**,所以用"最近的已存在祖先"判断(只读)。 */
|
|
46
|
+
function nearestExisting(dir) {
|
|
47
|
+
let cur = String(dir)
|
|
48
|
+
const seen = []
|
|
49
|
+
while (cur && cur !== '/' && !existsSync(cur)) { seen.push(cur); cur = join(cur, '..') }
|
|
50
|
+
return { ancestor: existsSync(cur) ? cur : null, missing: seen.length }
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/** 一个技能根画像(存在性 + 直接子目录里带 SKILL.md 的计数)。 */
|
|
54
|
+
export function skillRootProfile(dir) {
|
|
55
|
+
const out = { dir, exists: false, skills: [], count: 0, skipped: [] }
|
|
56
|
+
if (!existsSync(dir)) return out
|
|
57
|
+
out.exists = true
|
|
58
|
+
let entries = []
|
|
59
|
+
try { entries = readdirSync(dir, { withFileTypes: true }) } catch { return out }
|
|
60
|
+
for (const e of entries) {
|
|
61
|
+
if (!e.isDirectory()) continue
|
|
62
|
+
const has = existsSync(join(dir, e.name, 'SKILL.md'))
|
|
63
|
+
if (has) out.skills.push(e.name)
|
|
64
|
+
else out.skipped.push(e.name)
|
|
65
|
+
}
|
|
66
|
+
out.skills.sort()
|
|
67
|
+
out.count = out.skills.length
|
|
68
|
+
return out
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/** 许可画像(**不复制判据**:调 licenseVerdict;它自己读 `$ARGENT_DATA_DIR/license.json`)。 */
|
|
72
|
+
export function licenseProfile({ env = process.env, now = Date.now(), verdict = null } = {}) {
|
|
73
|
+
const v = verdict || licenseVerdict({ env, now })
|
|
74
|
+
return {
|
|
75
|
+
state: v.state, exit: v.exit, reason: v.reason || null,
|
|
76
|
+
file: v.path || null, exists: Boolean(v.path && existsSync(v.path)),
|
|
77
|
+
expiresAt: v.expiresAt || null, graceDays: v.graceDays ?? null, graceUntil: v.graceUntil || null,
|
|
78
|
+
allowed: v.allowed ?? null,
|
|
79
|
+
device: v.device ? { present: v.device.present, signed: v.device.signed, state: v.device.state, rebindsLeft: v.device.rebindsLeft ?? null } : null,
|
|
80
|
+
note: v.state === 'denied' ? '许可被拒 ⇒ 写类 verb 在 enforce 下会被拦(默认 observe 只留证据)' : null,
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* 数据目录画像(含三级链逐级的命中情况)。
|
|
86
|
+
* @returns {object} 形状与 `resolveDataDir()` 同源 + 画像字段
|
|
87
|
+
*/
|
|
88
|
+
export function dataDirProfile({ env = process.env, home = null } = {}) {
|
|
89
|
+
const r = resolveDataDir(home ? { env, home } : { env })
|
|
90
|
+
const noHostDefault = defaultDataDir(env, home || undefined)
|
|
91
|
+
const legacyDir = legacyDataDir(env, home || undefined)
|
|
92
|
+
const legacyExists = legacyDataDirExists(env, home || undefined)
|
|
93
|
+
const newInstallDir = join(osHome(env), ...NO_HOST_DATA_DIR_SEGMENTS)
|
|
94
|
+
const probe = nearestExisting(r.dir)
|
|
95
|
+
return {
|
|
96
|
+
dir: r.dir,
|
|
97
|
+
raw: r.raw,
|
|
98
|
+
source: r.source,
|
|
99
|
+
isolated: r.isolated,
|
|
100
|
+
defaultDir: r.defaultDir,
|
|
101
|
+
dshHome: r.dshHome,
|
|
102
|
+
exists: existsSync(r.dir),
|
|
103
|
+
isDirectory: existsSync(r.dir) && statSync(r.dir).isDirectory(),
|
|
104
|
+
nearestExistingAncestor: probe.ancestor,
|
|
105
|
+
missingLevels: probe.missing,
|
|
106
|
+
// 默认目录的两个候选(判据 = "老位置在不在",真源在 core/lib/argent-datadir.mjs)
|
|
107
|
+
noHostDefault,
|
|
108
|
+
noHostDefaultExists: existsSync(noHostDefault),
|
|
109
|
+
legacyDir,
|
|
110
|
+
legacyDirExists: legacyExists,
|
|
111
|
+
newInstallDir,
|
|
112
|
+
chain: [
|
|
113
|
+
{ level: 'ARGENT_DATA_DIR', hit: r.source === 'ARGENT_DATA_DIR', value: env.ARGENT_DATA_DIR ?? null },
|
|
114
|
+
{ level: 'DSH_HOME', hit: r.source === 'DSH_HOME', value: env.DSH_HOME ? join(dshHomeOf(env), 'argent') : null },
|
|
115
|
+
{ level: 'default', hit: r.source === 'default', value: r.defaultDir },
|
|
116
|
+
],
|
|
117
|
+
// Q6 已定(选项 B,两步都落地)⇒ 不再是"待拍板",但把两个候选与"谁在用"如实打出来
|
|
118
|
+
defaultDirDecision: {
|
|
119
|
+
id: 'Q6',
|
|
120
|
+
option: 'B',
|
|
121
|
+
landed: true,
|
|
122
|
+
rule: '存在即沿用:`~/.dsh/argent` 已存在 ⇒ 默认就是它;否则新装默认 `~/.argent`',
|
|
123
|
+
using: legacyExists ? 'legacy' : 'new-install',
|
|
124
|
+
legacyDir,
|
|
125
|
+
legacyDirExists: legacyExists,
|
|
126
|
+
newInstallDir,
|
|
127
|
+
note: legacyExists
|
|
128
|
+
? '老位置存在 ⇒ 沿用(老机器不会被误判隔离;失败回路照常运行)'
|
|
129
|
+
: '老位置不存在 ⇒ 走新装默认 `~/.argent`(宿主无关位置)',
|
|
130
|
+
hazard: '判据是"老位置在不在" ⇒ 先在 `~/.argent` 落过数据、之后才出现 `~/.dsh/argent` 的机器会切回老位置(可能读到空台账);'
|
|
131
|
+
+ '要钉死目录用 ARGENT_DATA_DIR / DSH_HOME 显式指定',
|
|
132
|
+
},
|
|
133
|
+
describe: describeDataDir(r),
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/** 宿主档事实(只探测;缺宿主 = 设计,不是 FAIL)。 */
|
|
138
|
+
export function hostProfile({ env = process.env, repo = '', which = null } = {}) {
|
|
139
|
+
const dshHome = dshHomeOf(env)
|
|
140
|
+
const dshBin = which ? which('dsh') : null
|
|
141
|
+
return {
|
|
142
|
+
dshHome,
|
|
143
|
+
dshHomeExists: existsSync(dshHome),
|
|
144
|
+
dshBin: dshBin || null,
|
|
145
|
+
note: '宿主档事实:缺 dsh **不是 FAIL**(M1 的判据③明写"无 dsh 不报 FAIL");'
|
|
146
|
+
+ '定时器/补丁的权威判据在宿主侧生成器(`bash scripts/flow-timers.sh status`、`node scripts/dsh-upgrade.mjs --patches-only`),本 verb 不重算。',
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* verb 入口:`argent init [--apply] [--json]`。
|
|
152
|
+
* @param {string[]} argv
|
|
153
|
+
* @param {{repo?:string, env?:object, apply?:boolean, deps?:object}} [deps]
|
|
154
|
+
*/
|
|
155
|
+
export function runInitVerb(argv = [], deps = {}) {
|
|
156
|
+
const bool = []
|
|
157
|
+
for (const t of argv) {
|
|
158
|
+
if (t === '--json') continue
|
|
159
|
+
if (t === '--apply' || t === '--dry-run' || t === '--help' || t === '-h') { bool.push(t); continue }
|
|
160
|
+
return initFail(2, 'usage_error', `未知参数:${t}(用法:argent init [--apply] [--json])`)
|
|
161
|
+
}
|
|
162
|
+
if (bool.includes('--help') || bool.includes('-h')) {
|
|
163
|
+
return { json: { ok: true, empty: false, code: 0, reason: null, verb: 'init', usage: INIT_USAGE }, human: INIT_USAGE, stderr: '', exit: 0 }
|
|
164
|
+
}
|
|
165
|
+
const apply = bool.includes('--apply') && !bool.includes('--dry-run')
|
|
166
|
+
const env = deps.env || process.env
|
|
167
|
+
const repo = deps.repo || ''
|
|
168
|
+
const dryRun = !apply
|
|
169
|
+
|
|
170
|
+
const dd = dataDirProfile({ env })
|
|
171
|
+
const py = pickPython({ env, dataDir: dd.dir, repo })
|
|
172
|
+
const lic = licenseProfile({ env, now: deps.now ? deps.now.getTime() : Date.now(), verdict: deps.verdict || null })
|
|
173
|
+
const channels = listChannels()
|
|
174
|
+
const host = hostProfile({ env, repo, which: deps.which || null })
|
|
175
|
+
|
|
176
|
+
const roots = [
|
|
177
|
+
{ id: 'data', dir: join(dd.dir, 'skills') },
|
|
178
|
+
{ id: 'dsh', dir: join(dd.dshHome, 'skills') },
|
|
179
|
+
...(repo ? [{ id: 'repo', dir: join(repo, 'skills') }] : []),
|
|
180
|
+
].map((r) => ({ id: r.id, ...skillRootProfile(r.dir) }))
|
|
181
|
+
|
|
182
|
+
let applied = false
|
|
183
|
+
let created = false
|
|
184
|
+
let writeError = null
|
|
185
|
+
if (apply) {
|
|
186
|
+
try {
|
|
187
|
+
const existed = existsSync(dd.dir)
|
|
188
|
+
mkdirSync(dd.dir, { recursive: true })
|
|
189
|
+
applied = true
|
|
190
|
+
created = !existed
|
|
191
|
+
} catch (e) {
|
|
192
|
+
return initFail(2, 'datadir_unwritable', `数据目录建不出来:${dd.dir}(${(e && e.message) || e})`, { dataDir: dd.dir, dryRun })
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
const json = {
|
|
197
|
+
ok: true, empty: false, code: 0, reason: null, verb: 'init',
|
|
198
|
+
dryRun, applied, created,
|
|
199
|
+
dataDir: { ...dd, existsAfter: existsSync(dd.dir), wouldCreate: !existsSync(dd.dir) },
|
|
200
|
+
interpreter: {
|
|
201
|
+
ok: py.ok, path: py.ok ? py.path : null, source: py.source || null, version: py.version || null,
|
|
202
|
+
candidates: (py.tried || []).map((t) => ({ path: t.path, source: t.source, ok: t.ok, reason: t.reason, version: t.version || null })),
|
|
203
|
+
diagnostics: py.ok ? null : py.diagnostics,
|
|
204
|
+
},
|
|
205
|
+
skills: { roots, total: roots.reduce((n, r) => n + r.count, 0) },
|
|
206
|
+
license: lic,
|
|
207
|
+
channels: { available: channels.filter((c) => c.implemented).map((c) => c.channel), reserved: channels.filter((c) => !c.implemented).map((c) => c.channel), all: channels },
|
|
208
|
+
host,
|
|
209
|
+
// 如实声明"没写什么"(不是客套:这三条是本次交付的红线,写进输出才可被调用方核验)
|
|
210
|
+
writes: {
|
|
211
|
+
settings: false, credentials: false, files: created ? [dd.dir] : [],
|
|
212
|
+
note: '本 verb 只创建数据目录本身(--apply 时);**不写** ~/.dsh/settings.yaml、**不碰** credentials、不改任何宿主配置。',
|
|
213
|
+
},
|
|
214
|
+
// Q6(默认目录)**已定并落地**:不再是"待拍板",把判据与两个候选如实打出来(真源在核心里)
|
|
215
|
+
defaultDirDecision: dd.defaultDirDecision,
|
|
216
|
+
pendingDecisions: [],
|
|
217
|
+
writeError,
|
|
218
|
+
note: dryRun ? 'dry-run(默认):只画像,**未创建任何目录**;要建目录加 --apply' : `已 --apply:数据目录 ${created ? '新建' : '已存在'}(${dd.dir})`,
|
|
219
|
+
}
|
|
220
|
+
const human = [
|
|
221
|
+
`📇 Argent 画像(${dryRun ? 'dry-run,未建目录' : '已 --apply'})`,
|
|
222
|
+
` 数据目录:${dd.describe}`,
|
|
223
|
+
` 存在=${dd.existsAfter ?? dd.exists} 来源=${dd.source} 隔离=${dd.isolated} 默认=${dd.defaultDir}`,
|
|
224
|
+
` 默认目录判据(§6.2 Q6 选项 B,已落地):老位置 ${dd.legacyDir} ${dd.legacyDirExists ? '**在**⇒沿用' : '不在⇒新装用 ' + dd.newInstallDir}`
|
|
225
|
+
+ `(${dd.defaultDirDecision.using})`,
|
|
226
|
+
` 解释器:${py.ok ? `${py.path}(${(py.version || []).join('.')},来源 ${py.source})` : '❌ 不可用'}`,
|
|
227
|
+
py.ok ? null : (py.diagnostics || '').split('\n').map((l) => ` ${l}`).join('\n'),
|
|
228
|
+
` 技能:${roots.map((r) => `${r.id}=${r.count}${r.exists ? '' : '(缺)'}`).join(' ')}`,
|
|
229
|
+
` 许可:${lic.state}(${lic.reason || 'ok'})${lic.expiresAt ? ` 到期 ${lic.expiresAt}` : ''}`,
|
|
230
|
+
` 通道:可用 ${json.channels.available.join('、') || '(无)'}|预留位 ${json.channels.reserved.join('、') || '(无)'}`,
|
|
231
|
+
` 宿主:dsh home ${host.dshHome}${host.dshHomeExists ? '' : '(不存在)'}(缺宿主不是 FAIL)`,
|
|
232
|
+
` ⛔ 未写 settings.yaml / credentials;本 verb 只动数据目录本身`,
|
|
233
|
+
].filter(Boolean).join('\n')
|
|
234
|
+
return { json, human, stderr: '', exit: 0 }
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
function initFail(code, reason, error, echo = {}) {
|
|
238
|
+
return { json: { ok: false, empty: false, code, reason, error, verb: 'init', ...echo }, human: `❌ ${reason}:${error}`, stderr: '', exit: code }
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
export const INIT_USAGE = [
|
|
242
|
+
'用法:argent init [--apply] [--json]',
|
|
243
|
+
'',
|
|
244
|
+
'数据目录画像(只读):数据目录(来源/隔离/存在性)、解释器候选、已装技能、许可状态、通道可用性、宿主档事实。',
|
|
245
|
+
'默认 **dry-run**(不建目录);--apply 才创建数据目录本身。',
|
|
246
|
+
'⛔ 不写 ~/.dsh/settings.yaml、不碰 credentials、不改宿主配置。',
|
|
247
|
+
].join('\n')
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 台账查询(0.3 P1,2026-10-03)—— **CLI 与 MCP 用同一套**(修 D1)。
|
|
3
|
+
*
|
|
4
|
+
* D1 实测(复现原文):`node core/cli/argent.mjs ledger --tail 3` 在 289849 字节的真实台账上返回
|
|
5
|
+
* {"ok": true, "count": 0, "rows": []}
|
|
6
|
+
* —— "唯一真相"说谎。两处根因:
|
|
7
|
+
* ① `args[args.indexOf('--flow') + 1]`:`--flow` 不存在时 `indexOf` = −1 ⇒ 取到 `args[0]`(`--tail`)当流程名过滤;
|
|
8
|
+
* ② **先 `slice(-tail)` 再 `filter`** ⇒ 即使给了 `--flow`,也只在该尾部窗口内筛。
|
|
9
|
+
* 本模块把顺序钉死:**读全量 → 过滤 → 再取末尾 N 行**;并把"实际生效的过滤条件"
|
|
10
|
+
* (`flow` / `flowPrefix` / `tail` / `matched`)一并回显 —— 空结果必须能一眼分辨
|
|
11
|
+
* "真没有" 与 "被过滤/被截断掉了"(这正是 D1 最贵的形态:看起来空、其实是过滤)。
|
|
12
|
+
*
|
|
13
|
+
* 匹配口径与 `scripts/flow_ledger.py --status --flow` 一致:`caseId` 前缀 `<flow>__`
|
|
14
|
+
* (台账里写的是**局部 id**:`full__…`/`wenyao-invoice__…`;同时容忍规范 id 前缀,防假空)。
|
|
15
|
+
*/
|
|
16
|
+
import { existsSync, readFileSync } from 'node:fs'
|
|
17
|
+
import { loadRegistry, resolveFlow } from './flow-registry.mjs'
|
|
18
|
+
|
|
19
|
+
export const DEFAULT_TAIL = 5
|
|
20
|
+
|
|
21
|
+
/** `--tail` 解析:非 ≥1 的整数 ⇒ 回落到缺省,并**说明**(不静默用 `slice(-0)` 把全量当"末尾 0 行")。 */
|
|
22
|
+
export function parseTail(input, dflt = DEFAULT_TAIL) {
|
|
23
|
+
if (input === undefined || input === null || String(input).trim() === '') return { tail: dflt, note: '' }
|
|
24
|
+
const n = Number(String(input).trim())
|
|
25
|
+
if (!Number.isFinite(n) || n < 1) {
|
|
26
|
+
return { tail: dflt, note: `--tail ${JSON.stringify(String(input))} 不是 ≥1 的整数 ⇒ 用默认 ${dflt}` }
|
|
27
|
+
}
|
|
28
|
+
return { tail: Math.floor(n), note: '' }
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/** 台账文本 → 事件行(坏行保留为 `{raw}`,绝不丢掉证据)。 */
|
|
32
|
+
export function parseLedger(text) {
|
|
33
|
+
return String(text || '').split('\n').map((l) => l.trim()).filter(Boolean)
|
|
34
|
+
.map((l) => { try { return JSON.parse(l) } catch { return { raw: l } } })
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/** caseId 是否属于该流程:`<prefix>__…`(或恰好等于 prefix)。 */
|
|
38
|
+
export function caseIdMatches(caseId, filter) {
|
|
39
|
+
const cid = String(caseId || '')
|
|
40
|
+
if (!cid || !filter) return false
|
|
41
|
+
return (filter.prefixes || []).some((p) => p && (cid === p || cid.startsWith(`${p}__`)))
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* 把 `--flow` 解析成"实际生效的过滤条件"。
|
|
46
|
+
*
|
|
47
|
+
* **best-effort**:台账是只读的"唯一真相",不能因为解释器缺失/注册表坏了就整个查不了;
|
|
48
|
+
* 但失败原因会写进 `note` 回显(**不是**空 catch 吞掉)—— 这是 D3 ③ 的口径。
|
|
49
|
+
*/
|
|
50
|
+
export function resolveLedgerFlow(input, { repo, py, pyFailure = null } = {}) {
|
|
51
|
+
const literal = String(input || '')
|
|
52
|
+
const literalFilter = (why) => ({
|
|
53
|
+
flow: literal,
|
|
54
|
+
prefixes: [literal],
|
|
55
|
+
known: false,
|
|
56
|
+
note: `--flow ${literal} 未经注册表解析(${why})⇒ 只按字面 caseId 前缀 \`${literal}__\` 匹配;`
|
|
57
|
+
+ '可用流程 id 见 python3 scripts/flow_registry.py --list',
|
|
58
|
+
})
|
|
59
|
+
if (pyFailure) return literalFilter('解释器不可用')
|
|
60
|
+
const reg = loadRegistry({ repo, py })
|
|
61
|
+
if (!reg.ok) return literalFilter(reg.reason)
|
|
62
|
+
const r = resolveFlow({ input: literal, doc: reg.doc })
|
|
63
|
+
if (!r.known) {
|
|
64
|
+
return {
|
|
65
|
+
flow: r.canonical,
|
|
66
|
+
prefixes: [r.local, r.canonical],
|
|
67
|
+
known: false,
|
|
68
|
+
note: `流程 \`${literal}\` 不在注册表里(规范 id \`${r.canonical}\`)⇒ 按前缀匹配;`
|
|
69
|
+
+ '可能是拼写错误或该流程未安装,见 python3 scripts/flow_registry.py --list',
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return { flow: r.canonical, prefixes: [r.local, r.canonical], known: true, note: '' }
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* 台账查询(纯逻辑 + 注入式读文件;CLI/MCP 共用)。
|
|
77
|
+
* 顺序:**读全量 → 过滤 → 取末尾 N 行**(D1 的根因②)。
|
|
78
|
+
*
|
|
79
|
+
* 2026-10-03(P1 前置④):补 `empty` / `code` / `reason`,让 A2 的真值表在 ledger 上也**判得出来** ——
|
|
80
|
+
* `count === 0 ⇒ empty:true`(`reason:"no_match"`);台账文件不在 ⇒ `ok:false, code:1, reason:"no_ledger"`
|
|
81
|
+
* (**不**降级成 empty:那正是 D1 最贵的形态,把"数据目录不对"伪装成"没有事件")。
|
|
82
|
+
*/
|
|
83
|
+
export function queryLedger({ file, flow = '', tailInput = '', resolveFilter = null,
|
|
84
|
+
readFile = (p) => readFileSync(p, 'utf8') } = {}) {
|
|
85
|
+
const t = parseTail(tailInput)
|
|
86
|
+
if (!existsSync(file)) {
|
|
87
|
+
return {
|
|
88
|
+
ok: false, empty: false, code: 1, reason: 'no_ledger',
|
|
89
|
+
error: `台账不存在:${file}`,
|
|
90
|
+
flow: null, flowPrefix: null, flowKnown: null, tail: t.tail, matched: 0, count: 0, rows: [],
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
const notes = []
|
|
94
|
+
if (t.note) notes.push(t.note)
|
|
95
|
+
|
|
96
|
+
const flowInput = String(flow || '').trim()
|
|
97
|
+
let filter = null
|
|
98
|
+
if (flowInput) {
|
|
99
|
+
filter = resolveFilter
|
|
100
|
+
? resolveFilter(flowInput)
|
|
101
|
+
: { flow: flowInput, prefixes: [flowInput], known: false, note: '' }
|
|
102
|
+
if (filter.note) notes.push(filter.note)
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const rows = parseLedger(readFile(file))
|
|
106
|
+
const matched = filter ? rows.filter((r) => caseIdMatches(r.caseId, filter)) : rows
|
|
107
|
+
const out = matched.slice(-t.tail) // ← 过滤**之后**才截断
|
|
108
|
+
const empty = out.length === 0
|
|
109
|
+
return {
|
|
110
|
+
ok: true,
|
|
111
|
+
empty,
|
|
112
|
+
code: 0,
|
|
113
|
+
// 空结果也必须能判:`no_match` = 真没有(或被过滤/被截断 —— 靠 matched/tail 分辨)
|
|
114
|
+
reason: empty ? 'no_match' : null,
|
|
115
|
+
flow: filter ? filter.flow : null,
|
|
116
|
+
flowPrefix: filter ? (filter.prefixes || [])[0] || null : null,
|
|
117
|
+
flowKnown: filter ? Boolean(filter.known) : null,
|
|
118
|
+
tail: t.tail,
|
|
119
|
+
matched: matched.length, // 过滤后、截断前的行数(分辨"真没有"与"被截断")
|
|
120
|
+
count: out.length,
|
|
121
|
+
rows: out,
|
|
122
|
+
...(notes.length ? { note: notes.join(';') } : {}),
|
|
123
|
+
}
|
|
124
|
+
}
|