@nasl/cli 0.2.1 → 0.2.3

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/dist/bin/nasl.mjs CHANGED
@@ -29125,7 +29125,7 @@ async function generateCompleteHeaders(options) {
29125
29125
  async function createAxios(options) {
29126
29126
  // 如果需要鉴权,拼接 /openapi/v3/nasl;否则使用原始 URL
29127
29127
  const serverBaseURL = new URL(options.serverBaseURL).origin;
29128
- const baseURL = options.useOPENAPI ? `${serverBaseURL}/openapi/v3/nasl` : `${serverBaseURL}/api/v1/nasl`;
29128
+ const baseURL = options.serverBaseURL.endsWith('app-ai-creator') ? options.serverBaseURL : options.useOPENAPI ? `${serverBaseURL}/openapi/v3/nasl` : `${serverBaseURL}/api/v1/nasl`;
29129
29129
  // 如果需要鉴权,生成完整的认证头;否则只使用基础 headers
29130
29130
  const headers = options.useOPENAPI ? await generateCompleteHeaders(options) : { 'Content-Type': 'application/json' };
29131
29131
  console.log('本次服务调用方为:', baseURL);
@@ -29138,7 +29138,7 @@ async function createAxios(options) {
29138
29138
  instance.post = async (url, data, config) => {
29139
29139
  return oldPost(url, data, config).then((res) => {
29140
29140
  const data = res.data;
29141
- if (data.code !== 200)
29141
+ if (data.code !== 200 && data.success !== true)
29142
29142
  throw new Error(JSON.stringify(data));
29143
29143
  return res;
29144
29144
  }).catch((err) => {
@@ -29326,7 +29326,10 @@ function validateNormalFile(file, nameFromPath, namespace, errors) {
29326
29326
  function composeToString(files) {
29327
29327
  files.sort((a, b) => sorter(a.path, b.path));
29328
29328
  const errors = [];
29329
+ let currentLine = 1;
29329
29330
  const result = files.map((file) => {
29331
+ // 记录这个文件的开始行号
29332
+ file.startLine = currentLine;
29330
29333
  const arr = file.path.split('.');
29331
29334
  const ext = arr.pop();
29332
29335
  const nameFromPath = arr.pop();
@@ -29352,7 +29355,14 @@ function composeToString(files) {
29352
29355
  else if (isThemeCss) {
29353
29356
  content = ` $theme\`${content}\`;`;
29354
29357
  }
29355
- return `namespace ${namespace} {\n${content}\n}\n`;
29358
+ const fileContent = `namespace ${namespace} {\n${content}\n}\n`;
29359
+ // 计算这个文件占用的行数(末尾 \n 导致 split 多一个空元素,减 1 得到实际行数)
29360
+ const lineCount = fileContent.split('\n').length - 1;
29361
+ // 记录这个文件的结束行号
29362
+ file.endLine = currentLine + lineCount - 1;
29363
+ // +1 是 join('\n') 在文件之间插入的换行符
29364
+ currentLine = file.endLine + 2;
29365
+ return fileContent;
29356
29366
  })
29357
29367
  .join('\n');
29358
29368
  if (errors.length > 0) {
@@ -38312,6 +38322,63 @@ async function tryCompile(entry, options) {
38312
38322
  }
38313
38323
  }
38314
38324
 
38325
+ /**
38326
+ * 将错误信息中的行号映射回原始文件
38327
+ * 直接替换错误头中的文件路径和行号,并调整代码片段中的行号
38328
+ */
38329
+ function mapErrorToSourceFile(options) {
38330
+ const { errorMessage, collectedFiles } = options;
38331
+ // 构建行号到文件的映射
38332
+ const lineToFileMap = new Map();
38333
+ collectedFiles.forEach((file) => {
38334
+ if (file.startLine !== undefined && file.endLine !== undefined) {
38335
+ for (let i = file.startLine; i <= file.endLine; i++) {
38336
+ const lineInFile = i - file.startLine;
38337
+ lineToFileMap.set(i, { file, lineInFile });
38338
+ }
38339
+ }
38340
+ });
38341
+ // 处理单个错误块:替换路径、行号,调整代码片段行号
38342
+ function processBlock(block) {
38343
+ // 匹配错误头: 可选的 "N. " 前缀 + "filepath: message (line:col)"
38344
+ const headerMatch = block.match(/^((?:\d+\.\s+)?)(.*?):\s+(.*?)\((\d+):(\d+)\)/);
38345
+ if (!headerMatch)
38346
+ return block;
38347
+ const [fullHeader, prefix, , message, lineStr, colStr] = headerMatch;
38348
+ const originalLine = parseInt(lineStr, 10);
38349
+ const fileInfo = lineToFileMap.get(originalLine);
38350
+ if (!fileInfo)
38351
+ return block;
38352
+ const { file, lineInFile } = fileInfo;
38353
+ const lineOffset = originalLine - lineInFile;
38354
+ // 替换错误头中的路径和行号
38355
+ let mapped = block.replace(fullHeader, `${prefix}${file.path}: ${message}(${lineInFile}:${colStr})`);
38356
+ // 收集代码片段中所有行号并计算映射后的值
38357
+ const snippetNumbers = [];
38358
+ const collectRegex = /^([> ]*?)(\d+)(\s*\|)/gm;
38359
+ let m;
38360
+ while ((m = collectRegex.exec(mapped)) !== null) {
38361
+ snippetNumbers.push(parseInt(m[2], 10) - lineOffset);
38362
+ }
38363
+ // 替换代码片段中的行号,保持对齐
38364
+ if (snippetNumbers.length > 0) {
38365
+ const maxWidth = Math.max(...snippetNumbers.map((n) => String(n).length));
38366
+ let idx = 0;
38367
+ mapped = mapped.replace(/^([> ]*?)(\d+)(\s*\|)/gm, (fullMatch, lead) => {
38368
+ const newNum = snippetNumbers[idx++];
38369
+ if (newNum <= 0)
38370
+ return fullMatch;
38371
+ const paddedNum = String(newNum).padStart(maxWidth);
38372
+ return lead.includes('>') ? `> ${paddedNum} |` : ` ${paddedNum} |`;
38373
+ });
38374
+ }
38375
+ return mapped;
38376
+ }
38377
+ // 按编号前缀分割为独立错误块,分别处理
38378
+ const parts = errorMessage.split(/(?=^\d+\.\s)/m);
38379
+ return parts.map(processBlock).join('');
38380
+ }
38381
+
38315
38382
  /**
38316
38383
  * 检查命令 - 检查 NASL 代码的语法和语义
38317
38384
  */
@@ -38333,6 +38400,13 @@ async function check(entry, options) {
38333
38400
  }
38334
38401
  if (!result && fullNaturalTS) {
38335
38402
  result = (await checkApi(fullNaturalTS, config)).trim();
38403
+ // 如果有错误,尝试映射回原始文件
38404
+ if (result) {
38405
+ result = mapErrorToSourceFile({
38406
+ errorMessage: result,
38407
+ collectedFiles,
38408
+ });
38409
+ }
38336
38410
  }
38337
38411
  const checkResult = {
38338
38412
  success: !result,
@@ -38342,6 +38416,8 @@ async function check(entry, options) {
38342
38416
  fastLogToFile(entry || '*', `check_${checkResult.success ? 'success' : 'error'}.log`, result + '\n================================================\n\n================================================\n' + fullNaturalTS);
38343
38417
  if (!checkResult.success)
38344
38418
  logger.error('\n' + checkResult.result);
38419
+ else
38420
+ logger.success('发现 0 个错误,检查通过!');
38345
38421
  return checkResult;
38346
38422
  }
38347
38423
 
@@ -38630,10 +38706,10 @@ async function createAppInIde(entry, options) {
38630
38706
  const projectRoot = getProjectRoot();
38631
38707
  const firstSpecFolderName = getFirstSpecFolderName(projectRoot);
38632
38708
  const suffix = dayjs().format('MMDDHHmmss');
38633
- const appName = `${firstSpecFolderName}_${suffix}`;
38634
- let cleaned = firstSpecFolderName.replace(/[^a-zA-Z0-9]/g, '').replace(/^\d+/, '');
38709
+ let cleaned = firstSpecFolderName.replace(/[^a-zA-Z0-9]/g, '').replace(/^\d+/, '').slice(0, 12);
38635
38710
  if (!cleaned)
38636
38711
  cleaned = 'app';
38712
+ const appName = `${cleaned}_${suffix}`;
38637
38713
  const packageName = `${cleaned}${suffix}`;
38638
38714
  logger.info(`应用名称: ${appName}`);
38639
38715
  logger.info(`包名: ${packageName}`);
@@ -38763,7 +38839,7 @@ async function transform(transformType, entry, options) {
38763
38839
  await transformFn(entry, options);
38764
38840
  }
38765
38841
 
38766
- var version = "0.2.1";
38842
+ var version = "0.2.3";
38767
38843
  var pkg = {
38768
38844
  version: version};
38769
38845
 
@@ -38780,7 +38856,7 @@ function askForConfirmation(question) {
38780
38856
  rl.question(question, (answer) => {
38781
38857
  rl.close();
38782
38858
  const normalizedAnswer = answer.trim().toLowerCase();
38783
- resolve(normalizedAnswer === 'y' || normalizedAnswer === 'yes' || normalizedAnswer === '是');
38859
+ resolve(!normalizedAnswer || normalizedAnswer === 'y' || normalizedAnswer === 'yes' || normalizedAnswer === '是');
38784
38860
  });
38785
38861
  });
38786
38862
  }
@@ -38873,7 +38949,7 @@ program
38873
38949
  if (!options?.quiet) {
38874
38950
  defaultLogger.newLine();
38875
38951
  defaultLogger.info('即将在 IDE 中创建新的应用,此操作将调用远程接口。');
38876
- const confirmed = await askForConfirmation('请确认是否继续?(y/N): ');
38952
+ const confirmed = await askForConfirmation('请确认是否继续?(Y/n): ');
38877
38953
  if (!confirmed) {
38878
38954
  defaultLogger.info('已取消操作');
38879
38955
  process.exit(0);