@cnbcool/cnb-api-generate 2.4.7 → 2.5.1
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/built/codegen/api/build-query-value-expression.js +62 -0
- package/built/codegen/api/generate-api-function-ast-node.js +2 -2
- package/built/codegen/api/generate-function-content-ast-node.js +16 -8
- package/built/codegen/api/generate-function-params-ast-node.js +69 -12
- package/built/utils/compute-param-aliases.js +55 -0
- package/built/utils/flatten-tool-options.js +7 -2
- package/built/utils/query-path-alias.js +38 -0
- package/client/index.ts +2 -0
- package/client/lib/format-params.ts +24 -2
- package/client/lib/git-credential.ts +182 -0
- package/client/utils/get-token-env-key.ts +7 -0
- package/client/utils/is-workbuddy-sandbox.ts +3 -0
- package/client/utils/refresh-token.ts +1 -1
- package/client/utils/resolve-token.ts +34 -0
- package/package.json +1 -1
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.buildQueryValueExpression = buildQueryValueExpression;
|
|
37
|
+
const ts = __importStar(require("typescript"));
|
|
38
|
+
const query_path_alias_1 = require("../../utils/query-path-alias");
|
|
39
|
+
/**
|
|
40
|
+
* 构造请求参数对象里 `params:` / `_originParams.query:` 字段的值表达式。
|
|
41
|
+
*
|
|
42
|
+
* 规则:
|
|
43
|
+
* - path 与 query 没有同名冲突时:直接返回 `Identifier(queryVarName)`,
|
|
44
|
+
* 生成代码形如 `params: __query`;
|
|
45
|
+
* - 存在同名冲突时(如 `/{repo}/-/knowledge/base/query/{query}` 同时带 `?query=xxx`):
|
|
46
|
+
* 返回对象字面量 `{ ...__query, ["<原始 key>"]: q_<原始 key>, ... }`,
|
|
47
|
+
* 通过计算属性把别名 `q_xxx` 映射回 swagger 里的原始 key,
|
|
48
|
+
* 保证发给后端的 query string key 与 swagger 契约一致。
|
|
49
|
+
*/
|
|
50
|
+
function buildQueryValueExpression(queryVarName, pathParams, queryParams) {
|
|
51
|
+
const aliasMap = (0, query_path_alias_1.collectQueryPathAliasMap)(pathParams, queryParams);
|
|
52
|
+
if (aliasMap.size === 0) {
|
|
53
|
+
return ts.factory.createIdentifier(queryVarName);
|
|
54
|
+
}
|
|
55
|
+
const properties = [
|
|
56
|
+
ts.factory.createSpreadAssignment(ts.factory.createIdentifier(queryVarName)),
|
|
57
|
+
];
|
|
58
|
+
aliasMap.forEach((aliasName, originalName) => {
|
|
59
|
+
properties.push(ts.factory.createPropertyAssignment(ts.factory.createComputedPropertyName(ts.factory.createStringLiteral(originalName)), ts.factory.createIdentifier(aliasName)));
|
|
60
|
+
});
|
|
61
|
+
return ts.factory.createObjectLiteralExpression(properties, false);
|
|
62
|
+
}
|
|
@@ -55,7 +55,7 @@ function generateApiFunctionASTNode(methodData, definitions, defintionsMap) {
|
|
|
55
55
|
let dependencies = new Set();
|
|
56
56
|
// 根据parameters的类型,处理请求中不同的数据的处理方式
|
|
57
57
|
const { pathParams, bodyParams, queryParams } = (0, filter_api_param_1.filterApiParam)(path, parameters);
|
|
58
|
-
const { requestParamsASTNodes: paramASTNodes, reduxActionMap, dependencies: paramsDependencies, requestInterfaceASTNodes: paramsInterfaceASTNodes, } = (0, generate_function_params_ast_node_1.generateFunctionParamsASTNode)(methodData, pathParams, queryParams, bodyParams, defintionsMap);
|
|
58
|
+
const { requestParamsASTNodes: paramASTNodes, reduxActionMap, dependencies: paramsDependencies, requestInterfaceASTNodes: paramsInterfaceASTNodes, paramAliases, } = (0, generate_function_params_ast_node_1.generateFunctionParamsASTNode)(methodData, pathParams, queryParams, bodyParams, defintionsMap);
|
|
59
59
|
if (paramsDependencies && paramsDependencies.size > 0) {
|
|
60
60
|
dependencies = new Set([...dependencies, ...paramsDependencies]);
|
|
61
61
|
}
|
|
@@ -113,7 +113,7 @@ function generateApiFunctionASTNode(methodData, definitions, defintionsMap) {
|
|
|
113
113
|
? undefined
|
|
114
114
|
: ts.factory.createIdentifier(methodData.exportName), undefined, paramASTNodes, ts.factory.createTypeReferenceNode(ts.factory.createIdentifier('Promise'), [
|
|
115
115
|
ts.factory.createTypeReferenceNode(ts.factory.createIdentifier(constants_1.CNB_REQUEST_RESULT_INTERFACE), [successApiReturnType, errorApiReturnType]),
|
|
116
|
-
]), (0, generate_function_content_ast_node_1.generateFunctionContentASTNode)(methodData, successApiReturnType, errorApiReturnType, pathParams, queryParams, bodyParams)));
|
|
116
|
+
]), (0, generate_function_content_ast_node_1.generateFunctionContentASTNode)(methodData, successApiReturnType, errorApiReturnType, pathParams, queryParams, bodyParams, paramAliases)));
|
|
117
117
|
for (const dependency of dependencies) {
|
|
118
118
|
const defintion = defintionsMap[dependency];
|
|
119
119
|
requestFileDependenciesASTNodes.push((0, generate_import_ast_node_1.generateImportAstNode)(defintion.exportName, defintion.filename, false, category, defintion.type));
|
|
@@ -38,11 +38,17 @@ const ts = __importStar(require("typescript"));
|
|
|
38
38
|
const constants_1 = require("../../constants");
|
|
39
39
|
const generate_request_url_ast_node_1 = require("./generate-request-url-ast-node");
|
|
40
40
|
const is_skills_1 = require("../../utils/is-skills");
|
|
41
|
-
|
|
41
|
+
const build_query_value_expression_1 = require("./build-query-value-expression");
|
|
42
|
+
function generateFunctionContentASTNode(methodData, fetchSuccessResponse, fetchErrorResponse, pathParams, queryParams, bodyParams, paramAliases) {
|
|
43
|
+
var _a, _b, _c, _d;
|
|
44
|
+
const queryVarName = (_a = paramAliases === null || paramAliases === void 0 ? void 0 : paramAliases.queryName) !== null && _a !== void 0 ? _a : constants_1.CNB_REQUEST_PARAM_QUERY_NAME;
|
|
45
|
+
const reqVarName = (_b = paramAliases === null || paramAliases === void 0 ? void 0 : paramAliases.reqName) !== null && _b !== void 0 ? _b : constants_1.CNB_REQUEST_PARAM_REQ_NAME;
|
|
46
|
+
const optionsVarName = (_c = paramAliases === null || paramAliases === void 0 ? void 0 : paramAliases.optionsName) !== null && _c !== void 0 ? _c : constants_1.CNB_REQUEST_PARAM_OPTIONS_NAME;
|
|
47
|
+
const axiosConfigVarName = (_d = paramAliases === null || paramAliases === void 0 ? void 0 : paramAliases.axiosConfigName) !== null && _d !== void 0 ? _d : constants_1.CNB_REQUEST_AXIOS_CONFIG_NAME;
|
|
42
48
|
const requestParams = [
|
|
43
|
-
ts.factory.createSpreadAssignment(ts.factory.createIdentifier(
|
|
44
|
-
ts.factory.createPropertyAssignment(ts.factory.createIdentifier(constants_1.CNB_REQUEST_CONFIG_NEXT_REQ), ts.factory.createIdentifier(
|
|
45
|
-
ts.factory.createPropertyAssignment(ts.factory.createIdentifier(constants_1.CNB_REQUEST_PARAM_OPTIONS_NAME), ts.factory.createIdentifier(
|
|
49
|
+
ts.factory.createSpreadAssignment(ts.factory.createIdentifier(axiosConfigVarName)),
|
|
50
|
+
ts.factory.createPropertyAssignment(ts.factory.createIdentifier(constants_1.CNB_REQUEST_CONFIG_NEXT_REQ), ts.factory.createIdentifier(reqVarName)),
|
|
51
|
+
ts.factory.createPropertyAssignment(ts.factory.createIdentifier(constants_1.CNB_REQUEST_PARAM_OPTIONS_NAME), ts.factory.createIdentifier(optionsVarName)),
|
|
46
52
|
];
|
|
47
53
|
// 请求路径
|
|
48
54
|
requestParams.push(ts.factory.createPropertyAssignment(ts.factory.createIdentifier(constants_1.CNB_REQUEST_CONFIG_URL), (0, generate_request_url_ast_node_1.generateRequestUrlASTNode)(methodData.path, pathParams)));
|
|
@@ -52,11 +58,12 @@ function generateFunctionContentASTNode(methodData, fetchSuccessResponse, fetchE
|
|
|
52
58
|
requestParams.push(ts.factory.createPropertyAssignment(ts.factory.createIdentifier(constants_1.CNB_REQUEST_CONFIG_METHOD), ts.factory.createStringLiteral(methodData.method)));
|
|
53
59
|
// 请求query
|
|
54
60
|
if (queryParams && queryParams.length > 0) {
|
|
55
|
-
requestParams.push(ts.factory.createPropertyAssignment(ts.factory.createIdentifier(constants_1.CNB_REQUEST_PARAM_QUERY_KEY_NAME),
|
|
61
|
+
requestParams.push(ts.factory.createPropertyAssignment(ts.factory.createIdentifier(constants_1.CNB_REQUEST_PARAM_QUERY_KEY_NAME), (0, build_query_value_expression_1.buildQueryValueExpression)(queryVarName, pathParams, queryParams)));
|
|
56
62
|
}
|
|
57
63
|
// 请求body
|
|
58
64
|
if (bodyParams) {
|
|
59
|
-
|
|
65
|
+
const bodyVarName = (paramAliases === null || paramAliases === void 0 ? void 0 : paramAliases.bodyParamName) || bodyParams.name;
|
|
66
|
+
requestParams.push(ts.factory.createPropertyAssignment(ts.factory.createIdentifier(constants_1.CNB_REQUEST_CONFIG_DATA), ts.factory.createIdentifier(bodyVarName)));
|
|
60
67
|
}
|
|
61
68
|
// 生成skills时,请求参数需要包含_originalParams,用于后处理时使用
|
|
62
69
|
if ((0, is_skills_1.isSkills)()) {
|
|
@@ -72,10 +79,11 @@ function generateFunctionContentASTNode(methodData, fetchSuccessResponse, fetchE
|
|
|
72
79
|
objectLiteralExpression.push(ts.factory.createPropertyAssignment(ts.factory.createIdentifier(constants_1.CNB_REQUEST_PATH_PARAM_ORIGIN_NAME), ts.factory.createObjectLiteralExpression(pathParams.map((p) => ts.factory.createShorthandPropertyAssignment(ts.factory.createIdentifier(p.name), undefined)), false)));
|
|
73
80
|
}
|
|
74
81
|
if (queryParams.length > 0) {
|
|
75
|
-
objectLiteralExpression.push(ts.factory.createPropertyAssignment(ts.factory.createIdentifier(constants_1.CNB_REQUEST_QUERY_PARAM_ORIGIN_NAME),
|
|
82
|
+
objectLiteralExpression.push(ts.factory.createPropertyAssignment(ts.factory.createIdentifier(constants_1.CNB_REQUEST_QUERY_PARAM_ORIGIN_NAME), (0, build_query_value_expression_1.buildQueryValueExpression)(queryVarName, pathParams, queryParams)));
|
|
76
83
|
}
|
|
77
84
|
if (bodyParams) {
|
|
78
|
-
|
|
85
|
+
const bodyVarName = (paramAliases === null || paramAliases === void 0 ? void 0 : paramAliases.bodyParamName) || bodyParams.name;
|
|
86
|
+
objectLiteralExpression.push(ts.factory.createPropertyAssignment(ts.factory.createIdentifier(constants_1.CNB_REQUEST_BODY_PARAM_ORIGIN_NAME), ts.factory.createIdentifier(bodyVarName)));
|
|
79
87
|
}
|
|
80
88
|
requestParams.push(ts.factory.createPropertyAssignment(ts.factory.createIdentifier(constants_1.CNB_REQUEST_ORIGIN_PARAM_NAME), ts.factory.createObjectLiteralExpression(objectLiteralExpression, true)));
|
|
81
89
|
}
|
|
@@ -42,8 +42,13 @@ const get_keyword_type_node_1 = require("../../utils/get-keyword-type-node");
|
|
|
42
42
|
const is_english_only_1 = require("../../utils/is-english-only");
|
|
43
43
|
const constants_1 = require("../../constants");
|
|
44
44
|
const md_array_type_content_ast_node_1 = require("../types/md-array-type-content-ast-node");
|
|
45
|
+
const compute_param_aliases_1 = require("../../utils/compute-param-aliases");
|
|
46
|
+
const query_path_alias_1 = require("../../utils/query-path-alias");
|
|
45
47
|
function generateFunctionParamsASTNode(methodData, pathParams, queryParams, bodyParams, defintionsMap) {
|
|
46
48
|
var _a;
|
|
49
|
+
// 计算保留变量名(query/req/options/axiosConfig)的安全别名,
|
|
50
|
+
// 避免与 path/body 参数重名导致 "Argument name clash" 等语法错误。
|
|
51
|
+
const paramAliases = (0, compute_param_aliases_1.computeParamAliases)(pathParams, bodyParams);
|
|
47
52
|
// request函数参数
|
|
48
53
|
const requestParamsASTNodes = [];
|
|
49
54
|
const requestInterfaceASTNodes = [];
|
|
@@ -60,8 +65,29 @@ function generateFunctionParamsASTNode(methodData, pathParams, queryParams, body
|
|
|
60
65
|
const paramsInterfaceName = `${(0, format_interface_name_1.formatInterfaceName)(methodData.exportName)}Params`;
|
|
61
66
|
const paramMap = {};
|
|
62
67
|
const firstParams = [];
|
|
63
|
-
//
|
|
64
|
-
|
|
68
|
+
// path 参数与 query 参数同名时(如 /{repo}/-/knowledge/base/query/{query} 同时带 ?query=xxx):
|
|
69
|
+
// 两者语义不同,需要同时在类型里表达:
|
|
70
|
+
// - path 字段沿用 swagger 的原名(URL 模板里的字面占位,必须保持原名);
|
|
71
|
+
// - query 字段通过 `q_<name>` 别名字段在 interface 中暴露,
|
|
72
|
+
// 运行时再通过计算属性把别名映射回 swagger 的原始 key(见 generate-function-content-ast-node.ts)。
|
|
73
|
+
const pathParamNameSet = new Set(pathParams.map((p) => p.name));
|
|
74
|
+
[
|
|
75
|
+
...pathParams,
|
|
76
|
+
...queryParams.map((q) => {
|
|
77
|
+
if (!pathParamNameSet.has(q.name))
|
|
78
|
+
return q;
|
|
79
|
+
// 同名冲突:拷贝 query 参数并改名为 `q_<name>`,以便在 interface 中作为独立字段出现
|
|
80
|
+
const aliasName = (0, query_path_alias_1.getQueryPathAliasName)(q.name);
|
|
81
|
+
const aliasDesc = q.description
|
|
82
|
+
? `${q.description}(query string 参数;该路由 path 也存在同名字段,为避免冲突此处使用别名 ${aliasName})`
|
|
83
|
+
: `query string 参数;该路由 path 也存在同名字段,为避免冲突此处使用别名 ${aliasName}`;
|
|
84
|
+
return {
|
|
85
|
+
...q,
|
|
86
|
+
name: aliasName,
|
|
87
|
+
description: aliasDesc,
|
|
88
|
+
};
|
|
89
|
+
}),
|
|
90
|
+
].map((item) => {
|
|
65
91
|
paramMap[item.name] = item;
|
|
66
92
|
});
|
|
67
93
|
Object.keys(paramMap).forEach((key) => {
|
|
@@ -146,10 +172,21 @@ function generateFunctionParamsASTNode(methodData, pathParams, queryParams, body
|
|
|
146
172
|
const pathParamsBindingElements = pathParams.map((item) => {
|
|
147
173
|
return ts.factory.createBindingElement(undefined, undefined, ts.factory.createIdentifier(item.name), undefined);
|
|
148
174
|
});
|
|
175
|
+
// 同名 path/query 别名字段(如 q_query),需要单独从对象中解构出来,
|
|
176
|
+
// 避免被 `...__query` 吞掉后再做重映射(保留了可读性,并与 generate-function-content-ast-node 联动)。
|
|
177
|
+
const aliasQueryBindingElements = (queryParams || [])
|
|
178
|
+
.filter((q) => pathParamNameSet.has(q.name))
|
|
179
|
+
.map((q) => {
|
|
180
|
+
const aliasName = (0, query_path_alias_1.getQueryPathAliasName)(q.name);
|
|
181
|
+
return ts.factory.createBindingElement(undefined, undefined, ts.factory.createIdentifier(aliasName), undefined);
|
|
182
|
+
});
|
|
149
183
|
// 创建解构参数
|
|
150
|
-
const deconstructionParams = [
|
|
184
|
+
const deconstructionParams = [
|
|
185
|
+
...pathParamsBindingElements,
|
|
186
|
+
...aliasQueryBindingElements,
|
|
187
|
+
];
|
|
151
188
|
if (queryParams && queryParams.length > 0) {
|
|
152
|
-
deconstructionParams.push(ts.factory.createBindingElement(ts.factory.createToken(ts.SyntaxKind.DotDotDotToken), undefined, ts.factory.createIdentifier(
|
|
189
|
+
deconstructionParams.push(ts.factory.createBindingElement(ts.factory.createToken(ts.SyntaxKind.DotDotDotToken), undefined, ts.factory.createIdentifier(paramAliases.queryName), undefined));
|
|
153
190
|
}
|
|
154
191
|
requestParamsASTNodes.push(ts.factory.createParameterDeclaration(undefined, undefined, ts.factory.createObjectBindingPattern(deconstructionParams), undefined, ts.factory.createTypeReferenceNode(ts.factory.createIdentifier(paramsInterfaceName), undefined), undefined));
|
|
155
192
|
// redux action参数定义
|
|
@@ -176,6 +213,11 @@ function generateFunctionParamsASTNode(methodData, pathParams, queryParams, body
|
|
|
176
213
|
// eslint-disable-next-line @typescript-eslint/prefer-for-of
|
|
177
214
|
for (let i = 0; i < allRequestParams.length; i++) {
|
|
178
215
|
const param = allRequestParams[i];
|
|
216
|
+
// 仅 body 参数需要用别名替换标识符;单个 path 参数保留原名(必须等于 URL 占位符)
|
|
217
|
+
const isBodyParam = bodyParams !== null && param === bodyParams;
|
|
218
|
+
const effectiveParamName = isBodyParam && paramAliases.bodyParamName
|
|
219
|
+
? paramAliases.bodyParamName
|
|
220
|
+
: param.name;
|
|
179
221
|
if (param === null || param === void 0 ? void 0 : param.schema) {
|
|
180
222
|
// 多维数组处理
|
|
181
223
|
if (param.schema.type &&
|
|
@@ -192,13 +234,13 @@ function generateFunctionParamsASTNode(methodData, pathParams, queryParams, body
|
|
|
192
234
|
requestInterfaceASTNodes.push(ts.factory.createExpressionStatement(ts.factory.createStringLiteral(`<$${paramsBodyName}$>`)));
|
|
193
235
|
// 创建interface主体
|
|
194
236
|
requestInterfaceASTNodes.push(ts.factory.createTypeAliasDeclaration([ts.factory.createToken(ts.SyntaxKind.ExportKeyword)], ts.factory.createIdentifier(paramsBodyName), undefined, returnType));
|
|
195
|
-
requestParamsASTNodes.push(ts.factory.createParameterDeclaration(undefined, undefined, ts.factory.createIdentifier(
|
|
237
|
+
requestParamsASTNodes.push(ts.factory.createParameterDeclaration(undefined, undefined, ts.factory.createIdentifier(effectiveParamName), undefined, ts.factory.createTypeReferenceNode(ts.factory.createIdentifier(paramsBodyName), undefined), undefined));
|
|
196
238
|
if (typeDependencies && typeDependencies.size > 0) {
|
|
197
239
|
dependencies = new Set([...dependencies, ...typeDependencies]);
|
|
198
240
|
}
|
|
199
241
|
// redux action参数定义
|
|
200
242
|
reduxActionMap.paramsSequence.push({
|
|
201
|
-
param:
|
|
243
|
+
param: effectiveParamName,
|
|
202
244
|
typeLiteral: paramsBodyName,
|
|
203
245
|
typeLiteralLabel: constants_1.TypeLiteralLabel.Quoted,
|
|
204
246
|
required: param.required,
|
|
@@ -207,7 +249,14 @@ function generateFunctionParamsASTNode(methodData, pathParams, queryParams, body
|
|
|
207
249
|
}
|
|
208
250
|
else {
|
|
209
251
|
const [imports, astNode, paramsSequenceItem] = (0, generate_function_refs_type_params_ast_node_1.generateFunctionReferenceTypeDataParamsASTNode)(param, defintionsMap);
|
|
210
|
-
astNode &&
|
|
252
|
+
if (astNode && isBodyParam && effectiveParamName !== param.name) {
|
|
253
|
+
// 替换 body 参数的标识符为别名
|
|
254
|
+
requestParamsASTNodes.push(ts.factory.updateParameterDeclaration(astNode, astNode.modifiers, astNode.dotDotDotToken, ts.factory.createIdentifier(effectiveParamName), astNode.questionToken, astNode.type, astNode.initializer));
|
|
255
|
+
paramsSequenceItem.param = effectiveParamName;
|
|
256
|
+
}
|
|
257
|
+
else {
|
|
258
|
+
astNode && requestParamsASTNodes.push(astNode);
|
|
259
|
+
}
|
|
211
260
|
if (imports && imports.size > 0) {
|
|
212
261
|
dependencies = new Set([...dependencies, ...imports]);
|
|
213
262
|
}
|
|
@@ -216,11 +265,14 @@ function generateFunctionParamsASTNode(methodData, pathParams, queryParams, body
|
|
|
216
265
|
}
|
|
217
266
|
}
|
|
218
267
|
else {
|
|
219
|
-
|
|
268
|
+
let normalParamsASTNode = (0, generate_function_normal_params_ast_node_1.generateFunctionNormalParamsASTNode)(param);
|
|
269
|
+
if (isBodyParam && effectiveParamName !== param.name) {
|
|
270
|
+
normalParamsASTNode = ts.factory.updateParameterDeclaration(normalParamsASTNode, normalParamsASTNode.modifiers, normalParamsASTNode.dotDotDotToken, ts.factory.createIdentifier(effectiveParamName), normalParamsASTNode.questionToken, normalParamsASTNode.type, normalParamsASTNode.initializer);
|
|
271
|
+
}
|
|
220
272
|
requestParamsASTNodes.push(normalParamsASTNode);
|
|
221
273
|
// redux action参数定义
|
|
222
274
|
reduxActionMap.paramsSequence.push({
|
|
223
|
-
param:
|
|
275
|
+
param: effectiveParamName,
|
|
224
276
|
typeLiteral: param.type || null,
|
|
225
277
|
typeLiteralLabel: constants_1.TypeLiteralLabel.Native,
|
|
226
278
|
required: param.required,
|
|
@@ -229,9 +281,13 @@ function generateFunctionParamsASTNode(methodData, pathParams, queryParams, body
|
|
|
229
281
|
}
|
|
230
282
|
// 添加requestConfig
|
|
231
283
|
requestParamsASTNodes.push(ts.factory.createParameterDeclaration(undefined, undefined, ts.factory.createObjectBindingPattern([
|
|
232
|
-
ts.factory.createBindingElement(undefined,
|
|
233
|
-
|
|
234
|
-
|
|
284
|
+
ts.factory.createBindingElement(undefined, paramAliases.reqName === constants_1.CNB_REQUEST_PARAM_REQ_NAME
|
|
285
|
+
? undefined
|
|
286
|
+
: ts.factory.createIdentifier(constants_1.CNB_REQUEST_PARAM_REQ_NAME), ts.factory.createIdentifier(paramAliases.reqName), undefined),
|
|
287
|
+
ts.factory.createBindingElement(undefined, paramAliases.optionsName === constants_1.CNB_REQUEST_PARAM_OPTIONS_NAME
|
|
288
|
+
? undefined
|
|
289
|
+
: ts.factory.createIdentifier(constants_1.CNB_REQUEST_PARAM_OPTIONS_NAME), ts.factory.createIdentifier(paramAliases.optionsName), undefined),
|
|
290
|
+
ts.factory.createBindingElement(ts.factory.createToken(ts.SyntaxKind.DotDotDotToken), undefined, ts.factory.createIdentifier(paramAliases.axiosConfigName), undefined),
|
|
235
291
|
]), undefined, ts.factory.createTypeReferenceNode(ts.factory.createIdentifier(constants_1.CNB_REQUEST_PARAM_TYPE_NAME), undefined), ts.factory.createObjectLiteralExpression([], false)));
|
|
236
292
|
// redux action参数定义
|
|
237
293
|
reduxActionMap.paramsSequence.push({
|
|
@@ -245,5 +301,6 @@ function generateFunctionParamsASTNode(methodData, pathParams, queryParams, body
|
|
|
245
301
|
reduxActionMap,
|
|
246
302
|
dependencies,
|
|
247
303
|
requestInterfaceASTNodes,
|
|
304
|
+
paramAliases,
|
|
248
305
|
};
|
|
249
306
|
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.computeParamAliases = computeParamAliases;
|
|
4
|
+
const constants_1 = require("../constants");
|
|
5
|
+
const RESERVED_DEFAULTS = {
|
|
6
|
+
queryName: constants_1.CNB_REQUEST_PARAM_QUERY_NAME,
|
|
7
|
+
reqName: constants_1.CNB_REQUEST_PARAM_REQ_NAME,
|
|
8
|
+
optionsName: constants_1.CNB_REQUEST_PARAM_OPTIONS_NAME,
|
|
9
|
+
axiosConfigName: constants_1.CNB_REQUEST_AXIOS_CONFIG_NAME,
|
|
10
|
+
};
|
|
11
|
+
/**
|
|
12
|
+
* 给定一个目标变量名 base 和已被占用的名字集合 used,
|
|
13
|
+
* 返回不与 used 冲突的安全名(依次尝试 _base、__base 等)。
|
|
14
|
+
*/
|
|
15
|
+
function pickUniqueName(base, used) {
|
|
16
|
+
if (!used.has(base))
|
|
17
|
+
return base;
|
|
18
|
+
let candidate = `_${base}`;
|
|
19
|
+
while (used.has(candidate)) {
|
|
20
|
+
candidate = `_${candidate}`;
|
|
21
|
+
}
|
|
22
|
+
return candidate;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* 根据 path/body 参数名计算需要使用的保留变量名别名,避免命名冲突。
|
|
26
|
+
*
|
|
27
|
+
* 注意 path 参数名是"硬约束"(必须等于 URL 模板中的占位符名),
|
|
28
|
+
* 因此优先占用 path 参数名,再依次给 body / 保留变量分配安全名。
|
|
29
|
+
*/
|
|
30
|
+
function computeParamAliases(pathParams = [], bodyParams = null) {
|
|
31
|
+
var _a;
|
|
32
|
+
const used = new Set();
|
|
33
|
+
pathParams.forEach((p) => {
|
|
34
|
+
if (p === null || p === void 0 ? void 0 : p.name)
|
|
35
|
+
used.add(p.name);
|
|
36
|
+
});
|
|
37
|
+
// body 参数名:与 path 参数 / 保留名冲突时改名
|
|
38
|
+
const originalBodyName = (_a = bodyParams === null || bodyParams === void 0 ? void 0 : bodyParams.name) !== null && _a !== void 0 ? _a : '';
|
|
39
|
+
const bodyParamName = originalBodyName
|
|
40
|
+
? pickUniqueName(originalBodyName, used)
|
|
41
|
+
: '';
|
|
42
|
+
if (bodyParamName)
|
|
43
|
+
used.add(bodyParamName);
|
|
44
|
+
const aliases = {
|
|
45
|
+
...RESERVED_DEFAULTS,
|
|
46
|
+
bodyParamName,
|
|
47
|
+
};
|
|
48
|
+
// 保留变量按顺序分配,先分配出去的别名也要避免后续被占用
|
|
49
|
+
['queryName', 'reqName', 'optionsName', 'axiosConfigName'].forEach((key) => {
|
|
50
|
+
const safe = pickUniqueName(RESERVED_DEFAULTS[key], used);
|
|
51
|
+
aliases[key] = safe;
|
|
52
|
+
used.add(safe);
|
|
53
|
+
});
|
|
54
|
+
return aliases;
|
|
55
|
+
}
|
|
@@ -36,20 +36,25 @@ function flattenToolOptions(toolInfo) {
|
|
|
36
36
|
});
|
|
37
37
|
}
|
|
38
38
|
}
|
|
39
|
-
// query
|
|
39
|
+
// query 参数:与 path 同名时,optKey 前缀 q- 以避免 Commander 选项名冲突
|
|
40
40
|
if (queryDef) {
|
|
41
|
+
const pathKeys = new Set(pathDef ? Object.keys(pathDef) : []);
|
|
41
42
|
for (const [key, param] of Object.entries(queryDef)) {
|
|
42
43
|
const valuePlaceholder = (0, option_value_flag_1.optionValueFlag)(param.type);
|
|
43
44
|
const rawDesc = (0, trim_summary_1.trimSummary)(param.description || '');
|
|
44
45
|
const desc = param.required ? `[必填] ${rawDesc}` : rawDesc;
|
|
46
|
+
const conflict = pathKeys.has(key);
|
|
47
|
+
const optKey = conflict ? `q-${key}` : key;
|
|
45
48
|
const opt = {
|
|
46
|
-
optKey
|
|
49
|
+
optKey,
|
|
47
50
|
valuePlaceholder,
|
|
48
51
|
description: desc,
|
|
49
52
|
required: !!param.required,
|
|
50
53
|
isArray: false,
|
|
51
54
|
source: 'query',
|
|
52
55
|
};
|
|
56
|
+
if (conflict)
|
|
57
|
+
opt.originalName = key;
|
|
53
58
|
if (param.enum)
|
|
54
59
|
opt.choices = param.enum;
|
|
55
60
|
if (param.default !== undefined)
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.QUERY_PATH_ALIAS_PREFIX = void 0;
|
|
4
|
+
exports.getQueryPathAliasName = getQueryPathAliasName;
|
|
5
|
+
exports.collectQueryPathAliasMap = collectQueryPathAliasMap;
|
|
6
|
+
/**
|
|
7
|
+
* path 参数与 query 参数同名时,query 字段在生成的 interface / 函数解构里
|
|
8
|
+
* 统一使用的别名前缀。
|
|
9
|
+
*
|
|
10
|
+
* 例如 swagger 中 `/{repo}/-/knowledge/base/query/{query}` 同时存在
|
|
11
|
+
* { name: 'query', in: 'path' }
|
|
12
|
+
* { name: 'query', in: 'query' }
|
|
13
|
+
* 两个语义不同的参数。我们保留 path 字段的原名(URL 占位),
|
|
14
|
+
* 而 query 字段改名为 `q_query`,运行时再用计算属性映射回原始 key。
|
|
15
|
+
*/
|
|
16
|
+
exports.QUERY_PATH_ALIAS_PREFIX = 'q_';
|
|
17
|
+
/**
|
|
18
|
+
* 给定 swagger 原始 query 参数名,返回它在生成代码里的别名。
|
|
19
|
+
*/
|
|
20
|
+
function getQueryPathAliasName(originalName) {
|
|
21
|
+
return `${exports.QUERY_PATH_ALIAS_PREFIX}${originalName}`;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* 收集 query 参数中名字与 path 参数同名的项,返回 `原始名 -> 别名` 的映射。
|
|
25
|
+
* 没有冲突时返回空 Map。
|
|
26
|
+
*/
|
|
27
|
+
function collectQueryPathAliasMap(pathParams = [], queryParams = []) {
|
|
28
|
+
const map = new Map();
|
|
29
|
+
if (!pathParams.length || !queryParams.length)
|
|
30
|
+
return map;
|
|
31
|
+
const pathNameSet = new Set(pathParams.map((p) => p.name));
|
|
32
|
+
for (const q of queryParams) {
|
|
33
|
+
if (pathNameSet.has(q.name)) {
|
|
34
|
+
map.set(q.name, getQueryPathAliasName(q.name));
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
return map;
|
|
38
|
+
}
|
package/client/index.ts
CHANGED
|
@@ -7,6 +7,7 @@ import { registerFallbackAction } from './lib/register-fallback';
|
|
|
7
7
|
import { registerLoginCommand } from './lib/login';
|
|
8
8
|
import { registerLogoutCommand } from './lib/logout';
|
|
9
9
|
import { registerStatusCommand } from './lib/status';
|
|
10
|
+
import { registerGitCredentialCommand } from './lib/git-credential';
|
|
10
11
|
|
|
11
12
|
// ============================================================
|
|
12
13
|
// Commander 程序定义
|
|
@@ -30,6 +31,7 @@ program
|
|
|
30
31
|
registerLoginCommand(program);
|
|
31
32
|
registerLogoutCommand(program);
|
|
32
33
|
registerStatusCommand(program);
|
|
34
|
+
registerGitCredentialCommand(program);
|
|
33
35
|
registerModuleCommands(program);
|
|
34
36
|
registerFallbackAction(program);
|
|
35
37
|
|
|
@@ -48,8 +48,7 @@ export function formatParams(
|
|
|
48
48
|
const bodyProps = paramDefs.body?.schema?.properties || {};
|
|
49
49
|
|
|
50
50
|
const reservedKeys = new Set([
|
|
51
|
-
'module', 'tool', 'help', 'short', 'verbose',
|
|
52
|
-
'path', 'query', 'data', 'h', 'v',
|
|
51
|
+
'module', 'tool', 'help', 'short', 'verbose', 'h', 'v',
|
|
53
52
|
]);
|
|
54
53
|
|
|
55
54
|
// 收集 array<object> 展开字段的临时存储:{ arrayName: { propName: string[] } }
|
|
@@ -97,6 +96,18 @@ export function formatParams(
|
|
|
97
96
|
// 归入 path
|
|
98
97
|
if (!formatted.path) formatted.path = {};
|
|
99
98
|
formatted.path[key] = value;
|
|
99
|
+
// path 与 query 同名时,query 参数通过 q- 前缀传入(见下方 q- 分支);
|
|
100
|
+
// 但若调用方直接用 --<name> 传了值(旧习惯/兼容),且 queryDef 里也有同名项,
|
|
101
|
+
// 则同时回填到 query,保证 URL 查询参数也被带上。
|
|
102
|
+
if (queryDef[key]) {
|
|
103
|
+
if (!formatted.query) formatted.query = {};
|
|
104
|
+
const paramType = queryDef[key].type;
|
|
105
|
+
if (paramType === 'number' && typeof value === 'string' && !isNaN(Number(value))) {
|
|
106
|
+
formatted.query[key] = Number(value);
|
|
107
|
+
} else {
|
|
108
|
+
formatted.query[key] = value;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
100
111
|
} else if (queryDef[key]) {
|
|
101
112
|
// 归入 query
|
|
102
113
|
if (!formatted.query) formatted.query = {};
|
|
@@ -106,6 +117,17 @@ export function formatParams(
|
|
|
106
117
|
} else {
|
|
107
118
|
formatted.query[key] = value;
|
|
108
119
|
}
|
|
120
|
+
} else if ((key.startsWith('q-') || key.startsWith('q_'))
|
|
121
|
+
&& queryDef[key.replace(/^q[-_]/, '')]) {
|
|
122
|
+
// query 参数与 path 同名时,CLI 层以 q- 前缀区分,这里还原为真实 query key
|
|
123
|
+
const originalKey = key.replace(/^q[-_]/, '');
|
|
124
|
+
if (!formatted.query) formatted.query = {};
|
|
125
|
+
const paramType = queryDef[originalKey].type;
|
|
126
|
+
if (paramType === 'number' && typeof value === 'string' && !isNaN(Number(value))) {
|
|
127
|
+
formatted.query[originalKey] = Number(value);
|
|
128
|
+
} else {
|
|
129
|
+
formatted.query[originalKey] = value;
|
|
130
|
+
}
|
|
109
131
|
} else if (bodyProps[key]) {
|
|
110
132
|
// body 字段(无冲突,直接用原 key)
|
|
111
133
|
if (!formatted.data) formatted.data = {};
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import { resolveToken } from '../utils/resolve-token';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* 用作 git credential helper。
|
|
6
|
+
*
|
|
7
|
+
* 在 .gitconfig 中配置:
|
|
8
|
+
* [credential]
|
|
9
|
+
* helper = "<cli> git-credential"
|
|
10
|
+
* useHttpPath = true
|
|
11
|
+
*
|
|
12
|
+
* git 在执行需要凭据的操作时,会以 `get`、`store`、`erase` 之一作为 action 调用本命令,
|
|
13
|
+
* 并通过 stdin 传入若干 `key=value` 形式的字段(protocol/host/path 等)。
|
|
14
|
+
* 我们仅处理 `get`:根据 host 白名单校验后,从后端获取凭据,按 git-credential
|
|
15
|
+
* 协议把 `username=...` / `password=...` 写到 stdout;`store` / `erase` 直接忽略。
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
interface GitCredentialInput {
|
|
19
|
+
protocol?: string;
|
|
20
|
+
host?: string;
|
|
21
|
+
path?: string;
|
|
22
|
+
[key: string]: string | undefined;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
interface Credential {
|
|
26
|
+
username: string;
|
|
27
|
+
password: string;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const ALLOWED_HOSTS = ['cnb.cool', 'cnb.woa.com'];
|
|
31
|
+
|
|
32
|
+
/** git-credential get 等待 stdin 的最长时间,避免 git 无限挂起。 */
|
|
33
|
+
const GET_INPUT_TIMEOUT_MS = 8000;
|
|
34
|
+
|
|
35
|
+
export function registerGitCredentialCommand(program: Command): void {
|
|
36
|
+
program
|
|
37
|
+
.command('git-credential <action>')
|
|
38
|
+
.description('作为 git credential helper,向 git 提供 CNB 凭据(仅供 git 内部调用)')
|
|
39
|
+
.allowUnknownOption()
|
|
40
|
+
.allowExcessArguments()
|
|
41
|
+
.helpOption('-h, --help', '显示帮助文档')
|
|
42
|
+
.action(async (action: string, _opts, cmd: Command) => {
|
|
43
|
+
// 透传给用户:剩余的位置参数都视为自定义参数
|
|
44
|
+
const customArgs = cmd.args.slice(0, -1);
|
|
45
|
+
if (customArgs.length > 0) {
|
|
46
|
+
console.error(`[git-credential]: custom args: ${customArgs.join(' ')}`);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// store / erase:保持与原 helper 一致,忽略并退出 0
|
|
50
|
+
if (action === 'store' || action === 'erase') {
|
|
51
|
+
console.error(`[git-credential]: ${action} ignored`);
|
|
52
|
+
process.exit(0);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if (action !== 'get') {
|
|
56
|
+
console.error(`[git-credential]: unknown action: ${action}`);
|
|
57
|
+
process.exit(1);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// get:从 stdin 读取字段并输出凭据
|
|
61
|
+
try {
|
|
62
|
+
const input = await readStdin(GET_INPUT_TIMEOUT_MS);
|
|
63
|
+
const data = parseText(input, '=') as GitCredentialInput;
|
|
64
|
+
normalizePath(data);
|
|
65
|
+
|
|
66
|
+
await auth(data);
|
|
67
|
+
process.exit(0);
|
|
68
|
+
} catch (err: any) {
|
|
69
|
+
console.error(err?.message ? err.message : String(err));
|
|
70
|
+
process.exit(2);
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* 校验白名单并向后端获取凭据,按照 git-credential 协议输出 username/password。
|
|
77
|
+
*/
|
|
78
|
+
async function auth(data: GitCredentialInput): Promise<void> {
|
|
79
|
+
const { host } = data;
|
|
80
|
+
console.error(
|
|
81
|
+
`[git-credential]: for ${data.protocol}://${data.host}/${data.path ?? ''}`,
|
|
82
|
+
);
|
|
83
|
+
|
|
84
|
+
// 必须校验域名,否则传入第三方域名就会被盗密码
|
|
85
|
+
if (!host || !ALLOWED_HOSTS.includes(host)) {
|
|
86
|
+
throw new Error(`unknown host: ${host}`);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const credential = await fetchCredential(data);
|
|
90
|
+
|
|
91
|
+
// git-credential 协议:通过 stdout 输出 key=value
|
|
92
|
+
process.stdout.write(`username=${credential.username}\n`);
|
|
93
|
+
process.stdout.write(`password=${credential.password}\n`);
|
|
94
|
+
|
|
95
|
+
console.error('[git-credential]: done');
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
async function fetchCredential(_data: GitCredentialInput): Promise<Credential> {
|
|
99
|
+
// 使用 resolveToken 获取 access_token 作为 password,username 固定为 cnb
|
|
100
|
+
const token = await resolveToken();
|
|
101
|
+
return {
|
|
102
|
+
username: 'cnb',
|
|
103
|
+
password: token,
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* git-credential 协议中 path 通常包含仓库目录,需要规范化。
|
|
109
|
+
*/
|
|
110
|
+
function normalizePath(data: GitCredentialInput): void {
|
|
111
|
+
if (!data.path) return;
|
|
112
|
+
if (data.path.endsWith('.git')) {
|
|
113
|
+
data.path = data.path.slice(0, -4);
|
|
114
|
+
} else if (data.path.endsWith('.git/info/lfs')) {
|
|
115
|
+
data.path = data.path.slice(0, -13);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* 从 stdin 收集所有数据。
|
|
121
|
+
*
|
|
122
|
+
* 同原始 helper 一致:必须设置超时,否则 git 在拿不到响应时会让用户输入账号密码,
|
|
123
|
+
* 进而把进程挂起。同时支持单字节回车(0x0a)作为提前结束的信号。
|
|
124
|
+
*/
|
|
125
|
+
function readStdin(timeoutMs: number): Promise<string> {
|
|
126
|
+
return new Promise((resolve, reject) => {
|
|
127
|
+
const buff: Buffer[] = [];
|
|
128
|
+
let settled = false;
|
|
129
|
+
|
|
130
|
+
const finish = (): void => {
|
|
131
|
+
if (settled) return;
|
|
132
|
+
settled = true;
|
|
133
|
+
clearTimeout(timer);
|
|
134
|
+
resolve(Buffer.concat(buff).toString('utf-8'));
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
const timer = setTimeout(() => {
|
|
138
|
+
if (settled) return;
|
|
139
|
+
settled = true;
|
|
140
|
+
console.error('[git-credential]: timeout occurred');
|
|
141
|
+
reject(new Error('stdin timeout'));
|
|
142
|
+
}, timeoutMs);
|
|
143
|
+
|
|
144
|
+
process.stdin.on('data', (chunk: Buffer) => {
|
|
145
|
+
buff.push(chunk);
|
|
146
|
+
// git 会在最后发送一个空行(仅一个 \n)作为结束标志
|
|
147
|
+
if (chunk.length === 1 && chunk[0] === 0x0a) {
|
|
148
|
+
finish();
|
|
149
|
+
}
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
process.stdin.on('end', () => {
|
|
153
|
+
finish();
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
process.stdin.on('error', (err) => {
|
|
157
|
+
if (settled) return;
|
|
158
|
+
settled = true;
|
|
159
|
+
clearTimeout(timer);
|
|
160
|
+
reject(err);
|
|
161
|
+
});
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* 把 git 通过 stdin 传入的多行 key=value 文本解析为对象。
|
|
167
|
+
*/
|
|
168
|
+
function parseText(content: string, separator: string): Record<string, string> {
|
|
169
|
+
return content
|
|
170
|
+
.split(/(?:\r\n|\r|\n)/)
|
|
171
|
+
.filter(Boolean)
|
|
172
|
+
.map((line) => {
|
|
173
|
+
const idx = line.indexOf(separator);
|
|
174
|
+
const key = idx >= 0 ? line.substring(0, idx).trim() : line.trim();
|
|
175
|
+
const value = idx >= 0 ? line.substring(idx + 1).trim() : '';
|
|
176
|
+
return { key, value };
|
|
177
|
+
})
|
|
178
|
+
.reduce<Record<string, string>>((acc, { key, value }) => {
|
|
179
|
+
if (key) acc[key] = value;
|
|
180
|
+
return acc;
|
|
181
|
+
}, {});
|
|
182
|
+
}
|
|
@@ -10,7 +10,7 @@ export async function refreshAccessToken(store: TokenStore): Promise<string> {
|
|
|
10
10
|
throw new Error('no refresh_token');
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
-
const platformURL = store.platform_url || 'https://cnb
|
|
13
|
+
const platformURL = store.platform_url || 'https://cnb.cool';
|
|
14
14
|
const clientID = store.client_id || process.env.OAUTH2_CLIENT_ID || 'cnb_cli';
|
|
15
15
|
const tokenURL = `${platformURL.replace(/\/+$/, '')}/oauth2/token`;
|
|
16
16
|
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { getTokenEnvKey } from './get-token-env-key';
|
|
2
|
+
import { isWorkBuddySandbox } from './is-workbuddy-sandbox';
|
|
1
3
|
import { loadToken } from './load-token';
|
|
2
4
|
import { refreshAccessToken } from './refresh-token';
|
|
3
5
|
|
|
@@ -9,6 +11,38 @@ import { refreshAccessToken } from './refresh-token';
|
|
|
9
11
|
* 刷新失败或无 refresh_token 时终止进程并提示用户重新登录。
|
|
10
12
|
*/
|
|
11
13
|
export async function resolveToken(): Promise<string> {
|
|
14
|
+
// WorkBuddy 沙箱环境,从远程接口获取 token
|
|
15
|
+
if (isWorkBuddySandbox()) {
|
|
16
|
+
const tokenURL = process.env[getTokenEnvKey()];
|
|
17
|
+
if (!tokenURL) {
|
|
18
|
+
console.error(`未找到环境变量 ${getTokenEnvKey()},无法在 WorkBuddy 沙箱环境中获取 token。`);
|
|
19
|
+
process.exit(1);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
try {
|
|
23
|
+
const resp = await fetch(tokenURL, {
|
|
24
|
+
method: 'GET',
|
|
25
|
+
headers: { Accept: 'application/json' },
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
if (!resp.ok) {
|
|
29
|
+
console.error(`从 WorkBuddy 获取 token 失败,HTTP 状态码: ${resp.status} ${resp.statusText}`);
|
|
30
|
+
process.exit(1);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const body: any = await resp.json();
|
|
34
|
+
if (body?.code !== 0 || !body?.data?.access_token) {
|
|
35
|
+
console.error(`从 WorkBuddy 获取 token 失败,响应内容异常: ${JSON.stringify(body)}`);
|
|
36
|
+
process.exit(1);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return body.data.access_token as string;
|
|
40
|
+
} catch (err: any) {
|
|
41
|
+
console.error(`从 WorkBuddy 获取 token 失败: ${err?.message || err}`);
|
|
42
|
+
process.exit(1);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
12
46
|
// 环境变量优先 CodeBuddy
|
|
13
47
|
if (process.env.CNB_TOKEN_FOR_CODEBUDDY) {
|
|
14
48
|
if (!process.env.CNB_NPC_SLUG && process.env.CNB_NPC_NAME === 'CodeBuddy') {
|