@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,208 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* validate-preset-schema.mjs — 校验 preset 行配置与插件配置 schema 的一致性
|
|
4
|
+
*
|
|
5
|
+
* 由来(2026-09-10 事故):`@deepseek-ai/dsh-persona` 的 Config 里 `prefix` 必填,
|
|
6
|
+
* 而阿根廷 14 个 preset 把 persona 文本写在 `text:` 下 ⇒ 该行**从未真正挂载**。
|
|
7
|
+
* 旧版 dsh 容忍 preset 挂载失败(静默),0.1.5 起挂载失败会让 session resume 直接失败,
|
|
8
|
+
* 才暴露出来。本脚本把"行 × schema 必填键"的核对固化为门禁。
|
|
9
|
+
*
|
|
10
|
+
* 判据:
|
|
11
|
+
* 1. 缺必填键 —— 若**官方 standard preset 的同名行同样未给**该键,视为"加载期注入"→ 豁免(仅提示);
|
|
12
|
+
* 否则判失败(hard)。
|
|
13
|
+
* 2. 未知键(schema 里没有的键)—— 同样按官方行对照豁免,否则只警告(zod 默认剥离未知键,不必然报错)。
|
|
14
|
+
*
|
|
15
|
+
* 用法:
|
|
16
|
+
* node scripts/validate-preset-schema.mjs # 校验仓库 presets/
|
|
17
|
+
* node scripts/validate-preset-schema.mjs --runtime # 追加校验 ~/.dsh/.agent-presets/
|
|
18
|
+
* node scripts/validate-preset-schema.mjs --json # 机器可读输出
|
|
19
|
+
* 退出码:0 全部通过;1 存在 hard 失败。
|
|
20
|
+
*/
|
|
21
|
+
import { readFileSync, existsSync, readdirSync, rmSync, statSync, writeFileSync } from 'node:fs'
|
|
22
|
+
import { join, dirname } from 'node:path'
|
|
23
|
+
import { fileURLToPath } from 'node:url'
|
|
24
|
+
import { homedir } from 'node:os'
|
|
25
|
+
import { requireDshScope } from './lib/dsh-scope.mjs'
|
|
26
|
+
import { MECHANISM, detectPresetMechanism, mechanismNote } from './lib/preset-mechanism.mjs'
|
|
27
|
+
import { REQUIRED_KEY_DEFAULTS } from './lib/preset-compat.mjs'
|
|
28
|
+
|
|
29
|
+
const __dirname = dirname(fileURLToPath(import.meta.url))
|
|
30
|
+
const REPO = join(__dirname, '..')
|
|
31
|
+
// 2026-09-28:env 覆盖之外再补自动探测(客户机没设 DSH_SCOPE 时不再假红)。
|
|
32
|
+
// 0.6.0 F1 修复批(2026-10-14):preset 机制判的是**宿主树**(`detectPresetMechanism(SCOPE)`)⇒
|
|
33
|
+
// 拿不到 scope 就是**探测失败**(≠"没有 preset")⇒ 响亮失败,⛔ 不用假路径把机制静默判成 unknown。
|
|
34
|
+
let SCOPE
|
|
35
|
+
try { SCOPE = requireDshScope({ what: 'preset schema 门禁(validate-preset-schema)' }) }
|
|
36
|
+
catch (e) { console.error(` ❌ ${e.message}`); process.exit(1) }
|
|
37
|
+
const PROFILE_NM = join(homedir(), '.dsh', 'profiles', 'node_modules')
|
|
38
|
+
// 官方参考 preset 的**位置随版本变化**(2026-09-28):
|
|
39
|
+
// 0.1.5:`dsh-agent-presets/presets/standard/agent.cordis.yml`(目录式)
|
|
40
|
+
// 0.1.7:`dsh-web-app/presets/standard.patch.yml`(行式;dsh-agent-presets 已删除)
|
|
41
|
+
const OFFICIAL_015 = join(SCOPE, 'dsh-agent-presets', 'presets', 'standard', 'agent.cordis.yml')
|
|
42
|
+
const OFFICIAL_017 = join(SCOPE, 'dsh-web-app', 'presets', 'standard.patch.yml')
|
|
43
|
+
const OFFICIAL = existsSync(OFFICIAL_015) ? OFFICIAL_015 : (existsSync(OFFICIAL_017) ? OFFICIAL_017 : OFFICIAL_015)
|
|
44
|
+
const MECHANISM_NOW = detectPresetMechanism(SCOPE)
|
|
45
|
+
const RUNTIME_PRESETS = join(homedir(), '.dsh', '.agent-presets')
|
|
46
|
+
/** 行式产物(生成器输出):也要校验——它才是 0.1.7 上真正被加载的那份。 */
|
|
47
|
+
const ROWS_BUNDLE = join(REPO, 'plugins', 'argent-presets')
|
|
48
|
+
|
|
49
|
+
const argv = process.argv.slice(2)
|
|
50
|
+
const asJson = argv.includes('--json')
|
|
51
|
+
const withRuntime = argv.includes('--runtime')
|
|
52
|
+
const explicitDirs = argv.filter((a) => !a.startsWith('--'))
|
|
53
|
+
|
|
54
|
+
const roots = explicitDirs.length
|
|
55
|
+
? explicitDirs
|
|
56
|
+
: [join(REPO, 'presets'), ...(withRuntime ? [RUNTIME_PRESETS] : [])]
|
|
57
|
+
|
|
58
|
+
/** 从行式 patch 文件里抽出 `config.plugins` 子行(去掉 10 空格前缀),交给同一个解析器。 */
|
|
59
|
+
function rowsFromPatch(file) {
|
|
60
|
+
const text = readFileSync(file, 'utf8')
|
|
61
|
+
const i = text.indexOf(' plugins:\n')
|
|
62
|
+
if (i === -1) return []
|
|
63
|
+
const body = text.slice(i + ' plugins:\n'.length).split('\n')
|
|
64
|
+
.map((l) => (l.startsWith(' '.repeat(10)) ? l.slice(10) : l))
|
|
65
|
+
.join('\n')
|
|
66
|
+
const tmp = `${file}.rows.tmp`
|
|
67
|
+
writeFileSync(tmp, body)
|
|
68
|
+
try { return parseRows(tmp) } finally { rmSync(tmp, { force: true }) }
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// ── 解析:preset 文件 → 行 [{id, name, keys}] ─────────────────────────────
|
|
72
|
+
function parseRows(file) {
|
|
73
|
+
const lines = readFileSync(file, 'utf8').split('\n')
|
|
74
|
+
const rows = []
|
|
75
|
+
let cur = null
|
|
76
|
+
for (const raw of lines) {
|
|
77
|
+
const s = raw.trim()
|
|
78
|
+
if (s.startsWith('- id:')) {
|
|
79
|
+
if (cur) rows.push(cur)
|
|
80
|
+
cur = { id: s.slice(5).trim().replace(/^['"]|['"]$/g, ''), name: null, keys: [], inCfg: false }
|
|
81
|
+
continue
|
|
82
|
+
}
|
|
83
|
+
if (!cur) continue
|
|
84
|
+
if (s.startsWith('name:') && cur.name === null) {
|
|
85
|
+
cur.name = s.slice(5).trim().replace(/^['"]|['"]$/g, '')
|
|
86
|
+
} else if (/^config:\s*$/.test(s)) {
|
|
87
|
+
cur.inCfg = true
|
|
88
|
+
} else if (cur.inCfg && /^[A-Za-z_][\w]*:/.test(s)) {
|
|
89
|
+
cur.keys.push(s.split(':')[0])
|
|
90
|
+
} else if (cur.inCfg && s && !raw.startsWith(' ') && !raw.startsWith('\t')) {
|
|
91
|
+
cur.inCfg = false
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
if (cur) rows.push(cur)
|
|
95
|
+
return rows.filter((r) => r.name)
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// ── 解析:包的 `const Config = z.object({...})` → { 必填键[], 全部键[] } ──
|
|
99
|
+
function schemaOf(pkg) {
|
|
100
|
+
const dir = [join(SCOPE, pkg.replace('@deepseek-ai/', '')), join(PROFILE_NM, pkg.replace('@deepseek-ai/', ''))]
|
|
101
|
+
.find((d) => existsSync(d))
|
|
102
|
+
if (!dir) return null
|
|
103
|
+
const entry = join(dir, 'lib', 'index.js')
|
|
104
|
+
if (!existsSync(entry)) return null
|
|
105
|
+
const src = readFileSync(entry, 'utf8')
|
|
106
|
+
const m = /const Config = z\.object\(\{/.exec(src)
|
|
107
|
+
if (!m) return { all: [], required: [] }
|
|
108
|
+
let depth = 0
|
|
109
|
+
let j = m.index + m[0].length - 1
|
|
110
|
+
const start = j
|
|
111
|
+
for (; j < src.length; j++) {
|
|
112
|
+
if (src[j] === '{') depth++
|
|
113
|
+
else if (src[j] === '}') {
|
|
114
|
+
depth--
|
|
115
|
+
if (depth === 0) { j++; break }
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
const body = src.slice(start, j)
|
|
119
|
+
const all = []
|
|
120
|
+
const required = []
|
|
121
|
+
for (const km of body.matchAll(/\n\t([A-Za-z_][\w]*):\s*([^\n]*)/g)) {
|
|
122
|
+
all.push(km[1])
|
|
123
|
+
if (!/\.default\(|\.optional\(\)/.test(km[2])) required.push(km[1])
|
|
124
|
+
}
|
|
125
|
+
return { all, required }
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// ── 官方 standard preset 的同名行(豁免基准)──────────────────────────────
|
|
129
|
+
const official = existsSync(OFFICIAL) ? parseRows(OFFICIAL) : []
|
|
130
|
+
const officialByPkg = new Map()
|
|
131
|
+
// 官方参考:目录式读 agent.cordis.yml;行式读 patch 文件(抽 plugins 子行)
|
|
132
|
+
if (MECHANISM_NOW === MECHANISM.ROWS && existsSync(OFFICIAL_017)) {
|
|
133
|
+
for (const r of rowsFromPatch(OFFICIAL_017)) {
|
|
134
|
+
if (!r.keys.length || !r.name) continue
|
|
135
|
+
if (!officialByPkg.has(r.name)) officialByPkg.set(r.name, [])
|
|
136
|
+
officialByPkg.get(r.name).push(r)
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
for (const r of official) {
|
|
140
|
+
if (!officialByPkg.has(r.name)) officialByPkg.set(r.name, [])
|
|
141
|
+
officialByPkg.get(r.name).push(r)
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
const findings = []
|
|
145
|
+
let checkedRows = 0
|
|
146
|
+
let checkedFiles = 0
|
|
147
|
+
|
|
148
|
+
// 说明(2026-09-28):**不再**对生成的 patch 文件做二次 schema 解析 —— 嵌套行列表解析容易
|
|
149
|
+
// 串行(实测报出 "未知键 undefined")。产物的正确性由两条更强的判据保证:
|
|
150
|
+
// ① 生成器保证"与作者格式逐行等价"(preset-mechanism.test.mjs 断言);
|
|
151
|
+
// ② 0.1.7 隔离试跑真挂载 13/13(注册表 broken 诊断会点名任何行问题)。
|
|
152
|
+
|
|
153
|
+
for (const root of roots) {
|
|
154
|
+
if (!existsSync(root)) continue
|
|
155
|
+
const dirs = readdirSync(root).filter((d) => {
|
|
156
|
+
try { return statSync(join(root, d)).isDirectory() } catch { return false }
|
|
157
|
+
})
|
|
158
|
+
for (const d of dirs) {
|
|
159
|
+
const file = join(root, d, 'agent.cordis.yml')
|
|
160
|
+
if (!existsSync(file)) continue
|
|
161
|
+
checkedFiles++
|
|
162
|
+
for (const row of parseRows(file)) {
|
|
163
|
+
if (!row.keys.length) continue
|
|
164
|
+
const schema = schemaOf(row.name)
|
|
165
|
+
if (!schema) continue
|
|
166
|
+
checkedRows++
|
|
167
|
+
const offRows = officialByPkg.get(row.name) || []
|
|
168
|
+
const offKeys = new Set(offRows.flatMap((r) => r.keys))
|
|
169
|
+
for (const k of schema.required) {
|
|
170
|
+
if (row.keys.includes(k)) continue
|
|
171
|
+
// 兼容层会在**生成行式产物时**补齐的键:作者格式缺它不算失败,但如实说明(附官方出处)。
|
|
172
|
+
// 0.1.5(客户机/作者格式实际生效的版本)上这些键本就不必填,故不影响现网判定。
|
|
173
|
+
const auto = (REQUIRED_KEY_DEFAULTS[row.name] || {})[k]
|
|
174
|
+
if (auto) {
|
|
175
|
+
findings.push({ level: 'info', preset: d, id: row.id, pkg: row.name, kind: 'auto-filled', key: k,
|
|
176
|
+
note: `兼容层生成时补 ${auto.value}(出处:${auto.source})` })
|
|
177
|
+
continue
|
|
178
|
+
}
|
|
179
|
+
const exempt = offRows.length > 0 && !offKeys.has(k) // 官方同样未给 → 加载期注入
|
|
180
|
+
findings.push({ level: exempt ? 'info' : 'fail', preset: d, id: row.id, pkg: row.name, kind: 'missing-required', key: k, exempt })
|
|
181
|
+
}
|
|
182
|
+
for (const k of row.keys) {
|
|
183
|
+
if (schema.all.includes(k)) continue
|
|
184
|
+
const exempt = offKeys.has(k)
|
|
185
|
+
findings.push({ level: exempt ? 'info' : 'warn', preset: d, id: row.id, pkg: row.name, kind: 'unknown-key', key: k, exempt })
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
const fails = findings.filter((f) => f.level === 'fail')
|
|
192
|
+
const warns = findings.filter((f) => f.level === 'warn')
|
|
193
|
+
|
|
194
|
+
if (asJson) {
|
|
195
|
+
console.log(JSON.stringify({ files: checkedFiles, rows: checkedRows, fails, warns, findings }, null, 2))
|
|
196
|
+
} else {
|
|
197
|
+
const verbose = argv.includes('--verbose')
|
|
198
|
+
console.log(`preset-schema 校验:${checkedFiles} 个 preset 文件 / ${checkedRows} 个带 config 的行`)
|
|
199
|
+
for (const f of (verbose ? findings : findings.filter((x) => x.level !== 'info'))) {
|
|
200
|
+
const tag = f.level === 'fail' ? '❌' : f.level === 'warn' ? '⚠️ ' : 'ℹ️ '
|
|
201
|
+
console.log(` ${tag} ${f.preset} :: ${f.id} (${f.pkg}) ${f.kind === 'missing-required' ? '缺必填键' : '未知键'} ${f.key}${f.level === 'info' ? '(官方同样未给 → 加载期注入,豁免)' : ''}`)
|
|
202
|
+
}
|
|
203
|
+
const infos = findings.filter((f) => f.level === 'info')
|
|
204
|
+
if (!findings.length) console.log(' ✅ 未发现问题')
|
|
205
|
+
else if (!verbose && infos.length) console.log(` ℹ️ 另有 ${infos.length} 项按官方 preset 对照豁免(加载期注入;--verbose 查看)`)
|
|
206
|
+
console.log(`\n结果: ${fails.length ? `❌ FAIL ${fails.length} 项` : '✅ PASS'}${warns.length ? `(另有 ${warns.length} 项警告)` : ''}`)
|
|
207
|
+
}
|
|
208
|
+
process.exit(fails.length ? 1 : 0)
|
|
@@ -0,0 +1,323 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* vertical-init —— 业务线脚手架(P1,2026-09-29)
|
|
4
|
+
*
|
|
5
|
+
* 目标:**不看引擎源码就能开一条新业务线**。生成物必须一次通过 `vertical-lint.mjs`
|
|
6
|
+
* (脚手架与门禁同源同规矩,见测试 `scripts/tests/vertical-lint.test.mjs`:
|
|
7
|
+
* 它对生成结果直接跑 lint —— 脚手架自己不合格就别指望别人合格)。
|
|
8
|
+
*
|
|
9
|
+
* 生成的骨架默认是**安全态**:`trigger.wake = off`(不唤醒角色)、`mode = shadow`(不真实写入)、
|
|
10
|
+
* 流程门内判据留 TODO。要把一条线跑起来,按生成文件里的注释逐项填。
|
|
11
|
+
*
|
|
12
|
+
* 用法:
|
|
13
|
+
* node scripts/vertical-init.mjs --id customer-order --title "客单系统" \
|
|
14
|
+
* [--flow intake] [--role clerk] [--skill intake] \
|
|
15
|
+
* [--from-preset argent-general] [--root <dir>] [--force]
|
|
16
|
+
*/
|
|
17
|
+
import { cpSync, existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs'
|
|
18
|
+
import { dirname, join } from 'node:path'
|
|
19
|
+
import { fileURLToPath } from 'node:url'
|
|
20
|
+
|
|
21
|
+
const HERE = dirname(fileURLToPath(import.meta.url))
|
|
22
|
+
const DEFAULT_ROOT = join(HERE, '..')
|
|
23
|
+
const argOf = (name, dflt = null) => {
|
|
24
|
+
const i = process.argv.indexOf(name)
|
|
25
|
+
return i > -1 ? process.argv[i + 1] ?? dflt : dflt
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const ROOT = argOf('--root', DEFAULT_ROOT)
|
|
29
|
+
const ID = argOf('--id')
|
|
30
|
+
const TITLE = argOf('--title')
|
|
31
|
+
const FLOW = argOf('--flow', 'main')
|
|
32
|
+
const ROLE = argOf('--role', 'clerk')
|
|
33
|
+
const SKILL = argOf('--skill', FLOW)
|
|
34
|
+
const FROM_PRESET = argOf('--from-preset', 'argent-general')
|
|
35
|
+
const FORCE = process.argv.includes('--force')
|
|
36
|
+
const ID_RE = /^[a-z0-9]+(?:-[a-z0-9]+)*$/
|
|
37
|
+
|
|
38
|
+
const die = (m) => { console.error(`✗ ${m}`); process.exit(2) }
|
|
39
|
+
if (!ID) die('必须给 --id(小写 kebab-case,如 customer-order)')
|
|
40
|
+
if (!ID_RE.test(ID)) die(`--id 不合法:${ID}(只允许小写字母/数字/连字符)`)
|
|
41
|
+
for (const [k, v] of [['--flow', FLOW], ['--role', ROLE], ['--skill', SKILL]]) {
|
|
42
|
+
if (!ID_RE.test(v)) die(`${k} 不合法:${v}(只允许小写字母/数字/连字符)`)
|
|
43
|
+
}
|
|
44
|
+
const VDIR = join(ROOT, 'verticals', ID)
|
|
45
|
+
if (existsSync(VDIR) && !FORCE) die(`业务线已存在:verticals/${ID}/(要覆盖加 --force)`)
|
|
46
|
+
|
|
47
|
+
const roleId = `argent-${ID}-${ROLE}`
|
|
48
|
+
const flowId = `${ID}.${FLOW}`
|
|
49
|
+
const skillId = `${ID}-${SKILL}`
|
|
50
|
+
const capId = `${ID}.${FLOW}`
|
|
51
|
+
const pauseSwitch = `verticals/${ID}/PAUSE.${FLOW}`
|
|
52
|
+
const envFile = `verticals/${ID}/env/example.env`
|
|
53
|
+
const promptTpl = `verticals/${ID}/prompts/${FLOW}.md`
|
|
54
|
+
|
|
55
|
+
const written = []
|
|
56
|
+
const put = (rel, content) => {
|
|
57
|
+
const p = join(VDIR, rel)
|
|
58
|
+
mkdirSync(dirname(p), { recursive: true })
|
|
59
|
+
writeFileSync(p, content)
|
|
60
|
+
written.push(`verticals/${ID}/${rel}`)
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// ── manifest ────────────────────────────────────────────────────
|
|
64
|
+
put('manifest.json', JSON.stringify({
|
|
65
|
+
id: ID,
|
|
66
|
+
version: '0.1.0',
|
|
67
|
+
title: TITLE || ID,
|
|
68
|
+
_note: '业务线元数据(唯一真源)。requires 声明可用的 argent/dsh 区间;verifiedOn 记录**已实测**过的版本(装机时据此判断能否装到目标机器)。',
|
|
69
|
+
requires: { argent: '>=0.2 <0.3', dsh: '>=0.1.5 <0.2' },
|
|
70
|
+
verifiedOn: { argent: [], dsh: [] },
|
|
71
|
+
ports: {
|
|
72
|
+
_note: '该线用到哪些底座端口(实现名见 verticals/README.md)。新增实现时同步更新 vertical-lint 的 KNOWN_PORTS。',
|
|
73
|
+
source: 'feishu-base',
|
|
74
|
+
transport: 'feishu',
|
|
75
|
+
notify: 'feishu-card',
|
|
76
|
+
actor: 'none',
|
|
77
|
+
identity: 'env',
|
|
78
|
+
},
|
|
79
|
+
capabilities: [{ id: capId, minPlan: 'pro', title: `${TITLE || ID} · ${FLOW}` }],
|
|
80
|
+
roles: [roleId],
|
|
81
|
+
skills: [skillId],
|
|
82
|
+
flows: [flowId],
|
|
83
|
+
billing: 'billing.json',
|
|
84
|
+
preflight: 'preflight.py',
|
|
85
|
+
}, null, 2) + '\n')
|
|
86
|
+
|
|
87
|
+
// ── flows(局部 id,合并后 = <id>.<flow>)────────────────────────
|
|
88
|
+
put('flows.json', JSON.stringify({
|
|
89
|
+
vertical: ID,
|
|
90
|
+
_note: '局部 id 不带点;合并后规范 id = `<vertical>.<local>`。冲突由 flow_registry.py 判定(冲突即 fail)。',
|
|
91
|
+
aliases: { [FLOW]: flowId },
|
|
92
|
+
flows: {
|
|
93
|
+
[FLOW]: {
|
|
94
|
+
title: `${TITLE || ID} · ${FLOW}`,
|
|
95
|
+
role: `${ID}-${ROLE}`,
|
|
96
|
+
// D2(2026-10-03):流程 → 技能目录的**单一真源**(`argent sop` 只认它,不用 role 拼路径)。
|
|
97
|
+
// 门禁 vertical-lint 要求:必须 ∈ manifest#skills 且 `skills/<名>/SKILL.md` 存在 —— 下面已生成。
|
|
98
|
+
skill: skillId,
|
|
99
|
+
source: {
|
|
100
|
+
_gateNote: 'TODO 门内判据:什么算待办。三个原语:equals(必须等于)/ excludeEquals(排除已完成)/ excludeAllPresent(多项都在=人工已完成)。',
|
|
101
|
+
pick: { equals: { 'TODO 状态列': 'TODO 值' } },
|
|
102
|
+
envFile,
|
|
103
|
+
baseEnv: 'TODO_BASE_TOKEN',
|
|
104
|
+
tableEnv: 'TODO_TABLE_ID',
|
|
105
|
+
identityEnv: 'TODO_LARK_PROFILE',
|
|
106
|
+
},
|
|
107
|
+
routing: { routeBy: '', match: '' },
|
|
108
|
+
trigger: {
|
|
109
|
+
intervalSec: 300,
|
|
110
|
+
jitterSec: 45,
|
|
111
|
+
wake: 'off',
|
|
112
|
+
mode: 'shadow',
|
|
113
|
+
pauseSwitch,
|
|
114
|
+
log: `flow-trigger-${ID}-${FLOW}.log`,
|
|
115
|
+
promptTemplate: promptTpl,
|
|
116
|
+
dispatch: { preset: roleId, cwd: '~' },
|
|
117
|
+
},
|
|
118
|
+
concurrency: { max: 1, leaseMin: 20 },
|
|
119
|
+
guards: { dryRunDefault: true, backup: true, audit: true, writeFields: [] },
|
|
120
|
+
required: [],
|
|
121
|
+
failures: { enabled: false },
|
|
122
|
+
capability: capId,
|
|
123
|
+
notes: [
|
|
124
|
+
'由 scripts/vertical-init.mjs 生成的骨架:默认 wake=off / mode=shadow,先只读跑通再接写路径。',
|
|
125
|
+
'上线顺序:填门内判据 → 只读跑通(triage/claim 干跑)→ 接执行器 → 开 mode=real → 开 wake=session。',
|
|
126
|
+
],
|
|
127
|
+
},
|
|
128
|
+
},
|
|
129
|
+
capabilities: {
|
|
130
|
+
[FLOW]: {
|
|
131
|
+
_note: '能力级共享规则(同一能力的多条流程必须共享这里,差异只能写在 flow.overrides 并被门禁检查)。',
|
|
132
|
+
gate: { equals: { 'TODO 状态列': 'TODO 值' } },
|
|
133
|
+
writeFields: [],
|
|
134
|
+
stateFields: [],
|
|
135
|
+
gapPolicy: 'TODO:缺口回填口径(哪些缺口该落卡等人、哪些只记错误信息)',
|
|
136
|
+
},
|
|
137
|
+
},
|
|
138
|
+
billing: { units: {} },
|
|
139
|
+
}, null, 2) + '\n')
|
|
140
|
+
|
|
141
|
+
// ── 提示词 / env / 计费 / 预检 / 测试 ────────────────────────────
|
|
142
|
+
put(`prompts/${FLOW}.md`, `# ${TITLE || ID} · ${FLOW} 派发提示词
|
|
143
|
+
|
|
144
|
+
你是「${TITLE || ID}」业务线的执行角色。本轮任务:
|
|
145
|
+
|
|
146
|
+
- 流程:\`${flowId}\`
|
|
147
|
+
- 范围:{{TICKETS}}(由引擎按门内判据挑选后传入)
|
|
148
|
+
|
|
149
|
+
## 必做
|
|
150
|
+
|
|
151
|
+
1. 先读技能 \`${skillId}\`(口径与踩坑都在那里)。
|
|
152
|
+
2. 逐条处理,**每条都要有终态**(完成/需人工/失败分类),不许留半成品。
|
|
153
|
+
3. 写路径遵守白名单(见注册表 \`guards.writeFields\`);写前备份、写后读回验收。
|
|
154
|
+
|
|
155
|
+
## 红线
|
|
156
|
+
|
|
157
|
+
- 不碰本业务线以外的数据与文件。
|
|
158
|
+
- 不确认的信息**落卡问人**,不要猜。
|
|
159
|
+
`)
|
|
160
|
+
|
|
161
|
+
put('env/example.env', `# ${TITLE || ID} 业务线环境(**命名空间隔离**:只在本线可见)
|
|
162
|
+
# 复制为 verticals/${ID}/env/<name>.env 并填真实值;权限 600。
|
|
163
|
+
# 变量名建议带业务线前缀,避免两条线同机时互相覆盖。
|
|
164
|
+
|
|
165
|
+
${ID.replace(/-/g, '_').toUpperCase()}_BASE_TOKEN=
|
|
166
|
+
${ID.replace(/-/g, '_').toUpperCase()}_TABLE_ID=
|
|
167
|
+
${ID.replace(/-/g, '_').toUpperCase()}_LARK_PROFILE=
|
|
168
|
+
`)
|
|
169
|
+
|
|
170
|
+
put('billing.json', JSON.stringify({
|
|
171
|
+
_why: '本业务线的计费单位(口径沿用平台规则:只算成功 / 一票一结 / 证据可核)。rateYuan 是占位,定价改这里,脚本不写死金额。',
|
|
172
|
+
units: {
|
|
173
|
+
[`${FLOW}-item`]: {
|
|
174
|
+
unit: 'TODO 计价单位(如:件 / 单 / 店·月)',
|
|
175
|
+
rateYuan: 0,
|
|
176
|
+
humanMinutes: 0,
|
|
177
|
+
evidence: ['TODO 证据标记(正则):能在产物/留言里核到的完成标记'],
|
|
178
|
+
},
|
|
179
|
+
},
|
|
180
|
+
}, null, 2) + '\n')
|
|
181
|
+
|
|
182
|
+
put('preflight.py', `#!/usr/bin/env python3
|
|
183
|
+
"""${TITLE || ID} 业务线的验收检查段(由 customer-preflight 汇总)。
|
|
184
|
+
|
|
185
|
+
规矩(与平台其它预检一致):
|
|
186
|
+
· 只读探测,**不写**客户数据;
|
|
187
|
+
· 每条检查给出 ok/warn/fail 与**可执行的修复建议**;
|
|
188
|
+
· 缺凭据/缺权限要能区分"没配"与"配了但没权限"(历史踩坑:笼统报 91403 会误导)。
|
|
189
|
+
"""
|
|
190
|
+
from __future__ import annotations
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
def checks(env: dict) -> list[tuple[str, str, str, str]]:
|
|
194
|
+
"""返回 [(名称, 级别 ok|warn|fail, 说明, 修复建议)]。"""
|
|
195
|
+
out: list[tuple[str, str, str, str]] = []
|
|
196
|
+
# 示例(把 TODO 换成真实探测):
|
|
197
|
+
# base = env.get('${ID.replace(/-/g, '_').toUpperCase()}_BASE_TOKEN')
|
|
198
|
+
# if not base:
|
|
199
|
+
# out.append(('${ID}.creds', 'fail', '缺 ${ID.replace(/-/g, '_').toUpperCase()}_BASE_TOKEN', '填 verticals/${ID}/env/<name>.env'))
|
|
200
|
+
out.append(('${ID}.todo', 'warn', '预检段尚未实现(脚手架占位)', '实现 checks() 后接入 customer-preflight'))
|
|
201
|
+
return out
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
if __name__ == '__main__':
|
|
205
|
+
for name, level, msg, fix in checks({}):
|
|
206
|
+
print(f'[{level}] {name}: {msg} → {fix}')
|
|
207
|
+
`)
|
|
208
|
+
|
|
209
|
+
put('tests/README.md', `# ${TITLE || ID} · 测试
|
|
210
|
+
|
|
211
|
+
放本业务线自己的测试与夹具(**只在本线内**,不引用别的业务线):
|
|
212
|
+
|
|
213
|
+
- \`*.test.mjs\` / \`*.test.py\`:会被 \`scripts/tests/run-all-tests.sh\` 收集执行;
|
|
214
|
+
- 夹具只落本目录或临时目录,**不要**放进仓库根或别的线;
|
|
215
|
+
- 端到端夹具建议从测试数据源复制一份(脱敏),不要直连生产表。
|
|
216
|
+
`)
|
|
217
|
+
|
|
218
|
+
put('README.md', `# 业务线:${TITLE || ID}(\`${ID}\`)
|
|
219
|
+
|
|
220
|
+
由 \`node scripts/vertical-init.mjs\` 生成。**待办清单**(按顺序做,做完一项勾一项):
|
|
221
|
+
|
|
222
|
+
1. [ ] 填 \`manifest.json\`:\`requires\` / \`verifiedOn\` / \`ports\`(实现名见 \`verticals/README.md\`)
|
|
223
|
+
2. [ ] 填 \`flows.json\` 的门内判据(\`source.pick\` 与 \`capabilities.<c>.gate\` 保持一致)
|
|
224
|
+
3. [ ] 复制 \`env/example.env\` 为真实 env(600)并填值;\`source.*Env\` 指向这些变量名
|
|
225
|
+
4. [ ] 写 \`prompts/${FLOW}.md\` 的派发提示词(现在是骨架)
|
|
226
|
+
5. [ ] 写技能 \`skills/${skillId}/SKILL.md\` 的口径与踩坑(**这是交付物的一部分**)
|
|
227
|
+
6. [ ] 角色:\`presets/${roleId}/\`(脚手架已从 \`${FROM_PRESET}\` 复制,改 persona 与技能清单)
|
|
228
|
+
7. [ ] 执行器与适配器:\`<flow>_runner.py\` + \`adapters/<system>/\`(人工门、错误词表、可执行计划)
|
|
229
|
+
8. [ ] 预检:实现 \`preflight.py#checks\` 并接入 \`customer-preflight\`
|
|
230
|
+
9. [ ] 门禁:\`node scripts/vertical-lint.mjs\` + \`node scripts/tests/run-all-tests.sh\`
|
|
231
|
+
10. [ ] 打包与授权:\`capabilities\` 的 \`minPlan\`、\`billing.json\` 的计价单位
|
|
232
|
+
11. [ ] 上线顺序:\`wake=off/mode=shadow\` 只读跑通 → \`mode=real\` → \`wake=session\`
|
|
233
|
+
`)
|
|
234
|
+
|
|
235
|
+
// ── 角色 preset(从底座角色复制,改 persona 与说明)─────────────
|
|
236
|
+
const srcPreset = join(ROOT, 'presets', FROM_PRESET)
|
|
237
|
+
if (!existsSync(srcPreset)) {
|
|
238
|
+
die(`底座角色不存在:presets/${FROM_PRESET}/(用 --from-preset 指定一个已存在的角色)`)
|
|
239
|
+
}
|
|
240
|
+
const dstPreset = join(ROOT, 'presets', roleId)
|
|
241
|
+
mkdirSync(dirname(dstPreset), { recursive: true })
|
|
242
|
+
cpSync(srcPreset, dstPreset, { recursive: true })
|
|
243
|
+
|
|
244
|
+
// preset.yml:名称与描述换成新角色
|
|
245
|
+
const presetYml = join(dstPreset, 'preset.yml')
|
|
246
|
+
if (existsSync(presetYml)) {
|
|
247
|
+
const t = readFileSync(presetYml, 'utf8')
|
|
248
|
+
const out = t
|
|
249
|
+
.replace(/^name:.*$/m, `name: ${TITLE || ID} · ${ROLE}`)
|
|
250
|
+
.replace(/^description:.*$/m, `description: ${TITLE || ID} 业务线角色(${roleId})—— 由 vertical-init 生成,请改写为真实职责说明。`)
|
|
251
|
+
writeFileSync(presetYml, out)
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
// agent.cordis.yml:persona 块的 prefix 文本换成新角色(保留其余行集合不动)
|
|
255
|
+
const cordis = join(dstPreset, 'agent.cordis.yml')
|
|
256
|
+
if (existsSync(cordis)) {
|
|
257
|
+
const lines = readFileSync(cordis, 'utf8').split('\n')
|
|
258
|
+
const out = []
|
|
259
|
+
let inPersona = false
|
|
260
|
+
let skipping = false
|
|
261
|
+
for (const line of lines) {
|
|
262
|
+
if (/^- id:\s*persona\s*$/.test(line)) { inPersona = true; out.push(line); continue }
|
|
263
|
+
if (inPersona && /^- id:/.test(line)) { inPersona = false; skipping = false }
|
|
264
|
+
if (inPersona && /^\s*prefix:\s*>?-?\s*$/.test(line)) {
|
|
265
|
+
out.push(line)
|
|
266
|
+
out.push(` 你是「${TITLE || ID}」业务线的执行角色(${roleId}),运行在 DeepSeek Harness 上(Argent)。`)
|
|
267
|
+
out.push(' 先读本业务线的技能再动手;不确定的信息落卡问人,不要猜。')
|
|
268
|
+
out.push(' 只操作本业务线的数据与文件。工作目录:{{cwd}}。模型:{{model}}。')
|
|
269
|
+
skipping = true
|
|
270
|
+
continue
|
|
271
|
+
}
|
|
272
|
+
if (skipping) {
|
|
273
|
+
if (/^\s{7,}\S/.test(line)) continue // 丢弃原 persona 文本行(缩进比键更深的续行)
|
|
274
|
+
skipping = false
|
|
275
|
+
}
|
|
276
|
+
out.push(line)
|
|
277
|
+
}
|
|
278
|
+
writeFileSync(cordis, out.join('\n'))
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
// ── 技能(kebab-case name + description,门禁 fix-skill-frontmatter 的判据)──
|
|
282
|
+
const skDir = join(ROOT, 'skills', skillId)
|
|
283
|
+
mkdirSync(skDir, { recursive: true })
|
|
284
|
+
writeFileSync(join(skDir, 'SKILL.md'), `---
|
|
285
|
+
name: ${skillId}
|
|
286
|
+
description: ${TITLE || ID} 业务线的 ${FLOW} 流程口径、操作步骤与踩坑(由 vertical-init 生成,待补全)。
|
|
287
|
+
---
|
|
288
|
+
|
|
289
|
+
# ${TITLE || ID} · ${FLOW}
|
|
290
|
+
|
|
291
|
+
## 何时用
|
|
292
|
+
|
|
293
|
+
处理 \`${flowId}\` 时**先读本技能**:口径、步骤、踩坑都在这里,不要凭常识猜。
|
|
294
|
+
|
|
295
|
+
## 口径(必须写死,避免每次口述)
|
|
296
|
+
|
|
297
|
+
- 什么算待办:
|
|
298
|
+
- 什么算完成(终态):
|
|
299
|
+
- 什么算人工已办(**防止重复派发**):
|
|
300
|
+
- 缺口怎么办(哪些落卡等人、哪些只记错误信息):
|
|
301
|
+
|
|
302
|
+
## 操作步骤
|
|
303
|
+
|
|
304
|
+
1. TODO
|
|
305
|
+
2. TODO
|
|
306
|
+
|
|
307
|
+
## 踩坑
|
|
308
|
+
|
|
309
|
+
- TODO(每踩一次坑就在这里加一条:现象 → 根因 → 正确做法)
|
|
310
|
+
`)
|
|
311
|
+
|
|
312
|
+
console.log(`✅ 已生成业务线 \`${ID}\`(${written.length} 个文件):`)
|
|
313
|
+
for (const f of written) console.log(` ${f}`)
|
|
314
|
+
console.log(` presets/${roleId}/(从 presets/${FROM_PRESET}/ 复制)`)
|
|
315
|
+
console.log(` skills/${skillId}/SKILL.md`)
|
|
316
|
+
console.log(`
|
|
317
|
+
下一步:
|
|
318
|
+
1) 填 verticals/${ID}/README.md 里的待办清单
|
|
319
|
+
2) node scripts/vertical-lint.mjs # 门禁(命名空间/跨线引用/manifest 自洽)
|
|
320
|
+
3) python3 scripts/flow_registry.py --check # 合并与冲突
|
|
321
|
+
4) node scripts/gen-preset-rows.mjs # 生成 0.1.7 行式 preset(改了角色后必跑)
|
|
322
|
+
5) node scripts/validate-preset-schema.mjs --runtime && node scripts/plugin-bootcheck.mjs
|
|
323
|
+
`)
|