@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/index.mjs CHANGED
@@ -114,7 +114,10 @@ function validateNormalFile(file, nameFromPath, namespace, errors) {
114
114
  function composeToString(files) {
115
115
  files.sort((a, b) => sorter(a.path, b.path));
116
116
  const errors = [];
117
+ let currentLine = 1;
117
118
  const result = files.map((file) => {
119
+ // 记录这个文件的开始行号
120
+ file.startLine = currentLine;
118
121
  const arr = file.path.split('.');
119
122
  const ext = arr.pop();
120
123
  const nameFromPath = arr.pop();
@@ -140,7 +143,14 @@ function composeToString(files) {
140
143
  else if (isThemeCss) {
141
144
  content = ` $theme\`${content}\`;`;
142
145
  }
143
- return `namespace ${namespace} {\n${content}\n}\n`;
146
+ const fileContent = `namespace ${namespace} {\n${content}\n}\n`;
147
+ // 计算这个文件占用的行数(末尾 \n 导致 split 多一个空元素,减 1 得到实际行数)
148
+ const lineCount = fileContent.split('\n').length - 1;
149
+ // 记录这个文件的结束行号
150
+ file.endLine = currentLine + lineCount - 1;
151
+ // +1 是 join('\n') 在文件之间插入的换行符
152
+ currentLine = file.endLine + 2;
153
+ return fileContent;
144
154
  })
145
155
  .join('\n');
146
156
  if (errors.length > 0) {
@@ -34817,7 +34827,7 @@ async function generateCompleteHeaders(options) {
34817
34827
  async function createAxios(options) {
34818
34828
  // 如果需要鉴权,拼接 /openapi/v3/nasl;否则使用原始 URL
34819
34829
  const serverBaseURL = new URL(options.serverBaseURL).origin;
34820
- const baseURL = options.useOPENAPI ? `${serverBaseURL}/openapi/v3/nasl` : `${serverBaseURL}/api/v1/nasl`;
34830
+ const baseURL = options.serverBaseURL.endsWith('app-ai-creator') ? options.serverBaseURL : options.useOPENAPI ? `${serverBaseURL}/openapi/v3/nasl` : `${serverBaseURL}/api/v1/nasl`;
34821
34831
  // 如果需要鉴权,生成完整的认证头;否则只使用基础 headers
34822
34832
  const headers = options.useOPENAPI ? await generateCompleteHeaders(options) : { 'Content-Type': 'application/json' };
34823
34833
  console.log('本次服务调用方为:', baseURL);
@@ -34830,7 +34840,7 @@ async function createAxios(options) {
34830
34840
  instance.post = async (url, data, config) => {
34831
34841
  return oldPost(url, data, config).then((res) => {
34832
34842
  const data = res.data;
34833
- if (data.code !== 200)
34843
+ if (data.code !== 200 && data.success !== true)
34834
34844
  throw new Error(JSON.stringify(data));
34835
34845
  return res;
34836
34846
  }).catch((err) => {
@@ -34982,6 +34992,63 @@ async function tryCompile(entry, options) {
34982
34992
  }
34983
34993
  }
34984
34994
 
34995
+ /**
34996
+ * 将错误信息中的行号映射回原始文件
34997
+ * 直接替换错误头中的文件路径和行号,并调整代码片段中的行号
34998
+ */
34999
+ function mapErrorToSourceFile(options) {
35000
+ const { errorMessage, collectedFiles } = options;
35001
+ // 构建行号到文件的映射
35002
+ const lineToFileMap = new Map();
35003
+ collectedFiles.forEach((file) => {
35004
+ if (file.startLine !== undefined && file.endLine !== undefined) {
35005
+ for (let i = file.startLine; i <= file.endLine; i++) {
35006
+ const lineInFile = i - file.startLine;
35007
+ lineToFileMap.set(i, { file, lineInFile });
35008
+ }
35009
+ }
35010
+ });
35011
+ // 处理单个错误块:替换路径、行号,调整代码片段行号
35012
+ function processBlock(block) {
35013
+ // 匹配错误头: 可选的 "N. " 前缀 + "filepath: message (line:col)"
35014
+ const headerMatch = block.match(/^((?:\d+\.\s+)?)(.*?):\s+(.*?)\((\d+):(\d+)\)/);
35015
+ if (!headerMatch)
35016
+ return block;
35017
+ const [fullHeader, prefix, , message, lineStr, colStr] = headerMatch;
35018
+ const originalLine = parseInt(lineStr, 10);
35019
+ const fileInfo = lineToFileMap.get(originalLine);
35020
+ if (!fileInfo)
35021
+ return block;
35022
+ const { file, lineInFile } = fileInfo;
35023
+ const lineOffset = originalLine - lineInFile;
35024
+ // 替换错误头中的路径和行号
35025
+ let mapped = block.replace(fullHeader, `${prefix}${file.path}: ${message}(${lineInFile}:${colStr})`);
35026
+ // 收集代码片段中所有行号并计算映射后的值
35027
+ const snippetNumbers = [];
35028
+ const collectRegex = /^([> ]*?)(\d+)(\s*\|)/gm;
35029
+ let m;
35030
+ while ((m = collectRegex.exec(mapped)) !== null) {
35031
+ snippetNumbers.push(parseInt(m[2], 10) - lineOffset);
35032
+ }
35033
+ // 替换代码片段中的行号,保持对齐
35034
+ if (snippetNumbers.length > 0) {
35035
+ const maxWidth = Math.max(...snippetNumbers.map((n) => String(n).length));
35036
+ let idx = 0;
35037
+ mapped = mapped.replace(/^([> ]*?)(\d+)(\s*\|)/gm, (fullMatch, lead) => {
35038
+ const newNum = snippetNumbers[idx++];
35039
+ if (newNum <= 0)
35040
+ return fullMatch;
35041
+ const paddedNum = String(newNum).padStart(maxWidth);
35042
+ return lead.includes('>') ? `> ${paddedNum} |` : ` ${paddedNum} |`;
35043
+ });
35044
+ }
35045
+ return mapped;
35046
+ }
35047
+ // 按编号前缀分割为独立错误块,分别处理
35048
+ const parts = errorMessage.split(/(?=^\d+\.\s)/m);
35049
+ return parts.map(processBlock).join('');
35050
+ }
35051
+
34985
35052
  /**
34986
35053
  * 检查命令 - 检查 NASL 代码的语法和语义
34987
35054
  */
@@ -35003,6 +35070,13 @@ async function check(entry, options) {
35003
35070
  }
35004
35071
  if (!result && fullNaturalTS) {
35005
35072
  result = (await checkApi(fullNaturalTS, config)).trim();
35073
+ // 如果有错误,尝试映射回原始文件
35074
+ if (result) {
35075
+ result = mapErrorToSourceFile({
35076
+ errorMessage: result,
35077
+ collectedFiles,
35078
+ });
35079
+ }
35006
35080
  }
35007
35081
  const checkResult = {
35008
35082
  success: !result,
@@ -35012,6 +35086,8 @@ async function check(entry, options) {
35012
35086
  fastLogToFile(entry || '*', `check_${checkResult.success ? 'success' : 'error'}.log`, result + '\n================================================\n\n================================================\n' + fullNaturalTS);
35013
35087
  if (!checkResult.success)
35014
35088
  logger.error('\n' + checkResult.result);
35089
+ else
35090
+ logger.success('发现 0 个错误,检查通过!');
35015
35091
  return checkResult;
35016
35092
  }
35017
35093
 
@@ -35300,10 +35376,10 @@ async function createAppInIde(entry, options) {
35300
35376
  const projectRoot = getProjectRoot();
35301
35377
  const firstSpecFolderName = getFirstSpecFolderName(projectRoot);
35302
35378
  const suffix = dayjs().format('MMDDHHmmss');
35303
- const appName = `${firstSpecFolderName}_${suffix}`;
35304
- let cleaned = firstSpecFolderName.replace(/[^a-zA-Z0-9]/g, '').replace(/^\d+/, '');
35379
+ let cleaned = firstSpecFolderName.replace(/[^a-zA-Z0-9]/g, '').replace(/^\d+/, '').slice(0, 12);
35305
35380
  if (!cleaned)
35306
35381
  cleaned = 'app';
35382
+ const appName = `${cleaned}_${suffix}`;
35307
35383
  const packageName = `${cleaned}${suffix}`;
35308
35384
  logger.info(`应用名称: ${appName}`);
35309
35385
  logger.info(`包名: ${packageName}`);