@xqyz/xq-cli 0.3.7 → 0.3.9
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/CHANGELOG.md +18 -0
- package/package.json +1 -1
- package/src/cli.mjs +92 -5
package/CHANGELOG.md
CHANGED
|
@@ -10,6 +10,24 @@
|
|
|
10
10
|
|
|
11
11
|
## [Unreleased]
|
|
12
12
|
|
|
13
|
+
## [0.3.9 / WorkBuddy Plugin 0.10.22 / SkillHub 1.0.6] - 2026-08-26
|
|
14
|
+
|
|
15
|
+
### WorkBuddy 规划模式自动推进
|
|
16
|
+
|
|
17
|
+
- 修复规划模式解析已经达到 `success=3`,但后端未在任务详情回显标包和 `planMode` 时被误判为 `TASK_PARSE_NOT_ADVANCED` 的问题。
|
|
18
|
+
- WorkBuddy 检测到 `analysis_ready` 后自动对同一 `cid` 执行 `plan confirm-outline`,不再展示 `analysis-review` 或智能解读确认卡。
|
|
19
|
+
- 已被旧版本误标失败、但错误中已记录解析 `success=3` 的规划任务,在查询同一 `runId` 时会原地恢复并自动确认生成大纲。
|
|
20
|
+
- 保留大纲和目录人工确认闸门;自动推进只覆盖后台智能解读到大纲生成这一段,不会提前生成正文。
|
|
21
|
+
|
|
22
|
+
## [0.3.8 / WorkBuddy Plugin 0.10.21 / SkillHub 1.0.5] - 2026-08-25
|
|
23
|
+
|
|
24
|
+
### 规划模式卡住保护
|
|
25
|
+
|
|
26
|
+
- 解析唤醒后的 `success=2` 只允许在 30 秒内等待后台一致性同步;状态没有推进时返回可诊断的 `TASK_PARSE_NOT_ADVANCED`,不再继续等待到 10/30 分钟超时。
|
|
27
|
+
- WorkBuddy Plugin 固定依赖 `@xqyz/xq-cli@0.3.8`,并把最低版本校验同步提升,避免旧 CLI 重新进入不完整链路。
|
|
28
|
+
- SkillHub 安装说明明确要求重启后核对实际加载的 Plugin/CLI 版本;旧 MCP 进程仍在运行时不得宣称已更新。
|
|
29
|
+
- 新增回归覆盖:`multiBiddingChoice` 延迟推进、持续 `success=2` 快速失败,以及旧版本运行时拒绝。
|
|
30
|
+
|
|
13
31
|
## [0.3.7 / WorkBuddy Plugin 0.10.20 / SkillHub 1.0.4] - 2026-08-25
|
|
14
32
|
|
|
15
33
|
### 规划模式解析唤醒修复
|
package/package.json
CHANGED
package/src/cli.mjs
CHANGED
|
@@ -5,7 +5,7 @@ import http from 'http';
|
|
|
5
5
|
import https from 'https';
|
|
6
6
|
import os from 'os';
|
|
7
7
|
import path from 'path';
|
|
8
|
-
import readline from 'readline/promises';
|
|
8
|
+
import readline from 'node:readline/promises';
|
|
9
9
|
import { spawn } from 'child_process';
|
|
10
10
|
import { randomUUID } from 'crypto';
|
|
11
11
|
import axios from 'axios';
|
|
@@ -35,6 +35,12 @@ const DEFAULT_BASE_URL = 'https://ai.bidfile.qianlima.com/api';
|
|
|
35
35
|
const DEFAULT_PARSE_INTERVAL_MS = 10_000;
|
|
36
36
|
const DEFAULT_TASK_INTERVAL_MS = 10_000;
|
|
37
37
|
const DEFAULT_TIMEOUT_MS = 10 * 60 * 1000;
|
|
38
|
+
// multiBiddingChoice acknowledges the request before the extraction service
|
|
39
|
+
// necessarily updates getFileAsyncResult. Give that short async hand-off a
|
|
40
|
+
// bounded window, then fail with diagnostics instead of entering the long
|
|
41
|
+
// outline polling timeout.
|
|
42
|
+
const DEFAULT_PARSER_WAKEUP_TIMEOUT_MS = 30_000;
|
|
43
|
+
const PARSER_WAKEUP_INTERVAL_MS = 2_000;
|
|
38
44
|
const DEFAULT_SOURCE_FROM = 'xique';
|
|
39
45
|
const DEFAULT_LOGIN_TIMEOUT_MS = 5 * 60 * 1000;
|
|
40
46
|
const DEFAULT_EXPORT_TEMPLATE_INDEX = 1;
|
|
@@ -1409,8 +1415,8 @@ async function handleOutlineUnlocked(args, state) {
|
|
|
1409
1415
|
|
|
1410
1416
|
const planningMode = Number(params.planMode) === 1;
|
|
1411
1417
|
const triggerMode = planningMode
|
|
1412
|
-
? await preparePlanningAnalysis(client, outlineSnapshot, params)
|
|
1413
|
-
: await triggerOutlineGeneration(client, outlineSnapshot, params);
|
|
1418
|
+
? await preparePlanningAnalysis(client, state, outlineSnapshot, params, effectiveArgs)
|
|
1419
|
+
: await triggerOutlineGeneration(client, state, outlineSnapshot, params, effectiveArgs);
|
|
1414
1420
|
saveTaskSnapshot(state, cid, {
|
|
1415
1421
|
...outlineSnapshot,
|
|
1416
1422
|
...pickTaskSnapshot(params),
|
|
@@ -2056,7 +2062,7 @@ function buildOutlineParams(snapshot, args) {
|
|
|
2056
2062
|
});
|
|
2057
2063
|
}
|
|
2058
2064
|
|
|
2059
|
-
async function triggerOutlineGeneration(client, snapshot, params) {
|
|
2065
|
+
async function triggerOutlineGeneration(client, state, snapshot, params, args = {}) {
|
|
2060
2066
|
// The web flow has two deliberately different paths:
|
|
2061
2067
|
//
|
|
2062
2068
|
// * legacy tasks (task_status === 0) already contain the parsed
|
|
@@ -2080,10 +2086,11 @@ async function triggerOutlineGeneration(client, snapshot, params) {
|
|
|
2080
2086
|
multiBidId: params.multiBidId ?? snapshot.multiBidId,
|
|
2081
2087
|
epcEngineerType: normalizeEpcEngineerTypeForProxy(params.epcEngineerType ?? snapshot.epcEngineerType),
|
|
2082
2088
|
}));
|
|
2089
|
+
await verifyParserWakeUp(client, state, snapshot.cid, snapshot.uuid, params, false, args);
|
|
2083
2090
|
return 'multiBiddingChoice';
|
|
2084
2091
|
}
|
|
2085
2092
|
|
|
2086
|
-
async function preparePlanningAnalysis(client, snapshot, params) {
|
|
2093
|
+
async function preparePlanningAnalysis(client, state, snapshot, params, args = {}) {
|
|
2087
2094
|
// Planning mode pauses before outline generation, but new tasks still need
|
|
2088
2095
|
// the same parser wake-up as quick mode. Without multiBiddingChoice,
|
|
2089
2096
|
// autoGenerateDirectoryJobHandler never fills requirement/rating/plan and
|
|
@@ -2100,9 +2107,83 @@ async function preparePlanningAnalysis(client, snapshot, params) {
|
|
|
2100
2107
|
multiBidId: params.multiBidId ?? snapshot.multiBidId,
|
|
2101
2108
|
epcEngineerType: normalizeEpcEngineerTypeForProxy(params.epcEngineerType ?? snapshot.epcEngineerType),
|
|
2102
2109
|
}));
|
|
2110
|
+
await verifyParserWakeUp(client, state, snapshot.cid, snapshot.uuid, params, true, args);
|
|
2103
2111
|
return 'planningAnalysisPrepared';
|
|
2104
2112
|
}
|
|
2105
2113
|
|
|
2114
|
+
async function verifyParserWakeUp(client, state, cid, uuid, params, planningMode, args = {}) {
|
|
2115
|
+
const expectedMultiBidId = String(params.multiBidId || '').trim();
|
|
2116
|
+
const requestedTimeoutMs = secondsToMs(
|
|
2117
|
+
numberOption(args, 'timeoutSec', DEFAULT_TIMEOUT_MS / 1000),
|
|
2118
|
+
);
|
|
2119
|
+
const timeoutMs = Math.min(DEFAULT_PARSER_WAKEUP_TIMEOUT_MS, requestedTimeoutMs);
|
|
2120
|
+
const deadline = Date.now() + timeoutMs;
|
|
2121
|
+
let latest = null;
|
|
2122
|
+
|
|
2123
|
+
// `multiBiddingChoice` returns HTTP 200 before the extraction record is
|
|
2124
|
+
// necessarily visible to the next read. Allow a short consistency window,
|
|
2125
|
+
// but never fall back to the long generation poll while the backend still
|
|
2126
|
+
// advertises "waiting for package selection" (success=2).
|
|
2127
|
+
for (let attempt = 0; ; attempt += 1) {
|
|
2128
|
+
const [taskResponse, parseStatus] = await Promise.all([
|
|
2129
|
+
getJson(client, `/task/getTaskDetail/${encodeURIComponent(cid)}`, {}, { retries: DEFAULT_RETRY_COUNT }),
|
|
2130
|
+
fetchParseStatus(client, state, cid, uuid),
|
|
2131
|
+
]);
|
|
2132
|
+
const taskDetail = taskResponse?.data || {};
|
|
2133
|
+
const parseSuccess = Number(parseStatus.success);
|
|
2134
|
+
const selectedMultiBidId = String(
|
|
2135
|
+
parseStatus.choiceMultiBiddingId
|
|
2136
|
+
|| taskDetail.choiceMultiBiddingId
|
|
2137
|
+
|| taskDetail.multiBidId
|
|
2138
|
+
|| '',
|
|
2139
|
+
).trim();
|
|
2140
|
+
const savedPlanMode = normalizePlanModeValue(taskDetail.planMode);
|
|
2141
|
+
latest = {
|
|
2142
|
+
taskDetail,
|
|
2143
|
+
parseStatus,
|
|
2144
|
+
parseSuccess,
|
|
2145
|
+
selectedMultiBidId,
|
|
2146
|
+
savedPlanMode,
|
|
2147
|
+
};
|
|
2148
|
+
saveTaskSnapshot(state, cid, pickTaskSnapshot({
|
|
2149
|
+
...taskDetail,
|
|
2150
|
+
...parseStatus,
|
|
2151
|
+
cid,
|
|
2152
|
+
uuid,
|
|
2153
|
+
choiceMultiBiddingId: selectedMultiBidId,
|
|
2154
|
+
}));
|
|
2155
|
+
|
|
2156
|
+
const parseAdvanced = [1, 3, 4].includes(parseSuccess);
|
|
2157
|
+
const selectionRequired = expectedMultiBidId && expectedMultiBidId !== 'notMultiBid';
|
|
2158
|
+
const selectionMatches = !selectionRequired || selectedMultiBidId === expectedMultiBidId;
|
|
2159
|
+
// Planning mode persists the full configuration again when
|
|
2160
|
+
// `plan confirm-outline` is called. Some backend deployments complete
|
|
2161
|
+
// intelligent analysis (success=3) without mirroring planMode or the
|
|
2162
|
+
// selected package onto getTaskDetail. Treat the parse result as the
|
|
2163
|
+
// authority here so WorkBuddy can trigger the explicit outline
|
|
2164
|
+
// confirmation instead of failing before that call.
|
|
2165
|
+
if (parseAdvanced && (planningMode || selectionMatches)) {
|
|
2166
|
+
return latest;
|
|
2167
|
+
}
|
|
2168
|
+
|
|
2169
|
+
const remainingMs = deadline - Date.now();
|
|
2170
|
+
if (remainingMs <= 0) {
|
|
2171
|
+
break;
|
|
2172
|
+
}
|
|
2173
|
+
await sleep(Math.min(PARSER_WAKEUP_INTERVAL_MS, remainingMs));
|
|
2174
|
+
}
|
|
2175
|
+
|
|
2176
|
+
const selected = latest?.selectedMultiBidId || '-';
|
|
2177
|
+
const expected = expectedMultiBidId || '-';
|
|
2178
|
+
const success = latest?.parseSuccess ?? '-';
|
|
2179
|
+
const savedPlanMode = latest?.savedPlanMode ?? '-';
|
|
2180
|
+
throw new Error(
|
|
2181
|
+
`[TASK_PARSE_NOT_ADVANCED] 标包/规划配置提交后后端状态没有推进,已停止等待。` +
|
|
2182
|
+
`期望标包=${expected},后端标包=${selected},解析状态 success=${success},` +
|
|
2183
|
+
`后端 planMode=${savedPlanMode}。请不要重复提交同一任务;先查询任务状态或重新安装最新版 WorkBuddy 插件。`,
|
|
2184
|
+
);
|
|
2185
|
+
}
|
|
2186
|
+
|
|
2106
2187
|
async function setPlanModePhase(client, cid, planModePhase) {
|
|
2107
2188
|
const response = await postJson(client, '/task/setPlanModePhase', { cid, planModePhase });
|
|
2108
2189
|
const returned = normalizePlanModePhase(response?.data?.planModePhase);
|
|
@@ -2620,6 +2701,11 @@ async function waitForPlanningAnalysisReady(client, state, cid, uuid, args) {
|
|
|
2620
2701
|
? await fetchParseStatus(client, state, cid, uuid)
|
|
2621
2702
|
: { success: 1 };
|
|
2622
2703
|
const parseSuccess = Number(parseStatus.success);
|
|
2704
|
+
if (parseSuccess === 2) {
|
|
2705
|
+
throw new Error(
|
|
2706
|
+
'[TASK_PARSE_NOT_ADVANCED] 规划模式仍处于等待标包/EPC选择(success=2),未进入解析阶段,已停止等待。',
|
|
2707
|
+
);
|
|
2708
|
+
}
|
|
2623
2709
|
const issues = collectParseIssues(parseStatus);
|
|
2624
2710
|
if (parseSuccess === 4 && issues.length > 0) {
|
|
2625
2711
|
const autoIgnore = resolveAutoIgnoreIssue(issues, args, uuid, handledIssues);
|
|
@@ -4925,6 +5011,7 @@ function pickTaskSnapshot(source) {
|
|
|
4925
5011
|
blindGenerationMode: source.blindGenerationMode || source.blindBidConfirm?.blindGenerationMode,
|
|
4926
5012
|
multiBidId: source.multiBidId,
|
|
4927
5013
|
multiBidType: source.multiBidType,
|
|
5014
|
+
choiceMultiBiddingId: source.choiceMultiBiddingId,
|
|
4928
5015
|
epcEngineerType: source.epcEngineerType,
|
|
4929
5016
|
outlineReferenceType: source.outlineReferenceType,
|
|
4930
5017
|
referenceStatus: source.referenceStatus,
|