@nasl/cli 0.1.15 → 0.1.17

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 (44) hide show
  1. package/build/nasl.bundle.js +4970 -4941
  2. package/dist/bin/nasl.mjs +243 -51
  3. package/dist/bin/nasl.mjs.map +1 -1
  4. package/dist/bin/naslc.mjs +196 -16
  5. package/dist/bin/naslc.mjs.map +1 -1
  6. package/dist/index.mjs +212 -49
  7. package/dist/index.mjs.map +1 -1
  8. package/out/apis/compileApi.d.ts.map +1 -1
  9. package/out/apis/compileApi.js +2 -4
  10. package/out/apis/compileApi.js.map +1 -1
  11. package/out/apis/createAppApi.js +1 -1
  12. package/out/apis/createAppApi.js.map +1 -1
  13. package/out/apis/createAxios.d.ts +7 -1
  14. package/out/apis/createAxios.d.ts.map +1 -1
  15. package/out/apis/createAxios.js +34 -5
  16. package/out/apis/createAxios.js.map +1 -1
  17. package/out/apis/openapi.d.ts +34 -0
  18. package/out/apis/openapi.d.ts.map +1 -0
  19. package/out/apis/openapi.js +114 -0
  20. package/out/apis/openapi.js.map +1 -0
  21. package/out/apis/transformApi.d.ts +1 -1
  22. package/out/apis/transformApi.d.ts.map +1 -1
  23. package/out/apis/transformApi.js +4 -6
  24. package/out/apis/transformApi.js.map +1 -1
  25. package/out/bin/nasl.js +63 -1
  26. package/out/bin/nasl.js.map +1 -1
  27. package/out/commands/check.d.ts.map +1 -1
  28. package/out/commands/check.js +1 -4
  29. package/out/commands/check.js.map +1 -1
  30. package/out/commands/compile.d.ts.map +1 -1
  31. package/out/commands/compile.js +1 -4
  32. package/out/commands/compile.js.map +1 -1
  33. package/out/commands/transform.d.ts.map +1 -1
  34. package/out/commands/transform.js +7 -19
  35. package/out/commands/transform.js.map +1 -1
  36. package/out/types/api.d.ts +8 -0
  37. package/out/types/api.d.ts.map +1 -1
  38. package/out/types/command.d.ts +1 -0
  39. package/out/types/command.d.ts.map +1 -1
  40. package/out/types/config.d.ts +6 -0
  41. package/out/types/config.d.ts.map +1 -1
  42. package/out/types/config.js +2 -2
  43. package/out/types/config.js.map +1 -1
  44. package/package.json +3 -1
package/dist/bin/nasl.mjs CHANGED
@@ -15,9 +15,10 @@ import require$$1$1 from 'tty';
15
15
  import require$$3 from 'http';
16
16
  import require$$4$1 from 'https';
17
17
  import require$$0$6 from 'url';
18
- import require$$8 from 'crypto';
18
+ import crypto$1 from 'crypto';
19
19
  import http2 from 'http2';
20
20
  import zlib from 'zlib';
21
+ import * as readline from 'readline';
21
22
  import { realpath as realpath$1, stat as stat$2, lstat as lstat$1, open, readdir as readdir$1 } from 'fs/promises';
22
23
  import { lstat, stat as stat$1, readdir, realpath } from 'node:fs/promises';
23
24
  import { Readable as Readable$1 } from 'node:stream';
@@ -6191,10 +6192,10 @@ var libExports = /*@__PURE__*/ requireLib();
6191
6192
  * 默认配置
6192
6193
  */
6193
6194
  const DEFAULT_CONFIG = {
6194
- serverBaseURL: 'https://nasl.lcap.163yun.com/api/v1/nasl',
6195
+ serverBaseURL: 'https://nasl.lcap.163yun.com',
6195
6196
  representation: 'NaturalTS',
6196
6197
  namespaceResolution: 'filename-as-namespace',
6197
- ideVersion: '4.3',
6198
+ ideVersion: '4.4',
6198
6199
  srcDir: 'src',
6199
6200
  outDir: 'out',
6200
6201
  };
