@xqyz/xq-cli 0.3.11 → 0.3.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/CHANGELOG.md CHANGED
@@ -12,6 +12,49 @@
12
12
 
13
13
  (暂无未发布变更)
14
14
 
15
+ ## [xq-cli 0.3.13 / WorkBuddy Plugin 0.10.32] - 2026-08-28
16
+
17
+ ### API Key-only 登录
18
+
19
+ - CLI 本机登录页只保留 API Key 输入,并继续通过 `/user/info` 使用现有校验链路。
20
+ - 移除 CLI 和后端旧的账号密码、浏览器授权及 CLI 一次性授权码流程。
21
+ - WorkBuddy 安装器只在没有 API Key 时打开本机登录页,并同步内置 CLI `0.3.13`。
22
+ - `@xqyz/xq-cli@0.3.13` 与 `@xqyz/workbuddy-plugin-xique@0.10.32` 待本次发布;SkillHub `1.0.14` 仅生成上传包,需发布人手动上传。
23
+
24
+ ## [WorkBuddy Plugin 0.10.31] - 2026-08-28
25
+
26
+ ### 模板 5 自定义编号排版
27
+
28
+ - 按 Web 页面参考重排模板 5 自定义编号:标题设置、正文序号设置、起始编号和各级排版控件层次更清晰。
29
+ - 宽屏按行横向排列,窄卡片按稳定的 2/3/3 控件分组换行,避免自适应网格造成错位或挤压。
30
+ - 保持 `customNumbering` 字段结构、默认值和导出参数不变。
31
+ - `@xqyz/workbuddy-plugin-xique@0.10.31` 已发布,npm `latest` 和 `workbuddy-stable` 均已指向该版本。
32
+ - 已生成 `output/skillhub-xique-ai-1.0.13.zip`,由发布人手动上传;SkillHub 不由代理自动发布。
33
+
34
+ ## [WorkBuddy Plugin 0.10.30] - 2026-08-27
35
+
36
+ ### 供应方式确认恢复
37
+
38
+ - 修复解析完成但 `bidderIdentity_judgement=0` 时 WorkBuddy 直接阻塞的问题,新增“我们自己生产 / 我们采购供应”选择卡。
39
+ - 供应方式提交后沿用同一 `runId/cid/uuid` 继续生成,兼容旧版本已标记为 `blocked` 的历史任务,并防止重复提交。
40
+ - 同步 CLI `bidder-role` 命令、MCP App、Hook 白名单、Connector/Plugin Skill 和回归测试。
41
+ - `@xqyz/xq-cli@0.3.12` 与 `@xqyz/workbuddy-plugin-xique@0.10.30` 已发布,npm `latest` 和 `workbuddy-stable` 均已指向对应版本;SkillHub `1.0.12` 上传包由发布人手动上传。
42
+
43
+ ## [WorkBuddy Plugin 0.10.29] - 2026-08-27
44
+
45
+ ### 模板5表格顺序
46
+
47
+ - 保持模板5紧凑编号表格位于“导出模板”之后,避免展开“导出格式设置”时顺序错乱。
48
+ - `@xqyz/workbuddy-plugin-xique@0.10.29` 已发布,`latest` 和 `workbuddy-stable` 均已指向该版本;SkillHub 无需新增版本。
49
+
50
+ ## [WorkBuddy Plugin 0.10.28] - 2026-08-27
51
+
52
+ ### 模板5自定义编号表格
53
+
54
+ - 修复模板5自定义编号在并入“导出格式设置”后误用通用单选卡的问题。
55
+ - 恢复标题级、正文级编号设置的紧凑表格下拉布局;数据结构和导出参数保持不变。
56
+ - `@xqyz/workbuddy-plugin-xique@0.10.28` 已发布,`latest` 和 `workbuddy-stable` 均已指向该版本;SkillHub 无需新增版本。
57
+
15
58
  ## [xq-cli 0.3.11 / WorkBuddy Plugin 0.10.27 / SkillHub 1.0.11] - 2026-08-27
16
59
 
17
60
  ### 写作质量与导出设置
@@ -20,7 +63,7 @@
20
63
  - 极短篇、超短篇按前端规则禁用专家智笔和旗舰智笔;图片来源选项保持直接展示。
21
64
  - 表格、流程图、模板、编号、修改标记、页边距和图片尺寸等低频选项统一收纳到默认收起的“导出格式设置”。
