@xqyz/xq-cli 0.3.4 → 0.3.5

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.
Files changed (3) hide show
  1. package/CHANGELOG.md +21 -0
  2. package/package.json +1 -1
  3. package/src/cli.mjs +114 -5
package/CHANGELOG.md CHANGED
@@ -10,6 +10,27 @@
10
10
 
11
11
  ## [Unreleased]
12
12
 
13
+ ## [0.3.5 / WorkBuddy Plugin 0.10.11] - 2026-08-20(待发布)
14
+
15
+ ### 文件解析与多标包统一配置
16
+
17
+ - 修复 `xq-cli init --wait` 未识别 Web 端 `success=2`(解析完成、进入标书设置)的错误,避免 WorkBuddy 在文件已经解析完成后仍反复查询并最终超时。
18
+ - WorkBuddy 新任务改为先调用一次 `xique_prepare_source`,解析完成后在同一配置页展示后台真实返回的多标包 / EPC 选项和全部普通配置。
19
+ - 用户提交配置后复用原 `runId`、`cid` 和 `uuid` 启动大纲,不再重复上传文件、重复创建任务或二次选择标包。
20
+ - 普通文件不显示多标包 / EPC 占位字段;提交的标包和 EPC 值必须来自本次解析结果,禁止手工构造 ID。
21
+
22
+ ### 暗标生成方式
23
+
24
+ - xq-cli 新增 `--blind-generation-mode normal|blind`,并保留 `--anonymous-bid` 兼容别名。
25
+ - WorkBuddy 配置页新增“按通用方式生成 / 按暗标生成”,默认通用方式;选择会真实写入大纲请求的 `blindBidConfirm`,不再只停留在界面字段。
26
+ - 新增回归测试覆盖 `success=2`、真实多标包统一配置、同一任务复用、重复提交拦截和暗标请求体。
27
+
28
+ ### 导出格式二次设置
29
+
30
+ - xq-cli 导出新增 `imageSizeConfig`,支持横向/纵向图片宽度和最大高度四项厘米参数,并与页边距一起写入 `/c/downWithStyle`。
31
+ - WorkBuddy 新增 `xique_export_configuration` 二次导出设置卡:正文完成后可视化设置标题/正文编号、字体、对齐、起始编号、页边距和图片尺寸,再经确认重新下载,不会重新生成任务。
32
+ - 模板5不再在 WorkBuddy 中禁用;编号设置由卡片生成结构化 `customNumbering`,不要求用户填写高级样式 JSON。
33
+
13
34
  ## [0.3.4 / WorkBuddy Plugin 0.10.10] - 2026-08-19
14
35
 
15
36
  ### 快速模式新任务生成链路修复
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xqyz/xq-cli",
3
- "version": "0.3.4",
3
+ "version": "0.3.5",
4
4
  "description": "CLI for xique bid-book task workflows",
5
5
  "type": "module",