@@ -22145,7 +22146,7 @@ function requireForm_data () {
22145
22146
  var parseUrl = require$$0$6.parse;
22146
22147
  var fs = require$$0$2;
22147
22148
  var Stream = stream$4.Stream;
22148
- var crypto = require$$8;
22149
+ var crypto = crypto$1;
22149
22150
  var mime = requireMimeTypes();
22150
22151
  var asynckit = requireAsynckit();
22151
22152
  var setToStringTag = /*@__PURE__*/ requireEsSetTostringtag();
@@ -23054,7 +23055,7 @@ const generateString = (size = 16, alphabet = ALPHABET.ALPHA_DIGIT) => {
23054
23055
  let str = '';
23055
23056
  const {length} = alphabet;
23056
23057
  const randomValues = new Uint32Array(size);
23057
- require$$8.randomFillSync(randomValues);
23058
+ crypto$1.randomFillSync(randomValues);
23058
23059
  for (let i = 0; i < size; i++) {
23059
23060
  str += alphabet[randomValues[i] % length];
23060
23061
  }
@@ -28839,14 +28840,199 @@ const {
28839
28840
  mergeConfig
28840
28841
  } = axios;
28841
28842
 
28842
- function createAxios(baseURL) {
28843
- return axios.create({
28843
+ // Unique ID creation requires a high quality random # generator. In the browser we therefore
28844
+ // require the crypto API and do not support built-in fallback to lower quality random number
28845
+ // generators (like Math.random()).
28846
+ let getRandomValues;
28847
+ const rnds8 = new Uint8Array(16);
28848
+ function rng() {
28849
+ // lazy load so that environments that need to polyfill have a chance to do so
28850
+ if (!getRandomValues) {
28851
+ // getRandomValues needs to be invoked in a context where "this" is a Crypto implementation.
28852
+ getRandomValues = typeof crypto !== 'undefined' && crypto.getRandomValues && crypto.getRandomValues.bind(crypto);
28853
+
28854
+ if (!getRandomValues) {
28855
+ throw new Error('crypto.getRandomValues() not supported. See https://github.com/uuidjs/uuid#getrandomvalues-not-supported');
28856
+ }
28857
+ }
28858
+
28859
+ return getRandomValues(rnds8);
28860
+ }
28861
+
28862
+ /**
28863
+ * Convert array of 16 byte values to UUID string format of the form:
28864
+ * XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX
28865
+ */
28866
+
28867
+ const byteToHex = [];
28868
+
28869
+ for (let i = 0; i < 256; ++i) {
28870
+ byteToHex.push((i + 0x100).toString(16).slice(1));
28871
+ }
28872
+
28873
+ function unsafeStringify(arr, offset = 0) {
28874
+ // Note: Be careful editing this code! It's been tuned for performance
28875
+ // and works in ways you may not expect. See https://github.com/uuidjs/uuid/pull/434
28876
+ return byteToHex[arr[offset + 0]] + byteToHex[arr[offset + 1]] + byteToHex[arr[offset + 2]] + byteToHex[arr[offset + 3]] + '-' + byteToHex[arr[offset + 4]] + byteToHex[arr[offset + 5]] + '-' + byteToHex[arr[offset + 6]] + byteToHex[arr[offset + 7]] + '-' + byteToHex[arr[offset + 8]] + byteToHex[arr[offset + 9]] + '-' + byteToHex[arr[offset + 10]] + byteToHex[arr[offset + 11]] + byteToHex[arr[offset + 12]] + byteToHex[arr[offset + 13]] + byteToHex[arr[offset + 14]] + byteToHex[arr[offset + 15]];
28877
+ }
28878
+
28879
+ const randomUUID = typeof crypto !== 'undefined' && crypto.randomUUID && crypto.randomUUID.bind(crypto);
28880
+ var native = {
28881
+ randomUUID
28882
+ };
28883
+
28884
+ function v4(options, buf, offset) {
28885
+ if (native.randomUUID && true && !options) {
28886
+ return native.randomUUID();
28887
+ }
28888
+
28889
+ options = options || {};
28890
+ const rnds = options.random || (options.rng || rng)(); // Per 4.4, set bits for version and `clock_seq_hi_and_reserved`
28891
+
28892
+ rnds[6] = rnds[6] & 0x0f | 0x40;
28893
+ rnds[8] = rnds[8] & 0x3f | 0x80; // Copy bytes to buffer, if provided
28894
+
28895
+ return unsafeStringify(rnds);
28896
+ }
28897
+
28898
+ /**
28899
+ * 构建签名字符串
28900
+ */
28901
+ function buildStringToSign(appKey, nonce, timestamp, secretKey) {
28902
+ return `${appKey}&${nonce}&${timestamp}&${secretKey}`;
28903
+ }
28904
+ /**
28905
+ * 生成 MD5 签名
28906
+ */
28907
+ function generateSignature(plainText) {
28908
+ return crypto$1.createHash('md5').update(plainText).digest('hex');
28909
+ }
28910
+ /**
28911
+ * 生成客户端签名信息
28912
+ * @param appKey AppKey
28913
+ * @param secretKey SecretKey
28914
+ * @returns 签名信息
28915
+ */
28916
+ function generateClientSignature(appKey, secretKey) {
28917
+ const timestamp = Math.floor(Date.now() / 1000).toString();
28918
+ const nonce = v4();
28919
+ const plainText = buildStringToSign(appKey, nonce, timestamp, secretKey);
28920
+ const signature = generateSignature(plainText);
28921
+ return {
28922
+ appKey,
28923
+ timestamp,
28924
+ nonce,
28925
+ signature,
28926
+ };
28927
+ }
28928
+ /**
28929
+ * 生成基础认证头
28930
+ * @param ak AppKey
28931
+ * @param sk SecretKey
28932
+ * @returns 基础认证头
28933
+ */
28934
+ function generateBaseHeaders(ak, sk) {
28935
+ const { timestamp, signature, nonce } = generateClientSignature(ak, sk);
28936
+ return {
28937
+ 'Content-Type': 'application/json',
28938
+ 'x-appKey': ak,
28939
+ 'x-timestamp': timestamp,
28940
+ 'x-nonce': nonce,
28941
+ 'x-signature': signature,
28942
+ };
28943
+ }
28944
+ /**
28945
+ * 获取租户的 signInfo
28946
+ * @param options 服务器选项
28947
+ * @param baseHeaders 基础认证头
28948
+ * @returns x-signInfo 值
28949
+ */
28950
+ async function fetchSignInfo(options, baseHeaders) {
28951
+ const tenantName = options.tenantName || 'defaulttenant';
28952
+ const userName = tenantName === 'defaulttenant' ? 'admin' : `${tenantName}-admin`;
28953
+ const data = {
28954
+ tenantName,
28955
+ userName,
28956
+ source: 'Normal',
28957
+ };
28958
+ const url = `${options.serverBaseURL}/openapi/v3/auth/getSignInfo`;
28959
+ try {
28960
+ const tempAxios = axios.create({
28961
+ headers: baseHeaders,
28962
+ timeout: 120000,
28963
+ });
28964
+ const response = await tempAxios.post(url, data);
28965
+ return response?.data?.result || null;
28966
+ }
28967
+ catch (error) {
28968
+ console.error('Error fetching sign info:', error.response?.data);
28969
+ return null;
28970
+ }
28971
+ }
28972
+ /**
28973
+ * 生成完整的认证头(包含 x-signInfo)
28974
+ * @param options 服务器选项
28975
+ * @returns 完整的认证头
28976
+ */
28977
+ async function generateCompleteHeaders(options) {
28978
+ const headers = {
28979
+ 'Content-Type': 'application/json',
28980
+ };
28981
+ const OPENAPI_AK = options.OPENAPI_AK || process.env.LCAP_OPENAPI_AK;
28982
+ const OPENAPI_SK = options.OPENAPI_SK || process.env.LCAP_OPENAPI_SK;
28983
+ // 如果没有提供 ak 和 sk,返回基础 headers
28984
+ if (!OPENAPI_AK || !OPENAPI_SK) {
28985
+ throw new Error(`配置了 useOPENAPI,但没有提供 OPENAPI_AK 和 OPENAPI_SK:
28986
+ - 可取消配置 useOPENAPI
28987
+ - 在 nasl.config.json 中配置 OPENAPI_AK 和 OPENAPI_SK,或者在环境变量中配置 LCAP_OPENAPI_AK 和 LCAP_OPENAPI_SK`);
28988
+ }
28989
+ // 生成基础认证头
28990
+ const baseHeaders = generateBaseHeaders(OPENAPI_AK, OPENAPI_SK);
28991
+ Object.assign(headers, baseHeaders);
28992
+ // 获取 signInfo
28993
+ const signInfo = await fetchSignInfo(options, headers);
28994
+ if (signInfo) {
28995
+ headers['x-signInfo'] = signInfo;
28996
+ }
28997
+ return headers;
28998
+ }
28999
+
29000
+ /**
29001
+ * 创建 Axios 实例
29002
+ * @param options 服务器选项,包含认证信息
29003
+ * @returns Axios 实例
29004
+ */
29005
+ async function createAxios(options) {
29006
+ // 如果需要鉴权,拼接 /openapi/v3/nasl;否则使用原始 URL
29007
+ const serverBaseURL = new URL(options.serverBaseURL).origin;
29008
+ const useOPENAPI = options.useOPENAPI || process.env.USE_LCAP_OPENAPI === 'true';
29009
+ const baseURL = useOPENAPI ? `${serverBaseURL}/openapi/v3/nasl` : `${serverBaseURL}/api/v1/nasl`;
29010
+ // 如果需要鉴权,生成完整的认证头;否则只使用基础 headers
29011
+ const headers = useOPENAPI ? await generateCompleteHeaders(options) : { 'Content-Type': 'application/json' };
29012
+ console.log('本次服务调用方为:', baseURL);
29013
+ const instance = axios.create({
28844
29014
  baseURL,
28845
- headers: {
28846
- 'Content-Type': 'application/json',
28847
- },
29015
+ headers,
28848
29016
  timeout: 120000,
28849
29017
  });
29018
+ const oldPost = instance.post;
29019
+ instance.post = async (url, data, config) => {
29020
+ return oldPost(url, data, config).then((res) => {
29021
+ const data = res.data;
29022
+ if (data.code !== 200)
29023
+ throw new Error(JSON.stringify(data));
29024
+ return res;
29025
+ }).catch((err) => {
29026
+ // console.log(err.response ? err.response.data : err);
29027
+ if (err.response) {
29028
+ throw new Error(JSON.stringify(err.response.data));
29029
+ }
29030
+ else {
29031
+ throw err;
29032
+ }
29033
+ });
29034
+ };
29035
+ return instance;
28850
29036
  }
28851
29037
 
28852
29038
  function truncate(str, maxLength) {
@@ -28860,13 +29046,11 @@ function truncate(str, maxLength) {
28860
29046
  async function compileApi(fullNaturalTS, options) {
28861
29047
  // 这里需要调用实际的编译服务接口
28862
29048
  // 示例实现:
28863
- const axios = createAxios(options.serverBaseURL);
29049
+ const axios = await createAxios(options);
28864
29050
  const res = await axios.post(`/compile/tsx?ideVersion=${options.ideVersion}&needAnnotation=true`, fullNaturalTS, {
28865
29051
  headers: { 'Content-Type': 'text/plain' },
28866
29052
  });
28867
29053
  const data = res.data;
28868
- if (data.code !== 200)
28869
- throw new Error(data.message);
28870
29054
  const { bundle } = data.result;
28871
29055
  const fileMap = bundle.frontendBundle.files;
28872
29056
  const files = Object.keys(fileMap).map((key) => {
@@ -28894,7 +29078,7 @@ window.backendApp = app;
28894
29078
  * TODO: 实现具体的 API 调用逻辑
28895
29079
  */
28896
29080
  async function checkApi(fullNaturalTS, options) {
28897
- const axios = createAxios(options.serverBaseURL);
29081
+ const axios = await createAxios(options);
28898
29082
  const res = await axios.post(`/check/tsx?ideVersion=${options.ideVersion}`, fullNaturalTS, {
28899
29083
  headers: { 'Content-Type': 'text/plain' },
28900
29084
  });
@@ -28912,7 +29096,7 @@ async function checkApi(fullNaturalTS, options) {
28912
29096
  async function createAppSyncApi(fullNaturalTS, options) {
28913
29097
  const url = new URL(options.serverBaseURL);
28914
29098
  const origin = url.origin + '/app-ai-creator';
28915
- const axios = createAxios(origin);
29099
+ const axios = await createAxios({ serverBaseURL: origin});
28916
29100
  try {
28917
29101
  const res = await axios.post('/api/createAppSync', {
28918
29102
  fullNaturalTS,
@@ -37795,10 +37979,7 @@ async function compile(entry, options) {
37795
37979
  logger.info('正在调用编译服务...');
37796
37980
  try {
37797
37981
  const fullNaturalTS = composeToString(collectedFiles);
37798
- const outputFiles = await compileApi(fullNaturalTS, {
37799
- serverBaseURL: config.serverBaseURL,
37800
- ideVersion: config.ideVersion,
37801
- });
37982
+ const outputFiles = await compileApi(fullNaturalTS, config);
37802
37983
  logger.success('编译成功!');
37803
37984
  // 写入输出文件
37804
37985
  for (const file of outputFiles) {
@@ -37845,10 +38026,7 @@ async function check(entry, options) {
37845
38026
  result = error.message.trim();
37846
38027
  }
37847
38028
  if (!result && fullNaturalTS) {
37848
- result = (await checkApi(fullNaturalTS, {
37849
- serverBaseURL: config.serverBaseURL,
37850
- ideVersion: config.ideVersion,
37851
- })).trim();
38029
+ result = (await checkApi(fullNaturalTS, config)).trim();
37852
38030
  }
37853
38031
  const checkResult = {
37854
38032
  success: !result,
@@ -38188,15 +38366,13 @@ async function createAppInIde(entry, options) {
38188
38366
  * 编译 NASL 代码
38189
38367
  * TODO: 实现具体的 API 调用逻辑
38190
38368
  */
38191
- async function transformJson2FilesApi(jsonContent, options) {
38369
+ async function transformJson2FilesApi(json, options) {
38192
38370
  // 示例实现:
38193
- const axios = createAxios(options.serverBaseURL);
38194
- const res = await axios.post(`/transform/json2files?ideVersion=${options.ideVersion}`, jsonContent, {
38195
- headers: { 'Content-Type': 'text/plain' },
38371
+ const axios = await createAxios(options);
38372
+ const res = await axios.post(`/transform/json2files?ideVersion=${options.ideVersion}`, json, {
38373
+ headers: { 'Content-Type': 'application/json' },
38196
38374
  });
38197
38375
  const data = res.data;
38198
- if (data.code !== 200)
38199
- throw new Error(data.message);
38200
38376
  return data.result;
38201
38377
  }
38202
38378
 
@@ -38236,14 +38412,19 @@ const transformFns = {
38236
38412
  const projectRoot = getProjectRoot();
38237
38413
  logger.info(`项目根目录: ${projectRoot}`);
38238
38414
  logger.info(`源代码目录: ${config.srcDir}`);
38415
+ if (!libExports.existsSync(config.srcDir)) {
38416
+ logger.error(`当前目录下 ${config.srcDir} 目录不存在,请先创建`);
38417
+ logger.exit(1);
38418
+ }
38239
38419
  let jsonContent = '';
38240
38420
  if (!entry) {
38241
38421
  logger.warn('没有指定 JSON 文件路径,按照默认 IDE 版本的基础模板转换');
38242
38422
  }
38243
38423
  else {
38244
38424
  jsonContent = libExports.readFileSync(entry, 'utf-8');
38425
+ logger.info(`读取到 JSON 文件: ${entry}`);
38245
38426
  }
38246
- const files = await transformJson2FilesApi(jsonContent, {
38427
+ const files = await transformJson2FilesApi(jsonContent ? JSON.parse(jsonContent) : {}, {
38247
38428
  serverBaseURL: config.serverBaseURL,
38248
38429
  ideVersion: config.ideVersion,
38249
38430
  });
@@ -38257,25 +38438,8 @@ const transformFns = {
38257
38438
  * 将 src 中的文件转换成一个 JSON
38258
38439
  */
38259
38440
  async files2json(entry, options) {
38260
- const logger = options?.logger || defaultLogger;
38261
- // 收集需要处理的文件
38262
- const { collectedFiles, projectRoot } = await resolveNASLFiles(entry, logger, false, options?.verbose);
38263
- if (collectedFiles.length === 0) {
38264
- logger.error('未找到需要转换的文件');
38265
- logger.exit(1);
38266
- }
38267
- logger.info(`找到 ${collectedFiles.length} 个文件`);
38268
- // 转换为 JSON
38269
- logger.newLine();
38270
- logger.info('正在转换为 JSON...');
38271
- const jsonContent = JSON.stringify(collectedFiles, null, 2);
38272
- // 确定输出路径
38273
- const outputPath = options?.output
38274
- ? sysPath.resolve(projectRoot, options.output)
38275
- : sysPath.join(projectRoot, './files.json');
38276
- // 写入文件
38277
- writeFileWithLog(outputPath, jsonContent, logger);
38278
- logger.success(`JSON 文件已输出到: ${outputPath}`);
38441
+ options?.logger || defaultLogger;
38442
+ throw new Error('files2json 转换类型尚未实现');
38279
38443
  },
38280
38444
  };
38281
38445
  /**
@@ -38292,11 +38456,27 @@ async function transform(transformType, entry, options) {
38292
38456
  await transformFn(entry, options);
38293
38457
  }
38294
38458
 
38295
- var version = "0.1.15";
38459
+ var version = "0.1.17";
38296
38460
  var pkg = {
38297
38461
  version: version};
38298
38462
 
38299
38463
  const program = new Command();
38464
+ /**
38465
+ * 使用 readline 询问用户确认
38466
+ */
38467
+ function askForConfirmation(question) {
38468
+ const rl = readline.createInterface({
38469
+ input: process.stdin,
38470
+ output: process.stdout,
38471
+ });
38472
+ return new Promise((resolve) => {
38473
+ rl.question(question, (answer) => {
38474
+ rl.close();
38475
+ const normalizedAnswer = answer.trim().toLowerCase();
38476
+ resolve(normalizedAnswer === 'y' || normalizedAnswer === 'yes' || normalizedAnswer === '是');
38477
+ });
38478
+ });
38479
+ }
38300
38480
  const entryDescription = `是相对于项目根目录的路径,支持 glob 模式(注意要用引号包裹),例如:
38301
38481
  - src/app.enums.Status.ts 支持具体文件
38302
38482
  - "src/app.enums.*.ts" 表示所有枚举
@@ -38379,8 +38559,20 @@ program
38379
38559
  .command('create-app-in-ide [entry]')
38380
38560
  .description('在 IDE 中创建应用')
38381
38561
  .option('-v, --verbose', '显示详细信息,如依赖分析树')
38562
+ .option('-q, --quiet', '不询问问题')
38382
38563
  .action(async (entry, options) => {
38383
38564
  try {
38565
+ // 如果不是 quiet 模式,询问用户确认
38566
+ if (!options?.quiet) {
38567
+ defaultLogger.newLine();
38568
+ defaultLogger.info('即将在 IDE 中创建新的应用,此操作将调用远程接口。');
38569
+ const confirmed = await askForConfirmation('请确认是否继续?(y/N): ');
38570
+ if (!confirmed) {
38571
+ defaultLogger.info('已取消操作');
38572
+ process.exit(0);
38573
+ }
38574
+ defaultLogger.newLine();
38575
+ }
38384
38576
  await createAppInIde(entry, options);
38385
38577
  }
38386
38578
  catch (error) {
@@ -38390,7 +38582,7 @@ program
38390
38582
  });
38391
38583
  program
38392
38584
  .command('transform <transformType> [entry]')
38393
- .description('转换文件格式\n transformType: files2full (将 src 文件组合成 fullNaturalTS), json2files (将 JSON 转换为文件, 待实现), files2json (将 src 文件转换为 JSON)')
38585
+ .description('转换文件格式\n transformType: files2full (将 src 文件组合成 fullNaturalTS), json2files (将 JSON 转换为文件), files2json (将 src 文件转换为 JSON)')
38394
38586
  .option('-o, --output <outputPath>', '指定输出路径')
38395
38587
  .option('-v, --verbose', '显示详细信息,如依赖分析树')
38396
38588
  .action(async (transformType, entry, options) => {