22
65
  - 同步 CLI、MCP App、Connector 和 Skill 文档,保持配置提交和规划流程不变。
23
- - `@xqyz/xq-cli@0.3.11` 与 `@xqyz/workbuddy-plugin-xique@0.10.27` 待发布;SkillHub `1.0.11` 上传包待手动上传。
66
+ - `@xqyz/xq-cli@0.3.11` 与 `@xqyz/workbuddy-plugin-xique@0.10.27` 已发布;`latest` 和 `workbuddy-stable` 均已指向对应版本。SkillHub `1.0.11` 上传包待手动上传。
24
67
 
25
68
  ## [WorkBuddy Plugin 0.10.26 / SkillHub 1.0.10] - 2026-08-27
26
69
 
package/README.md CHANGED
@@ -53,12 +53,11 @@ npm install -g .\xqyz-xq-cli-0.2.0.tgz
53
53
 
54
54
  ```bash
55
55
  xq-cli login
56
- xq-cli login --name your-account --password your-password
57
56
  xq-cli login --api-key xq_sk_xxx
58
- xq-cli login --browser
59
57
  xq-cli wizard
60
58
  xq-cli wizard --file D:\bids\tender.docx
61
59
  xq-cli init --file D:\bids\tender.docx --wait
60
+ xq-cli bidder-role --cid <cid> --uuid <uuid> --bidder-role <1|2>
62
61
  xq-cli outline --cid <cid> --interactive --wait
63
62
  xq-cli outline --cid <cid> --wait
64
63
  xq-cli outline view --cid <cid>
@@ -76,15 +75,12 @@ xq-cli export --cid <cid> --out D:\output
76
75
 
77
76
  Fresh installs default to the production API address `https://ai.bidfile.qianlima.com/api`. Use `--base-url` only when you need to target another API endpoint.
78
77
 
79
- `login` now defaults to a local page with browser and API Key choices. If you explicitly pass `--name` and `--password`, the CLI uses password login. If you pass `--api-key`, it saves the API key locally and uses it for later requests.
78
+ `login` now supports API Key authentication only. Without `--api-key`, it opens a local page for entering the API Key; with `--api-key`, it verifies and saves the key directly. The old account/password and browser-authorization options are no longer supported.
80
79
 
81
80
  When no credential flags are supplied, `xq-cli login` opens a local login page on
82
- `127.0.0.1` with both choices: browser authorization or an API Key from the
83
- Xique API Key management page. The page submits the Key only to the local CLI,
81
+ `127.0.0.1` for an API Key from the Xique API Key management page. The page submits the Key only to the local CLI,
84
82
  which verifies it with `/user/info` before saving it to the user-scoped config.
85
- Use `xq-cli login --browser` to skip the local page and open browser
86
- authorization directly, or `xq-cli login --no-open` to copy the local page URL
87
- manually. Never paste an API Key into chat or commit it to a repository.
83
+ Use `xq-cli login --no-open` to copy the local page URL manually. Never paste an API Key into chat or commit it to a repository.
88
84
 
89
85
  `directory view` reads the complete generated directory tree without triggering outline, directory, or content generation. Add `--json` for the raw API structure, or `--out <file-or-directory>` to save a UTF-8 JSON document.
90
86
 
@@ -95,6 +91,11 @@ uploads an image in the Xique frontend.
95
91
 
96
92
  When `outline --wait` or `write --wait` hits a required response-basis step, the CLI will pause and ask you to choose `0/1/2` before continuing. It no longer auto-selects a default when you just press Enter.
97
93
 
94
+ For goods procurement tasks paused on the bidder supply method, use
95
+ `xq-cli bidder-role --cid <cid> --uuid <uuid> --bidder-role 1|2` (`1` means
96
+ self production, `2` means procured supply). The command only submits the
97
+ choice for the existing parsed task; it does not upload or create a task.
98
+
98
99
  `write --wait` now also retries `sequelForOutline` when the directory phase stalls at the outline-complete step, and auto-calls `generateAllUnfinished` if the backend reports `100%` but still leaves unfinished chapters behind.
99
100
 
100
101
  `write` follows the frontend flow:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xqyz/xq-cli",
3
- "version": "0.3.11",
3
+ "version": "0.3.13",
4
4
  "description": "CLI for xique bid-book task workflows",
5
5
  "type": "module",
