Skip to content

Commit 84383f1

Browse files
committed
feat(bootstrap): 支持使用AK/SK生成CLI访问令牌并重构用户创建流程
- 新增命令行参数,支持通过--access-key-id和--access-key-secret传入阿里云凭证 - 集成generateCLIAccessToken接口实现AK/SK转CLI访问令牌 - 调用BailianControl OpenAPI完成用户创建,实现与控制台用户同步 - 新增获取工作空间列表接口,解析agentId以便权限管理 - 调用ResetPolicies4Agent接口完成用户权限授权 - 优化命令步骤日志输出,更详细展示执行流程 - 将轮询次数由120次减少至20次,缩短等待激活时长 - 移除旧有测试代码,适配新实现 - core包新增bailian-control客户端支持对应OpenAPI调用 - client模块添加openApiJson通用方法支持ROA风格接口调用 - console模块导出类型扩展,涵盖新的网关目标类型 - 修改acs请求签名类型支持number类型参数,增强签名兼容性
1 parent e0f3d45 commit 84383f1

8 files changed

Lines changed: 336 additions & 209 deletions

File tree

packages/commands/src/commands/bootstrap/index.ts

Lines changed: 151 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,47 @@
1-
import { defineCommand, detectOutputFormat, BailianError, ExitCode } from "bailian-cli-core";
1+
import {
2+
defineCommand,
3+
detectOutputFormat,
4+
BailianError,
5+
ExitCode,
6+
generateCLIAccessToken,
7+
callConsoleGateway,
8+
effectiveConsoleGatewayConfig,
9+
createBailianControlUser,
10+
listBailianControlWorkspaces,
11+
resetBailianControlPolicies4Agent,
12+
type ConsoleGatewayTarget,
13+
type FlagsDef,
14+
} from "bailian-cli-core";
215
import { emitResult, emitBare } from "bailian-cli-runtime";
316

417
const API = {
518
loginInfo: "zeldaEasy.cornerstone-portal.cs-console.loginInfo",
619
initSpace: "zeldaEasy.bailian-dash-workspace.space.initSpace",
7-
createUser: "zeldaEasy.bailian-dash-workspace.account.createUser",
820
queryBuyResult: "zeldaEasy.bailian-commerce.bill.queryBuyPostpaidResult",
921
commodityOrderInfo: "zeldaEasy.bailian-commerce.bill.postpaidCommodityOrderInfo",
1022
buyCommodity: "zeldaEasy.bailian-commerce.bill.buyPostpaidCommodity",
1123
} as const;
1224

25+
const FLAGS = {
26+
accessKeyId: {
27+
type: "string",
28+
valueHint: "<id>",
29+
description: "Alibaba Cloud Access Key ID",
30+
},
31+
accessKeySecret: {
32+
type: "string",
33+
valueHint: "<secret>",
34+
description: "Alibaba Cloud Access Key Secret",
35+
},
36+
securityToken: {
37+
type: "string",
38+
valueHint: "<token>",
39+
description: "Alibaba Cloud STS Security Token (optional)",
40+
},
41+
} satisfies FlagsDef;
42+
1343
const POLL_INTERVAL_MS = 1000;
14-
const MAX_POLL_ATTEMPTS = 120;
44+
const MAX_POLL_ATTEMPTS = 20;
1545

