openfeel 1.2.0 → 1.2.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/README.en.md +27 -8
- package/README.md +24 -5
- package/README.zh-CN.md +27 -8
- package/bin/openfeel.js +3 -2
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +5 -4
- package/dist/cli/index.js.map +1 -1
- package/dist/commands/config.d.ts.map +1 -1
- package/dist/commands/config.js +6 -0
- package/dist/commands/config.js.map +1 -1
- package/dist/commands/flow.d.ts.map +1 -1
- package/dist/commands/flow.js +106 -8
- package/dist/commands/flow.js.map +1 -1
- package/dist/commands/plan.d.ts.map +1 -1
- package/dist/commands/plan.js +7 -1
- package/dist/commands/plan.js.map +1 -1
- package/dist/commands/req.d.ts +2 -1
- package/dist/commands/req.d.ts.map +1 -1
- package/dist/commands/req.js +199 -26
- package/dist/commands/req.js.map +1 -1
- package/dist/commands/stage.d.ts.map +1 -1
- package/dist/commands/stage.js +7 -1
- package/dist/commands/stage.js.map +1 -1
- package/dist/core/config.d.ts +6 -4
- package/dist/core/config.d.ts.map +1 -1
- package/dist/core/config.js +6 -5
- package/dist/core/config.js.map +1 -1
- package/dist/core/flow-manager.d.ts +60 -9
- package/dist/core/flow-manager.d.ts.map +1 -1
- package/dist/core/flow-manager.js +189 -38
- package/dist/core/flow-manager.js.map +1 -1
- package/dist/core/i18n-data/en.d.ts.map +1 -1
- package/dist/core/i18n-data/en.js +44 -8
- package/dist/core/i18n-data/en.js.map +1 -1
- package/dist/core/i18n-data/zh-CN.d.ts.map +1 -1
- package/dist/core/i18n-data/zh-CN.js +44 -8
- package/dist/core/i18n-data/zh-CN.js.map +1 -1
- package/dist/core/managed-region.d.ts +16 -2
- package/dist/core/managed-region.d.ts.map +1 -1
- package/dist/core/managed-region.js +24 -3
- package/dist/core/managed-region.js.map +1 -1
- package/dist/core/metrics.d.ts +2 -0
- package/dist/core/metrics.d.ts.map +1 -1
- package/dist/core/metrics.js +15 -0
- package/dist/core/metrics.js.map +1 -1
- package/dist/core/model-config.d.ts +1 -1
- package/dist/core/model-config.d.ts.map +1 -1
- package/dist/core/model-config.js +2 -2
- package/dist/core/model-config.js.map +1 -1
- package/dist/core/opencode-config.js +1 -1
- package/dist/core/opencode-config.js.map +1 -1
- package/dist/core/pipeline-schema.d.ts +15 -0
- package/dist/core/pipeline-schema.d.ts.map +1 -1
- package/dist/core/pipeline-schema.js +17 -0
- package/dist/core/pipeline-schema.js.map +1 -1
- package/dist/core/plan/stage.d.ts +2 -1
- package/dist/core/plan/stage.d.ts.map +1 -1
- package/dist/core/plan/stage.js +7 -2
- package/dist/core/plan/stage.js.map +1 -1
- package/dist/core/req/aggregate.d.ts +2 -1
- package/dist/core/req/aggregate.d.ts.map +1 -1
- package/dist/core/req/aggregate.js.map +1 -1
- package/dist/core/req/body-file.d.ts.map +1 -1
- package/dist/core/req/body-file.js +7 -3
- package/dist/core/req/body-file.js.map +1 -1
- package/dist/core/req/requirement-ops.d.ts +44 -2
- package/dist/core/req/requirement-ops.d.ts.map +1 -1
- package/dist/core/req/requirement-ops.js +108 -3
- package/dist/core/req/requirement-ops.js.map +1 -1
- package/dist/core/req/types.d.ts +7 -1
- package/dist/core/req/types.d.ts.map +1 -1
- package/dist/core/req/types.js +0 -6
- package/dist/core/req/types.js.map +1 -1
- package/dist/core/runtime-log.d.ts +18 -0
- package/dist/core/runtime-log.d.ts.map +1 -1
- package/dist/core/runtime-log.js +72 -0
- package/dist/core/runtime-log.js.map +1 -1
- package/dist/core/session.d.ts +24 -0
- package/dist/core/session.d.ts.map +1 -0
- package/dist/core/session.js +107 -0
- package/dist/core/session.js.map +1 -0
- package/dist/core/template-loader.d.ts.map +1 -1
- package/dist/core/template-loader.js +2982 -6686
- package/dist/core/template-loader.js.map +1 -1
- package/dist/core/update.d.ts.map +1 -1
- package/dist/core/update.js +694 -33
- package/dist/core/update.js.map +1 -1
- package/package.json +1 -1
package/dist/commands/flow.js
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import { execSync } from 'node:child_process';
|
|
2
2
|
import { existsSync, copyFileSync, readFileSync, rmSync } from 'node:fs';
|
|
3
3
|
import { resolve } from 'node:path';
|
|
4
|
-
import { FlowManager, isFlowConcurrentError, normalizeAgentName, detectFillState, PHASE_PATH_MAX_DEPTH } from '../core/flow-manager.js';
|
|
5
|
-
import { PipelinePhaseSchema, PIPELINE_PHASES } from '../core/pipeline-schema.js';
|
|
4
|
+
import { FlowManager, isFlowConcurrentError, normalizeAgentName, detectFillState, MODE_DERIVED_TIER, PHASE_PATH_MAX_DEPTH } from '../core/flow-manager.js';
|
|
5
|
+
import { PipelinePhaseSchema, PIPELINE_PHASES, REVIEW_TIERS, TIER_PRUNED_PHASES } from '../core/pipeline-schema.js';
|
|
6
6
|
import { validateStageId, suggestStageId, normalizeStageId, findStageStatusPath, isArchivedVersionOf } from '../core/plan/path.js';
|
|
7
7
|
import { getConfigFieldLegalValues, setConfigValue } from '../core/config.js';
|
|
8
8
|
import { parse as parseYaml } from 'yaml';
|
|
9
|
-
import { MetricsStore } from '../core/metrics.js';
|
|
9
|
+
import { MetricsStore, PHASE_AGENT_MAP } from '../core/metrics.js';
|
|
10
|
+
import { runtimeLog } from '../core/runtime-log.js';
|
|
10
11
|
import { addReviewEntry } from '../core/view/entry.js';
|
|
11
12
|
import { handleAddStageError, handleConcurrentConflict } from './shared/errors.js';
|
|
12
13
|
import { t, getCliLang } from '../core/i18n.js';
|
|
@@ -348,7 +349,7 @@ export function registerFlowCommand(program) {
|
|
|
348
349
|
.command('phases')
|
|
349
350
|
.description('列出全部合法 phase 及其流转映射(自描述)')
|
|
350
351
|
.option('--json', 'Output { phases, transitions, advanceAccepted, schemaVersion } as JSON')
|
|
351
|
-
.option('--mode <mode>', '显式指定工作流模式(full | fast;缺省经 --stage / 当前阶段派生)')
|
|
352
|
+
.option('--mode <mode>', '显式指定工作流模式(full | fast | general;缺省经 --stage / 当前阶段派生)')
|
|
352
353
|
.option('--stage <id>', '按指定阶段的模式展示转移表(缺省 = 当前阶段)')
|
|
353
354
|
.action((options) => {
|
|
354
355
|
const projectPath = process.cwd();
|
|
@@ -399,7 +400,8 @@ export function registerFlowCommand(program) {
|
|
|
399
400
|
if (options.json) {
|
|
400
401
|
// advanceAccepted = 内置 15 phase(flow advance 的推进白名单),与 phases(运行时存在视图,可含自定义)显式区分
|
|
401
402
|
// 既有三字段(phases/transitions/advanceAccepted)+ transitionsDiff 逐字保留;schemaVersion 与 workflowMode 追加于末位(B1-6 / stage-75 op-002)
|
|
402
|
-
|
|
403
|
+
// reviewTier 再追加于末位(stage-92 op-001;既有 6 键逐字保留、顺序不变)
|
|
404
|
+
console.log(JSON.stringify({ schemaVersion: 1, phases, transitions, advanceAccepted: [...PIPELINE_PHASES], transitionsDiff, workflowMode: derivedMode, reviewTier: MODE_DERIVED_TIER[derivedMode] }, null, 2));
|
|
403
405
|
return;
|
|
404
406
|
}
|
|
405
407
|
console.log(t('flow.phases.title', lang));
|
|
@@ -411,6 +413,9 @@ export function registerFlowCommand(program) {
|
|
|
411
413
|
// 模式分节行(stage-75 op-002:模式 + 来源 + 查看指引)
|
|
412
414
|
console.log(t('flow.mode.lineTmpl', lang, { mode: derivedMode, source: explicitMode ? '--mode' : resolved.source }));
|
|
413
415
|
console.log(t('flow.phases.modeHint', lang));
|
|
416
|
+
// 审查档位分节行(stage-92 op-001:档位 + 来源(模式派生)+ 查看指引;档位仅由模式**派生展示**,不改变推进语义)
|
|
417
|
+
console.log(t('flow.phases.tierLineTmpl', lang, { tier: MODE_DERIVED_TIER[derivedMode], mode: derivedMode }));
|
|
418
|
+
console.log(t('flow.phases.tierHint', lang));
|
|
414
419
|
console.log(t('flow.phases.transitionsLabel', lang) + ':');
|
|
415
420
|
for (const [key, targets] of Object.entries(transitions)) {
|
|
416
421
|
console.log(` ${key} → [${targets.join(', ')}]`);
|
|
@@ -475,6 +480,7 @@ export function registerFlowCommand(program) {
|
|
|
475
480
|
const modes = {
|
|
476
481
|
full: mgr.getPipelineTransitions('full'),
|
|
477
482
|
fast: mgr.getPipelineTransitions('fast'),
|
|
483
|
+
general: mgr.getPipelineTransitions('general'),
|
|
478
484
|
};
|
|
479
485
|
if (options.json) {
|
|
480
486
|
// 未初始化亦保纯 JSON 单文档(stage 空串 + 无 stage 域级联解析值)
|
|
@@ -722,6 +728,7 @@ export function registerFlowCommand(program) {
|
|
|
722
728
|
.option('--stage <id>', '阶段 ID(如 stage-03),必须指定')
|
|
723
729
|
.option('--force', '强制执行(跳过非法 phase 校验和阶段跳跃检查,但不可绕过 REV 阻塞检查)')
|
|
724
730
|
.option('--dry-run', '仅验证不执行修改(预览输出)。与 --force 组合时跳过校验但仍不执行修改')
|
|
731
|
+
.option('--review-tier <tier>', '审查档位(full / lean / skip;缺省=按模式派生;仅显式声明时裁剪审查关,不可绕过 REV 阻塞检查)')
|
|
725
732
|
.option('--quiet', '静默非错误输出(成功确认与 Git 警告均不打印)')
|
|
726
733
|
.action((options) => {
|
|
727
734
|
const lang = getCliLang(process.cwd());
|
|
@@ -730,6 +737,19 @@ export function registerFlowCommand(program) {
|
|
|
730
737
|
console.error(t('flow.advance.errorNoStage', lang));
|
|
731
738
|
process.exit(1);
|
|
732
739
|
}
|
|
740
|
+
// 审查档位值域校验(stage-92 op-001;fail-fast 且**先于任何取值 / 路径解析 / autoRepair 写盘**)
|
|
741
|
+
// 复用 stage.set.invalidValueTmpl(与 flow phases --mode 同款,零新增键);非法 → exit 1 零写盘
|
|
742
|
+
let explicitTier;
|
|
743
|
+
if (options.reviewTier !== undefined) {
|
|
744
|
+
if (!REVIEW_TIERS.includes(options.reviewTier)) {
|
|
745
|
+
console.error(t('stage.set.invalidValueTmpl', lang, { field: '审查档位', value: options.reviewTier, allowed: REVIEW_TIERS.join(', ') }));
|
|
746
|
+
process.exit(1);
|
|
747
|
+
return;
|
|
748
|
+
}
|
|
749
|
+
explicitTier = options.reviewTier;
|
|
750
|
+
}
|
|
751
|
+
// 生效档位:显式声明 ?? full(**不传 = 零裁剪**;派生档仅用于 `flow phases` 展示与提示,不改变推进语义)
|
|
752
|
+
const effTier = explicitTier ?? 'full';
|
|
733
753
|
const mgr = createManager();
|
|
734
754
|
if (!mgr.isLoaded()) {
|
|
735
755
|
console.error(t('common.errorNoInit', lang));
|
|
@@ -781,11 +801,11 @@ export function registerFlowCommand(program) {
|
|
|
781
801
|
// - 已在 to → no-op 成功
|
|
782
802
|
let phasePath = [options.to];
|
|
783
803
|
if (!options.force) {
|
|
784
|
-
const pathResult = mgr.findPhasePath(stageArg, options.to);
|
|
804
|
+
const pathResult = mgr.findPhasePath(stageArg, options.to, effTier);
|
|
785
805
|
if (pathResult.reason === 'no-path' || pathResult.reason === 'ambiguous' || pathResult.reason === 'depth-exceeded') {
|
|
786
806
|
const data = mgr.getData();
|
|
787
807
|
const currentPhase = data?.stages[stageArg]?.phase ?? t('common.unknown', lang);
|
|
788
|
-
const availableTargets = mgr.getAvailablePhases(stageArg);
|
|
808
|
+
const availableTargets = mgr.getAvailablePhases(stageArg, effTier);
|
|
789
809
|
if (pathResult.reason === 'ambiguous') {
|
|
790
810
|
console.error(t('flow.advance.ambiguousTmpl', lang));
|
|
791
811
|
}
|
|
@@ -793,6 +813,12 @@ export function registerFlowCommand(program) {
|
|
|
793
813
|
console.error(t('flow.advance.depthExceededTmpl', lang, { max: String(PHASE_PATH_MAX_DEPTH) }));
|
|
794
814
|
}
|
|
795
815
|
else {
|
|
816
|
+
// general(无流水线)→ 显式错误引导(防御路径:常规写路径不产生 general 阶段态;REV-90-005 / D-90-3)
|
|
817
|
+
if (mgr.resolveStageWorkflowMode(stageArg).value === 'general') {
|
|
818
|
+
console.error(t('flow.advance.generalNoPipeline', lang, { stage: stageArg }));
|
|
819
|
+
process.exit(1);
|
|
820
|
+
return;
|
|
821
|
+
}
|
|
796
822
|
console.error(t('flow.advance.noPathTmpl', lang, {
|
|
797
823
|
from: currentPhase,
|
|
798
824
|
to: options.to,
|
|
@@ -829,6 +855,14 @@ export function registerFlowCommand(program) {
|
|
|
829
855
|
console.warn(t('flow.advance.warnSkipReview', lang));
|
|
830
856
|
}
|
|
831
857
|
}
|
|
858
|
+
// 审查档位协同警告(stage-92 op-001 / D-92-4):**仅显式声明**非全量档位时输出一行
|
|
859
|
+
// 非阻断(退出码不变);`--review-tier full` 与缺省不告警 ⟹ 既有流程零输出回归
|
|
860
|
+
if (explicitTier === 'lean' || explicitTier === 'skip') {
|
|
861
|
+
console.warn(t('flow.advance.warnTierTmpl', lang, {
|
|
862
|
+
tier: explicitTier,
|
|
863
|
+
gates: TIER_PRUNED_PHASES[explicitTier].join(', '),
|
|
864
|
+
}));
|
|
865
|
+
}
|
|
832
866
|
// REV 闭环(命令层兜底):推进到 done 时检查 blocking REV(单步入口;多步循环每步后另复检,B5-3)
|
|
833
867
|
if (options.to === 'done' && options.stage) {
|
|
834
868
|
const blockingOpen = assertNoBlockingOpenRev(mgr, stageArg, lang);
|
|
@@ -853,6 +887,13 @@ export function registerFlowCommand(program) {
|
|
|
853
887
|
console.log(` ` + t('common.stage', lang) + `: ${stageArg}`);
|
|
854
888
|
console.log(` ` + t('flow.advance.dryRunFrom', lang) + `: ${startPhase}`);
|
|
855
889
|
console.log(` ` + t('flow.advance.dryRunTo', lang) + `: ${options.to}`);
|
|
890
|
+
// 审查档位行(stage-92 op-001):仅显式声明时追加(缺省零输出回归);档位为空裁剪时 gates 显示「(无)」
|
|
891
|
+
if (explicitTier !== undefined) {
|
|
892
|
+
console.log(` ` + t('flow.advance.dryRunTierTmpl', lang, {
|
|
893
|
+
tier: explicitTier,
|
|
894
|
+
gates: TIER_PRUNED_PHASES[explicitTier].join(', ') || t('common.none', lang),
|
|
895
|
+
}));
|
|
896
|
+
}
|
|
856
897
|
if (phasePath.length > 1) {
|
|
857
898
|
const fullPath = [startPhase, ...phasePath].join(t('flow.advance.pathArrow', lang));
|
|
858
899
|
console.log(` ` + t('flow.advance.pathTitle', lang) + ` ${fullPath}`);
|
|
@@ -863,13 +904,19 @@ export function registerFlowCommand(program) {
|
|
|
863
904
|
}
|
|
864
905
|
// 逐步推进(B5-2 / A3):唯一路径 → 依次调用 advanceStagePhase(保留每步校验/日志/checkpoint)
|
|
865
906
|
// 中间态不回滚(A3);失败 → 输出「已完成 / 失败点 / 剩余路径」+ exit 1
|
|
907
|
+
// T1:recordRun 接线(命令层;--dry-run 已于上方提前 return——天然排除)
|
|
908
|
+
const metricsStore = MetricsStore.getInstance();
|
|
909
|
+
metricsStore.load(); // 读盘合并(防 save 覆盖历史记录);幂等
|
|
910
|
+
const logLenBefore = mgr.getData()?.log.length ?? 0; // 基线回扫上界(排除本次写入;REV-86-002)
|
|
911
|
+
let metricsRecorded = false;
|
|
912
|
+
let prevStepDoneMs = 0; // 多步时上一步完成时刻(i>0 的窗口基线)
|
|
866
913
|
const totalSteps = phasePath.length;
|
|
867
914
|
let archivedAny = false;
|
|
868
915
|
for (let i = 0; i < totalSteps; i++) {
|
|
869
916
|
const next = phasePath[i];
|
|
870
917
|
let archived = false;
|
|
871
918
|
try {
|
|
872
|
-
archived = mgr.advanceStagePhase(stageArg, next, 'cli');
|
|
919
|
+
archived = mgr.advanceStagePhase(stageArg, next, 'cli', explicitTier ? { reviewTier: explicitTier } : undefined);
|
|
873
920
|
}
|
|
874
921
|
catch (err) {
|
|
875
922
|
if (totalSteps > 1) {
|
|
@@ -884,6 +931,21 @@ export function registerFlowCommand(program) {
|
|
|
884
931
|
}
|
|
885
932
|
archivedAny = archivedAny || archived;
|
|
886
933
|
mgr.save();
|
|
934
|
+
// T1:对刚退出的 fromPhase 记一条 run(窗口 = 进入该 phase 的转换 → 本次退出)
|
|
935
|
+
const exitedPhase = i > 0 ? phasePath[i - 1] : startPhase;
|
|
936
|
+
const agentName = PHASE_AGENT_MAP[exitedPhase];
|
|
937
|
+
if (agentName) {
|
|
938
|
+
const baseMs = i > 0 ? prevStepDoneMs : findPhaseEnteredAtMs(mgr, stageArg, exitedPhase, logLenBefore);
|
|
939
|
+
if (baseMs !== null && baseMs > 0) {
|
|
940
|
+
metricsStore.recordRun(agentName, Date.now() - baseMs, 'success', 0);
|
|
941
|
+
metricsRecorded = true;
|
|
942
|
+
}
|
|
943
|
+
else {
|
|
944
|
+
// 无基线 → 跳过不记(不写 0 值污染 avg);debug 留痕(默认关,不扰输出)
|
|
945
|
+
runtimeLog('debug', 'metrics skip (no baseline): stage=' + stageArg + ' phase=' + exitedPhase);
|
|
946
|
+
}
|
|
947
|
+
}
|
|
948
|
+
prevStepDoneMs = Date.now();
|
|
887
949
|
// 归档 commit 必须在 flow.json save 之后执行,确保 commit 包含本次 phase 变更
|
|
888
950
|
if (archived) {
|
|
889
951
|
mgr.autoCommitOnDone(stageArg);
|
|
@@ -912,6 +974,10 @@ export function registerFlowCommand(program) {
|
|
|
912
974
|
}
|
|
913
975
|
}
|
|
914
976
|
}
|
|
977
|
+
// T1:仅本命令内有记录时落盘一次(低并发非关键路径;推进失败提前 exit 则不 save)
|
|
978
|
+
if (metricsRecorded) {
|
|
979
|
+
metricsStore.save();
|
|
980
|
+
}
|
|
915
981
|
// 成功确认(单步与多步统一);--quiet 完全静默
|
|
916
982
|
if (!options.quiet) {
|
|
917
983
|
console.log(t('flow.advance.okTmpl', lang, { stage: stageArg, to: options.to }));
|
|
@@ -2152,6 +2218,38 @@ function hasExplicitProjectDefault(projectPath, key) {
|
|
|
2152
2218
|
return false;
|
|
2153
2219
|
}
|
|
2154
2220
|
}
|
|
2221
|
+
/**
|
|
2222
|
+
* 求「进入 fromPhase 的那次转换」的时间戳(T1 duration 基线;REV-86-002 口径)。
|
|
2223
|
+
* 回扫范围 [0, logLenBefore)——本次命令写入之前的存量条目(天然排除本次新写入);
|
|
2224
|
+
* 按 detail.to === fromPhase 匹配 advance/reset 转换条目;plan_pending 无转换条目时兜底 register_stage;
|
|
2225
|
+
* 无基线返回 null(调用方跳过不记——不写 0 值)。
|
|
2226
|
+
*/
|
|
2227
|
+
function findPhaseEnteredAtMs(mgr, stageArg, fromPhase, logLenBefore) {
|
|
2228
|
+
const log = mgr.getData()?.log ?? [];
|
|
2229
|
+
const upper = Math.min(logLenBefore, log.length);
|
|
2230
|
+
for (let k = upper - 1; k >= 0; k--) {
|
|
2231
|
+
const e = log[k];
|
|
2232
|
+
if (e.action !== 'advance_stage_phase' && e.action !== 'reset_stage_phase') {
|
|
2233
|
+
continue;
|
|
2234
|
+
}
|
|
2235
|
+
if (e.detail.stageName !== stageArg || e.detail.to !== fromPhase) {
|
|
2236
|
+
continue;
|
|
2237
|
+
}
|
|
2238
|
+
const ms = Date.parse(e.time);
|
|
2239
|
+
return Number.isNaN(ms) ? null : ms;
|
|
2240
|
+
}
|
|
2241
|
+
if (fromPhase === 'plan_pending') {
|
|
2242
|
+
for (let k = upper - 1; k >= 0; k--) {
|
|
2243
|
+
const e = log[k];
|
|
2244
|
+
if (e.action !== 'register_stage' || e.detail.stageName !== stageArg) {
|
|
2245
|
+
continue;
|
|
2246
|
+
}
|
|
2247
|
+
const ms = Date.parse(e.time);
|
|
2248
|
+
return Number.isNaN(ms) ? null : ms;
|
|
2249
|
+
}
|
|
2250
|
+
}
|
|
2251
|
+
return null;
|
|
2252
|
+
}
|
|
2155
2253
|
/** 创建 FlowManager 实例(使用当前工作目录) */
|
|
2156
2254
|
function createManager() {
|
|
2157
2255
|
return new FlowManager(process.cwd());
|