svharness 0.15.10 → 0.15.13
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.md +106 -2
- package/auto/auto-harness-detail-design.md +342 -36
- package/auto/skills/svharness/SKILL.md +44 -1
- package/auto/templates/auto-pack-req.config.example.yaml +29 -0
- package/auto/templates/auto.config.example.yaml +2 -2
- package/auto/templates/phase-prompts/S40-adversarial-review.md +31 -0
- package/auto/templates/phase-prompts/S40-review.md +22 -0
- package/auto/templates/phase-prompts/S40-sample-trace.md +26 -0
- package/auto/templates/requirements-review-rubric.yaml +79 -0
- package/dist/commands/auto-pack-req.js +70 -0
- package/dist/commands/auto.js +18 -2
- package/dist/commands/convert.js +2 -0
- package/dist/commands/shell-integration.js +29 -56
- package/dist/config/merge-options.js +2 -0
- package/dist/config/normalize.js +7 -0
- package/dist/constants/convert.js +5 -0
- package/dist/core/markitdown-client.js +7 -2
- package/dist/dashboard/event-bus.js +3 -0
- package/dist/dashboard/index.js +1 -1
- package/dist/dashboard/sse-handler.js +3 -0
- package/dist/dashboard/static/js/app.js +5 -0
- package/dist/index.js +79 -6
- package/dist/lib/acp-agent-cli-resolver.js +193 -0
- package/dist/lib/acp-client.js +4 -2
- package/dist/lib/agent-launcher.js +9 -4
- package/dist/lib/auto-assets.js +1 -0
- package/dist/lib/auto-optimize.js +23 -3
- package/dist/lib/auto-orchestrator.js +79 -20
- package/dist/lib/auto-pack-req-context.js +88 -0
- package/dist/lib/auto-pack-req-finalize.js +28 -0
- package/dist/lib/auto-req-pack-review.js +161 -0
- package/dist/lib/auto-sample-trace.js +144 -0
- package/dist/lib/auto-state.js +130 -14
- package/dist/lib/cost-tracker.js +30 -2
- package/dist/lib/gate-checker.js +20 -10
- package/dist/lib/phase-prompt-loader.js +4 -0
- package/dist/lib/phase-runner.js +234 -71
- package/dist/lib/ps-codechat-alias.js +29 -16
- package/dist/lib/review-parser.js +14 -0
- package/dist/lib/score-aggregator.js +21 -0
- package/dist/lib/win-registry.js +1 -1
- package/dist/utils/validate-args.js +2 -1
- package/package.json +3 -2
- package/templates/codechat/Start-CodeChat.ps1 +176 -135
- package/templates/codechat/bash.exe.stackdump +29 -0
- package/templates/codechat/docs/project-structure.md +59 -0
- package/templates/svharness.config.example.yaml +2 -0
- package/dist/lib/launch-script-path.js +0 -24
- package/templates/codechat/.claude/.env +0 -59
|
@@ -169,7 +169,7 @@ svharness auto [--work-dir <path>] [options]
|
|
|
169
169
|
| `--arch <arch>` | 架构模板 | `android-compose` |
|
|
170
170
|
| `--harness-name <name>` | harness 名称(资料包模式必填) | — |
|
|
171
171
|
| `--target-score <score>` | 目标综合评分 | `9.5` |
|
|
172
|
-
| `--max-optimize-rounds <n>` | 最大优化轮数 | `
|
|
172
|
+
| `--max-optimize-rounds <n>` | 最大优化轮数 | `10` |
|
|
173
173
|
| `--auto-approve` | 自动通过表单门禁 | `false` |
|
|
174
174
|
| `-y, --yes` | 跳过所有交互确认 | `false` |
|
|
175
175
|
| `--max-total-calls <n>` | ACP 调用次数上限 | `30` |
|
|
@@ -193,6 +193,49 @@ svharness auto --config auto.config.yaml --auto-approve
|
|
|
193
193
|
|
|
194
194
|
**首次运行自动注入**:首次执行 `svharness auto` 时,CLI 会自动将 `svharness` skill 注入到 `~/.claude/skills/svharness/`,使 Claude Code 能识别 `/svharness` 命令。之后可在任意项目中对 Agent 说"自动构建 harness"触发。
|
|
195
195
|
|
|
196
|
+
### `svharness auto_pack_req` — 需求包全自动条目化(S40 专项)
|
|
197
|
+
|
|
198
|
+
独立于 `auto` 命令,专门用于需求文档条目化 + 三维度质量闭环,不污染完整 harness 构建流程。
|
|
199
|
+
|
|
200
|
+
```bash
|
|
201
|
+
# 推荐:在 requirements 资料目录直接运行(目录名任意)
|
|
202
|
+
cd ./requirements && svharness auto_pack_req --yes
|
|
203
|
+
|
|
204
|
+
svharness auto_pack_req [workdir] [options]
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
**工作目录**:无参数时默认当前目录。若当前目录本身是 requirements 资料夹(含 doc/md/pdf 等),自动视为输入并在上级目录创建 harness。
|
|
208
|
+
|
|
209
|
+
**内置默认**:`targetScore=9.5`(须**严格大于**)、`maxOptimizeRounds=10`、`sampleCount=7`、`autoApprove=true`、`yes=true`
|
|
210
|
+
|
|
211
|
+
**评分模型**:
|
|
212
|
+
```
|
|
213
|
+
综合分 = 机械门禁 × 0.40 + 对抗验证 × 0.30 + 抽样验证 × 0.30
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
| 维度 | 说明 |
|
|
217
|
+
|------|------|
|
|
218
|
+
| 机械门禁 | `verify --strict` + `doctor --mode requirements --strict` 全通过 |
|
|
219
|
+
| 对抗验证 | 独立 Reviewer Agent 逐 block 挑刺(不读 Builder 自评) |
|
|
220
|
+
| 抽样验证 | 从 `requirements/md` 随机抽 N 片段反向追溯 yaml 产物 |
|
|
221
|
+
|
|
222
|
+
**两种入口**:
|
|
223
|
+
- **资料包新建**:目录下无 `harness.yaml` → 创建 harness → S30+S40
|
|
224
|
+
- **已有 harness 重做**:检测到 `harness.yaml` → 重置 S40=PENDING → 只跑 S40
|
|
225
|
+
|
|
226
|
+
**典型用法**:
|
|
227
|
+
|
|
228
|
+
```bash
|
|
229
|
+
cd ./requirements && svharness auto_pack_req --yes
|
|
230
|
+
svharness auto_pack_req ./req-pack --yes
|
|
231
|
+
svharness auto_pack_req ./existing-harness --requirements ./docs --yes
|
|
232
|
+
svharness auto_pack_req --dry-run
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
配置模板:`auto/templates/auto-pack-req.config.example.yaml`(`auto_pack_req:` 节)
|
|
236
|
+
|
|
237
|
+
**产物**:`requirements/yaml/`、`auto-review/pack-req-complete.json`(不执行 S90 封存)
|
|
238
|
+
|
|
196
239
|
### `svharness shell` — Windows 右键菜单和别名管理
|
|
197
240
|
|
|
198
241
|
```bash
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
version: 1
|
|
2
|
+
|
|
3
|
+
defaults:
|
|
4
|
+
yes: true
|
|
5
|
+
verbose: false
|
|
6
|
+
|
|
7
|
+
auto_pack_req:
|
|
8
|
+
workDir: .
|
|
9
|
+
harnessName: my-req-pack
|
|
10
|
+
goal: "描述此需求包的条目化目的"
|
|
11
|
+
arch: documents
|
|
12
|
+
agent: claude-code
|
|
13
|
+
|
|
14
|
+
requirements: ./requirements
|
|
15
|
+
|
|
16
|
+
targetScore: 9.5
|
|
17
|
+
maxOptimizeRounds: 10
|
|
18
|
+
sampleCount: 7
|
|
19
|
+
autoApprove: true
|
|
20
|
+
|
|
21
|
+
acpMode: auto
|
|
22
|
+
acpTimeoutMs: 600000
|
|
23
|
+
maxRetriesPerPhase: 2
|
|
24
|
+
maxTotalCalls: 100
|
|
25
|
+
budgetUsd: null
|
|
26
|
+
costAlertThreshold: 5
|
|
27
|
+
|
|
28
|
+
convertEndpoint: "http://markitdown.desaysz.site"
|
|
29
|
+
dryRun: false
|
|
@@ -23,7 +23,7 @@ auto:
|
|
|
23
23
|
|
|
24
24
|
# 质量目标与流程控制。
|
|
25
25
|
targetScore: 9.5
|
|
26
|
-
maxOptimizeRounds:
|
|
26
|
+
maxOptimizeRounds: 10
|
|
27
27
|
maxPhase: null
|
|
28
28
|
checkpoint: null
|
|
29
29
|
autoApprove: true
|
|
@@ -33,7 +33,7 @@ auto:
|
|
|
33
33
|
acpMode: auto
|
|
34
34
|
acpTimeoutMs: 600000
|
|
35
35
|
maxRetriesPerPhase: 2
|
|
36
|
-
maxTotalCalls:
|
|
36
|
+
maxTotalCalls: 100
|
|
37
37
|
budgetUsd: null
|
|
38
38
|
costAlertThreshold: 5
|
|
39
39
|
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
你是**独立**的需求审计 Agent(未参与 S40 条目化构建)。当前任务:对 {{HARNESS_ROOT}} 执行第 {{ROUND}} 轮对抗性验证。
|
|
2
|
+
|
|
3
|
+
## 硬约束(违反则任务失败)
|
|
4
|
+
- **禁止读取** `auto-review/第{{ROUND}}轮需求自评报告.md` 及任何 Builder 自评内容。
|
|
5
|
+
- 仅基于源文档与产物独立审查。
|
|
6
|
+
|
|
7
|
+
## 项目上下文
|
|
8
|
+
- 构建目的:{{AUTO_GOAL}}
|
|
9
|
+
- 机械门禁:doctor exit={{DOCTOR_EXIT}},verify exit={{VERIFY_EXIT}}
|
|
10
|
+
|
|
11
|
+
## 审查方法
|
|
12
|
+
1. 读取 `requirements/_work/structure-index.json`,逐 block 对照 `requirements/yaml/*.yaml`。
|
|
13
|
+
2. 挑刺重点:
|
|
14
|
+
- 遗漏条目(block 无对应 requirement 且无合理 waived)
|
|
15
|
+
- `source_excerpt` 缩略或占位语(「见上文」「同前」)
|
|
16
|
+
- `description` 信息缩减(条件/数值/枚举/时序丢失)
|
|
17
|
+
- waived 条目缺少 `aggregation_reason`
|
|
18
|
+
3. 读取 `{{HARNESS_ROOT}}/auto/requirements-review-rubric.yaml` 中 adversarial 维度。
|
|
19
|
+
|
|
20
|
+
## 产出要求
|
|
21
|
+
1. 写入 `{{HARNESS_ROOT}}/auto-review/第{{ROUND}}轮对抗评审.md`。
|
|
22
|
+
2. frontmatter 必须包含:
|
|
23
|
+
- `round: {{ROUND}}`
|
|
24
|
+
- `score.adversarial.coverage`(0-10)
|
|
25
|
+
- `score.adversarial.fidelity`(0-10)
|
|
26
|
+
- `score.adversarial.completeness`(0-10)
|
|
27
|
+
- `score.adversarial.average`(三项平均,0-10)
|
|
28
|
+
- `issues`(数组,每项含 severity/category/description/unit_id 或 block_id)
|
|
29
|
+
- `critical_count`(issues 中 severity=critical 的数量)
|
|
30
|
+
3. 正文列出发现的问题清单。
|
|
31
|
+
4. 输出末尾:`<AUTO_SIGNAL>ADV_REVIEW_DONE</AUTO_SIGNAL>`。
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
你是 harness 构建 Agent。当前任务:对 {{HARNESS_ROOT}} 执行第 {{ROUND}} 轮 S40 需求自评。
|
|
2
|
+
|
|
3
|
+
## 项目上下文
|
|
4
|
+
- 构建目的:{{AUTO_GOAL}}
|
|
5
|
+
- 机械门禁:doctor exit={{DOCTOR_EXIT}},verify exit={{VERIFY_EXIT}},文件齐全度={{FILE_COMPLETENESS_PCT}}%
|
|
6
|
+
|
|
7
|
+
## 评审契约
|
|
8
|
+
1. 读取 `{{HARNESS_ROOT}}/auto/requirements-review-rubric.yaml`。
|
|
9
|
+
2. 仅评审 **requirements** 维度(不评 wiki/specs/references 等)。
|
|
10
|
+
3. 检查 `requirements/yaml/*.yaml`、`requirements/coverage-report.yaml`、`requirements/_work/verify-gaps.json`。
|
|
11
|
+
|
|
12
|
+
## 产出要求
|
|
13
|
+
1. 写入 `{{HARNESS_ROOT}}/auto-review/第{{ROUND}}轮需求自评报告.md`。
|
|
14
|
+
2. frontmatter 必须包含:
|
|
15
|
+
- `round: {{ROUND}}`
|
|
16
|
+
- `score.dimensions.requirements`(0-10)
|
|
17
|
+
- `gates.doctor_exit` / `gates.verify_exit` / `gates.file_completeness_pct`
|
|
18
|
+
- `gate_pass`(布尔)
|
|
19
|
+
- `critical_count`
|
|
20
|
+
- `modifications_required`
|
|
21
|
+
3. 正文必须包含「修改建议汇总」表(优先级 | 阶段 | 问题 | 建议动作),供优化 Agent 读取。
|
|
22
|
+
4. 输出末尾:`<AUTO_SIGNAL>REQ_REVIEW_DONE</AUTO_SIGNAL>`。
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
你是需求抽样验证 Agent。当前任务:对 {{HARNESS_ROOT}} 执行第 {{ROUND}} 轮反向追溯抽样验证。
|
|
2
|
+
|
|
3
|
+
## 项目上下文
|
|
4
|
+
- 构建目的:{{AUTO_GOAL}}
|
|
5
|
+
- 抽样片段文件:{{SAMPLE_FRAGMENTS_PATH}}
|
|
6
|
+
|
|
7
|
+
## 验证方法(Sample-based Reverse Trace)
|
|
8
|
+
1. 读取 `{{SAMPLE_FRAGMENTS_PATH}}` 中的 `fragments[]`。
|
|
9
|
+
2. 对每个片段,在 `requirements/yaml/*.yaml` 中**反向查找**对应条目。
|
|
10
|
+
3. 对每个片段评三个子维度(0-10):
|
|
11
|
+
- **traceability**:能否找到对应 `unit_id`
|
|
12
|
+
- **fidelity**:`source_excerpt` 是否覆盖片段关键信息(条件/数值/枚举/时序)
|
|
13
|
+
- **usability**:`description` + `acceptance` 是否足以支撑开发者直接实现功能
|
|
14
|
+
4. 读取 `{{HARNESS_ROOT}}/auto/requirements-review-rubric.yaml` 中 sample_trace 维度。
|
|
15
|
+
|
|
16
|
+
## 产出要求
|
|
17
|
+
1. 写入 `{{HARNESS_ROOT}}/auto-review/第{{ROUND}}轮抽样验证报告.md`。
|
|
18
|
+
2. frontmatter 必须包含:
|
|
19
|
+
- `round: {{ROUND}}`
|
|
20
|
+
- `score.sample_trace.traceability`(所有片段平均,0-10)
|
|
21
|
+
- `score.sample_trace.fidelity`(0-10)
|
|
22
|
+
- `score.sample_trace.usability`(0-10)
|
|
23
|
+
- `score.sample_trace.average`(三项平均,0-10)
|
|
24
|
+
- `score.sample_trace.items`(数组,每项含 fragment_id、found_unit_id、traceability、fidelity、usability、note)
|
|
25
|
+
3. 正文含逐片段验证明细表。
|
|
26
|
+
4. 输出末尾:`<AUTO_SIGNAL>SAMPLE_TRACE_DONE</AUTO_SIGNAL>`。
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
# requirements-review-rubric.yaml — auto_pack_req 三维度评审契约
|
|
2
|
+
version: "1.0"
|
|
3
|
+
|
|
4
|
+
scope: requirements
|
|
5
|
+
|
|
6
|
+
dimensions:
|
|
7
|
+
- id: requirements
|
|
8
|
+
weight_composite: 0.0
|
|
9
|
+
title: Builder 自评(requirements 维度)
|
|
10
|
+
role: gap_generation
|
|
11
|
+
questions:
|
|
12
|
+
- 需求文档是否充分解析?覆盖率是否 100%?
|
|
13
|
+
- source_excerpt 是否保真?有无压缩导致细节丢失?
|
|
14
|
+
- unit_id / source_anchor 是否可索引?
|
|
15
|
+
evidence_hints:
|
|
16
|
+
- requirements/coverage-report.yaml
|
|
17
|
+
- requirements/yaml/*.yaml
|
|
18
|
+
- requirements/_work/verify-gaps.json
|
|
19
|
+
|
|
20
|
+
- id: adversarial
|
|
21
|
+
weight_composite: 0.30
|
|
22
|
+
title: 对抗性验证
|
|
23
|
+
role: scoring
|
|
24
|
+
questions:
|
|
25
|
+
- 逐 block 对照 structure-index,是否有遗漏条目?
|
|
26
|
+
- source_excerpt 是否缩略或使用占位语?
|
|
27
|
+
- description 是否缩减了条件/数值/枚举/时序?
|
|
28
|
+
- waived 条目是否有 aggregation_reason?
|
|
29
|
+
sub_scores:
|
|
30
|
+
- coverage
|
|
31
|
+
- fidelity
|
|
32
|
+
- completeness
|
|
33
|
+
evidence_hints:
|
|
34
|
+
- requirements/_work/structure-index.json
|
|
35
|
+
- requirements/yaml/*.yaml
|
|
36
|
+
|
|
37
|
+
- id: sample_trace
|
|
38
|
+
weight_composite: 0.30
|
|
39
|
+
title: 抽样反向追溯验证
|
|
40
|
+
role: scoring
|
|
41
|
+
questions:
|
|
42
|
+
- 随机源文档片段能否在 yaml 中找到 unit_id?
|
|
43
|
+
- source_excerpt 是否覆盖片段关键信息?
|
|
44
|
+
- description + acceptance 是否足以支撑功能开发?
|
|
45
|
+
sub_scores:
|
|
46
|
+
- traceability
|
|
47
|
+
- fidelity
|
|
48
|
+
- usability
|
|
49
|
+
evidence_hints:
|
|
50
|
+
- auto-review/sample-fragments.json
|
|
51
|
+
- requirements/md/*.md
|
|
52
|
+
- requirements/yaml/*.yaml
|
|
53
|
+
|
|
54
|
+
mechanical_gate:
|
|
55
|
+
weight_composite: 0.40
|
|
56
|
+
checks:
|
|
57
|
+
- svharness requirements verify --strict exit 0
|
|
58
|
+
- svharness doctor --mode requirements --strict exit 0
|
|
59
|
+
- coverage_ratio == 1
|
|
60
|
+
- critical_count == 0
|
|
61
|
+
|
|
62
|
+
pass_criteria:
|
|
63
|
+
composite_score_gt: 9.5
|
|
64
|
+
gate_pass: true
|
|
65
|
+
critical_count: 0
|
|
66
|
+
|
|
67
|
+
modification_summary_columns:
|
|
68
|
+
- priority
|
|
69
|
+
- phase
|
|
70
|
+
- problem
|
|
71
|
+
- action
|
|
72
|
+
- status
|
|
73
|
+
|
|
74
|
+
outputs:
|
|
75
|
+
self_review: "auto-review/第{n}轮需求自评报告.md"
|
|
76
|
+
adversarial: "auto-review/第{n}轮对抗评审.md"
|
|
77
|
+
sample_trace: "auto-review/第{n}轮抽样验证报告.md"
|
|
78
|
+
score_snapshot: auto-review/auto-score.json
|
|
79
|
+
complete_marker: auto-review/pack-req-complete.json
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.runAutoPackReq = runAutoPackReq;
|
|
7
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
8
|
+
const fs_extra_1 = __importDefault(require("fs-extra"));
|
|
9
|
+
const config_1 = require("../config");
|
|
10
|
+
const logger_1 = require("../utils/logger");
|
|
11
|
+
const auto_state_1 = require("../lib/auto-state");
|
|
12
|
+
const auto_pack_req_context_1 = require("../lib/auto-pack-req-context");
|
|
13
|
+
const auto_1 = require("./auto");
|
|
14
|
+
const PACK_REQ_DEFAULTS = {
|
|
15
|
+
scope: 'requirements',
|
|
16
|
+
targetScore: 9.5,
|
|
17
|
+
maxOptimizeRounds: 10,
|
|
18
|
+
autoApprove: true,
|
|
19
|
+
yes: true,
|
|
20
|
+
sampleCount: 7,
|
|
21
|
+
};
|
|
22
|
+
async function runAutoPackReq(positionalWorkdir, opts, cmd) {
|
|
23
|
+
const loaded = (0, config_1.loadConfig)({ configPath: opts.config });
|
|
24
|
+
const autoSection = loaded?.config.auto_pack_req ?? loaded?.config.auto;
|
|
25
|
+
const merged = {
|
|
26
|
+
...(0, config_1.mergeAutoOptions)(opts, autoSection, loaded?.config.defaults, cmd),
|
|
27
|
+
...PACK_REQ_DEFAULTS,
|
|
28
|
+
...opts,
|
|
29
|
+
scope: 'requirements',
|
|
30
|
+
targetScore: opts.targetScore ?? autoSection?.targetScore ?? PACK_REQ_DEFAULTS.targetScore,
|
|
31
|
+
maxOptimizeRounds: opts.maxOptimizeRounds ?? autoSection?.maxOptimizeRounds ?? PACK_REQ_DEFAULTS.maxOptimizeRounds,
|
|
32
|
+
sampleCount: opts.sampleCount ?? autoSection?.sampleCount ?? 7,
|
|
33
|
+
autoApprove: opts.autoApprove ?? true,
|
|
34
|
+
yes: opts.yes ?? true,
|
|
35
|
+
};
|
|
36
|
+
const workCtx = await (0, auto_pack_req_context_1.resolveAutoPackReqWorkContext)(positionalWorkdir, merged.workDir, merged.requirements);
|
|
37
|
+
const runOpts = {
|
|
38
|
+
...merged,
|
|
39
|
+
...(workCtx.requirementsPath ? { requirements: workCtx.requirementsPath } : {}),
|
|
40
|
+
};
|
|
41
|
+
if (workCtx.harnessRoot) {
|
|
42
|
+
logger_1.logger.info(`[auto_pack_req] 检测到已有 harness:${workCtx.harnessRoot},将重做 S40`);
|
|
43
|
+
await resetS40ForRerun(workCtx.harnessRoot);
|
|
44
|
+
}
|
|
45
|
+
else if (workCtx.mode === 'requirements-cwd') {
|
|
46
|
+
logger_1.logger.info(`[auto_pack_req] 当前目录识别为 requirements 输入:${workCtx.requirementsPath}`);
|
|
47
|
+
logger_1.logger.info(`[auto_pack_req] 将在上级目录创建 harness:${workCtx.workDir}`);
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
logger_1.logger.info('[auto_pack_req] 资料包模式:将创建 harness 并执行 S30+S40');
|
|
51
|
+
}
|
|
52
|
+
await (0, auto_1.runAuto)(workCtx.workDir, runOpts, cmd);
|
|
53
|
+
}
|
|
54
|
+
async function resetS40ForRerun(harnessRoot) {
|
|
55
|
+
const statePath = node_path_1.default.join(harnessRoot, '.harness-build-state.yaml');
|
|
56
|
+
if (!(await fs_extra_1.default.pathExists(statePath)))
|
|
57
|
+
return;
|
|
58
|
+
const state = await (0, auto_state_1.readBuildState)(harnessRoot);
|
|
59
|
+
if (!state.phases.S40_extract_requirements)
|
|
60
|
+
return;
|
|
61
|
+
state.phases.S40_extract_requirements = {
|
|
62
|
+
...state.phases.S40_extract_requirements,
|
|
63
|
+
status: 'PENDING',
|
|
64
|
+
};
|
|
65
|
+
delete state.phases.S40_extract_requirements.outdated_reason;
|
|
66
|
+
state.current_phase = 'S40_extract_requirements';
|
|
67
|
+
state.next_action = 'auto_pack_req 已重置 S40,等待重新条目化';
|
|
68
|
+
await (0, auto_state_1.writeBuildState)(harnessRoot, state);
|
|
69
|
+
logger_1.logger.success('已重置 S40_extract_requirements 为 PENDING');
|
|
70
|
+
}
|
package/dist/commands/auto.js
CHANGED
|
@@ -12,6 +12,7 @@ const logger_1 = require("../utils/logger");
|
|
|
12
12
|
const harness_resolver_1 = require("../lib/harness-resolver");
|
|
13
13
|
const auto_orchestrator_1 = require("../lib/auto-orchestrator");
|
|
14
14
|
const dashboard_1 = require("../dashboard");
|
|
15
|
+
const acp_agent_cli_resolver_1 = require("../lib/acp-agent-cli-resolver");
|
|
15
16
|
/**
|
|
16
17
|
* Prevent system sleep on Windows during long-running auto execution.
|
|
17
18
|
* Registers process-level handlers so sleep is restored on any exit path.
|
|
@@ -46,6 +47,13 @@ async function runAuto(positionalWorkdir, opts, cmd) {
|
|
|
46
47
|
const merged = (0, config_1.mergeAutoOptions)(opts, loaded?.config.auto, loaded?.config.defaults, cmd);
|
|
47
48
|
const ctx = await resolveAutoContext(positionalWorkdir, merged);
|
|
48
49
|
(0, logger_1.setVerbose)(ctx.verbose);
|
|
50
|
+
if (!ctx.dryRun) {
|
|
51
|
+
logger_1.logger.section('[auto] 启动前检查:Agent CLI');
|
|
52
|
+
if (!ctx.acpAgentCli) {
|
|
53
|
+
throw new Error('内部错误:ACP Agent CLI 未解析');
|
|
54
|
+
}
|
|
55
|
+
logger_1.logger.success(`已找到 Agent CLI → ${(0, acp_agent_cli_resolver_1.formatAcpAgentCliLog)(ctx.acpAgentCli)}`);
|
|
56
|
+
}
|
|
49
57
|
// Start dashboard server (non-blocking, best-effort)
|
|
50
58
|
(0, dashboard_1.startDashboard)(7879).catch(() => { });
|
|
51
59
|
// Prevent system sleep during execution; restore on any exit path
|
|
@@ -63,28 +71,33 @@ async function runAuto(positionalWorkdir, opts, cmd) {
|
|
|
63
71
|
async function resolveAutoContext(positionalWorkdir, opts) {
|
|
64
72
|
const workDir = node_path_1.default.resolve(positionalWorkdir ?? opts.workDir ?? process.cwd());
|
|
65
73
|
const harnessRoot = await (0, harness_resolver_1.resolveHarnessRoot)(workDir);
|
|
74
|
+
const acpAgentCli = opts.dryRun ? null : (0, acp_agent_cli_resolver_1.ensureAcpAgentCliAvailable)();
|
|
66
75
|
return {
|
|
67
76
|
workDir,
|
|
68
77
|
harnessRoot,
|
|
69
78
|
harnessExists: !!harnessRoot,
|
|
79
|
+
acpAgentCli,
|
|
70
80
|
targetScore: normalizeNumber(opts.targetScore, 9.5, '--target-score'),
|
|
71
|
-
maxOptimizeRounds: normalizeInteger(opts.maxOptimizeRounds,
|
|
81
|
+
maxOptimizeRounds: normalizeInteger(opts.maxOptimizeRounds, 10, '--max-optimize-rounds'),
|
|
72
82
|
dryRun: !!opts.dryRun,
|
|
73
83
|
autoApprove: !!opts.autoApprove,
|
|
74
84
|
yes: !!opts.yes,
|
|
75
85
|
verbose: !!opts.verbose,
|
|
76
|
-
maxTotalCalls: normalizeInteger(opts.maxTotalCalls,
|
|
86
|
+
maxTotalCalls: normalizeInteger(opts.maxTotalCalls, 100, '--max-total-calls'),
|
|
77
87
|
budgetUsd: opts.budgetUsd ?? null,
|
|
78
88
|
costAlertThreshold: normalizeNumber(opts.costAlertThreshold, 5, '--cost-alert'),
|
|
79
89
|
acpMode: opts.acpMode === 'sdk' || opts.acpMode === 'cli-print' ? opts.acpMode : 'auto',
|
|
80
90
|
acpTimeoutMs: normalizeInteger(opts.acpTimeoutMs, 1_800_000, '--acp-timeout-ms'),
|
|
81
91
|
maxRetriesPerPhase: normalizeInteger(opts.maxRetriesPerPhase, 2, '--max-retries-per-phase'),
|
|
82
92
|
baselineAutoExtract: normalizeBaselineExtract(opts.baselineAutoExtract),
|
|
93
|
+
scope: normalizeScope(opts.scope),
|
|
94
|
+
sampleCount: normalizeInteger(opts.sampleCount, 7, '--sample-count'),
|
|
83
95
|
promptDir: opts.promptDir,
|
|
84
96
|
checkpoint: opts.checkpoint,
|
|
85
97
|
maxPhase: opts.maxPhase,
|
|
86
98
|
goal: opts.goal,
|
|
87
99
|
buildOverrides: opts,
|
|
100
|
+
autoRepair: opts.autoRepair ?? true,
|
|
88
101
|
};
|
|
89
102
|
}
|
|
90
103
|
function normalizeNumber(value, fallback, name) {
|
|
@@ -106,3 +119,6 @@ function normalizeBaselineExtract(value) {
|
|
|
106
119
|
}
|
|
107
120
|
return 'conservative';
|
|
108
121
|
}
|
|
122
|
+
function normalizeScope(value) {
|
|
123
|
+
return value === 'requirements' ? 'requirements' : 'full';
|
|
124
|
+
}
|
package/dist/commands/convert.js
CHANGED
|
@@ -182,6 +182,8 @@ async function runConvert(opts) {
|
|
|
182
182
|
catch (err) {
|
|
183
183
|
const e = err;
|
|
184
184
|
logger_1.logger.error(`markitdown_serve unreachable at ${v.endpoint}: ${e.message}`);
|
|
185
|
+
logger_1.logger.plain(' - if another user is converting a large PDF, the service may still be busy; retry in a minute');
|
|
186
|
+
logger_1.logger.plain(' - production should run uvicorn with --workers 4+ and nginx proxy_read_timeout ≥ 900s');
|
|
185
187
|
logger_1.logger.plain('');
|
|
186
188
|
logger_1.logger.plain(' - confirm the service is deployed and reachable from this machine');
|
|
187
189
|
logger_1.logger.plain(' - pass --endpoint <url> or comma-separated URLs for failover');
|
|
@@ -3,7 +3,6 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.resolveLaunchScriptPath = void 0;
|
|
7
6
|
exports.resolveShellMenuIconSourcePath = resolveShellMenuIconSourcePath;
|
|
8
7
|
exports.installShellMenuIcon = installShellMenuIcon;
|
|
9
8
|
exports.resolveWindowsTerminalPath = resolveWindowsTerminalPath;
|
|
@@ -19,11 +18,8 @@ const child_process_1 = require("child_process");
|
|
|
19
18
|
const logger_1 = require("../utils/logger");
|
|
20
19
|
const win_registry_1 = require("../lib/win-registry");
|
|
21
20
|
const ps_codechat_alias_1 = require("../lib/ps-codechat-alias");
|
|
22
|
-
const launch_script_path_1 = require("../lib/launch-script-path");
|
|
23
|
-
Object.defineProperty(exports, "resolveLaunchScriptPath", { enumerable: true, get: function () { return launch_script_path_1.resolveLaunchScriptPath; } });
|
|
24
21
|
const STUB_DIR = path_1.default.join(process.env.LOCALAPPDATA ?? path_1.default.join(os_1.default.homedir(), 'AppData', 'Local'), 'svharness', 'bin');
|
|
25
|
-
const
|
|
26
|
-
const LEGACY_STUB_PATH = path_1.default.join(STUB_DIR, 'launch_codechat_cli.cmd');
|
|
22
|
+
const PS1_INSTALL_PATH = path_1.default.join(STUB_DIR, 'Start-CodeChat.ps1');
|
|
27
23
|
const ICON_FILENAME = 'codechat-agent.ico';
|
|
28
24
|
const ICON_STUB_PATH = path_1.default.join(STUB_DIR, ICON_FILENAME);
|
|
29
25
|
const STANDALONE_MENU_KEY_BG = 'HKCU\\Software\\Classes\\Directory\\Background\\shell\\CodeChatStandaloneAgent';
|
|
@@ -102,31 +98,24 @@ function resolveWindowsTerminalPath() {
|
|
|
102
98
|
}
|
|
103
99
|
return null;
|
|
104
100
|
}
|
|
105
|
-
function buildStubContent(launchScriptPath) {
|
|
106
|
-
const escapedNodeScript = launchScriptPath.replace(/'/g, "''");
|
|
107
|
-
// ASCII-only + UTF-8 BOM. Legacy conhost fallback sets UTF-8 before CodeChat TUI.
|
|
108
|
-
return `param(
|
|
109
|
-
[string]$WorkDir = (Get-Location).Path
|
|
110
|
-
)
|
|
111
|
-
|
|
112
|
-
$ErrorActionPreference = 'Stop'
|
|
113
|
-
|
|
114
|
-
try { chcp 65001 | Out-Null } catch {}
|
|
115
|
-
[Console]::InputEncoding = [System.Text.UTF8Encoding]::new($false)
|
|
116
|
-
[Console]::OutputEncoding = [System.Text.UTF8Encoding]::new($false)
|
|
117
|
-
$OutputEncoding = [System.Text.UTF8Encoding]::new($false)
|
|
118
|
-
|
|
119
|
-
Set-Location -LiteralPath $WorkDir
|
|
120
|
-
& node '${escapedNodeScript}' "$WorkDir"
|
|
121
|
-
exit $LASTEXITCODE
|
|
122
|
-
`;
|
|
123
|
-
}
|
|
124
|
-
function writeStubFile(launchScriptPath) {
|
|
125
|
-
const content = buildStubContent(launchScriptPath);
|
|
126
|
-
return fs_extra_1.default.writeFile(STUB_PATH, `\uFEFF${content}`, 'utf8');
|
|
127
|
-
}
|
|
128
101
|
function buildPsInvokeArgs(ps1Path, dirPlaceholder) {
|
|
129
|
-
return `-NoExit -NoProfile -ExecutionPolicy Bypass -File "${ps1Path}" ${dirPlaceholder}`;
|
|
102
|
+
return `-NoExit -NoProfile -ExecutionPolicy Bypass -File "${ps1Path}" -Action Start -WorkDir ${dirPlaceholder}`;
|
|
103
|
+
}
|
|
104
|
+
async function installStartCodeChatPs1() {
|
|
105
|
+
const fromDist = path_1.default.resolve(__dirname, '..', '..', 'templates', 'codechat', 'Start-CodeChat.ps1');
|
|
106
|
+
const fromCwd = path_1.default.resolve(__dirname, '..', '..', '..', 'templates', 'codechat', 'Start-CodeChat.ps1');
|
|
107
|
+
let source = '';
|
|
108
|
+
if (fs_extra_1.default.existsSync(fromDist)) {
|
|
109
|
+
source = fromDist;
|
|
110
|
+
}
|
|
111
|
+
else if (fs_extra_1.default.existsSync(fromCwd)) {
|
|
112
|
+
source = fromCwd;
|
|
113
|
+
}
|
|
114
|
+
else {
|
|
115
|
+
throw new Error(`\u672A\u627E\u5230 Start-CodeChat.ps1\uFF08\u9884\u671F\uFF1A${fromDist}\uFF09`);
|
|
116
|
+
}
|
|
117
|
+
await fs_extra_1.default.copy(source, PS1_INSTALL_PATH, { overwrite: true });
|
|
118
|
+
return PS1_INSTALL_PATH;
|
|
130
119
|
}
|
|
131
120
|
function buildMenuCommand(ps1Path, dirPlaceholder, wtPath) {
|
|
132
121
|
const psArgs = buildPsInvokeArgs(ps1Path, dirPlaceholder);
|
|
@@ -150,23 +139,18 @@ async function runShellInstall(opts = {}) {
|
|
|
150
139
|
// Avoid duplicate menu entries: overwrite standalone menu if present.
|
|
151
140
|
(0, win_registry_1.regDeleteKey)(STANDALONE_MENU_KEY_BG);
|
|
152
141
|
(0, win_registry_1.regDeleteKey)(STANDALONE_MENU_KEY_DIR);
|
|
153
|
-
const launchScriptPath = (0, launch_script_path_1.resolveLaunchScriptPath)();
|
|
154
142
|
await fs_extra_1.default.ensureDir(STUB_DIR);
|
|
155
|
-
await
|
|
156
|
-
if (await fs_extra_1.default.pathExists(LEGACY_STUB_PATH)) {
|
|
157
|
-
await fs_extra_1.default.remove(LEGACY_STUB_PATH);
|
|
158
|
-
}
|
|
143
|
+
const ps1Path = await installStartCodeChatPs1();
|
|
159
144
|
const iconValue = await installShellMenuIcon();
|
|
160
145
|
const wtPath = resolveWindowsTerminalPath();
|
|
161
|
-
const bgCommand = buildMenuCommand(
|
|
162
|
-
const dirCommand = buildMenuCommand(
|
|
146
|
+
const bgCommand = buildMenuCommand(PS1_INSTALL_PATH, '"%V"', wtPath);
|
|
147
|
+
const dirCommand = buildMenuCommand(PS1_INSTALL_PATH, '"%1"', wtPath);
|
|
163
148
|
registerMenuKey(win_registry_1.SHELL_MENU_KEY_BG, bgCommand, iconValue);
|
|
164
149
|
registerMenuKey(win_registry_1.SHELL_MENU_KEY_DIR, dirCommand, iconValue);
|
|
165
150
|
const via = wtPath ? 'Windows Terminal' : 'PowerShell(UTF-8)';
|
|
166
151
|
logSuccess(`Windows 右键菜单已注册:「在此启动 CodeChat Agent」(${via})`, opts.silent);
|
|
167
|
-
logInfo(`
|
|
152
|
+
logInfo(` 脚本: ${ps1Path}`, opts.silent);
|
|
168
153
|
logInfo(` icon: ${ICON_STUB_PATH}`, opts.silent);
|
|
169
|
-
logInfo(` 脚本: ${launchScriptPath}`, opts.silent);
|
|
170
154
|
if (wtPath) {
|
|
171
155
|
logInfo(` wt: ${wtPath}`, opts.silent);
|
|
172
156
|
}
|
|
@@ -183,15 +167,12 @@ async function runShellUninstall(opts = {}) {
|
|
|
183
167
|
// Also remove standalone script menu (Start-CodeChat.ps1 independent install)
|
|
184
168
|
(0, win_registry_1.regDeleteKey)(STANDALONE_MENU_KEY_BG);
|
|
185
169
|
(0, win_registry_1.regDeleteKey)(STANDALONE_MENU_KEY_DIR);
|
|
186
|
-
if (await fs_extra_1.default.pathExists(
|
|
187
|
-
await fs_extra_1.default.remove(
|
|
170
|
+
if (await fs_extra_1.default.pathExists(PS1_INSTALL_PATH)) {
|
|
171
|
+
await fs_extra_1.default.remove(PS1_INSTALL_PATH);
|
|
188
172
|
}
|
|
189
173
|
if (await fs_extra_1.default.pathExists(ICON_STUB_PATH)) {
|
|
190
174
|
await fs_extra_1.default.remove(ICON_STUB_PATH);
|
|
191
175
|
}
|
|
192
|
-
if (await fs_extra_1.default.pathExists(LEGACY_STUB_PATH)) {
|
|
193
|
-
await fs_extra_1.default.remove(LEGACY_STUB_PATH);
|
|
194
|
-
}
|
|
195
176
|
// Clean up standalone script directory (Start-CodeChat.ps1 independent install)
|
|
196
177
|
const standaloneDir = path_1.default.join(process.env.LOCALAPPDATA ?? path_1.default.join(os_1.default.homedir(), 'AppData', 'Local'), 'codechat-standalone');
|
|
197
178
|
if (await fs_extra_1.default.pathExists(standaloneDir)) {
|
|
@@ -205,24 +186,17 @@ async function runShellUninstall(opts = {}) {
|
|
|
205
186
|
*/
|
|
206
187
|
function getShellStatus() {
|
|
207
188
|
assertWin32();
|
|
208
|
-
|
|
209
|
-
try {
|
|
210
|
-
launchScriptPath = (0, launch_script_path_1.resolveLaunchScriptPath)();
|
|
211
|
-
}
|
|
212
|
-
catch {
|
|
213
|
-
launchScriptPath = '(未找到)';
|
|
214
|
-
}
|
|
189
|
+
const ps1Installed = fs_extra_1.default.existsSync(PS1_INSTALL_PATH);
|
|
215
190
|
const backgroundMenu = (0, win_registry_1.regKeyExists)(win_registry_1.SHELL_MENU_KEY_BG);
|
|
216
191
|
const directoryMenu = (0, win_registry_1.regKeyExists)(win_registry_1.SHELL_MENU_KEY_DIR);
|
|
217
|
-
const stubExists = fs_extra_1.default.existsSync(STUB_PATH);
|
|
218
192
|
const wtPath = resolveWindowsTerminalPath();
|
|
219
193
|
const aliasStatus = (0, ps_codechat_alias_1.getCodechatAliasStatus)();
|
|
220
194
|
return {
|
|
221
|
-
installed: backgroundMenu && directoryMenu &&
|
|
222
|
-
stubPath:
|
|
195
|
+
installed: backgroundMenu && directoryMenu && ps1Installed,
|
|
196
|
+
stubPath: PS1_INSTALL_PATH,
|
|
223
197
|
backgroundMenu,
|
|
224
198
|
directoryMenu,
|
|
225
|
-
launchScriptPath,
|
|
199
|
+
launchScriptPath: PS1_INSTALL_PATH,
|
|
226
200
|
viaWindowsTerminal: wtPath !== null,
|
|
227
201
|
aliasInstalled: aliasStatus.installed,
|
|
228
202
|
profilePath: aliasStatus.profilePath,
|
|
@@ -232,10 +206,9 @@ function printShellStatus() {
|
|
|
232
206
|
const status = getShellStatus();
|
|
233
207
|
logger_1.logger.section('Windows shell 集成状态');
|
|
234
208
|
logger_1.logger.plain(` 右键菜单完整: ${status.installed ? '是' : '否'}`);
|
|
235
|
-
logger_1.logger.plain(`
|
|
209
|
+
logger_1.logger.plain(` Start-CodeChat.ps1: ${status.stubPath}`);
|
|
236
210
|
logger_1.logger.plain(` 空白处菜单: ${status.backgroundMenu ? '已注册' : '未注册'}`);
|
|
237
211
|
logger_1.logger.plain(` 文件夹菜单: ${status.directoryMenu ? '已注册' : '未注册'}`);
|
|
238
|
-
logger_1.logger.plain(` 启动脚本: ${status.launchScriptPath}`);
|
|
239
212
|
logger_1.logger.plain(` 经 WT 启动: ${status.viaWindowsTerminal ? '是(install 时检测到 wt.exe)' : '否'}`);
|
|
240
213
|
logger_1.logger.plain(` codechat 别名: ${status.aliasInstalled ? '已安装' : '未安装'}`);
|
|
241
214
|
logger_1.logger.plain(` profile 路径: ${status.profilePath}`);
|
|
@@ -85,6 +85,8 @@ function mergeAutoOptions(cli, configSection, defaults, cmd) {
|
|
|
85
85
|
promptDir: pickString('promptDir', cli.promptDir, cfg.promptDir, cmd),
|
|
86
86
|
baselineAutoExtract: pickString('baselineAutoExtract', cli.baselineAutoExtract, cfg.baselineAutoExtract, cmd),
|
|
87
87
|
convertEndpoint: pickString('convertEndpoint', cli.convertEndpoint, cfg.convertEndpoint, cmd),
|
|
88
|
+
scope: pickString('scope', cli.scope, cfg.scope, cmd),
|
|
89
|
+
sampleCount: pickNumber('sampleCount', cli.sampleCount, cfg.sampleCount, cmd),
|
|
88
90
|
};
|
|
89
91
|
}
|
|
90
92
|
function mergeBuildOptions(cli, configSection, defaults, cmd) {
|
package/dist/config/normalize.js
CHANGED
|
@@ -50,6 +50,7 @@ function pickAutoSection(raw) {
|
|
|
50
50
|
'costAlertThreshold',
|
|
51
51
|
'acpTimeoutMs',
|
|
52
52
|
'maxRetriesPerPhase',
|
|
53
|
+
'sampleCount',
|
|
53
54
|
]) {
|
|
54
55
|
if (raw[k] !== undefined && raw[k] !== null) {
|
|
55
56
|
s[k] = Number(raw[k]);
|
|
@@ -235,5 +236,11 @@ function normalizeConfig(raw, configPath) {
|
|
|
235
236
|
}
|
|
236
237
|
cfg.auto = pickAutoSection(raw.auto);
|
|
237
238
|
}
|
|
239
|
+
if (raw.auto_pack_req !== undefined) {
|
|
240
|
+
if (!isPlainObject(raw.auto_pack_req)) {
|
|
241
|
+
throw new Error(`配置 auto_pack_req 必须是对象:${configPath}`);
|
|
242
|
+
}
|
|
243
|
+
cfg.auto_pack_req = pickAutoSection(raw.auto_pack_req);
|
|
244
|
+
}
|
|
238
245
|
return cfg;
|
|
239
246
|
}
|
|
@@ -57,7 +57,7 @@ function parseEndpointList(raw) {
|
|
|
57
57
|
}
|
|
58
58
|
return out;
|
|
59
59
|
}
|
|
60
|
-
async function ping(endpoint, timeoutMs =
|
|
60
|
+
async function ping(endpoint, timeoutMs = 30_000) {
|
|
61
61
|
const controller = new AbortController();
|
|
62
62
|
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
63
63
|
try {
|
|
@@ -80,7 +80,7 @@ async function ping(endpoint, timeoutMs = 10_000) {
|
|
|
80
80
|
}
|
|
81
81
|
}
|
|
82
82
|
/** Probe endpoints in order; return those that respond to /healthz. */
|
|
83
|
-
async function filterHealthyEndpoints(endpoints, timeoutMs =
|
|
83
|
+
async function filterHealthyEndpoints(endpoints, timeoutMs = 30_000) {
|
|
84
84
|
const healthy = [];
|
|
85
85
|
for (const endpoint of endpoints) {
|
|
86
86
|
try {
|
|
@@ -186,6 +186,11 @@ function extractTraceId(body) {
|
|
|
186
186
|
}
|
|
187
187
|
function describeHttpError(status, body, endpoint) {
|
|
188
188
|
const prefix = endpoint ? `[${endpoint}] ` : '';
|
|
189
|
+
if (status === 504) {
|
|
190
|
+
return (`${prefix}gateway timeout (HTTP 504): reverse proxy gave up waiting for markitdown_serve ` +
|
|
191
|
+
`(worker may still be running VLM/OCR). Increase nginx proxy_read_timeout to match ` +
|
|
192
|
+
`--timeout-sec, or reduce MARKITDOWN_OCR_MAX_RENDER_PX / use a shorter PDF export.`);
|
|
193
|
+
}
|
|
189
194
|
if (body && typeof body === 'object') {
|
|
190
195
|
const b = body;
|
|
191
196
|
if (status === 415 && b.error === 'unsupported_format') {
|
|
@@ -9,6 +9,9 @@ class DashboardEventBus extends node_events_1.EventEmitter {
|
|
|
9
9
|
emitBuildCompleted(finalScore) {
|
|
10
10
|
this.emitEvent('build_completed', { finalScore });
|
|
11
11
|
}
|
|
12
|
+
emitBuildFailed(finalScore) {
|
|
13
|
+
this.emitEvent('build_failed', { finalScore });
|
|
14
|
+
}
|
|
12
15
|
emitPhaseStarted(key, short, description, attempt) {
|
|
13
16
|
this.emitEvent('phase_started', { key, short, description, attempt });
|
|
14
17
|
}
|
package/dist/dashboard/index.js
CHANGED
|
@@ -24,7 +24,7 @@ function startDashboard(port = 7879) {
|
|
|
24
24
|
server = (0, server_1.createServer)(event_bus_1.dashboardEventBus, sseManager);
|
|
25
25
|
// Subscribe to all events from the bus and forward to SSE clients
|
|
26
26
|
const eventTypes = [
|
|
27
|
-
'build_started', 'build_completed',
|
|
27
|
+
'build_started', 'build_completed', 'build_failed',
|
|
28
28
|
'phase_started', 'phase_completed', 'phase_failed',
|
|
29
29
|
'acp_output',
|
|
30
30
|
'cost_updated', 'score_updated',
|