@nasl/cli 0.3.3 → 0.3.5
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 +24 -3
- package/dist/bin/nasl.mjs +192 -124
- package/dist/bin/nasl.mjs.map +1 -1
- package/dist/bin/naslc.mjs +38 -15
- package/dist/bin/naslc.mjs.map +1 -1
- package/dist/index.mjs +179 -97
- package/dist/index.mjs.map +1 -1
- package/out/apis/appApi.d.ts +17 -0
- package/out/apis/appApi.d.ts.map +1 -0
- package/out/apis/appApi.js +36 -0
- package/out/apis/appApi.js.map +1 -0
- package/out/apis/index.d.ts +1 -1
- package/out/apis/index.d.ts.map +1 -1
- package/out/apis/index.js +1 -1
- package/out/apis/index.js.map +1 -1
- package/out/bin/nasl.js +14 -61
- package/out/bin/nasl.js.map +1 -1
- package/out/commands/createAppInIde.d.ts +0 -3
- package/out/commands/createAppInIde.d.ts.map +1 -1
- package/out/commands/createAppInIde.js +26 -93
- package/out/commands/createAppInIde.js.map +1 -1
- package/out/commands/index.d.ts +1 -0
- package/out/commands/index.d.ts.map +1 -1
- package/out/commands/index.js +1 -0
- package/out/commands/index.js.map +1 -1
- package/out/commands/updateAppInIde.d.ts +3 -0
- package/out/commands/updateAppInIde.d.ts.map +1 -0
- package/out/commands/updateAppInIde.js +59 -0
- package/out/commands/updateAppInIde.js.map +1 -0
- package/out/constants/nasl-file-types.d.ts +1 -1
- package/out/constants/nasl-file-types.d.ts.map +1 -1
- package/out/constants/nasl-file-types.js +8 -1
- package/out/constants/nasl-file-types.js.map +1 -1
- package/out/services/compose.d.ts.map +1 -1
- package/out/services/compose.js +21 -8
- package/out/services/compose.js.map +1 -1
- package/out/services/resolve.d.ts.map +1 -1
- package/out/services/resolve.js +7 -5
- package/out/services/resolve.js.map +1 -1
- package/out/utils/appName.d.ts +8 -0
- package/out/utils/appName.d.ts.map +1 -0
- package/out/utils/appName.js +71 -0
- package/out/utils/appName.js.map +1 -0
- package/out/utils/prompt.d.ts +5 -0
- package/out/utils/prompt.d.ts.map +1 -0
- package/out/utils/prompt.js +54 -0
- package/out/utils/prompt.js.map +1 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -159,9 +159,13 @@ nasl transform files2json [entry] --output [outputPath]
|
|
|
159
159
|
# 默认输出到 ./files.json
|
|
160
160
|
```
|
|
161
161
|
|
|
162
|
-
### 在 IDE
|
|
162
|
+
### 在 IDE 中创建 / 更新应用
|
|
163
163
|
|
|
164
|
-
|
|
164
|
+
**注意**:`create-app-in-ide` 与 `update-app-in-ide` 只能在测试环境使用。
|
|
165
|
+
|
|
166
|
+
两个命令都会先按 [入口文件说明](#入口文件说明) 解析本地 NASL(可选 `[entry]`,不传则处理整个 `src`),将源码组合为 fullNaturalTS(含依赖)后调用远程服务。均支持 `--quiet` 跳过确认、`--verbose` 输出依赖分析等详细信息。
|
|
167
|
+
|
|
168
|
+
#### 在 IDE 中创建应用(nasl create-app-in-ide)
|
|
165
169
|
|
|
166
170
|
```bash
|
|
167
171
|
# 会询问确认(默认)
|
|
@@ -174,7 +178,24 @@ nasl create-app-in-ide [entry] --quiet
|
|
|
174
178
|
nasl create-app-in-ide [entry] --verbose
|
|
175
179
|
```
|
|
176
180
|
|
|
177
|
-
|
|
181
|
+
执行前会展示即将使用的**应用名称**、**包名**、IDE 版本与租户。应用名与包名由项目目录推导:若存在 `specs/` 目录,则取其中**第一个子文件夹名**作为基础名;否则使用**项目根目录名**。基础名经清洗(仅保留字母数字、去掉开头数字、最长 12 字符)后,再拼接时间后缀生成最终名称。
|
|
182
|
+
|
|
183
|
+
#### 在 IDE 中更新已有应用(nasl update-app-in-ide)
|
|
184
|
+
|
|
185
|
+
第一个参数为远程应用的 **appId**(必填),其余与创建命令一致。
|
|
186
|
+
|
|
187
|
+
```bash
|
|
188
|
+
# 会询问确认(默认)
|
|
189
|
+
nasl update-app-in-ide <appId> [entry]
|
|
190
|
+
|
|
191
|
+
# 跳过确认
|
|
192
|
+
nasl update-app-in-ide <appId> [entry] --quiet
|
|
193
|
+
|
|
194
|
+
# 显示详细信息
|
|
195
|
+
nasl update-app-in-ide <appId> [entry] --verbose
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
将当前本地 NASL 同步到指定应用,用于在 IDE 中更新已有应用而非新建。
|
|
178
199
|
|
|
179
200
|
## 环境变量配置
|
|
180
201
|
|
package/dist/bin/nasl.mjs
CHANGED
|
@@ -29227,41 +29227,33 @@ async function checkApi(fullNaturalTS, options) {
|
|
|
29227
29227
|
}
|
|
29228
29228
|
|
|
29229
29229
|
/**
|
|
29230
|
-
*
|
|
29230
|
+
* 创建应用(POST /nasl/app/create)
|
|
29231
29231
|
*/
|
|
29232
|
-
async function
|
|
29233
|
-
const
|
|
29234
|
-
const
|
|
29235
|
-
|
|
29236
|
-
|
|
29237
|
-
|
|
29238
|
-
|
|
29239
|
-
|
|
29240
|
-
|
|
29241
|
-
|
|
29242
|
-
|
|
29243
|
-
|
|
29244
|
-
|
|
29245
|
-
|
|
29246
|
-
|
|
29247
|
-
|
|
29248
|
-
|
|
29249
|
-
|
|
29250
|
-
|
|
29251
|
-
|
|
29252
|
-
|
|
29253
|
-
|
|
29254
|
-
|
|
29255
|
-
|
|
29256
|
-
|
|
29257
|
-
error: res.data.message || '应用创建失败',
|
|
29258
|
-
};
|
|
29259
|
-
}
|
|
29260
|
-
}
|
|
29261
|
-
catch (error) {
|
|
29262
|
-
console.log(error);
|
|
29263
|
-
throw error;
|
|
29264
|
-
}
|
|
29232
|
+
async function createApp(fullNaturalTS, options, appName, packageName) {
|
|
29233
|
+
const axiosInstance = await createAxios(options);
|
|
29234
|
+
const res = await axiosInstance.post('/app/create', {
|
|
29235
|
+
fullNaturalTS,
|
|
29236
|
+
packageName,
|
|
29237
|
+
appName,
|
|
29238
|
+
ideVersion: options.ideVersion,
|
|
29239
|
+
}, {
|
|
29240
|
+
headers: { ...buildNaslHeaders(options), 'Content-Type': 'application/json' },
|
|
29241
|
+
});
|
|
29242
|
+
return res.data.result;
|
|
29243
|
+
}
|
|
29244
|
+
/**
|
|
29245
|
+
* 更新应用(POST /nasl/app/update)
|
|
29246
|
+
*/
|
|
29247
|
+
async function updateApp(fullNaturalTS, options, appId) {
|
|
29248
|
+
const axiosInstance = await createAxios(options);
|
|
29249
|
+
const res = await axiosInstance.post('/app/update', {
|
|
29250
|
+
fullNaturalTS,
|
|
29251
|
+
appId,
|
|
29252
|
+
ideVersion: options.ideVersion,
|
|
29253
|
+
}, {
|
|
29254
|
+
headers: { ...buildNaslHeaders(options), 'Content-Type': 'application/json' },
|
|
29255
|
+
});
|
|
29256
|
+
return res.data.result;
|
|
29265
29257
|
}
|
|
29266
29258
|
|
|
29267
29259
|
async function installDependenciesApi(dependenciesJSON, options) {
|
|
@@ -29276,7 +29268,18 @@ async function installDependenciesApi(dependenciesJSON, options) {
|
|
|
29276
29268
|
}
|
|
29277
29269
|
|
|
29278
29270
|
function createSorter() {
|
|
29279
|
-
const priorityOrder = [
|
|
29271
|
+
const priorityOrder = [
|
|
29272
|
+
'extensions',
|
|
29273
|
+
'apis',
|
|
29274
|
+
'connectors',
|
|
29275
|
+
'app.backend.variables',
|
|
29276
|
+
'app.roles',
|
|
29277
|
+
'app.enums',
|
|
29278
|
+
'app.dataSources',
|
|
29279
|
+
'app.structures',
|
|
29280
|
+
'app.logics',
|
|
29281
|
+
'app.frontendTypes',
|
|
29282
|
+
];
|
|
29280
29283
|
// 获取优先级索引
|
|
29281
29284
|
const getPriority = (str) => {
|
|
29282
29285
|
const prefixIndex = priorityOrder.findIndex((p) => str.startsWith(p));
|
|
@@ -29290,13 +29293,11 @@ function createSorter() {
|
|
|
29290
29293
|
return aPriority - bPriority;
|
|
29291
29294
|
}
|
|
29292
29295
|
// 如果是frontendTypes,按字母顺序排序
|
|
29293
|
-
if (
|
|
29296
|
+
if (a.startsWith('app.frontendTypes') && b.startsWith('app.frontendTypes')) {
|
|
29294
29297
|
return a.localeCompare(b);
|
|
29295
29298
|
}
|
|
29296
|
-
|
|
29297
|
-
|
|
29298
|
-
return 0;
|
|
29299
|
-
}
|
|
29299
|
+
// 其他情况保持原顺序
|
|
29300
|
+
return 0;
|
|
29300
29301
|
};
|
|
29301
29302
|
}
|
|
29302
29303
|
const sorter = createSorter();
|
|
@@ -29364,11 +29365,15 @@ function composeToString(files, options) {
|
|
|
29364
29365
|
const isVariablesFile = nameFromPath === 'variables';
|
|
29365
29366
|
const isExtensionsFile = arr[0] === 'extensions';
|
|
29366
29367
|
const isThemeCss = ext === 'css' && nameFromPath === 'theme';
|
|
29367
|
-
const
|
|
29368
|
+
const isRolesJson = file.path === 'app.roles.json';
|
|
29369
|
+
const isSpecialFile = isVariablesFile || isExtensionsFile || isThemeCss || isRolesJson;
|
|
29368
29370
|
// 特殊文件的 namespace ${包含文件名,普通文件不包含
|
|
29369
29371
|
const namespace = isSpecialFile ? [...arr, nameFromPath].join('.') : arr.join('.');
|
|
29370
29372
|
let content = file.content;
|
|
29371
|
-
if (
|
|
29373
|
+
if (isRolesJson) {
|
|
29374
|
+
content = ` $roles(${content});`;
|
|
29375
|
+
}
|
|
29376
|
+
else if (['ts', 'tsx'].includes(ext)) {
|
|
29372
29377
|
if (isVariablesFile) {
|
|
29373
29378
|
validateVariablesFile(file, errors);
|
|
29374
29379
|
}
|
|
@@ -37935,6 +37940,13 @@ var globby = /*@__PURE__*/getDefaultExportFromCjs(globbyExports);
|
|
|
37935
37940
|
* NASL 支持的所有文件类型配置
|
|
37936
37941
|
*/
|
|
37937
37942
|
const NASL_FILE_TYPES = [
|
|
37943
|
+
{
|
|
37944
|
+
name: 'roles',
|
|
37945
|
+
description: '角色',
|
|
37946
|
+
fileNamePattern: 'app\\.roles\\.json',
|
|
37947
|
+
codeRefPattern: '',
|
|
37948
|
+
extension: 'json',
|
|
37949
|
+
},
|
|
37938
37950
|
{
|
|
37939
37951
|
name: 'theme-css',
|
|
37940
37952
|
description: '主题样式',
|
|
@@ -38031,7 +38043,7 @@ const fileNamePatterns = getFileNamePatterns();
|
|
|
38031
38043
|
* 获取用于提取代码引用的正则表达式数组
|
|
38032
38044
|
*/
|
|
38033
38045
|
function getCodeRefPatterns() {
|
|
38034
|
-
return NASL_FILE_TYPES.map((type) => new RegExp(type.codeRefPattern, 'g'));
|
|
38046
|
+
return NASL_FILE_TYPES.filter((type) => type.codeRefPattern).map((type) => new RegExp(type.codeRefPattern, 'g'));
|
|
38035
38047
|
}
|
|
38036
38048
|
const codeRefPatterns = getCodeRefPatterns();
|
|
38037
38049
|
/**
|
|
@@ -38066,7 +38078,7 @@ function shouldSkipDependencyTraversal(depPath) {
|
|
|
38066
38078
|
async function scanNASLFiles(srcDir, representation, logger) {
|
|
38067
38079
|
try {
|
|
38068
38080
|
const pattern = representation === 'NaturalTS' ? '**/*.{ts,tsx}' : '**/*.nasl';
|
|
38069
|
-
const files = await globby([pattern, '
|
|
38081
|
+
const files = await globby([pattern, 'app.frontendTypes.pc.frontends.pc.theme.css', 'app.roles.json'], {
|
|
38070
38082
|
cwd: srcDir,
|
|
38071
38083
|
onlyFiles: true,
|
|
38072
38084
|
absolute: false,
|
|
@@ -38300,9 +38312,9 @@ function buildExternalDependencies(refs, mergedDeps, logger) {
|
|
|
38300
38312
|
function replaceViewAsSignature(content, hasSubViews = false) {
|
|
38301
38313
|
if (hasSubViews) {
|
|
38302
38314
|
// 如果有子页面,替换为返回 ElRouterView 的函数
|
|
38303
|
-
return content.replace(/export function ((\w+)\([
|
|
38315
|
+
return content.replace(/export function ((\w+)\([\s\S]*?\)) \{[\s\S]+$/, 'export function $1 {\n return <ElRouterView />\n}');
|
|
38304
38316
|
}
|
|
38305
|
-
return content.replace(/export function ((\w+)\([
|
|
38317
|
+
return content.replace(/export function ((\w+)\([\s\S]*?\)) \{[\s\S]+$/, 'export declare function $1;');
|
|
38306
38318
|
}
|
|
38307
38319
|
/**
|
|
38308
38320
|
* 处理单个文件的依赖
|
|
@@ -38344,8 +38356,10 @@ function processFileDeps(pathRelativeToSrc, srcDir, matchedFileSet, processedFil
|
|
|
38344
38356
|
processedFileMap.set(pathRelativeToSrc, fileInfo);
|
|
38345
38357
|
// 提取依赖
|
|
38346
38358
|
// extensions/connectors/apis/uilibs 作为叶子节点,不再向下递归查找依赖
|
|
38359
|
+
// JSON 文件(如 app.roles.json)无代码引用,跳过依赖分析
|
|
38347
38360
|
const isExternalDep = shouldSkipDependencyTraversal(pathRelativeToSrc);
|
|
38348
|
-
const
|
|
38361
|
+
const isRolesJson = pathRelativeToSrc === 'app.roles.json';
|
|
38362
|
+
const deps = isExternalDep || isRolesJson ? [] : extractDeps(fileInfo.content);
|
|
38349
38363
|
// 查找依赖文件
|
|
38350
38364
|
if (isView) {
|
|
38351
38365
|
const pathArr = pathRelativeToSrc.split('.');
|
|
@@ -38471,7 +38485,7 @@ async function resolveNASLFiles(entry, logger, depMode, verbose) {
|
|
|
38471
38485
|
}
|
|
38472
38486
|
let externalRefs = null;
|
|
38473
38487
|
if (!entry && depMode)
|
|
38474
|
-
entry = 'src/**/app.*.{ts,tsx,css}';
|
|
38488
|
+
entry = ['src/**/app.*.{ts,tsx,css}', 'src/app.roles.json'];
|
|
38475
38489
|
if (entry) {
|
|
38476
38490
|
// 检查入口路径是否在源代码目录外面
|
|
38477
38491
|
// if (entry.startsWith('..')) throw new Error('入口路径不能在源代码目录外面');
|
|
@@ -38935,78 +38949,94 @@ async function build(entry, options) {
|
|
|
38935
38949
|
}
|
|
38936
38950
|
|
|
38937
38951
|
/**
|
|
38938
|
-
*
|
|
38952
|
+
* 从 specs/ 目录取第一个子文件夹名;若无则退回项目根目录名
|
|
38939
38953
|
*/
|
|
38940
|
-
function
|
|
38941
|
-
const dirname = path$1.basename(projectRoot);
|
|
38954
|
+
function getSpecBaseName(projectRoot) {
|
|
38942
38955
|
const specsDir = path$1.join(projectRoot, 'specs');
|
|
38943
|
-
if (
|
|
38944
|
-
|
|
38945
|
-
|
|
38946
|
-
|
|
38947
|
-
.
|
|
38948
|
-
|
|
38949
|
-
|
|
38950
|
-
|
|
38951
|
-
return folders[0];
|
|
38956
|
+
if (libExports.existsSync(specsDir)) {
|
|
38957
|
+
const folders = libExports.readdirSync(specsDir, { withFileTypes: true })
|
|
38958
|
+
.filter(e => e.isDirectory())
|
|
38959
|
+
.map(e => e.name);
|
|
38960
|
+
if (folders.length > 0)
|
|
38961
|
+
return folders[0];
|
|
38962
|
+
}
|
|
38963
|
+
return path$1.basename(projectRoot);
|
|
38952
38964
|
}
|
|
38953
38965
|
/**
|
|
38954
|
-
*
|
|
38966
|
+
* 从项目目录推导出合法的应用名称和包名
|
|
38955
38967
|
*/
|
|
38968
|
+
function deriveAppNames(projectRoot) {
|
|
38969
|
+
const baseName = getSpecBaseName(projectRoot);
|
|
38970
|
+
const suffix = dayjs().format('MMDDHHmmss');
|
|
38971
|
+
let cleaned = baseName.replace(/[^a-zA-Z0-9]/g, '').replace(/^\d+/, '').slice(0, 12);
|
|
38972
|
+
if (!cleaned)
|
|
38973
|
+
cleaned = 'app';
|
|
38974
|
+
return {
|
|
38975
|
+
appName: `${cleaned}_${suffix}`,
|
|
38976
|
+
packageName: `${cleaned}${suffix}`,
|
|
38977
|
+
};
|
|
38978
|
+
}
|
|
38979
|
+
|
|
38980
|
+
/**
|
|
38981
|
+
* 使用 readline 询问用户确认
|
|
38982
|
+
*/
|
|
38983
|
+
function askForConfirmation(question) {
|
|
38984
|
+
const rl = readline.createInterface({
|
|
38985
|
+
input: process.stdin,
|
|
38986
|
+
output: process.stdout,
|
|
38987
|
+
});
|
|
38988
|
+
return new Promise((resolve) => {
|
|
38989
|
+
rl.question(question, (answer) => {
|
|
38990
|
+
rl.close();
|
|
38991
|
+
const a = answer.trim().toLowerCase();
|
|
38992
|
+
resolve(!a || a === 'y' || a === 'yes' || a === '是');
|
|
38993
|
+
});
|
|
38994
|
+
});
|
|
38995
|
+
}
|
|
38996
|
+
|
|
38956
38997
|
async function createAppInIde(entry, options) {
|
|
38957
38998
|
const logger = options?.logger || defaultLogger;
|
|
38958
|
-
|
|
38959
|
-
|
|
38960
|
-
|
|
38961
|
-
|
|
38962
|
-
|
|
38963
|
-
|
|
38999
|
+
// ── 扫描 ─────────────────────────────────────────────
|
|
39000
|
+
const { collectedFiles, config, mergedDependencies } = await resolveNASLFiles(entry, logger, false, options?.verbose);
|
|
39001
|
+
// ── 展示参数 ──────────────────────────────────────────
|
|
39002
|
+
const { appName, packageName } = deriveAppNames(getProjectRoot());
|
|
39003
|
+
logger.newLine();
|
|
39004
|
+
logger.info(`应用名称: ${appName}`);
|
|
39005
|
+
logger.info(`包名: ${packageName}`);
|
|
39006
|
+
logger.info(`IDE 版本: ${config.ideVersion}`);
|
|
39007
|
+
logger.info(`租户名称: ${config.tenantName || 'defaulttenant'}`);
|
|
39008
|
+
// ── 确认 ──────────────────────────────────────────────
|
|
39009
|
+
if (!options?.quiet) {
|
|
39010
|
+
logger.newLine();
|
|
39011
|
+
const confirmed = await askForConfirmation('即将在 IDE 中创建新的应用,请确认是否继续?(Y/n): ');
|
|
39012
|
+
if (!confirmed) {
|
|
39013
|
+
logger.info('已取消操作');
|
|
39014
|
+
process.exit(0);
|
|
39015
|
+
}
|
|
38964
39016
|
}
|
|
38965
|
-
//
|
|
39017
|
+
// ── 执行 ──────────────────────────────────────────────
|
|
38966
39018
|
logger.newLine();
|
|
38967
39019
|
logger.info('正在生成 NaturalTS 代码...');
|
|
38968
|
-
let fullNaturalTS
|
|
39020
|
+
let fullNaturalTS;
|
|
38969
39021
|
try {
|
|
38970
39022
|
fullNaturalTS = composeToString(collectedFiles);
|
|
39023
|
+
fullNaturalTS = prependDependencies(fullNaturalTS, mergedDependencies);
|
|
38971
39024
|
}
|
|
38972
39025
|
catch (error) {
|
|
38973
39026
|
logger.error(`生成 NaturalTS 代码失败: ${error.message}`);
|
|
38974
39027
|
throw error;
|
|
38975
39028
|
}
|
|
38976
|
-
// 获取 specs 下第1个文件夹
|
|
38977
|
-
const projectRoot = getProjectRoot();
|
|
38978
|
-
const firstSpecFolderName = getFirstSpecFolderName(projectRoot);
|
|
38979
|
-
const suffix = dayjs().format('MMDDHHmmss');
|
|
38980
|
-
let cleaned = firstSpecFolderName.replace(/[^a-zA-Z0-9]/g, '').replace(/^\d+/, '').slice(0, 12);
|
|
38981
|
-
if (!cleaned)
|
|
38982
|
-
cleaned = 'app';
|
|
38983
|
-
const appName = `${cleaned}_${suffix}`;
|
|
38984
|
-
const packageName = `${cleaned}${suffix}`;
|
|
38985
|
-
logger.info(`应用名称: ${appName}`);
|
|
38986
|
-
logger.info(`包名: ${packageName}`);
|
|
38987
|
-
logger.info(`IDE 版本: ${config.ideVersion}`);
|
|
38988
|
-
logger.info(`完整版本: ${config.ideVersion}.0`);
|
|
38989
|
-
// 调用创建应用 API
|
|
38990
|
-
logger.newLine();
|
|
38991
39029
|
logger.info('正在调用创建应用服务...');
|
|
38992
39030
|
try {
|
|
38993
|
-
const result = await
|
|
38994
|
-
packageName: packageName,
|
|
38995
|
-
appName: appName,
|
|
38996
|
-
serverBaseURL: config.serverBaseURL,
|
|
38997
|
-
ideVersion: config.ideVersion,
|
|
38998
|
-
});
|
|
39031
|
+
const result = await createApp(fullNaturalTS, config, appName, packageName);
|
|
38999
39032
|
if (result.success) {
|
|
39000
39033
|
logger.success('应用创建成功!');
|
|
39001
|
-
if (result.appURL)
|
|
39034
|
+
if (result.appURL)
|
|
39002
39035
|
logger.info(`应用 URL: ${result.appURL}`);
|
|
39003
|
-
|
|
39004
|
-
if (result.appId) {
|
|
39036
|
+
if (result.appId)
|
|
39005
39037
|
logger.info(`应用 ID: ${result.appId}`);
|
|
39006
|
-
|
|
39007
|
-
if (result.message) {
|
|
39038
|
+
if (result.message)
|
|
39008
39039
|
logger.info(result.message);
|
|
39009
|
-
}
|
|
39010
39040
|
}
|
|
39011
39041
|
else {
|
|
39012
39042
|
logger.error(`应用创建失败: ${result.error || '未知错误'}`);
|
|
@@ -39019,6 +39049,57 @@ async function createAppInIde(entry, options) {
|
|
|
39019
39049
|
}
|
|
39020
39050
|
}
|
|
39021
39051
|
|
|
39052
|
+
async function updateAppInIde(appId, entry, options) {
|
|
39053
|
+
const logger = options?.logger || defaultLogger;
|
|
39054
|
+
// ── 扫描 ─────────────────────────────────────────────
|
|
39055
|
+
const { collectedFiles, config, mergedDependencies } = await resolveNASLFiles(entry, logger, false, options?.verbose);
|
|
39056
|
+
// ── 展示参数 ──────────────────────────────────────────
|
|
39057
|
+
logger.newLine();
|
|
39058
|
+
logger.info(`应用 ID: ${appId}`);
|
|
39059
|
+
logger.info(`IDE 版本: ${config.ideVersion}`);
|
|
39060
|
+
logger.info(`租户名称: ${config.tenantName || 'defaulttenant'}`);
|
|
39061
|
+
// ── 确认 ──────────────────────────────────────────────
|
|
39062
|
+
if (!options?.quiet) {
|
|
39063
|
+
logger.newLine();
|
|
39064
|
+
const confirmed = await askForConfirmation(`即将更新应用 ${appId},请确认是否继续?(Y/n): `);
|
|
39065
|
+
if (!confirmed) {
|
|
39066
|
+
logger.info('已取消操作');
|
|
39067
|
+
process.exit(0);
|
|
39068
|
+
}
|
|
39069
|
+
}
|
|
39070
|
+
// ── 执行 ──────────────────────────────────────────────
|
|
39071
|
+
logger.newLine();
|
|
39072
|
+
logger.info('正在生成 NaturalTS 代码...');
|
|
39073
|
+
let fullNaturalTS;
|
|
39074
|
+
try {
|
|
39075
|
+
fullNaturalTS = composeToString(collectedFiles);
|
|
39076
|
+
fullNaturalTS = prependDependencies(fullNaturalTS, mergedDependencies);
|
|
39077
|
+
}
|
|
39078
|
+
catch (error) {
|
|
39079
|
+
logger.error(`生成 NaturalTS 代码失败: ${error.message}`);
|
|
39080
|
+
throw error;
|
|
39081
|
+
}
|
|
39082
|
+
logger.info('正在调用更新应用服务...');
|
|
39083
|
+
try {
|
|
39084
|
+
const result = await updateApp(fullNaturalTS, config, appId);
|
|
39085
|
+
if (result.success) {
|
|
39086
|
+
logger.success('应用更新成功!');
|
|
39087
|
+
if (result.appURL)
|
|
39088
|
+
logger.info(`应用 URL: ${result.appURL}`);
|
|
39089
|
+
if (result.message)
|
|
39090
|
+
logger.info(result.message);
|
|
39091
|
+
}
|
|
39092
|
+
else {
|
|
39093
|
+
logger.error(`应用更新失败: ${result.error || '未知错误'}`);
|
|
39094
|
+
throw new Error(result.error || '应用更新失败');
|
|
39095
|
+
}
|
|
39096
|
+
}
|
|
39097
|
+
catch (error) {
|
|
39098
|
+
logger.error(`调用更新应用服务失败: ${error.message}`);
|
|
39099
|
+
throw error;
|
|
39100
|
+
}
|
|
39101
|
+
}
|
|
39102
|
+
|
|
39022
39103
|
/**
|
|
39023
39104
|
* 编译 NASL 代码
|
|
39024
39105
|
* TODO: 实现具体的 API 调用逻辑
|
|
@@ -39384,27 +39465,11 @@ async function installByJSON(json, options) {
|
|
|
39384
39465
|
logger.success(`成功写入 ${writtenCount} 个依赖文件到 ${config.srcDir} 目录`);
|
|
39385
39466
|
}
|
|
39386
39467
|
|
|
39387
|
-
var version = "0.3.
|
|
39468
|
+
var version = "0.3.5";
|
|
39388
39469
|
var pkg = {
|
|
39389
39470
|
version: version};
|
|
39390
39471
|
|
|
39391
39472
|
const program = new Command();
|
|
39392
|
-
/**
|
|
39393
|
-
* 使用 readline 询问用户确认
|
|
39394
|
-
*/
|
|
39395
|
-
function askForConfirmation(question) {
|
|
39396
|
-
const rl = readline.createInterface({
|
|
39397
|
-
input: process.stdin,
|
|
39398
|
-
output: process.stdout,
|
|
39399
|
-
});
|
|
39400
|
-
return new Promise((resolve) => {
|
|
39401
|
-
rl.question(question, (answer) => {
|
|
39402
|
-
rl.close();
|
|
39403
|
-
const normalizedAnswer = answer.trim().toLowerCase();
|
|
39404
|
-
resolve(!normalizedAnswer || normalizedAnswer === 'y' || normalizedAnswer === 'yes' || normalizedAnswer === '是');
|
|
39405
|
-
});
|
|
39406
|
-
});
|
|
39407
|
-
}
|
|
39408
39473
|
const entryDescription = `是相对于项目根目录的路径,支持 glob 模式(注意要用引号包裹),例如:
|
|
39409
39474
|
- src/app.enums.Status.ts 支持具体文件
|
|
39410
39475
|
- "src/app.enums.*.ts" 表示所有枚举
|
|
@@ -39526,17 +39591,6 @@ program
|
|
|
39526
39591
|
.option('-q, --quiet', '不询问问题')
|
|
39527
39592
|
.action(async (entry, options) => {
|
|
39528
39593
|
try {
|
|
39529
|
-
// 如果不是 quiet 模式,询问用户确认
|
|
39530
|
-
if (!options?.quiet) {
|
|
39531
|
-
defaultLogger.newLine();
|
|
39532
|
-
defaultLogger.info('即将在 IDE 中创建新的应用,此操作将调用远程接口。');
|
|
39533
|
-
const confirmed = await askForConfirmation('请确认是否继续?(Y/n): ');
|
|
39534
|
-
if (!confirmed) {
|
|
39535
|
-
defaultLogger.info('已取消操作');
|
|
39536
|
-
process.exit(0);
|
|
39537
|
-
}
|
|
39538
|
-
defaultLogger.newLine();
|
|
39539
|
-
}
|
|
39540
39594
|
await createAppInIde(entry, options);
|
|
39541
39595
|
}
|
|
39542
39596
|
catch (error) {
|
|
@@ -39544,6 +39598,20 @@ program
|
|
|
39544
39598
|
process.exit(1);
|
|
39545
39599
|
}
|
|
39546
39600
|
});
|
|
39601
|
+
program
|
|
39602
|
+
.command('update-app-in-ide <appId> [entry]')
|
|
39603
|
+
.description('在 IDE 中更新已有应用')
|
|
39604
|
+
.option('-v, --verbose', '显示详细信息,如依赖分析树')
|
|
39605
|
+
.option('-q, --quiet', '不询问问题')
|
|
39606
|
+
.action(async (appId, entry, options) => {
|
|
39607
|
+
try {
|
|
39608
|
+
await updateAppInIde(appId, entry, options);
|
|
39609
|
+
}
|
|
39610
|
+
catch (error) {
|
|
39611
|
+
defaultLogger.error(`更新应用过程发生错误:${error.message}`);
|
|
39612
|
+
process.exit(1);
|
|
39613
|
+
}
|
|
39614
|
+
});
|
|
39547
39615
|
program
|
|
39548
39616
|
.command('transform <transformType> [entry]')
|
|
39549
39617
|
.description('转换文件格式\n transformType: files2full (将 src 文件组合成 fullNaturalTS), json2files (将 JSON 转换为文件), files2json (将 src 文件转换为 JSON), doc2full (将 plan markdown 文档组合成 fullNaturalTS)')
|