byteplan-cli 1.2.0 → 1.2.2

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/package.json +1 -1
  2. package/src/api.js +78 -22
  3. package/src/cli.js +7 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "byteplan-cli",
3
- "version": "1.2.0",
3
+ "version": "1.2.2",
4
4
  "description": "BytePlan CLI - Command line tool for BytePlan API",
5
5
  "keywords": [
6
6
  "byteplan",
package/src/api.js CHANGED
@@ -6,7 +6,7 @@ import Conf from 'conf';
6
6
  import dotenv from 'dotenv';
7
7
  import { homedir } from 'os';
8
8
  import { join } from 'path';
9
- import { existsSync, mkdirSync, writeFileSync } from 'fs';
9
+ import { existsSync, mkdirSync, writeFileSync, readFileSync } from 'fs';
10
10
 
11
11
  const configDir = join(homedir(), '.byteplan');
12
12
  const envFile = join(configDir, '.env');
@@ -38,6 +38,30 @@ const DEFAULT_MENU_HEADERS = {
38
38
  let currentEnv = process.env.BP_ENV || 'uat';
39
39
  let currentToken = process.env.ACCESS_TOKEN || null;
40
40
 
41
+ // 统一的响应处理函数
42
+ async function handleResponse(response) {
43
+ const data = await response.json();
44
+
45
+ // 检查 HTTP 状态码
46
+ if (!response.ok) {
47
+ // 401 表示 token 无效或过期
48
+ if (response.status === 401) {
49
+ throw new Error('Token invalid or expired. Please run "byteplan login" to re-authenticate.');
50
+ }
51
+ // 其他 HTTP 错误
52
+ const errMsg = data.message || data.error_description || data.msg || `HTTP ${response.status}`;
53
+ throw new Error('API error: ' + errMsg);
54
+ }
55
+
56
+ // 检查响应中的错误字段(有些 API 返回 200 但包含错误信息)
57
+ if (data.error || data.code === 'ERROR' || data.success === false) {
58
+ const errMsg = data.message || data.msg || data.error_description || JSON.stringify(data);
59
+ throw new Error('API error: ' + errMsg);
60
+ }
61
+
62
+ return data;
63
+ }
64
+
41
65
  export function setEnvironment(env) {
42
66
  if (ENVIRONMENTS[env]) {
43
67
  currentEnv = env;
@@ -87,7 +111,7 @@ export async function getPublicKey() {
87
111
  // 保存凭证到 .env
88
112
  function saveCredentials(data) {
89
113
  const envPath = join(homedir(), '.byteplan', '.env');
90
-
114
+
91
115
  const lines = [];
92
116
  lines.push('BP_ENV=' + currentEnv);
93
117
  lines.push('BP_USER=' + data.username);
@@ -101,10 +125,42 @@ function saveCredentials(data) {
101
125
  if (data.expiresIn) {
102
126
  lines.push('TOKEN_EXPIRES_IN=' + (Date.now() + data.expiresIn * 1000));
103
127
  }
104
-
128
+
105
129
  writeFileSync(envPath, lines.join('\n') + '\n');
106
130
  }
107
131
 
132
+ // 保存 token 到 .env(切换租户时使用)
133
+ export function saveToken(accessToken, refreshToken, expiresIn) {
134
+ const envPath = join(homedir(), '.byteplan', '.env');
135
+
136
+ // 读取现有的 .env 内容
137
+ let existingLines = [];
138
+ if (existsSync(envPath)) {
139
+ existingLines = readFileSync(envPath, 'utf-8').split('\n').filter(line =>
140
+ !line.startsWith('ACCESS_TOKEN=') &&
141
+ !line.startsWith('REFRESH_TOKEN=') &&
142
+ !line.startsWith('TOKEN_EXPIRES_IN=')
143
+ );
144
+ }
145
+
146
+ // 添加新的 token 信息
147
+ const newLines = [...existingLines];
148
+ if (accessToken) {
149
+ newLines.push('ACCESS_TOKEN=' + accessToken);
150
+ }
151
+ if (refreshToken) {
152
+ newLines.push('REFRESH_TOKEN=' + refreshToken);
153
+ }
154
+ if (expiresIn) {
155
+ newLines.push('TOKEN_EXPIRES_IN=' + (Date.now() + expiresIn * 1000));
156
+ }
157
+
158
+ writeFileSync(envPath, newLines.join('\n') + '\n');
159
+
160
+ // 更新内存中的 token
161
+ setToken(accessToken);
162
+ }
163
+
108
164
  // 登录
109
165
  export async function login(username, password, env = 'uat') {
110
166
  if (env) setEnvironment(env);
@@ -212,8 +268,8 @@ export async function getUserInfo(token) {
212
268
  'authorization': 'Bearer ' + (token || currentToken),
213
269
  },
214
270
  });
215
-
216
- return response.json();
271
+
272
+ return handleResponse(response);
217
273
  }
218
274
 
219
275
  // 切换租户
@@ -227,27 +283,27 @@ export async function switchTenant(token, tenantId) {
227
283
  'authorization': 'Bearer ' + (token || currentToken),
228
284
  },
229
285
  });
230
-
231
- return response.json();
286
+
287
+ return handleResponse(response);
232
288
  }
233
289
 
234
290
  // 查询模型列表
235
291
  export async function queryModels(token, options = {}) {
236
292
  const { page = 0, size = 100 } = options;
237
293
  const baseUrl = getBaseUrl();
238
-
294
+
239
295
  const headers = {
240
296
  'accept': 'application/json, text/plain, */*',
241
297
  'authorization': 'Bearer ' + (token || currentToken),
242
298
  ...DEFAULT_MENU_HEADERS,
243
299
  };
244
-
300
+
245
301
  const response = await fetch(baseUrl + '/data/api/model/query?page=' + page + '&size=' + size, {
246
302
  method: 'GET',
247
303
  headers,
248
304
  });
249
-
250
- return response.json();
305
+
306
+ return handleResponse(response);
251
307
  }
252
308
 
253
309
  // 获取模型字段
@@ -263,8 +319,8 @@ export async function getModelColumns(token, modelCodes) {
263
319
  },
264
320
  body: JSON.stringify(modelCodes),
265
321
  });
