byteplan-cli 1.2.0 → 1.3.0

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 +83 -23
  3. package/src/cli.js +8 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "byteplan-cli",
3
- "version": "1.2.0",
3
+ "version": "1.3.0",
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
  // 查询模型数据
@@ -280,18 +336,22 @@ export async function getModelData(token, modelCode, params = {}) {
280
336
  },
281
337
  body: JSON.stringify({
282
338
  modelCode,
283
- ...params,
339
+ params: {
340
+ pageNum: params.pageNum || 0,
341
+ pageSize: params.pageSize || 100,
342
+ ...params,
343
+ },
284
344
  }),
285
345
  });
286
-
287
- return response.json();
346
+
347
+ return handleResponse(response);
288
348
  }
289
349
 
290
350
  // 获取维度值
291
351
  export async function getDimValues(token, dimCode, options = {}) {
292
352
  const { page = 0, size = 100 } = options;
293
353
  const baseUrl = getBaseUrl();
294
-
354
+
295
355
  const response = await fetch(baseUrl + '/data/api/online/lov/DATA_DIM_VALUE_LOV?dimCode=' + dimCode + '&page=' + page + '&size=' + size, {
296
356
  method: 'POST',
297
357
  headers: {
@@ -302,18 +362,18 @@ export async function getDimValues(token, dimCode, options = {}) {
302
362
  },
303
363
  body: JSON.stringify({ dimCode, page, size }),
304
364
  });
305
-
306
- return response.json();
365
+
366
+ return handleResponse(response);
307
367
  }
308
368
 
309
369
  // 获取 LOV 值
310
370
  export async function getLovValues(token, lovCode, options = {}) {
311
371
  const { keywords = null } = options;
312
372
  const baseUrl = getBaseUrl();
313
-
373
+
314
374
  const body = {};
315
375
  if (keywords) body.keywords = keywords;
316
-
376
+
317
377
  const response = await fetch(baseUrl + '/data/api/online/lov/' + lovCode, {
318
378
  method: 'POST',
319
379
  headers: {
@@ -324,6 +384,6 @@ export async function getLovValues(token, lovCode, options = {}) {
324
384
  },
325
385
  body: JSON.stringify(body),
326
386
  });
327
-
328
- return response.json();
387
+
388
+ return handleResponse(response);
329
389
  }
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);
@@ -229,7 +236,7 @@ const dataCmd = program
229
236
  dataCmd
230
237
  .command('query <modelCode>')
231
238
  .description('Query data from a model')
232
- .option('-p, --page <number>', 'Page number', '0')
239
+ .option('-p, --page <number>', 'Page number (starting from 0)', '0')
233
240
  .option('-s, --size <number>', 'Page size', '100')
234
241
  .action(async (modelCode, options) => {
235
242
  try {