@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 +44 -1
- package/README.md +9 -8
- package/package.json +1 -1
- package/src/cli.mjs +59 -139
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`
|
|
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
|
|
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`
|
|
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 --
|
|
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
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
|
-
--
|
|
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 =
|
|
644
|
-
const loginResult =
|
|
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
|
|
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
|
|
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: (
|
|
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
|
|
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:
|
|
775
|
-
|
|
776
|
-
|
|
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
|
|
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
|
-
|
|
4263
|
-
|
|
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(
|
|
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,
|