266
-
267
- return response.json();
322
+
323
+ return handleResponse(response);
268
324
  }
269
325
 
270
326
  // 查询模型数据
@@ -283,15 +339,15 @@ export async function getModelData(token, modelCode, params = {}) {
283
339
  ...params,
284
340
  }),
285
341
  });
286
-
287
- return response.json();
342
+
343
+ return handleResponse(response);
288
344
  }
289
345
 
290
346
  // 获取维度值
291
347
  export async function getDimValues(token, dimCode, options = {}) {
292
348
  const { page = 0, size = 100 } = options;
293
349
  const baseUrl = getBaseUrl();
294
-
350
+
295
351
  const response = await fetch(baseUrl + '/data/api/online/lov/DATA_DIM_VALUE_LOV?dimCode=' + dimCode + '&page=' + page + '&size=' + size, {
296
352
  method: 'POST',
297
353
  headers: {
@@ -302,18 +358,18 @@ export async function getDimValues(token, dimCode, options = {}) {
302
358
  },
303
359
  body: JSON.stringify({ dimCode, page, size }),
304
360
  });
305
-
306
- return response.json();
361
+
362
+ return handleResponse(response);
307
363
  }
308
364
 
309
365
  // 获取 LOV 值
310
366
  export async function getLovValues(token, lovCode, options = {}) {
311
367
  const { keywords = null } = options;
312
368
  const baseUrl = getBaseUrl();
313
-
369
+
314
370
  const body = {};
315
371
  if (keywords) body.keywords = keywords;
316
-
372
+
317
373
  const response = await fetch(baseUrl + '/data/api/online/lov/' + lovCode, {
318
374
  method: 'POST',
319
375
  headers: {
@@ -324,6 +380,6 @@ export async function getLovValues(token, lovCode, options = {}) {
324
380
  },
325
381
  body: JSON.stringify(body),
326
382
  });
327
-
328
- return response.json();
383
+
384
+ return handleResponse(response);
329
385
  }
package/src/cli.js CHANGED
@@ -16,6 +16,7 @@ import {
16
16
  getModelData,
17
17
  getDimValues,
18
18
  getLovValues,
19
+ saveToken,
19
20
  } from './api.js';
20
21
  import { skillsCmd } from './commands/skills.js';
21
22
 
@@ -141,12 +142,18 @@ tenantCmd
141
142
  await loginWithEnv();
142
143
  const result = await switchTenant(null, tenantId);
143
144
 
145
+ // 保存切换后的新 token
146
+ if (result.access_token) {
147
+ saveToken(result.access_token, result.refresh_token, result.expires_in);
148
+ }
149
+
144
150
  printJSON({
145
151
  success: true,
146
152
  tenant: {
147
153
  tenantId: tenantId,
148
154
  tenantName: result.data?.tenantName || '',
149
155
  },
156
+ tokenUpdated: !!result.access_token,
150
157
  });
151
158
  } catch (error) {
152
159
  printError(error);