@nasl/cli 0.1.17 → 0.1.18

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.
@@ -3,8 +3,8 @@
3
3
  import require$$0$1, { EventEmitter } from 'events';
4
4
  import require$$1 from 'child_process';
5
5
  import * as sysPath from 'path';
6
- import sysPath__default from 'path';
7
- import require$$0$2 from 'fs';
6
+ import sysPath__default, { join } from 'path';
7
+ import require$$0$2, { promises } from 'fs';
8
8
  import require$$4 from 'process';
9
9
  import require$$0$5 from 'os';
10
10
  import require$$1$1 from 'tty';
@@ -15,7 +15,7 @@ import require$$5 from 'assert';
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 crypto$1 from 'crypto';
18
+ import crypto$1, { createHash } from 'crypto';
19
19
  import http2 from 'http2';
20
20
  import zlib from 'zlib';
21
21
 
@@ -8304,10 +8304,39 @@ function loadConfig(configDir) {
8304
8304
  const content = libExports.readFileSync(configPath, 'utf-8');
8305
8305
  const config = JSON.parse(content);
8306
8306
  // 验证必需字段
8307
- if (!config.serverBaseURL || !config.ideVersion || !config.srcDir || !config.outDir) {
8308
- defaultLogger.error('配置文件格式不正确,缺少必需字段');
8307
+ if (!config.srcDir || !config.outDir) {
8308
+ defaultLogger.error('配置文件格式不正确,缺少必需的 srcDir 或 outDir 字段');
8309
8309
  return defaultLogger.exit(1);
8310
8310
  }
8311
+ if (!config.ideVersion) {
8312
+ config.ideVersion = process.env.NASL_IDE_VERSION || '';
8313
+ if (!config.ideVersion) {
8314
+ defaultLogger.error('缺少配置 ideVersion,请在配置文件中添加,或在环境变量中配置 NASL_IDE_VERSION');
8315
+ return defaultLogger.exit(1);
8316
+ }
8317
+ }
8318
+ if (!config.serverBaseURL) {
8319
+ config.serverBaseURL = process.env.NASL_SERVER_BASE_URL || '';
8320
+ if (!config.serverBaseURL) {
8321
+ defaultLogger.error('缺少配置 serverBaseURL,请在配置文件中添加,或在环境变量中配置 NASL_SERVER_BASE_URL');
8322
+ return defaultLogger.exit(1);
8323
+ }
8324
+ }
8325
+ if (!config.useOPENAPI)
8326
+ config.useOPENAPI = process.env.USE_LCAP_OPENAPI === 'true';
8327
+ if (!config.OPENAPI_AK)
8328
+ config.OPENAPI_AK = process.env.LCAP_OPENAPI_AK;
8329
+ if (!config.OPENAPI_SK)
8330
+ config.OPENAPI_SK = process.env.LCAP_OPENAPI_SK;
8331
+ if (config.useOPENAPI) {
8332
+ if (!config.OPENAPI_AK || !config.OPENAPI_SK) {
8333
+ defaultLogger.error(`配置了 useOPENAPI,但缺少配置 OPENAPI_AK 和 OPENAPI_SK:
8334
+ - 在 nasl.config.json 中配置 OPENAPI_AK 和 OPENAPI_SK,或者在环境变量中配置 LCAP_OPENAPI_AK 和 LCAP_OPENAPI_SK
8335
+ - 或将 useOPENAPI 配置为 false
8336
+ `);
8337
+ return defaultLogger.exit(1);
8338
+ }
8339
+ }
8311
8340
  return config;
8312
8341
  }
8313
8342
  catch (error) {
@@ -28826,18 +28855,99 @@ function v4(options, buf, offset) {
28826
28855
  return unsafeStringify(rnds);
28827
28856
  }
28828
28857
 
28858
+ function generateHash(params) {
28859
+ const hash = createHash('sha256');
28860
+ hash.update(JSON.stringify(params));
28861
+ return hash.digest('hex');
28862
+ }
28863
+ async function getCachePathWithTimestamp(hash) {
28864
+ const cacheDir = join(process.cwd(), '.cache');
28865
+ await promises.mkdir(cacheDir, { recursive: true });
28866
+ return join(cacheDir, `${hash}_${Date.now()}.json`);
28867
+ }
28829
28868
  /**
28830
- * 构建签名字符串
28869
+ * 查找指定 hash 的有效缓存文件
28870
+ * @param hash 缓存标识
28871
+ * @param ttl 缓存有效期(毫秒)
28872
+ * @returns 缓存文件路径和缓存内容,如果没有有效缓存则返回 null
28831
28873
  */
28832
- function buildStringToSign(appKey, nonce, timestamp, secretKey) {
28833
- return `${appKey}&${nonce}&${timestamp}&${secretKey}`;
28874
+ async function findValidCache(hash, ttl) {
28875
+ const cacheDir = join(process.cwd(), '.cache');
28876
+ try {
28877
+ await promises.mkdir(cacheDir, { recursive: true });
28878
+ const files = await promises.readdir(cacheDir);
28879
+ const now = Date.now();
28880
+ // 查找所有匹配的缓存文件
28881
+ const matchingFiles = files
28882
+ .filter((file) => file.startsWith(`${hash}_`) && file.endsWith('.json'))
28883
+ .map((file) => {
28884
+ const match = file.match(/_(\d+)\.json$/);
28885
+ if (!match)
28886
+ return null;
28887
+ const timestamp = parseInt(match[1], 10);
28888
+ return {
28889
+ path: join(cacheDir, file),
28890
+ timestamp,
28891
+ isExpired: now - timestamp > ttl,
28892
+ };
28893
+ })
28894
+ .filter((item) => item !== null);
28895
+ // 删除过期的缓存文件
28896
+ const expiredFiles = matchingFiles.filter((item) => item.isExpired);
28897
+ await Promise.all(expiredFiles.map((item) => promises.unlink(item.path).catch(() => { })));
28898
+ // 查找有效的缓存文件(按时间戳降序排序,获取最新的)
28899
+ const validFiles = matchingFiles.filter((item) => !item.isExpired).sort((a, b) => b.timestamp - a.timestamp);
28900
+ if (validFiles.length > 0) {
28901
+ const cacheFile = validFiles[0];
28902
+ const cacheContent = await promises.readFile(cacheFile.path, 'utf-8');
28903
+ const cachedResult = JSON.parse(cacheContent);
28904
+ return { path: cacheFile.path, data: cachedResult };
28905
+ }
28906
+ return null;
28907
+ }
28908
+ catch (error) {
28909
+ return null;
28910
+ }
28834
28911
  }
28835
28912
  /**
28836
- * 生成 MD5 签名
28913
+ * 高阶函数,为异步函数添加带时间戳的缓存功能
28914
+ * @param fn 需要添加缓存的函数
28915
+ * @param ttl 缓存有效期(毫秒),默认 1 小时
28916
+ * @param getCacheKey 可选的自定义缓存 key 生成函数,如果不提供则使用整个 params
28917
+ * @returns 带缓存的函数版本
28837
28918
  */
28838
- function generateSignature(plainText) {
28839
- return crypto$1.createHash('md5').update(plainText).digest('hex');
28919
+ function cachableWithTTL(fn, ttl = 3600 * 1000, // 默认 1 小时
28920
+ getCacheKey) {
28921
+ return async (params, extraParams) => {
28922
+ const functionName = fn.name;
28923
+ const logPrefix = functionName ? `[${functionName}]` : '[cachableWithTTL]';
28924
+ // 检查是否启用缓存
28925
+ if (process.env.NO_AI_CACHE) {
28926
+ return fn(params, extraParams);
28927
+ }
28928
+ // 使用自定义的 getCacheKey 或默认使用整个 params
28929
+ const cacheKey = getCacheKey ? getCacheKey(params) : params;
28930
+ const hash = generateHash(cacheKey);
28931
+ // 查找有效的缓存
28932
+ const validCache = await findValidCache(hash, ttl);
28933
+ if (validCache) {
28934
+ console.log(`${logPrefix} ✓ 使用缓存`);
28935
+ return validCache.data;
28936
+ }
28937
+ // 调用原函数
28938
+ const result = await fn(params, extraParams);
28939
+ // 保存结果到缓存(带时间戳)
28940
+ try {
28941
+ const cachePath = await getCachePathWithTimestamp(hash);
28942
+ await promises.writeFile(cachePath, JSON.stringify(result, null, 2), 'utf-8');
28943
+ }
28944
+ catch (error) {
28945
+ // 缓存写入失败,但不影响返回结果
28946
+ }
28947
+ return result;
28948
+ };
28840
28949
  }
28950
+
28841
28951
  /**
28842
28952
  * 生成客户端签名信息
28843
28953
  * @param appKey AppKey
@@ -28847,8 +28957,8 @@ function generateSignature(plainText) {
28847
28957
  function generateClientSignature(appKey, secretKey) {
28848
28958
  const timestamp = Math.floor(Date.now() / 1000).toString();
28849
28959
  const nonce = v4();
28850
- const plainText = buildStringToSign(appKey, nonce, timestamp, secretKey);
28851
- const signature = generateSignature(plainText);
28960
+ const plainText = `${appKey}&${nonce}&${timestamp}&${secretKey}`;
28961
+ const signature = crypto$1.createHash('md5').update(plainText).digest('hex');
28852
28962
  return {
28853
28963
  appKey,
28854
28964
  timestamp,
@@ -28873,12 +28983,12 @@ function generateBaseHeaders(ak, sk) {
28873
28983
  };
28874
28984
  }
28875
28985
  /**
28876
- * 获取租户的 signInfo
28986
+ * 获取租户的 signInfo(内部实现)
28877
28987
  * @param options 服务器选项
28878
28988
  * @param baseHeaders 基础认证头
28879
28989
  * @returns x-signInfo 值
28880
28990
  */
28881
- async function fetchSignInfo(options, baseHeaders) {
28991
+ async function _fetchSignInfo({ options, baseHeaders }) {
28882
28992
  const tenantName = options.tenantName || 'defaulttenant';
28883
28993
  const userName = tenantName === 'defaulttenant' ? 'admin' : `${tenantName}-admin`;
28884
28994
  const data = {
@@ -28900,6 +29010,14 @@ async function fetchSignInfo(options, baseHeaders) {
28900
29010
  return null;
28901
29011
  }
28902
29012
  }
29013
+ /**
29014
+ * 获取租户的 signInfo(带缓存,1 小时过期)
29015
+ * @param options 服务器选项
29016
+ * @param baseHeaders 基础认证头
29017
+ * @returns x-signInfo 值
29018
+ */
29019
+ const fetchSignInfo = cachableWithTTL(_fetchSignInfo, 3600 * 1000, // 1 小时
29020
+ (params) => params.options);
28903
29021
  /**
28904
29022
  * 生成完整的认证头(包含 x-signInfo)
28905
29023
  * @param options 服务器选项
@@ -28909,19 +29027,18 @@ async function generateCompleteHeaders(options) {
28909
29027
  const headers = {
28910
29028
  'Content-Type': 'application/json',
28911
29029
  };
28912
- const OPENAPI_AK = options.OPENAPI_AK || process.env.LCAP_OPENAPI_AK;
28913
- const OPENAPI_SK = options.OPENAPI_SK || process.env.LCAP_OPENAPI_SK;
28914
29030
  // 如果没有提供 ak 和 sk,返回基础 headers
28915
- if (!OPENAPI_AK || !OPENAPI_SK) {
28916
- throw new Error(`配置了 useOPENAPI,但没有提供 OPENAPI_AK 和 OPENAPI_SK:
28917
- - 可取消配置 useOPENAPI
28918
- - nasl.config.json 中配置 OPENAPI_AK 和 OPENAPI_SK,或者在环境变量中配置 LCAP_OPENAPI_AK 和 LCAP_OPENAPI_SK`);
29031
+ if (!options.OPENAPI_AK || !options.OPENAPI_SK) {
29032
+ throw new Error(`配置了 useOPENAPI,但缺少配置 OPENAPI_AK 和 OPENAPI_SK:
29033
+ - nasl.config.json 中配置 OPENAPI_AK 和 OPENAPI_SK,或者在环境变量中配置 LCAP_OPENAPI_AK 和 LCAP_OPENAPI_SK
29034
+ - 或将 useOPENAPI 配置为 false
29035
+ `);
28919
29036
  }
28920
29037
  // 生成基础认证头
28921
- const baseHeaders = generateBaseHeaders(OPENAPI_AK, OPENAPI_SK);
29038
+ const baseHeaders = generateBaseHeaders(options.OPENAPI_AK, options.OPENAPI_SK);
28922
29039
  Object.assign(headers, baseHeaders);
28923
29040
  // 获取 signInfo
28924
- const signInfo = await fetchSignInfo(options, headers);
29041
+ const signInfo = await fetchSignInfo({ options, baseHeaders: headers });
28925
29042
  if (signInfo) {
28926
29043
  headers['x-signInfo'] = signInfo;
28927
29044
  }
@@ -28936,10 +29053,9 @@ async function generateCompleteHeaders(options) {
28936
29053
  async function createAxios(options) {
28937
29054
  // 如果需要鉴权,拼接 /openapi/v3/nasl;否则使用原始 URL
28938
29055
  const serverBaseURL = new URL(options.serverBaseURL).origin;
28939
- const useOPENAPI = options.useOPENAPI || process.env.USE_LCAP_OPENAPI === 'true';
28940
- const baseURL = useOPENAPI ? `${serverBaseURL}/openapi/v3/nasl` : `${serverBaseURL}/api/v1/nasl`;
29056
+ const baseURL = options.useOPENAPI ? `${serverBaseURL}/openapi/v3/nasl` : `${serverBaseURL}/api/v1/nasl`;
28941
29057
  // 如果需要鉴权,生成完整的认证头;否则只使用基础 headers
28942
- const headers = useOPENAPI ? await generateCompleteHeaders(options) : { 'Content-Type': 'application/json' };
29058
+ const headers = options.useOPENAPI ? await generateCompleteHeaders(options) : { 'Content-Type': 'application/json' };
28943
29059
  console.log('本次服务调用方为:', baseURL);
28944
29060
  const instance = axios.create({
28945
29061
  baseURL,
@@ -37877,7 +37993,7 @@ async function tryCompile(entry, options) {
37877
37993
  }
37878
37994
  }
37879
37995
 
37880
- var version = "0.1.17";
37996
+ var version = "0.1.18";
37881
37997
  var pkg = {
37882
37998
  version: version};
37883
37999