6
6
  "scripts": {
package/src/cli.mjs CHANGED
@@ -127,6 +127,12 @@ const DEFAULT_PAGE_MARGIN_SETTING = {
127
127
  leftMargin: 3.18,
128
128
  rightMargin: 3.18,
129
129
  };
130
+ const DEFAULT_IMAGE_SIZE_CONFIG = {
131
+ landscapeWidth: 10.58,
132
+ landscapeMaxHeight: 8,
133
+ portraitWidth: 5.5,
134
+ portraitMaxHeight: 8,
135
+ };
130
136
  const OUTLINE_REFERENCE_OPTIONS = [
131
137
  {
132
138
  value: 0,
@@ -205,6 +211,10 @@ const PLAN_MODE_OPTIONS = [
205
211
  { value: 0, label: 'quick', description: 'quick mode', aliases: ['0', 'quick'] },
206
212
  { value: 1, label: 'planning', description: 'planning mode', aliases: ['1', 'planning', 'plan'] },
207
213
  ];
214
+ const BLIND_GENERATION_MODE_OPTIONS = [
215
+ { value: 'normal', label: 'normal', description: 'general bid generation', aliases: ['normal', 'general', '0', 'open'] },
216
+ { value: 'blind', label: 'blind', description: 'blind-bid protected generation', aliases: ['blind', 'anonymous', '1'] },
217
+ ];
208
218
  const PLAN_MODE_PHASE_LABELS = {
209
219
  0: 'analysis',
210
220
  1: 'outline',
@@ -238,6 +248,12 @@ const PAGE_MARGIN_FIELDS = [
238
248
  { key: 'leftMargin', optionKeys: ['leftMargin', 'marginLeft'] },
239
249
  { key: 'rightMargin', optionKeys: ['rightMargin', 'marginRight'] },
240
250
  ];
251
+ const IMAGE_SIZE_FIELDS = [
252
+ { key: 'landscapeWidth', optionKeys: ['landscapeWidth', 'imageLandscapeWidth'] },
253
+ { key: 'landscapeMaxHeight', optionKeys: ['landscapeMaxHeight', 'imageLandscapeMaxHeight'] },
254
+ { key: 'portraitWidth', optionKeys: ['portraitWidth', 'imagePortraitWidth'] },
255
+ { key: 'portraitMaxHeight', optionKeys: ['portraitMaxHeight', 'imagePortraitMaxHeight'] },
256
+ ];
241
257
  const EXPORT_TEMPLATE_OPTIONS = templateData.map((template, index) => ({
242
258
  value: index + 1,
243
259
  label: `template-${index + 1}`,
@@ -514,13 +530,16 @@ Outline config
514
530
  --knowledge-img <0|1> knowledge-base image toggle
515
531
  --mermaid-style <1|2|3|4|5|6|gray|purple|blue|green|orange|red>
516
532
  --plan-mode <0|1|quick|planning>
533
+ --blind-generation-mode <normal|blind> Bid body generation mode; default normal
534
+ --anonymous-bid <0|1> Deprecated alias of --blind-generation-mode
517
535
  --multi-bid-id <id> override the selected bid package when needed
518
536
  --multi-bid-type <text> persist the selected bid package type
519
537
  --epc-engineer-type <text> accepts raw backend value or aliases: all, construction, design, not-epc
520
538
 
521
539
  Export style
522
540
  --template <1-${templateData.length}> Same template order as the frontend download drawer
523
- --style-json <file> Advanced export style JSON with startIndex/directorySettings/contentSettings
541
+ --style-json <file> Export style JSON file (CLI/automation compatibility)
542
+ --style-json-data <json> Structured export style payload (automation; no user file required)
524
543
  --start-index <n> Override the template heading numbering start index
525
544
  --auto-number <0|1> Toggle Word auto numbering
526
545
  --add-mark <0|1> Add suggest-modify marks
@@ -529,6 +548,10 @@ Export style
529
548
  --bottom-margin <cm> Page bottom margin
530
549
  --left-margin <cm> Page left margin
531
550
  --right-margin <cm> Page right margin
551
+ --landscape-width <cm> Landscape image fixed width
552
+ --landscape-max-height <cm> Landscape image maximum height
553
+ --portrait-width <cm> Portrait image fixed width
554
+ --portrait-max-height <cm> Portrait image maximum height
532
555
  --skeleton-color <hex> Alias of --table-bolder-color-of-skeleton
533
556
  --img-title-color <hex>
534
557
  --heading4-color <hex>
@@ -1816,6 +1839,7 @@ async function handleExport(args, state) {
1816
1839
  const tableStyle = enumOption(effectiveArgs, 'tableStyle', TABLE_STYLE_OPTIONS, snapshot.tableStyle ?? DEFAULT_TABLE_STYLE);
1817
1840
  const themeStyleSetting = buildThemeStyleSetting(snapshot, effectiveArgs);
1818
1841
  const pageMarginSetting = buildPageMarginSetting(snapshot, effectiveArgs);
1842
+ const imageSizeConfig = buildImageSizeConfig(snapshot, effectiveArgs);
1819
1843
  const templatePostData = buildTemplatePostData(template, effectiveArgs);
1820
1844
  const requestData = {
1821
1845
  cid,
@@ -1829,6 +1853,7 @@ async function handleExport(args, state) {
1829
1853
  },
1830
1854
  themeStyleSetting,
1831
1855
  pageMarginSetting,
1856
+ imageSizeConfig,
1832
1857
  t: Date.now(),
1833
1858
  ...templatePostData,
1834
1859
  };
@@ -1840,6 +1865,7 @@ async function handleExport(args, state) {
1840
1865
  tableStyle,
1841
1866
  themeStyleSetting,
1842
1867
  pageMarginSetting,
1868
+ imageSizeConfig,
1843
1869
  });
1844
1870
  const exportConfig = pickExportConfig(requestData, templateIndex);
1845
1871
 
@@ -1887,6 +1913,12 @@ function buildOutlineParams(snapshot, args) {
1887
1913
  const plan = stringOption(args, 'plan', snapshot.plan || '');
1888
1914
  const requirement = stringOption(args, 'requirement', snapshot.requirement || '');
1889
1915
  const rating = stringOption(args, 'rating', snapshot.rating || '');
1916
+ const blindGenerationMode = enumOption(
1917
+ args,
1918
+ ['blindGenerationMode', 'anonymousBid'],
1919
+ BLIND_GENERATION_MODE_OPTIONS,
1920
+ snapshot.blindGenerationMode ?? snapshot.blindBidConfirm?.blindGenerationMode ?? 'normal',
1921
+ );
1890
1922
 
1891
1923
  if (!uuid) {
1892
1924
  throw new Error('Missing uuid for outline generation. Run init first or make sure the task snapshot contains uuid.');
@@ -1924,6 +1956,11 @@ function buildOutlineParams(snapshot, args) {
1924
1956
  multiBidType: stringOption(args, 'multiBidType', snapshot.multiBidType),
1925
1957
  planMode: enumOption(args, 'planMode', PLAN_MODE_OPTIONS, snapshot.planMode ?? 0),
1926
1958
  epcEngineerType: normalizeEpcEngineerType(stringOption(args, 'epcEngineerType', snapshot.epcEngineerType)),
1959
+ blindBidConfirm: {
1960
+ blindGenerationMode,
1961
+ reviewMode: blindGenerationMode === 'blind' ? 'blind_review' : 'open_review',
1962
+ rules: [],
1963
+ },
1927
1964
  });
1928
1965
  }
1929
1966
 
@@ -2282,7 +2319,10 @@ async function ensurePeriodReady(client, state, cid, args) {
2282
2319
 
2283
2320
  function isParseReadyForNextStep(success) {
2284
2321
  const parsed = Number(success);
2285
- return parsed === 1 || parsed === 4;
2322
+ // The web client treats 2 as the completed parse state that opens the
2323
+ // bid-settings page. Waiting past it keeps WorkBuddy polling forever even
2324
+ // though multi-bid/EPC results are already available.
2325
+ return parsed === 1 || parsed === 2 || parsed === 4;
2286
2326
  }
2287
2327
 
2288
2328
  async function pollTaskUntil(client, state, cid, args, isDone, formatProgress) {
@@ -3060,6 +3100,7 @@ function buildChoicesPayload(scope) {
3060
3100
  imageToggles: ['infoImg', 'sceneImg', 'onlineImg', 'mermaidImg', 'knowledgeImg'],
3061
3101
  mermaidStyle: MERMAID_STYLE_OPTIONS,
3062
3102
  planMode: PLAN_MODE_OPTIONS,
3103
+ blindGenerationMode: BLIND_GENERATION_MODE_OPTIONS,
3063
3104
  multiBid: ['--multi-bid-id', '--multi-bid-type'],
3064
3105
  epcEngineerType: {
3065
3106
  option: '--epc-engineer-type',
@@ -3077,9 +3118,11 @@ function buildChoicesPayload(scope) {
3077
3118
  tableStyle: TABLE_STYLE_OPTIONS,
3078
3119
  advancedStyle: {
3079
3120
  option: '--style-json',
3121
+ automationOption: '--style-json-data',
3080
3122
  fields: ['startIndex', 'directorySettings', 'contentSettings'],
3081
3123
  },
3082
3124
  pageMargins: PAGE_MARGIN_FIELDS.map(field => `--${toKebabCase(field.optionKeys[0])}`),
3125
+ imageSize: IMAGE_SIZE_FIELDS.map(field => `--${toKebabCase(field.optionKeys[0])}`),
3083
3126
  themeColors: THEME_STYLE_SETTING_FIELDS.map(field => field.optionKeys.map(key => `--${toKebabCase(key)}`).join('/')),
3084
3127
  };
3085
3128
  }
@@ -3100,6 +3143,7 @@ function renderChoicesLines(payload) {
3100
3143
  lines.push(` table-style: ${formatChoiceOptions(TABLE_STYLE_OPTIONS)}`);
3101
3144
  lines.push(` mermaid-style: ${formatChoiceOptions(MERMAID_STYLE_OPTIONS)}`);
3102
3145
  lines.push(` plan-mode: ${formatChoiceOptions(PLAN_MODE_OPTIONS)}`);
3146
+ lines.push(` blind-generation-mode: ${formatChoiceOptions(BLIND_GENERATION_MODE_OPTIONS)}`);
3103
3147
  lines.push(' image toggles: --info-img, --scene-img, --online-img, --mermaid-img, --knowledge-img (0|1)');
3104
3148
  lines.push(' multi-bid override: --multi-bid-id <id>, --multi-bid-type <text>');
3105
3149
  lines.push(` epc-engineer-type: --epc-engineer-type <text>; aliases=${Object.keys(EPC_ENGINEER_TYPE_ALIAS_MAP).join(', ')}`);
@@ -3111,8 +3155,9 @@ function renderChoicesLines(payload) {
3111
3155
  lines.push(` table-color: ${formatChoiceOptions(TABLE_COLOR_OPTIONS)}`);
3112
3156
  lines.push(` table-style: ${formatChoiceOptions(TABLE_STYLE_OPTIONS)}`);
3113
3157
  lines.push(' binary toggles: --auto-number, --add-mark (0|1)');
3114
- lines.push(' advanced style: --style-json <file> with startIndex, directorySettings, contentSettings');
3158
+ lines.push(' custom numbering: --style-json-data <json> (automation) or --style-json <file> (compatibility) with startIndex, directorySettings, contentSettings');
3115
3159
  lines.push(` margins: ${PAGE_MARGIN_FIELDS.map(field => `--${toKebabCase(field.optionKeys[0])}`).join(', ')}`);
3160
+ lines.push(` image sizes (cm): ${IMAGE_SIZE_FIELDS.map(field => `--${toKebabCase(field.optionKeys[0])}`).join(', ')}`);
3116
3161
  lines.push(` theme colors: ${THEME_STYLE_SETTING_FIELDS.map(field => field.optionKeys.map(key => `--${toKebabCase(key)}`).join('/')).join(', ')}`);
3117
3162
  }
3118
3163
  return lines;
@@ -3393,6 +3438,22 @@ async function collectExportInteractiveArgs(args, snapshot, context = null, opti
3393
3438
  }
3394
3439
  }
3395
3440
 
3441
+ const customizeImageSize = await promptYesNo(
3442
+ promptContext,
3443
+ '是否要自定义图片尺寸',
3444
+ hasAnyOption(args, IMAGE_SIZE_FIELDS.flatMap(field => field.optionKeys)),
3445
+ );
3446
+ if (customizeImageSize) {
3447
+ for (const field of IMAGE_SIZE_FIELDS) {
3448
+ nextArgs[field.optionKeys[0]] = await promptNumber(promptContext, {
3449
+ title: `图片尺寸: ${field.key}`,
3450
+ question: `请输入 ${field.key} (单位 cm)`,
3451
+ defaultValue: numberOptionAny(args, field.optionKeys, defaults.imageSizeConfig?.[field.key] ?? DEFAULT_IMAGE_SIZE_CONFIG[field.key]),
3452
+ min: 0.1,
3453
+ });
3454
+ }
3455
+ }
3456
+
3396
3457
  const useStyleJson = await promptYesNo(
3397
3458
  promptContext,
3398
3459
  '\u662F\u5426\u8981\u6302\u8F7D\u9AD8\u7EA7\u6837\u5F0F JSON',
@@ -3442,6 +3503,7 @@ function buildInteractiveSnapshotDefaults(source = {}) {
3442
3503
  multiBidType: source.multiBidType ?? '',
3443
3504
  epcEngineerType: source.epcEngineerType ?? '',
3444
3505
  pageMarginSetting: source.pageMarginSetting ?? DEFAULT_PAGE_MARGIN_SETTING,
3506
+ imageSizeConfig: source.imageSizeConfig ?? DEFAULT_IMAGE_SIZE_CONFIG,
3445
3507
  themeStyleSetting: source.themeStyleSetting ?? DEFAULT_THEME_STYLE_SETTING,
3446
3508
  };
3447
3509
  }
@@ -4413,19 +4475,48 @@ function buildPageMarginSetting(snapshot, args) {
4413
4475
  return pageMarginSetting;
4414
4476
  }
4415
4477
 
4478
+ function buildImageSizeConfig(snapshot, args) {
4479
+ const imageSizeConfig = {
4480
+ ...DEFAULT_IMAGE_SIZE_CONFIG,
4481
+ ...(snapshot.imageSizeConfig || {}),
4482
+ };
4483
+ for (const field of IMAGE_SIZE_FIELDS) {
4484
+ const value = numberOptionAny(args, field.optionKeys);
4485
+ if (value === undefined) {
4486
+ continue;
4487
+ }
4488
+ if (!Number.isFinite(value) || value <= 0 || value > 50) {
4489
+ throw new Error(`Option --${toKebabCase(field.optionKeys[0])} expects a number greater than 0 and no greater than 50 cm.`);
4490
+ }
4491
+ imageSizeConfig[field.key] = value;
4492
+ }
4493
+ return imageSizeConfig;
4494
+ }
4495
+
4416
4496
  function buildTemplatePostData(template, args) {
4417
4497
  const styleJsonPath = stringOption(args, 'styleJson');
4418
4498
  let styleJson = {};
4499
+ const styleJsonData = stringOption(args, 'styleJsonData');
4500
+ if (styleJsonData) {
4501
+ try {
4502
+ styleJson = JSON.parse(styleJsonData);
4503
+ } catch {
4504
+ throw new Error('Option --style-json-data expects a valid JSON object.');
4505
+ }
4506
+ }
4419
4507
  if (styleJsonPath) {
4420
4508
  const resolvedPath = path.resolve(styleJsonPath);
4421
4509
  ensureFileExists(resolvedPath);
4422
4510
  try {
4423
- styleJson = JSON.parse(fs.readFileSync(resolvedPath, 'utf8'));
4511
+ styleJson = {
4512
+ ...styleJson,
4513
+ ...JSON.parse(fs.readFileSync(resolvedPath, 'utf8')),
4514
+ };
4424
4515
  } catch (error) {
4425
4516
  throw new Error(`Failed to parse style JSON: ${resolvedPath}`);
4426
4517
  }
4427
4518
  if (!styleJson || Array.isArray(styleJson) || typeof styleJson !== 'object') {
4428
- throw new Error('Option --style-json expects a JSON object.');
4519
+ throw new Error('Export style expects a JSON object.');
4429
4520
  }
4430
4521
  if (styleJson.directorySettings !== undefined && !Array.isArray(styleJson.directorySettings)) {
4431
4522
  throw new Error('Option --style-json expects directorySettings to be an array.');
@@ -4434,6 +4525,15 @@ function buildTemplatePostData(template, args) {
4434
4525
  throw new Error('Option --style-json expects contentSettings to be an object.');
4435
4526
  }
4436
4527
  }
4528
+ if (!styleJson || Array.isArray(styleJson) || typeof styleJson !== 'object') {
4529
+ throw new Error('Export style expects a JSON object.');
4530
+ }
4531
+ if (styleJson.directorySettings !== undefined && !Array.isArray(styleJson.directorySettings)) {
4532
+ throw new Error('Export style expects directorySettings to be an array.');
4533
+ }
4534
+ if (styleJson.contentSettings !== undefined && (Array.isArray(styleJson.contentSettings) || typeof styleJson.contentSettings !== 'object' || styleJson.contentSettings === null)) {
4535
+ throw new Error('Export style expects contentSettings to be an object.');
4536
+ }
4437
4537
  const startIndex = numberOption(args, 'startIndex', styleJson.startIndex ?? template.postData.startIndex ?? 1);
4438
4538
  if (!Number.isInteger(startIndex) || startIndex < 1) {
4439
4539
  throw new Error('Option --start-index expects an integer greater than or equal to 1.');
@@ -4484,6 +4584,7 @@ function pickOutlineConfig(params) {
4484
4584
  knowledgeImg: params.knowledgeImg,
4485
4585
  mermaidStyle: params.mermaidStyle,
4486
4586
  planMode: params.planMode,
4587
+ blindBidConfirm: params.blindBidConfirm,
4487
4588
  multiBidId: params.multiBidId,
4488
4589
  multiBidType: params.multiBidType,
4489
4590
  epcEngineerType: params.epcEngineerType,
@@ -4499,6 +4600,9 @@ function buildOutlineConfigSummary(params) {
4499
4600
  params.tableColor !== undefined ? `tableColor=${describeEnumValue(TABLE_COLOR_OPTIONS, params.tableColor)}` : null,
4500
4601
  params.tableStyle !== undefined ? `tableStyle=${describeEnumValue(TABLE_STYLE_OPTIONS, params.tableStyle)}` : null,
4501
4602
  `planMode=${describeEnumValue(PLAN_MODE_OPTIONS, params.planMode)}`,
4603
+ params.blindBidConfirm?.blindGenerationMode
4604
+ ? `blindGenerationMode=${params.blindBidConfirm.blindGenerationMode}`
4605
+ : null,
4502
4606
  ].filter(Boolean).join(', ');
4503
4607
  }
4504
4608
 
@@ -4514,6 +4618,7 @@ function pickExportConfig(requestData, templateIndex) {
4514
4618
  directorySettings: requestData.directorySettings,
4515
4619
  contentSettings: requestData.contentSettings,
4516
4620
  pageMarginSetting: requestData.pageMarginSetting,
4621
+ imageSizeConfig: requestData.imageSizeConfig,
4517
4622
  themeStyleSetting: requestData.themeStyleSetting,
4518
4623
  };
4519
4624
  }
@@ -4529,6 +4634,7 @@ function buildExportConfigSummary(config) {
4529
4634
  `autoNumber=${config.autoNumber}`,
4530
4635
  `addMark=${config.addMark}`,
4531
4636
  `margins=${margins.topMargin}/${margins.bottomMargin}/${margins.leftMargin}/${margins.rightMargin}`,
4637
+ `images=${config.imageSizeConfig.landscapeWidth}/${config.imageSizeConfig.landscapeMaxHeight}/${config.imageSizeConfig.portraitWidth}/${config.imageSizeConfig.portraitMaxHeight}`,
4532
4638
  ].join(', ');
4533
4639
  }
4534
4640
 
@@ -4652,6 +4758,8 @@ function pickTaskSnapshot(source) {
4652
4758
  tableStyle: source.tableStyle,
4653
4759
  tableQuantity: source.tableQuantity,
4654
4760
  planMode: source.planMode,
4761
+ blindBidConfirm: source.blindBidConfirm,
4762
+ blindGenerationMode: source.blindGenerationMode || source.blindBidConfirm?.blindGenerationMode,
4655
4763
  multiBidId: source.multiBidId,
4656
4764
  multiBidType: source.multiBidType,
4657
4765
  epcEngineerType: source.epcEngineerType,
@@ -4659,6 +4767,7 @@ function pickTaskSnapshot(source) {
4659
4767
  referenceStatus: source.referenceStatus,
4660
4768
  themeStyleSetting: source.themeStyleSetting,
4661
4769
  pageMarginSetting: source.pageMarginSetting,
4770
+ imageSizeConfig: source.imageSizeConfig,
4662
4771
  outlineTriggeredAt: source.outlineTriggeredAt,
4663
4772
  triggerMode: source.triggerMode,
4664
4773
  writeTriggeredAt: source.writeTriggeredAt,