6
6
  "scripts": {
package/src/cli.mjs CHANGED
@@ -7,7 +7,6 @@ import os from 'os';
7
7
  import path from 'path';
8
8
  import readline from 'node:readline/promises';
9
9
  import { spawn } from 'child_process';
10
- import { randomUUID } from 'crypto';
11
10
  import axios from 'axios';
12
11
  import FormData from 'form-data';
13
12
  import { templateData } from './templateData.mjs';
@@ -73,7 +72,6 @@ const DIRECTORY_SEQUEL_RETRY_INTERVAL_MS = 60_000;
73
72
  const GENERATE_UNFINISHED_RETRY_INTERVAL_MS = 60_000;
74
73
  const RETRYABLE_NETWORK_CODES = new Set(['ECONNRESET', 'ECONNABORTED', 'ETIMEDOUT', 'EAI_AGAIN']);
75
74
  const SOFT_CONTENT_TRIGGER_CODES = new Set([411, 429]);
76
- const BROWSER_LOGIN_CALLBACK_PATH = '/callback';
77
75
  const HTTP_AGENT = new http.Agent({ keepAlive: true });
78
76
  const HTTPS_AGENT = new https.Agent({ keepAlive: true });
79
77
  const AUTO_IGNORE_ISSUE_CONFIG = {
@@ -398,6 +396,9 @@ async function main() {
398
396
  case 'period-confirm':
399
397
  await handlePeriodConfirm(parsed, state);
400
398
  return;
399
+ case 'bidder-role':
400
+ await handleBidderRole(parsed, state);
401
+ return;
401
402
  case 'outline':
402
403
  if (isOutlineAction(parsed, ['view', 'show'])) {
403
404
  await handleOutlineView(parsed, state);
@@ -467,6 +468,7 @@ Commands
467
468
  parse-status Query or wait for parse completion
468
469
  period-status Query engineering project period confirmation status
469
470
  period-confirm Save confirmed engineering project period
471
+ bidder-role Save the bidder's goods supply method for the current task
470
472
  outline Pre-set task params and trigger outline generation
471
473
  outline view View the current outline without triggering generation
472
474
  outline update Update the current outline from an edited JSON file
@@ -494,12 +496,11 @@ Plan actions
494
496
  Examples
495
497
  xq-cli login
496
498
  xq-cli login --no-open
497
- xq-cli login --name your-account --password your-password
498
499
  xq-cli login --api-key xq_sk_xxx
499
- xq-cli login --browser
500
500
  $env:XQ_API_KEY="xq_sk_xxx"
501
501
  xq-cli init --file D:\\bids\\tender.docx --wait
502
502
  xq-cli parse-status --cid <cid> --uuid <uuid> --wait
503
+ xq-cli bidder-role --cid <cid> --uuid <uuid> --bidder-role <1|2>
503
504
  xq-cli choices
504
505
  xq-cli choices outline --json
505
506
  xq-cli gallery-status --json
@@ -539,7 +540,7 @@ Common options
539
540
  --timeout-sec <n> Poll timeout in seconds, default 600
540
541
  --interval-sec <n> Poll interval in seconds, default 10
541
542
  --reference-type <0|1|2> Outline reference choice: 0=merge, 1=format only, 2=score only
542
- --browser Skip the local login page and open browser authorization directly
543
+ --bidder-role <1|2> Goods supply method: 1=self production, 2=procured supply
543
544
  --api-key <key> Save a user API key locally and verify it with /user/info
544
545
  --no-open Do not open the local login page automatically
545
546
  --interactive Open prompt menus for the current command
@@ -611,6 +612,9 @@ async function handleLogin(args, state) {
611
612
  if (!baseUrl) {
612
613
  throw new Error('Missing base URL. Pass --base-url <url>.');
613
614
  }
615
+ if (args.browser !== undefined || stringOption(args, 'name') || stringOption(args, 'password')) {
616
+ throw new Error('当前仅支持 API Key 登录,请使用 xq-cli login --api-key <key> 或打开本机登录页。');
617
+ }
614
618
  const sourceFrom = stringOption(args, 'sourceFrom', DEFAULT_SOURCE_FROM);
615
619
  const apiKey = normalizeApiKey(stringOption(args, 'apiKey'));
616
620
  if (apiKey) {
@@ -640,12 +644,8 @@ async function handleLogin(args, state) {
640
644
  ]);
641
645
  return;
642
646
  }
643
- const loginMode = resolveLoginMode(args);
644
- const loginResult = loginMode === 'browser'
645
- ? { result: await loginWithBrowser(baseUrl, sourceFrom, args), authType: 'token' }
646
- : loginMode === 'page'
647
- ? await loginWithLoginPage(baseUrl, sourceFrom, args)
648
- : { result: await loginWithPassword(baseUrl, sourceFrom, args), authType: 'token' };
647
+ const loginMode = 'page';
648
+ const loginResult = await loginWithLoginPage(baseUrl, args);
649
649
  const result = loginResult.result;
650
650
  const apiKeyFromPage = loginResult.apiKey || '';
651
651
 
@@ -664,7 +664,6 @@ async function handleLogin(args, state) {
664
664
  authType: loginResult.authType,
665
665
  baseUrl,
666
666
  username: state.user.username,
667
- tokenSaved: loginResult.authType === 'token',
668
667
  apiKeySaved: loginResult.authType === 'api-key',
669
668
  }, [
670
669
  `login success: ${state.user.username}`,
@@ -675,83 +674,24 @@ async function handleLogin(args, state) {
675
674
  ]);
676
675
  }
677
676
 
678
- function resolveLoginMode(args) {
679
- if (booleanOption(args, 'browser')) {
680
- return 'browser';
681
- }
682
- if (stringOption(args, 'apiKey')) {
683
- return 'api-key';
684
- }
685
- const hasName = stringOption(args, 'name');
686
- const hasPassword = stringOption(args, 'password');
687
- return hasName || hasPassword ? 'password' : 'page';
688
- }
689
-
690
677
  async function loginWithApiKey(baseUrl, apiKey) {
691
678
  const client = createClient(baseUrl, apiKey);
692
679
  return getJson(client, '/user/info');
693
680
  }
694
681
 
695
- async function loginWithPassword(baseUrl, sourceFrom, args) {
696
- const name = requiredOption(args, 'name');
697
- const password = requiredOption(args, 'password');
698
- const client = createClient(baseUrl);
699
- return postJson(client, '/user/login', {
700
- name,
701
- password,
702
- sourceFrom,
703
- });
704
- }
705
-
706
- async function loginWithBrowser(baseUrl, sourceFrom, args) {
707
- const client = createClient(baseUrl);
708
- const loginState = randomUUID();
709
- const timeoutMs = secondsToMs(numberOption(args, 'timeoutSec', DEFAULT_LOGIN_TIMEOUT_MS / 1000));
710
- const callbackServer = await startBrowserLoginServer();
711
- const authorizeUrl = buildBrowserAuthorizeUrl(baseUrl, callbackServer.redirectUri, loginState, sourceFrom);
712
-
713
- writeNotice(args, `browser login url: ${authorizeUrl}`);
714
- if (openBrowser(authorizeUrl)) {
715
- writeNotice(args, 'browser login: waiting for authorization callback');
716
- } else {
717
- writeNotice(args, 'browser login: automatic open failed, paste the URL above into a browser on this machine');
718
- }
719
-
720
- try {
721
- const callback = await withTimeout(
722
- callbackServer.result,
723
- timeoutMs,
724
- `Browser login timed out after ${Math.round(timeoutMs / 1000)} seconds.`,
725
- );
726
- if (callback.state !== loginState) {
727
- throw new Error('Browser login state validation failed.');
728
- }
729
- return await postJson(client, '/user/cli/token', {
730
- code: callback.code,
731
- state: callback.state,
732
- });
733
- } finally {
734
- await callbackServer.close();
735
- }
736
- }
737
-
738
- async function loginWithLoginPage(baseUrl, sourceFrom, args) {
682
+ async function loginWithLoginPage(baseUrl, args) {
739
683
  const timeoutMs = secondsToMs(numberOption(args, 'timeoutSec', DEFAULT_LOGIN_TIMEOUT_MS / 1000));
740
- const loginState = randomUUID();
741
- const server = await startBrowserLoginServer({
742
- loginState,
684
+ const server = await startApiKeyLoginServer({
743
685
  onApiKey: async apiKey => ({
744
686
  result: await loginWithApiKey(baseUrl, apiKey),
745
687
  apiKey,
746
688
  }),
747
- renderPage: ({ redirectUri }) => renderLoginPage({
748
- authorizeUrl: buildBrowserAuthorizeUrl(baseUrl, redirectUri, loginState, sourceFrom),
749
- }),
689
+ renderPage: () => renderLoginPage(),
750
690
  });
751
691
  writeNotice(args, `login page url: ${server.pageUrl}`);
752
692
  if (process.env.XQ_CLI_NO_BROWSER !== '1') {
753
693
  if (openBrowser(server.pageUrl)) {
754
- writeNotice(args, 'login page: waiting for API key or browser authorization');
694
+ writeNotice(args, 'login page: waiting for API key');
755
695
  } else {
756
696
  writeNotice(args, 'login page: automatic open failed, paste the URL above into a browser on this machine');
757
697
  }
@@ -763,19 +703,10 @@ async function loginWithLoginPage(baseUrl, sourceFrom, args) {
763
703
  timeoutMs,
764
704
  `Login timed out after ${Math.round(timeoutMs / 1000)} seconds.`,
765
705
  );
766
- if (selected.mode === 'api-key') {
767
- return {
768
- result: selected.result,
769
- apiKey: selected.apiKey,
770
- authType: 'api-key',
771
- };
772
- }
773
706
  return {
774
- result: await postJson(createClient(baseUrl), '/user/cli/token', {
775
- code: selected.code,
776
- state: selected.state,
777
- }),
778
- authType: 'token',
707
+ result: selected.result,
708
+ apiKey: selected.apiKey,
709
+ authType: 'api-key',
779
710
  };
780
711
  } finally {
781
712
  await server.close();
@@ -948,6 +879,41 @@ async function handlePeriodConfirm(args, state) {
948
879
  return payload;
949
880
  }
950
881
 
882
+ async function handleBidderRole(args, state) {
883
+ const client = createAuthorizedClient(args, state);
884
+ const cid = resolveCid(args, state);
885
+ const snapshot = await hydrateTaskSnapshot(client, state, cid);
886
+ const uuid = stringOption(args, 'uuid', snapshot.uuid);
887
+ if (!uuid) {
888
+ throw new Error(`No uuid found for task ${cid}. Pass --uuid explicitly or run init first.`);
889
+ }
890
+ const bidderRole = numberOption(args, 'bidderRole');
891
+ if (![1, 2].includes(bidderRole)) {
892
+ throw new Error('Option --bidder-role expects 1 (self production) or 2 (procured supply).');
893
+ }
894
+ const response = await getJson(client, '/proxy/savebidderRole', {
895
+ uuid,
896
+ bidderRole,
897
+ });
898
+ saveTaskSnapshot(state, cid, {
899
+ uuid,
900
+ bidderRole,
901
+ });
902
+ const payload = {
903
+ command: 'bidder-role',
904
+ cid,
905
+ uuid,
906
+ bidderRole,
907
+ response,
908
+ };
909
+ outputResult(args, payload, [
910
+ `bidder role saved: cid=${cid}`,
911
+ `uuid: ${uuid}`,
912
+ `bidder role: ${bidderRole === 1 ? 'self production' : 'procured supply'}`,
913
+ ]);
914
+ return payload;
915
+ }
916
+
951
917
  function handleChoices(args) {
952
918
  const scope = normalizeChoicesScope(args._[1] || 'all');
953
919
  const payload = buildChoicesPayload(scope);
@@ -4205,16 +4171,7 @@ function saveLoginState(state, options) {
4205
4171
  writeState(state);
4206
4172
  }
4207
4173
 
4208
- function buildBrowserAuthorizeUrl(baseUrl, redirectUri, state, sourceFrom) {
4209
- const query = new URLSearchParams({
4210
- redirect_uri: redirectUri,
4211
- state,
4212
- sourceFrom,
4213
- });
4214
- return `${baseUrl}/user/cli/authorize?${query.toString()}`;
4215
- }
4216
-
4217
- async function startBrowserLoginServer({ loginState = '', renderPage = null, onApiKey = null } = {}) {
4174
+ async function startApiKeyLoginServer({ renderPage = null, onApiKey = null } = {}) {
4218
4175
  let settled = false;
4219
4176
  let resolveResult;
4220
4177
  let rejectResult;
@@ -4234,9 +4191,7 @@ async function startBrowserLoginServer({ loginState = '', renderPage = null, onA
4234
4191
  }
4235
4192
  if (pathname === '/' && typeof renderPage === 'function') {
4236
4193
  response.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
4237
- response.end(renderPage({
4238
- redirectUri: `http://127.0.0.1:${origin?.port || 0}${BROWSER_LOGIN_CALLBACK_PATH}`,
4239
- }));
4194
+ response.end(renderPage());
4240
4195
  return;
4241
4196
  }
4242
4197
  if (pathname === '/api-key' && request.method === 'POST' && typeof onApiKey === 'function') {
@@ -4259,39 +4214,8 @@ async function startBrowserLoginServer({ loginState = '', renderPage = null, onA
4259
4214
  }
4260
4215
  return;
4261
4216
  }
4262
- if (pathname !== BROWSER_LOGIN_CALLBACK_PATH) {
4263
- response.writeHead(404, { 'Content-Type': 'text/plain; charset=utf-8' });
4264
- response.end('Not found');
4265
- return;
4266
- }
4267
-
4268
- const code = requestUrl.searchParams.get('code');
4269
- const state = requestUrl.searchParams.get('state');
4270
- if (!code || !state) {
4271
- response.writeHead(400, { 'Content-Type': 'text/html; charset=utf-8' });
4272
- response.end(renderBrowserLoginResultPage('登录未完成', '缺少 code 或 state,返回 CLI 重新执行登录即可。'));
4273
- if (!settled) {
4274
- settled = true;
4275
- rejectResult(new Error('Browser login callback missing code or state.'));
4276
- }
4277
- return;
4278
- }
4279
-
4280
- if (loginState && state !== loginState) {
4281
- response.writeHead(400, { 'Content-Type': 'text/html; charset=utf-8' });
4282
- response.end(renderBrowserLoginResultPage('登录未完成', '授权状态校验失败,请返回 CLI 重新执行登录。'));
4283
- if (!settled) {
4284
- settled = true;
4285
- rejectResult(new Error('Browser login state validation failed.'));
4286
- }
4287
- return;
4288
- }
4289
- response.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
4290
- response.end(renderBrowserLoginResultPage('登录成功', '授权已完成,现在可以回到 CLI 继续使用了。'));
4291
- if (!settled) {
4292
- settled = true;
4293
- resolveResult({ mode: 'browser', code, state });
4294
- }
4217
+ response.writeHead(404, { 'Content-Type': 'text/plain; charset=utf-8' });
4218
+ response.end('Not found');
4295
4219
  });
4296
4220
  server.on('error', error => {
4297
4221
  if (!settled) {
@@ -4309,11 +4233,8 @@ async function startBrowserLoginServer({ loginState = '', renderPage = null, onA
4309
4233
  });
4310
4234
 
4311
4235
  const address = server.address();
4312
- const redirectUri = `http://127.0.0.1:${address.port}${BROWSER_LOGIN_CALLBACK_PATH}`;
4313
4236
  return {
4314
- redirectUri,
4315
4237
  pageUrl: `http://127.0.0.1:${address.port}/`,
4316
- loginState,
4317
4238
  result,
4318
4239
  close: () => closeServer(server),
4319
4240
  };
@@ -4340,7 +4261,7 @@ async function readJsonRequestBody(request, maxBytes) {
4340
4261
  }
4341
4262
  }
4342
4263
 
4343
- function renderLoginPage({ authorizeUrl }) {
4264
+ function renderLoginPage() {
4344
4265
  return `<!doctype html>
4345
4266
  <html lang="zh-CN">
4346
4267
  <head>
@@ -4407,8 +4328,6 @@ function renderLoginPage({ authorizeUrl }) {
4407
4328
  <div class="input-wrap"><span class="field-icon" aria-hidden="true"></span><input id="api-key" name="apiKey" type="password" autocomplete="off" placeholder="xq_sk_..." required /></div>
4408
4329
  <button type="submit"><span>使用 API Key 登录</span><span class="button-arrow">→</span></button>
4409
4330
  </form>
4410
- <div class="divider">或</div>
4411
- <a href="${escapeHtml(authorizeUrl)}"><span>使用浏览器授权登录</span><span class="button-arrow">↗</span></a>
4412
4331
  <p class="hint">API Key 可在喜鹊的 API Key 管理页创建。验证成功后即可关闭页面。</p>
4413
4332
  <div class="secure-note"><span></span>登录状态仅用于当前 CLI 工作流</div>
4414
4333
  </main>
@@ -5254,6 +5173,7 @@ function pickTaskSnapshot(source) {
5254
5173
  multiBidType: source.multiBidType,
5255
5174
  choiceMultiBiddingId: source.choiceMultiBiddingId,
5256
5175
  epcEngineerType: source.epcEngineerType,
5176
+ bidderRole: source.bidderRole,
5257
5177
  outlineReferenceType: source.outlineReferenceType,
5258
5178
  referenceStatus: source.referenceStatus,
5259
5179
  themeStyleSetting: source.themeStyleSetting,