@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.
- package/README.md +113 -11
- package/dist/bin/nasl.mjs +151 -34
- package/dist/bin/nasl.mjs.map +1 -1
- package/dist/bin/naslc.mjs +143 -27
- package/dist/bin/naslc.mjs.map +1 -1
- package/dist/index.mjs +150 -33
- package/dist/index.mjs.map +1 -1
- package/out/apis/cachable.d.ts +16 -0
- package/out/apis/cachable.d.ts.map +1 -0
- package/out/apis/cachable.js +142 -0
- package/out/apis/cachable.js.map +1 -0
- package/out/apis/createAxios.d.ts.map +1 -1
- package/out/apis/createAxios.js +2 -3
- package/out/apis/createAxios.js.map +1 -1
- package/out/apis/openapi.d.ts +5 -2
- package/out/apis/openapi.d.ts.map +1 -1
- package/out/apis/openapi.js +21 -25
- package/out/apis/openapi.js.map +1 -1
- package/out/commands/createAppInIde.d.ts.map +1 -1
- package/out/commands/createAppInIde.js +4 -0
- package/out/commands/createAppInIde.js.map +1 -1
- package/out/commands/transform.d.ts.map +1 -1
- package/out/commands/transform.js +1 -4
- package/out/commands/transform.js.map +1 -1
- package/out/utils/config.d.ts.map +1 -1
- package/out/utils/config.js +31 -2
- package/out/utils/config.js.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as sysPath from 'path';
|
|
2
|
-
import sysPath__default from 'path';
|
|
3
|
-
import require$$0$3, { unwatchFile, watchFile, watch as watch$1, stat as stat$3 } from 'fs';
|
|
2
|
+
import sysPath__default, { join } from 'path';
|
|
3
|
+
import require$$0$3, { promises, unwatchFile, watchFile, watch as watch$1, stat as stat$3 } from 'fs';
|
|
4
4
|
import stream$4, { Readable } from 'stream';
|
|
5
5
|
import require$$0$1, { type as type$1 } from 'os';
|
|
6
6
|
import require$$0$2 from 'util';
|
|
@@ -11,14 +11,14 @@ import require$$5 from 'assert';
|
|
|
11
11
|
import require$$3 from 'http';
|
|
12
12
|
import require$$4 from 'https';
|
|
13
13
|
import require$$0$6 from 'url';
|
|
14
|
-
import crypto$1 from 'crypto';
|
|
14
|
+
import crypto$1, { createHash } from 'crypto';
|
|
15
15
|
import http2 from 'http2';
|
|
16
16
|
import zlib from 'zlib';
|
|
17
17
|
import { spawn, spawnSync } from 'child_process';
|
|
18
18
|
import { realpath as realpath$1, stat as stat$2, lstat as lstat$1, open, readdir as readdir$1 } from 'fs/promises';
|
|
19
19
|
import { lstat, stat as stat$1, readdir, realpath } from 'node:fs/promises';
|
|
20
20
|
import { Readable as Readable$1 } from 'node:stream';
|
|
21
|
-
import { resolve, join, relative, sep } from 'node:path';
|
|
21
|
+
import { resolve, join as join$1, relative, sep } from 'node:path';
|
|
22
22
|
|
|
23
23
|
/**
|
|
24
24
|
* 默认配置
|
|
@@ -13582,10 +13582,39 @@ function loadConfig(configDir) {
|
|
|
13582
13582
|
const content = libExports.readFileSync(configPath, 'utf-8');
|
|
13583
13583
|
const config = JSON.parse(content);
|
|
13584
13584
|
// 验证必需字段
|
|
13585
|
-
if (!config.
|
|
13586
|
-
defaultLogger.error('
|
|
13585
|
+
if (!config.srcDir || !config.outDir) {
|
|
13586
|
+
defaultLogger.error('配置文件格式不正确,缺少必需的 srcDir 或 outDir 字段');
|
|
13587
13587
|
return defaultLogger.exit(1);
|
|
13588
13588
|
}
|
|
13589
|
+
if (!config.ideVersion) {
|
|
13590
|
+
config.ideVersion = process.env.NASL_IDE_VERSION || '';
|
|
13591
|
+
if (!config.ideVersion) {
|
|
13592
|
+
defaultLogger.error('缺少配置 ideVersion,请在配置文件中添加,或在环境变量中配置 NASL_IDE_VERSION');
|
|
13593
|
+
return defaultLogger.exit(1);
|
|
13594
|
+
}
|
|
13595
|
+
}
|
|
13596
|
+
if (!config.serverBaseURL) {
|
|
13597
|
+
config.serverBaseURL = process.env.NASL_SERVER_BASE_URL || '';
|
|
13598
|
+
if (!config.serverBaseURL) {
|
|
13599
|
+
defaultLogger.error('缺少配置 serverBaseURL,请在配置文件中添加,或在环境变量中配置 NASL_SERVER_BASE_URL');
|
|
13600
|
+
return defaultLogger.exit(1);
|
|
13601
|
+
}
|
|
13602
|
+
}
|
|
13603
|
+
if (!config.useOPENAPI)
|
|
13604
|
+
config.useOPENAPI = process.env.USE_LCAP_OPENAPI === 'true';
|
|
13605
|
+
if (!config.OPENAPI_AK)
|
|
13606
|
+
config.OPENAPI_AK = process.env.LCAP_OPENAPI_AK;
|
|
13607
|
+
if (!config.OPENAPI_SK)
|
|
13608
|
+
config.OPENAPI_SK = process.env.LCAP_OPENAPI_SK;
|
|
13609
|
+
if (config.useOPENAPI) {
|
|
13610
|
+
if (!config.OPENAPI_AK || !config.OPENAPI_SK) {
|
|
13611
|
+
defaultLogger.error(`配置了 useOPENAPI,但缺少配置 OPENAPI_AK 和 OPENAPI_SK:
|
|
13612
|
+
- 在 nasl.config.json 中配置 OPENAPI_AK 和 OPENAPI_SK,或者在环境变量中配置 LCAP_OPENAPI_AK 和 LCAP_OPENAPI_SK
|
|
13613
|
+
- 或将 useOPENAPI 配置为 false
|
|
13614
|
+
`);
|
|
13615
|
+
return defaultLogger.exit(1);
|
|
13616
|
+
}
|
|
13617
|
+
}
|
|
13589
13618
|
return config;
|
|
13590
13619
|
}
|
|
13591
13620
|
catch (error) {
|
|
@@ -34401,18 +34430,99 @@ function v4(options, buf, offset) {
|
|
|
34401
34430
|
return unsafeStringify(rnds);
|
|
34402
34431
|
}
|
|
34403
34432
|
|
|
34433
|
+
function generateHash(params) {
|
|
34434
|
+
const hash = createHash('sha256');
|
|
34435
|
+
hash.update(JSON.stringify(params));
|
|
34436
|
+
return hash.digest('hex');
|
|
34437
|
+
}
|
|
34438
|
+
async function getCachePathWithTimestamp(hash) {
|
|
34439
|
+
const cacheDir = join(process.cwd(), '.cache');
|
|
34440
|
+
await promises.mkdir(cacheDir, { recursive: true });
|
|
34441
|
+
return join(cacheDir, `${hash}_${Date.now()}.json`);
|
|
34442
|
+
}
|
|
34404
34443
|
/**
|
|
34405
|
-
*
|
|
34444
|
+
* 查找指定 hash 的有效缓存文件
|
|
34445
|
+
* @param hash 缓存标识
|
|
34446
|
+
* @param ttl 缓存有效期(毫秒)
|
|
34447
|
+
* @returns 缓存文件路径和缓存内容,如果没有有效缓存则返回 null
|
|
34406
34448
|
*/
|
|
34407
|
-
function
|
|
34408
|
-
|
|
34449
|
+
async function findValidCache(hash, ttl) {
|
|
34450
|
+
const cacheDir = join(process.cwd(), '.cache');
|
|
34451
|
+
try {
|
|
34452
|
+
await promises.mkdir(cacheDir, { recursive: true });
|
|
34453
|
+
const files = await promises.readdir(cacheDir);
|
|
34454
|
+
const now = Date.now();
|
|
34455
|
+
// 查找所有匹配的缓存文件
|
|
34456
|
+
const matchingFiles = files
|
|
34457
|
+
.filter((file) => file.startsWith(`${hash}_`) && file.endsWith('.json'))
|
|
34458
|
+
.map((file) => {
|
|
34459
|
+
const match = file.match(/_(\d+)\.json$/);
|
|
34460
|
+
if (!match)
|
|
34461
|
+
return null;
|
|
34462
|
+
const timestamp = parseInt(match[1], 10);
|
|
34463
|
+
return {
|
|
34464
|
+
path: join(cacheDir, file),
|
|
34465
|
+
timestamp,
|
|
34466
|
+
isExpired: now - timestamp > ttl,
|
|
34467
|
+
};
|
|
34468
|
+
})
|
|
34469
|
+
.filter((item) => item !== null);
|
|
34470
|
+
// 删除过期的缓存文件
|
|
34471
|
+
const expiredFiles = matchingFiles.filter((item) => item.isExpired);
|
|
34472
|
+
await Promise.all(expiredFiles.map((item) => promises.unlink(item.path).catch(() => { })));
|
|
34473
|
+
// 查找有效的缓存文件(按时间戳降序排序,获取最新的)
|
|
34474
|
+
const validFiles = matchingFiles.filter((item) => !item.isExpired).sort((a, b) => b.timestamp - a.timestamp);
|
|
34475
|
+
if (validFiles.length > 0) {
|
|
34476
|
+
const cacheFile = validFiles[0];
|
|
34477
|
+
const cacheContent = await promises.readFile(cacheFile.path, 'utf-8');
|
|
34478
|
+
const cachedResult = JSON.parse(cacheContent);
|
|
34479
|
+
return { path: cacheFile.path, data: cachedResult };
|
|
34480
|
+
}
|
|
34481
|
+
return null;
|
|
34482
|
+
}
|
|
34483
|
+
catch (error) {
|
|
34484
|
+
return null;
|
|
34485
|
+
}
|
|
34409
34486
|
}
|
|
34410
34487
|
/**
|
|
34411
|
-
*
|
|
34488
|
+
* 高阶函数,为异步函数添加带时间戳的缓存功能
|
|
34489
|
+
* @param fn 需要添加缓存的函数
|
|
34490
|
+
* @param ttl 缓存有效期(毫秒),默认 1 小时
|
|
34491
|
+
* @param getCacheKey 可选的自定义缓存 key 生成函数,如果不提供则使用整个 params
|
|
34492
|
+
* @returns 带缓存的函数版本
|
|
34412
34493
|
*/
|
|
34413
|
-
function
|
|
34414
|
-
|
|
34494
|
+
function cachableWithTTL(fn, ttl = 3600 * 1000, // 默认 1 小时
|
|
34495
|
+
getCacheKey) {
|
|
34496
|
+
return async (params, extraParams) => {
|
|
34497
|
+
const functionName = fn.name;
|
|
34498
|
+
const logPrefix = functionName ? `[${functionName}]` : '[cachableWithTTL]';
|
|
34499
|
+
// 检查是否启用缓存
|
|
34500
|
+
if (process.env.NO_AI_CACHE) {
|
|
34501
|
+
return fn(params, extraParams);
|
|
34502
|
+
}
|
|
34503
|
+
// 使用自定义的 getCacheKey 或默认使用整个 params
|
|
34504
|
+
const cacheKey = getCacheKey ? getCacheKey(params) : params;
|
|
34505
|
+
const hash = generateHash(cacheKey);
|
|
34506
|
+
// 查找有效的缓存
|
|
34507
|
+
const validCache = await findValidCache(hash, ttl);
|
|
34508
|
+
if (validCache) {
|
|
34509
|
+
console.log(`${logPrefix} ✓ 使用缓存`);
|
|
34510
|
+
return validCache.data;
|
|
34511
|
+
}
|
|
34512
|
+
// 调用原函数
|
|
34513
|
+
const result = await fn(params, extraParams);
|
|
34514
|
+
// 保存结果到缓存(带时间戳)
|
|
34515
|
+
try {
|
|
34516
|
+
const cachePath = await getCachePathWithTimestamp(hash);
|
|
34517
|
+
await promises.writeFile(cachePath, JSON.stringify(result, null, 2), 'utf-8');
|
|
34518
|
+
}
|
|
34519
|
+
catch (error) {
|
|
34520
|
+
// 缓存写入失败,但不影响返回结果
|
|
34521
|
+
}
|
|
34522
|
+
return result;
|
|
34523
|
+
};
|
|
34415
34524
|
}
|
|
34525
|
+
|
|
34416
34526
|
/**
|
|
34417
34527
|
* 生成客户端签名信息
|
|
34418
34528
|
* @param appKey AppKey
|
|
@@ -34422,8 +34532,8 @@ function generateSignature(plainText) {
|
|
|
34422
34532
|
function generateClientSignature(appKey, secretKey) {
|
|
34423
34533
|
const timestamp = Math.floor(Date.now() / 1000).toString();
|
|
34424
34534
|
const nonce = v4();
|
|
34425
|
-
const plainText =
|
|
34426
|
-
const signature =
|
|
34535
|
+
const plainText = `${appKey}&${nonce}&${timestamp}&${secretKey}`;
|
|
34536
|
+
const signature = crypto$1.createHash('md5').update(plainText).digest('hex');
|
|
34427
34537
|
return {
|
|
34428
34538
|
appKey,
|
|
34429
34539
|
timestamp,
|
|
@@ -34448,12 +34558,12 @@ function generateBaseHeaders(ak, sk) {
|
|
|
34448
34558
|
};
|
|
34449
34559
|
}
|
|
34450
34560
|
/**
|
|
34451
|
-
* 获取租户的 signInfo
|
|
34561
|
+
* 获取租户的 signInfo(内部实现)
|
|
34452
34562
|
* @param options 服务器选项
|
|
34453
34563
|
* @param baseHeaders 基础认证头
|
|
34454
34564
|
* @returns x-signInfo 值
|
|
34455
34565
|
*/
|
|
34456
|
-
async function
|
|
34566
|
+
async function _fetchSignInfo({ options, baseHeaders }) {
|
|
34457
34567
|
const tenantName = options.tenantName || 'defaulttenant';
|
|
34458
34568
|
const userName = tenantName === 'defaulttenant' ? 'admin' : `${tenantName}-admin`;
|
|
34459
34569
|
const data = {
|
|
@@ -34475,6 +34585,14 @@ async function fetchSignInfo(options, baseHeaders) {
|
|
|
34475
34585
|
return null;
|
|
34476
34586
|
}
|
|
34477
34587
|
}
|
|
34588
|
+
/**
|
|
34589
|
+
* 获取租户的 signInfo(带缓存,1 小时过期)
|
|
34590
|
+
* @param options 服务器选项
|
|
34591
|
+
* @param baseHeaders 基础认证头
|
|
34592
|
+
* @returns x-signInfo 值
|
|
34593
|
+
*/
|
|
34594
|
+
const fetchSignInfo = cachableWithTTL(_fetchSignInfo, 3600 * 1000, // 1 小时
|
|
34595
|
+
(params) => params.options);
|
|
34478
34596
|
/**
|
|
34479
34597
|
* 生成完整的认证头(包含 x-signInfo)
|
|
34480
34598
|
* @param options 服务器选项
|
|
@@ -34484,19 +34602,18 @@ async function generateCompleteHeaders(options) {
|
|
|
34484
34602
|
const headers = {
|
|
34485
34603
|
'Content-Type': 'application/json',
|
|
34486
34604
|
};
|
|
34487
|
-
const OPENAPI_AK = options.OPENAPI_AK || process.env.LCAP_OPENAPI_AK;
|
|
34488
|
-
const OPENAPI_SK = options.OPENAPI_SK || process.env.LCAP_OPENAPI_SK;
|
|
34489
34605
|
// 如果没有提供 ak 和 sk,返回基础 headers
|
|
34490
|
-
if (!OPENAPI_AK || !OPENAPI_SK) {
|
|
34491
|
-
throw new Error(`配置了 useOPENAPI
|
|
34492
|
-
-
|
|
34493
|
-
-
|
|
34606
|
+
if (!options.OPENAPI_AK || !options.OPENAPI_SK) {
|
|
34607
|
+
throw new Error(`配置了 useOPENAPI,但缺少配置 OPENAPI_AK 和 OPENAPI_SK:
|
|
34608
|
+
- 在 nasl.config.json 中配置 OPENAPI_AK 和 OPENAPI_SK,或者在环境变量中配置 LCAP_OPENAPI_AK 和 LCAP_OPENAPI_SK
|
|
34609
|
+
- 或将 useOPENAPI 配置为 false
|
|
34610
|
+
`);
|
|
34494
34611
|
}
|
|
34495
34612
|
// 生成基础认证头
|
|
34496
|
-
const baseHeaders = generateBaseHeaders(OPENAPI_AK, OPENAPI_SK);
|
|
34613
|
+
const baseHeaders = generateBaseHeaders(options.OPENAPI_AK, options.OPENAPI_SK);
|
|
34497
34614
|
Object.assign(headers, baseHeaders);
|
|
34498
34615
|
// 获取 signInfo
|
|
34499
|
-
const signInfo = await fetchSignInfo(options, headers);
|
|
34616
|
+
const signInfo = await fetchSignInfo({ options, baseHeaders: headers });
|
|
34500
34617
|
if (signInfo) {
|
|
34501
34618
|
headers['x-signInfo'] = signInfo;
|
|
34502
34619
|
}
|
|
@@ -34511,10 +34628,9 @@ async function generateCompleteHeaders(options) {
|
|
|
34511
34628
|
async function createAxios(options) {
|
|
34512
34629
|
// 如果需要鉴权,拼接 /openapi/v3/nasl;否则使用原始 URL
|
|
34513
34630
|
const serverBaseURL = new URL(options.serverBaseURL).origin;
|
|
34514
|
-
const
|
|
34515
|
-
const baseURL = useOPENAPI ? `${serverBaseURL}/openapi/v3/nasl` : `${serverBaseURL}/api/v1/nasl`;
|
|
34631
|
+
const baseURL = options.useOPENAPI ? `${serverBaseURL}/openapi/v3/nasl` : `${serverBaseURL}/api/v1/nasl`;
|
|
34516
34632
|
// 如果需要鉴权,生成完整的认证头;否则只使用基础 headers
|
|
34517
|
-
const headers = useOPENAPI ? await generateCompleteHeaders(options) : { 'Content-Type': 'application/json' };
|
|
34633
|
+
const headers = options.useOPENAPI ? await generateCompleteHeaders(options) : { 'Content-Type': 'application/json' };
|
|
34518
34634
|
console.log('本次服务调用方为:', baseURL);
|
|
34519
34635
|
const instance = axios.create({
|
|
34520
34636
|
baseURL,
|
|
@@ -34602,7 +34718,7 @@ async function checkApi(fullNaturalTS, options) {
|
|
|
34602
34718
|
async function createAppSyncApi(fullNaturalTS, options) {
|
|
34603
34719
|
const url = new URL(options.serverBaseURL);
|
|
34604
34720
|
const origin = url.origin + '/app-ai-creator';
|
|
34605
|
-
const axios = await createAxios({ serverBaseURL: origin});
|
|
34721
|
+
const axios = await createAxios({ serverBaseURL: origin, ideVersion: options.ideVersion });
|
|
34606
34722
|
try {
|
|
34607
34723
|
const res = await axios.post('/api/createAppSync', {
|
|
34608
34724
|
fullNaturalTS,
|
|
@@ -34975,6 +35091,10 @@ async function createAppInIde(entry, options) {
|
|
|
34975
35091
|
logger.info('开始创建应用在 IDE 中...');
|
|
34976
35092
|
// 收集需要处理的文件
|
|
34977
35093
|
const { collectedFiles, config } = await resolveNASLFiles(entry, logger, false, options?.verbose);
|
|
35094
|
+
if (config.useOPENAPI) {
|
|
35095
|
+
logger.error('create-app-in-ide 暂不支持 useOPENAPI 模式');
|
|
35096
|
+
logger.exit(1);
|
|
35097
|
+
}
|
|
34978
35098
|
// 生成 fullNaturalTS
|
|
34979
35099
|
logger.newLine();
|
|
34980
35100
|
logger.info('正在生成 NaturalTS 代码...');
|
|
@@ -35094,10 +35214,7 @@ const transformFns = {
|
|
|
35094
35214
|
jsonContent = libExports.readFileSync(entry, 'utf-8');
|
|
35095
35215
|
logger.info(`读取到 JSON 文件: ${entry}`);
|
|
35096
35216
|
}
|
|
35097
|
-
const files = await transformJson2FilesApi(jsonContent ? JSON.parse(jsonContent) : {},
|
|
35098
|
-
serverBaseURL: config.serverBaseURL,
|
|
35099
|
-
ideVersion: config.ideVersion,
|
|
35100
|
-
});
|
|
35217
|
+
const files = await transformJson2FilesApi(jsonContent ? JSON.parse(jsonContent) : {}, config);
|
|
35101
35218
|
await Promise.all(files.map(async (file) => {
|
|
35102
35219
|
const outputPath = sysPath.join(projectRoot, config.srcDir, file.path);
|
|
35103
35220
|
await libExports.writeFile(outputPath, file.content);
|
|
@@ -35281,7 +35398,7 @@ class ReaddirpStream extends Readable$1 {
|
|
|
35281
35398
|
let entry;
|
|
35282
35399
|
const basename = this._isDirent ? dirent.name : dirent;
|
|
35283
35400
|
try {
|
|
35284
|
-
const fullPath = resolve(join(path, basename));
|
|
35401
|
+
const fullPath = resolve(join$1(path, basename));
|
|
35285
35402
|
entry = { path: relative(this._root, fullPath), fullPath, basename };
|
|
35286
35403
|
entry[this._statsProp] = this._isDirent ? dirent : await this._stat(fullPath);
|
|
35287
35404
|
}
|