1646
function sleep(ms: number): Promise<void> {
1747
return new Promise((resolve) => setTimeout(resolve, ms));
@@ -21,25 +51,44 @@ function extractData(resp: any): any {
2151
return resp?.data?.DataV2?.data?.data;
2252
}
2353

54+
/**
55+
* Resolve the agent id from a ListWorkspaces response. Workspaces live under
56+
* `data.data`; the agent id is the workspace's `tenantId`. Prefer the default
57+
* workspace (`defaultAgent`), else fall back to the first one.
58+
*/
59+
function extractAgentId(resp: any): number | undefined {
60+
const workspaces = resp?.data?.data;
61+
if (!Array.isArray(workspaces) || workspaces.length === 0) return undefined;
62+
const chosen = workspaces.find((workspace) => workspace?.defaultAgent === true) ?? workspaces[0];
63+
const tenantId = chosen?.tenantId;
64+
const agentId = typeof tenantId === "string" ? Number(tenantId) : tenantId;
65+
return typeof agentId === "number" && Number.isFinite(agentId) ? agentId : undefined;
66+
}
67+
2468
interface CommodityItem {
2569
commodityCode?: string;
2670
status?: number;
2771
}
2872

2973
export default defineCommand({
3074
description: "Initialize Bailian workspace and activate postpaid services",
31-
auth: "console",
32-
usageArgs: "",
33-
flags: {},
34-
exampleArgs: [],
75+
auth: "none",
76+
usageArgs: "--access-key-id <id> --access-key-secret <secret> [--security-token <token>]",
77+
flags: FLAGS,
78+
exampleArgs: ["--access-key-id LTAIxxxxx --access-key-secret xxxxx"],
3579
async run(ctx) {
36-
const { settings } = ctx;
80+
const { settings, flags } = ctx;
3781
const format = detectOutputFormat(settings.output);
3882

3983
if (settings.dryRun) {
4084
emitResult(
4185
{
4286
apis: [
87+
{
88+
step: 0,
89+
api: "GenerateCLIAccessToken",
90+
description: "Generate CLI access token from AK/SK",
91+
},
4392
{
4493
step: 1,
4594
api: API.loginInfo,
@@ -52,21 +101,31 @@ export default defineCommand({
52101
},
53102
{
54103
step: 3,
55-
api: API.createUser,
56-
description: "Create console account user",
104+
api: "CreateUser",
105+
description: "Create console user via BailianControl OpenAPI (CreateUser)",
57106
},
58107
{
59108
step: 4,
109+
api: "ListWorkspaces",
110+
description: "List workspaces to resolve agentId",
111+
},
112+
{
113+
step: 5,
114+
api: "ResetPolicies4Agent",
115+
description: "Authorize user permissions",
116+
},
117+
{
118+
step: 6,
60119
api: API.queryBuyResult,
61120
description: "Query postpaid order status",
62121
},
63122
{
64-
step: 5,
123+
step: 7,
65124
api: API.commodityOrderInfo,
66125
description: "Query commodity activation status",
67126
},
68127
{
69-
step: 6,
128+
step: 8,
70129
api: API.buyCommodity,
71130
description: "Activate postpaid commodities (if needed)",
72131
},
@@ -77,11 +136,42 @@ export default defineCommand({
77136
return;
78137
}
79138

139+
const { accessKeyId, accessKeySecret } = flags;
140+
if (!accessKeyId || !accessKeySecret) {
141+
throw new BailianError(
142+
"bootstrap requires --access-key-id and --access-key-secret.",
143+
ExitCode.USAGE,
144+
);
145+
}
146+
const securityToken = flags.securityToken || undefined;
147+
148+
// Step 0: Exchange AK/SK for a temporary CLI access token used by console calls.
149+
const tokenResp = await generateCLIAccessToken({
150+
identity: ctx.identity,
151+
settings,
152+
baseUrl: ctx.client.baseUrl,
153+
accessKeyId,
154+
accessKeySecret,
155+
securityToken,
156+
});
157+
const accessToken: string | undefined = tokenResp.cliAccessToken;
158+
if (!accessToken) {
159+
throw new BailianError("Failed to generate CLI access token from AK/SK.", ExitCode.GENERAL);
160+
}
161+
162+
const gateway = effectiveConsoleGatewayConfig(settings);
163+
const target: ConsoleGatewayTarget = {
164+
region: gateway.consoleRegion,
165+
site: gateway.consoleSite,
166+
...(gateway.consoleSwitchAgent != null ? { switchAgent: gateway.consoleSwitchAgent } : {}),
167+
token: accessToken,
168+
};
169+
80170
const verbose = settings.verbose;
81171
const callApi = async (api: string, data: Record<string, unknown> = {}) => {
82172
if (verbose) process.stderr.write(`> ${api}\n`);
83173
try {
84-
const resp = await ctx.client.console<any>(api, data);
174+
const resp = await callConsoleGateway(target, settings.timeout, { api, data }, settings);
85175
if (verbose) process.stderr.write(`< ${JSON.stringify(resp)}\n`);
86176
return resp;
87177
} catch (err) {
@@ -109,20 +199,60 @@ export default defineCommand({
109199
emitBare("Workspace already initialized.");
110200
}
111201

112-
// Step 3: Create console user
202+
// Step 3: Create console user via BailianControl OpenAPI (AK/SK signed)
113203
const uid = loginData?.aliyun?.uid;
114204
if (typeof uid !== "string" || uid.length === 0) {
115205
throw new BailianError("Console login info did not include aliyun.uid.", ExitCode.GENERAL);
116206
}
117-
await callApi(API.createUser, {
118-
reqDTO: {
119-
outerKey: uid,
120-
nickName: uid,
121-
userName: uid,
122-
},
207+
const bailianControlAuth = {
208+
identity: ctx.identity,
209+
settings,
210+
baseUrl: ctx.client.baseUrl,
211+
regionId: gateway.consoleRegion,
212+
accessKeyId,
213+
accessKeySecret,
214+
securityToken,
215+
};
216+
217+
try {
218+
await createBailianControlUser({
219+
...bailianControlAuth,
220+
reqDTO: {
221+
outerKey: uid,
222+
nickName: uid,
223+
userName: uid,
224+
},
225+
});
226+
} catch (err) {
227+
// Re-running bootstrap is idempotent: an already-existing user is not
228+
// fatal, so swallow it and continue with the remaining steps.
229+
if (!(err instanceof BailianError) || !/already exists/i.test(err.message)) {
230+
throw err;
231+
}
232+
emitBare("Console user already exists, continuing.");
233+
}
234+
235+
// Step 4-5: Resolve the workspace agent id, then authorize user permissions.
236+
emitBare("Resolving workspace agent...");
237+
const workspacesResp = await listBailianControlWorkspaces(bailianControlAuth);
238+
const agentId = extractAgentId(workspacesResp);
239+
if (agentId == null) {
240+
throw new BailianError(
241+
"Could not resolve agentId from ListWorkspaces response.",
242+
ExitCode.GENERAL,
243+
"Re-run with --verbose to inspect the ListWorkspaces response body.",
244+
);
245+
}
246+
247+
emitBare("Authorizing user permissions...");
248+
await resetBailianControlPolicies4Agent({
249+
...bailianControlAuth,
250+
outerKey: uid,
251+
agentId,
252+
policyIndexList: [1],
123253
});
124254

125-
// Step 4-6: Order & commodity flow
255+
// Step 6-8: Order & commodity flow
126256
await ensureCommoditiesActive(callApi, format);
127257
},
128258
});

0 commit comments

Comments
 (0)