@h-ai/cli 1.0.0-beta.1 → 1.0.0-beta.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/README.en.md +20 -8
- package/README.ja.md +20 -8
- package/README.md +20 -8
- package/dist/{chunk-M7HN2OE3.js → chunk-K6RWTTHD.js} +573 -106
- package/dist/chunk-K6RWTTHD.js.map +1 -0
- package/dist/{chunk-AKV2CQZV.js → chunk-SWEU7KUE.js} +71 -59
- package/dist/chunk-SWEU7KUE.js.map +1 -0
- package/dist/{chunk-3JCZO34U.js → chunk-YVNNMC4H.js} +365 -137
- package/dist/chunk-YVNNMC4H.js.map +1 -0
- package/dist/cli-config-templates-LUJGDQNW.js +5 -0
- package/dist/{cli-config-templates-RCWQMHZN.js.map → cli-config-templates-LUJGDQNW.js.map} +1 -1
- package/dist/{cli-skill-templates-PN4FLGBC.js → cli-skill-templates-VV2XWYON.js} +3 -3
- package/dist/{cli-skill-templates-PN4FLGBC.js.map → cli-skill-templates-VV2XWYON.js.map} +1 -1
- package/dist/index.js +118 -209
- package/dist/index.js.map +1 -1
- package/dist/metafile-esm.json +1 -1
- package/dist/{src-JRKSWJ2Q.js → src-IR5AU43K.js} +3 -3
- package/dist/src-IR5AU43K.js.map +1 -0
- package/package.json +11 -11
- package/templates/apps/api/.dockerignore.hbs +10 -0
- package/templates/apps/api/apps/{{projectName}}-serv/config/_serv.yml.hbs +20 -1
- package/templates/apps/api/apps/{{projectName}}-serv/src/app.ts.hbs +6 -1
- package/templates/apps/api/apps/{{projectName}}-serv/src/lib/server/init.ts.hbs +1 -1
- package/templates/apps/api/images/serv.Dockerfile.hbs +3 -2
- package/templates/apps/fullstack/.dockerignore.hbs +10 -0
- package/templates/apps/fullstack/apps/{{projectName}}-web/README.md.hbs +1 -1
- package/templates/apps/fullstack/e2e/fullstack.spec.ts.hbs +16 -2
- package/templates/apps/fullstack/images/serv.Dockerfile.hbs +6 -2
- package/templates/apps/fullstack/images/web.Dockerfile.hbs +6 -1
- package/templates/apps/fullstack/packages/{{projectName}}-serv/README.md.hbs +1 -1
- package/templates/apps/fullstack/packages/{{projectName}}-serv/config/_reach.yml.hbs +10 -7
- package/templates/apps/fullstack/packages/{{projectName}}-serv/config/_serv.yml.hbs +14 -7
- package/templates/apps/fullstack/packages/{{projectName}}-serv/package.json.hbs +6 -0
- package/templates/apps/fullstack/packages/{{projectName}}-serv/src/app-server.ts.hbs +1 -1
- package/templates/apps/fullstack/packages/{{projectName}}-serv/tests/app-server.test.ts.hbs +7 -1
- package/templates/apps/fullstack/packages/{{projectName}}-serv/tests/security.test.ts.hbs +1 -1
- package/templates/apps/fullstack/pnpm-workspace.yaml.hbs +1 -0
- package/templates/apps/website/scripts/static-website.mjs.hbs +3 -3
- package/templates/base/.dockerignore.hbs +10 -0
- package/templates/base/images/web.Dockerfile.hbs +4 -2
- package/templates/base/package.json.hbs +1 -1
- package/templates/base/src/hooks.server.ts.hbs +9 -7
- package/templates/features/payment/routes/api/v1/payment/create/+server.ts +3 -1
- package/templates/skills/AGENTS.md +2 -0
- package/templates/skills/bridges/admin/AGENTS.md +2 -0
- package/templates/skills/bridges/api/AGENTS.md +2 -0
- package/templates/skills/bridges/fullstack/AGENTS.md.hbs +2 -0
- package/templates/skills/bridges/h5/AGENTS.md +2 -0
- package/templates/skills/bridges/mobile-app/AGENTS.md +2 -0
- package/templates/skills/bridges/website/AGENTS.md +2 -0
- package/templates/skills/hai-ai/guide.md +5 -3
- package/templates/skills/hai-app-create/SKILL.md +1 -1
- package/templates/skills/hai-app-create/collaboration.md +6 -1
- package/templates/skills/hai-app-create/development.md +10 -4
- package/templates/skills/hai-app-create/reference.md +4 -2
- package/templates/skills/hai-app-review/SKILL.md +2 -0
- package/templates/skills/hai-app-tests/SKILL.md +2 -0
- package/templates/skills/hai-build/SKILL.md +5 -5
- package/templates/skills/hai-build/paths.md +1 -1
- package/templates/skills/hai-build/reference.md +1 -1
- package/templates/skills/hai-change-plan/SKILL.md +46 -0
- package/templates/skills/hai-deploy/SKILL.md +16 -4
- package/templates/skills/hai-deploy/middleware.md +4 -2
- package/templates/skills/hai-feedback/SKILL.md +1 -1
- package/templates/skills/hai-pr-review/SKILL.md +2 -0
- package/templates/skills/hai-serv/reference.md +5 -8
- package/templates/skills/hai-spec-implement/SKILL.md +2 -0
- package/templates/skills/hai-storage/SKILL.md +2 -2
- package/templates/skills/hai-storage/reference.md +1 -1
- package/dist/chunk-3JCZO34U.js.map +0 -1
- package/dist/chunk-AKV2CQZV.js.map +0 -1
- package/dist/chunk-M7HN2OE3.js.map +0 -1
- package/dist/cli-config-templates-RCWQMHZN.js +0 -5
- package/dist/src-JRKSWJ2Q.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { generateConfigFile, runtimeEnvironmentVariables, renderEnvironment, generateProductionEnvironment, moduleEnvironmentVariables, parameterizeConfig } from './chunk-
|
|
3
|
-
import { cliM } from './chunk-
|
|
4
|
-
import { external_exports, normalizeProjectName } from './chunk-
|
|
2
|
+
import { generateConfigFile, runtimeEnvironmentVariables, renderEnvironment, generateProductionEnvironment, moduleEnvironmentVariables, documentConfig, parameterizeConfig } from './chunk-YVNNMC4H.js';
|
|
3
|
+
import { cliM } from './chunk-SWEU7KUE.js';
|
|
4
|
+
import { external_exports, normalizeProjectName } from './chunk-K6RWTTHD.js';
|
|
5
5
|
import './chunk-ZSDEVXZ7.js';
|
|
6
6
|
import process4 from 'process';
|
|
7
7
|
import { cac } from 'cac';
|
|
@@ -524,6 +524,10 @@ function buildFullstackCatalogPackages(frontends, deploy = false, features = {})
|
|
|
524
524
|
packageNames.push("@h-ai/ai");
|
|
525
525
|
if (features.vecdb)
|
|
526
526
|
packageNames.push("@h-ai/vecdb");
|
|
527
|
+
if (features.payment)
|
|
528
|
+
packageNames.push("@h-ai/payment");
|
|
529
|
+
if (features.datapipe)
|
|
530
|
+
packageNames.push("@h-ai/datapipe");
|
|
527
531
|
return pickCatalogPackages(packageNames, FULLSTACK_CATALOG_PACKAGES);
|
|
528
532
|
}
|
|
529
533
|
var FEATURE_ROUTE_DIRS = {
|
|
@@ -912,7 +916,7 @@ async function renderHbsInDir(dir, destRoot, context) {
|
|
|
912
916
|
}
|
|
913
917
|
const destPath = path2.join(destRoot, outRelPath);
|
|
914
918
|
const configMatch = /(?:^|[/\\])config[/\\]_([^/\\]+)\.ya?ml$/.exec(outRelPath);
|
|
915
|
-
const normalizedRendered = normalizeRenderedContent(configMatch ? parameterizeConfig(configMatch[1], rendered) : rendered, outRelPath);
|
|
919
|
+
const normalizedRendered = normalizeRenderedContent(configMatch ? documentConfig(configMatch[1], parameterizeConfig(configMatch[1], rendered)) : rendered, outRelPath);
|
|
916
920
|
await fse.ensureDir(path2.dirname(destPath));
|
|
917
921
|
await fse.writeFile(destPath, normalizedRendered, "utf-8");
|
|
918
922
|
}
|
|
@@ -980,8 +984,7 @@ var APP_TYPES = {
|
|
|
980
984
|
},
|
|
981
985
|
get description() {
|
|
982
986
|
return cliM("cli_appAdminDescription");
|
|
983
|
-
}
|
|
984
|
-
defaultFeatures: ["iam", "db", "cache", "crypto"]
|
|
987
|
+
}
|
|
985
988
|
},
|
|
986
989
|
"website": {
|
|
987
990
|
get name() {
|
|
@@ -989,8 +992,7 @@ var APP_TYPES = {
|
|
|
989
992
|
},
|
|
990
993
|
get description() {
|
|
991
994
|
return cliM("cli_appWebsiteDescription");
|
|
992
|
-
}
|
|
993
|
-
defaultFeatures: ["db", "cache"]
|
|
995
|
+
}
|
|
994
996
|
},
|
|
995
997
|
"h5": {
|
|
996
998
|
get name() {
|
|
@@ -998,8 +1000,7 @@ var APP_TYPES = {
|
|
|
998
1000
|
},
|
|
999
1001
|
get description() {
|
|
1000
1002
|
return cliM("cli_appH5Description");
|
|
1001
|
-
}
|
|
1002
|
-
defaultFeatures: ["db", "cache"]
|
|
1003
|
+
}
|
|
1003
1004
|
},
|
|
1004
1005
|
"api": {
|
|
1005
1006
|
get name() {
|
|
@@ -1007,8 +1008,7 @@ var APP_TYPES = {
|
|
|
1007
1008
|
},
|
|
1008
1009
|
get description() {
|
|
1009
1010
|
return cliM("cli_appApiDescription");
|
|
1010
|
-
}
|
|
1011
|
-
defaultFeatures: ["db", "cache"]
|
|
1011
|
+
}
|
|
1012
1012
|
},
|
|
1013
1013
|
"mobile-app": {
|
|
1014
1014
|
get name() {
|
|
@@ -1016,8 +1016,7 @@ var APP_TYPES = {
|
|
|
1016
1016
|
},
|
|
1017
1017
|
get description() {
|
|
1018
1018
|
return cliM("cli_appMobileDescription");
|
|
1019
|
-
}
|
|
1020
|
-
defaultFeatures: ["api-client", "capacitor"]
|
|
1019
|
+
}
|
|
1021
1020
|
},
|
|
1022
1021
|
"fullstack": {
|
|
1023
1022
|
get name() {
|
|
@@ -1025,8 +1024,7 @@ var APP_TYPES = {
|
|
|
1025
1024
|
},
|
|
1026
1025
|
get description() {
|
|
1027
1026
|
return cliM("cli_appFullstackDescription");
|
|
1028
|
-
}
|
|
1029
|
-
defaultFeatures: ["iam", "db", "cache", "crypto", "storage", "audit", "scheduler"]
|
|
1027
|
+
}
|
|
1030
1028
|
}
|
|
1031
1029
|
};
|
|
1032
1030
|
var FRONTEND_TARGETS = {
|
|
@@ -1225,79 +1223,20 @@ var FEATURES = {
|
|
|
1225
1223
|
packages: ["@h-ai/capacitor"]
|
|
1226
1224
|
}
|
|
1227
1225
|
};
|
|
1228
|
-
var
|
|
1229
|
-
"iam",
|
|
1230
|
-
"db",
|
|
1231
|
-
"cache",
|
|
1232
|
-
"ai",
|
|
1233
|
-
"storage",
|
|
1234
|
-
"crypto",
|
|
1235
|
-
"audit",
|
|
1236
|
-
"reach",
|
|
1237
|
-
"feedback",
|
|
1238
|
-
"payment",
|
|
1239
|
-
"vecdb",
|
|
1240
|
-
"datapipe",
|
|
1241
|
-
"scheduler",
|
|
1242
|
-
"api-client",
|
|
1243
|
-
"capacitor"
|
|
1244
|
-
];
|
|
1226
|
+
var FULL_FEATURES = ["iam", "db", "cache", "crypto", "ai", "storage", "audit", "reach", "feedback", "payment", "vecdb", "datapipe", "scheduler", "deploy", "api-client", "capacitor"];
|
|
1245
1227
|
var PROJECT_NAME_REGEX = /^[a-z0-9-]+$/;
|
|
1246
|
-
var PROJECT_TEMPLATES = {
|
|
1247
|
-
minimal: {
|
|
1248
|
-
name: "minimal",
|
|
1249
|
-
get description() {
|
|
1250
|
-
return cliM("cli_templateMinimalDescription");
|
|
1251
|
-
},
|
|
1252
|
-
features: []
|
|
1253
|
-
},
|
|
1254
|
-
default: {
|
|
1255
|
-
name: "default",
|
|
1256
|
-
get description() {
|
|
1257
|
-
return cliM("cli_templateDefaultDescription");
|
|
1258
|
-
},
|
|
1259
|
-
features: ["iam", "db", "cache", "crypto"]
|
|
1260
|
-
},
|
|
1261
|
-
full: {
|
|
1262
|
-
name: "full",
|
|
1263
|
-
get description() {
|
|
1264
|
-
return cliM("cli_templateFullDescription");
|
|
1265
|
-
},
|
|
1266
|
-
features: [
|
|
1267
|
-
"iam",
|
|
1268
|
-
"db",
|
|
1269
|
-
"cache",
|
|
1270
|
-
"crypto",
|
|
1271
|
-
"ai",
|
|
1272
|
-
"storage",
|
|
1273
|
-
"audit",
|
|
1274
|
-
"reach",
|
|
1275
|
-
"feedback",
|
|
1276
|
-
"payment",
|
|
1277
|
-
"vecdb",
|
|
1278
|
-
"datapipe",
|
|
1279
|
-
"scheduler",
|
|
1280
|
-
"deploy",
|
|
1281
|
-
"api-client",
|
|
1282
|
-
"capacitor"
|
|
1283
|
-
]
|
|
1284
|
-
},
|
|
1285
|
-
custom: {
|
|
1286
|
-
name: "custom",
|
|
1287
|
-
get description() {
|
|
1288
|
-
return cliM("cli_templateCustomDescription");
|
|
1289
|
-
},
|
|
1290
|
-
features: []
|
|
1291
|
-
}
|
|
1292
|
-
};
|
|
1293
1228
|
async function createProject(options) {
|
|
1294
1229
|
const spinner = ora();
|
|
1295
1230
|
try {
|
|
1231
|
+
if (["template", "features", "examples", "yes"].some((key) => Object.hasOwn(options, key)))
|
|
1232
|
+
throw new Error(cliM("cli_createOptionsRemoved"));
|
|
1296
1233
|
const resolvedOptions = await resolveOptions(options);
|
|
1297
1234
|
if (!PROJECT_NAME_REGEX.test(resolvedOptions.name))
|
|
1298
1235
|
throw new Error(cliM("cli_createInvalidName", { params: { name: resolvedOptions.name } }));
|
|
1299
1236
|
const projectPath = path2.resolve(resolvedOptions.cwd ?? ".", resolvedOptions.name);
|
|
1300
1237
|
if (await fileExists(projectPath)) {
|
|
1238
|
+
if (resolvedOptions.defaults)
|
|
1239
|
+
throw new Error(cliM("cli_createDirectoryExists"));
|
|
1301
1240
|
const { overwrite } = await prompts({
|
|
1302
1241
|
type: "confirm",
|
|
1303
1242
|
active: cliM("cli_yes"),
|
|
@@ -1327,7 +1266,7 @@ async function createProject(options) {
|
|
|
1327
1266
|
});
|
|
1328
1267
|
await generateFromTemplates(projectPath, context);
|
|
1329
1268
|
{
|
|
1330
|
-
const { generateConfigFile: generateConfigFile2, generateEnvExample, generateProductionEnvironment: generateProductionEnvironment2 } = await import('./cli-config-templates-
|
|
1269
|
+
const { generateConfigFile: generateConfigFile2, generateEnvExample, generateProductionEnvironment: generateProductionEnvironment2 } = await import('./cli-config-templates-LUJGDQNW.js');
|
|
1331
1270
|
const configs = resolvedOptions.moduleConfigs;
|
|
1332
1271
|
const isApiWorkspace = resolvedOptions.appType === "api";
|
|
1333
1272
|
const isFullstack = resolvedOptions.appType === "fullstack";
|
|
@@ -1335,7 +1274,7 @@ async function createProject(options) {
|
|
|
1335
1274
|
const envExamplePath = isApiWorkspace ? path2.join(projectPath, "apps", `${resolvedOptions.name}-serv`, ".env.example") : path2.join(projectPath, ".env.example");
|
|
1336
1275
|
for (const featureId of resolvedOptions.features) {
|
|
1337
1276
|
const configKey = getFeatureConfigKey(featureId);
|
|
1338
|
-
if (configKey && configKey !== "deploy") {
|
|
1277
|
+
if (!context.isSvelteOnlyApp && configKey && configKey !== "deploy") {
|
|
1339
1278
|
const configPath = path2.join(configDir, `_${configKey}.yml`);
|
|
1340
1279
|
if (isFullstack && await fse.pathExists(configPath))
|
|
1341
1280
|
continue;
|
|
@@ -1357,7 +1296,7 @@ async function createProject(options) {
|
|
|
1357
1296
|
if (/^_.*\.yml$/.test(file))
|
|
1358
1297
|
configContents[file.slice(1, -4)] = await fse.readFile(path2.join(configDir, file), "utf8");
|
|
1359
1298
|
}
|
|
1360
|
-
const envFeatures = isFullstack ? [.../* @__PURE__ */ new Set([...resolvedOptions.features, "reach"])] : resolvedOptions.features;
|
|
1299
|
+
const envFeatures = context.isSvelteOnlyApp ? [] : isFullstack ? [.../* @__PURE__ */ new Set([...resolvedOptions.features, "reach"])] : resolvedOptions.features;
|
|
1361
1300
|
const envContents = generateEnvExample(envFeatures, configs, isFullstack, configContents);
|
|
1362
1301
|
let envExample = isApiWorkspace ? envContents : isFullstack ? `${envContents}
|
|
1363
1302
|
# First-start administrator password; must satisfy the IAM password policy.
|
|
@@ -1417,7 +1356,7 @@ HAI_CRYPTO_TRANSPORT_SERVERKEYRING='${JSON.stringify(ring)}'
|
|
|
1417
1356
|
}
|
|
1418
1357
|
await fse.writeFile(path2.join(envDirectory, ".env.local"), "# \u672C\u673A\u79C1\u6709\u8986\u76D6\uFF0C\u4E0D\u63D0\u4EA4 Git\uFF1B\u521D\u59CB\u7BA1\u7406\u5458\u5BC6\u7801\u548C\u4F20\u8F93\u5BC6\u94A5\u73AF\u89C1 .env.dev / .env.test / .env.prod\u3002\n", { mode: 384 });
|
|
1419
1358
|
}
|
|
1420
|
-
const { generateSkillFiles } = await import('./cli-skill-templates-
|
|
1359
|
+
const { generateSkillFiles } = await import('./cli-skill-templates-VV2XWYON.js');
|
|
1421
1360
|
await generateSkillFiles(
|
|
1422
1361
|
projectPath,
|
|
1423
1362
|
resolvedOptions.features,
|
|
@@ -1428,7 +1367,7 @@ HAI_CRYPTO_TRANSPORT_SERVERKEYRING='${JSON.stringify(ring)}'
|
|
|
1428
1367
|
const readmeContent = resolvedOptions.appType === "fullstack" ? generateFullstackReadme(resolvedOptions.name, resolvedOptions.packageManager, resolvedOptions.frontends, resolvedOptions.features.includes("feedback")) : resolvedOptions.appType === "api" ? generateApiWorkspaceReadme(resolvedOptions.name, resolvedOptions.packageManager) : generateReadme(resolvedOptions.name, appTypeLabel, resolvedOptions.packageManager, resolvedOptions.appType);
|
|
1429
1368
|
await writeFile(
|
|
1430
1369
|
path2.join(projectPath, "README.md"),
|
|
1431
|
-
resolvedOptions.deploy === false ? readmeContent.replace(/### 构建与部署[\s\S]*?(?=\n#{2,3} |$)/, "### \u6784\u5EFA\n\npnpm build\npnpm build --target
|
|
1370
|
+
resolvedOptions.deploy === false ? readmeContent.replace(/### 构建与部署[\s\S]*?(?=\n#{2,3} |$)/, "### \u6784\u5EFA\n\npnpm build\npnpm build --target image\n\n\u672C\u9879\u76EE\u521B\u5EFA\u65F6\u5173\u95ED\u4E86\u90E8\u7F72\u5DE5\u5177\u3002\n") : readmeContent
|
|
1432
1371
|
);
|
|
1433
1372
|
spinner.succeed();
|
|
1434
1373
|
if (resolvedOptions.git) {
|
|
@@ -1456,20 +1395,8 @@ HAI_CRYPTO_TRANSPORT_SERVERKEYRING='${JSON.stringify(ring)}'
|
|
|
1456
1395
|
throw error;
|
|
1457
1396
|
}
|
|
1458
1397
|
}
|
|
1459
|
-
function parseCreateFeatures(value) {
|
|
1460
|
-
const selected = value.split(",").map((feature) => feature.trim());
|
|
1461
|
-
if (selected.includes("none")) {
|
|
1462
|
-
if (selected.length !== 1)
|
|
1463
|
-
throw new Error(cliM("cli_createFeaturesNoneExclusive"));
|
|
1464
|
-
return [];
|
|
1465
|
-
}
|
|
1466
|
-
const invalid = selected.find((feature) => !feature || !Object.hasOwn(FEATURES, feature) && feature !== "ui" && feature !== "kit");
|
|
1467
|
-
if (invalid !== void 0)
|
|
1468
|
-
throw new Error(cliM("cli_createFeaturesInvalid"));
|
|
1469
|
-
return [...new Set(selected)];
|
|
1470
|
-
}
|
|
1471
1398
|
async function resolveOptions(options) {
|
|
1472
|
-
const useDefaults = options.
|
|
1399
|
+
const useDefaults = options.defaults ?? false;
|
|
1473
1400
|
writeCliMessage("");
|
|
1474
1401
|
writeCliMessage(chalk.bold.cyan(" \u{1F680} hai Agent Framework"));
|
|
1475
1402
|
writeCliMessage(chalk.gray(cliM("cli_createTagline")));
|
|
@@ -1518,78 +1445,11 @@ ${formatPromptDescription(cliM("cli_selectHint"))}`,
|
|
|
1518
1445
|
}
|
|
1519
1446
|
const selectedAppType = resolvedAppType || "admin";
|
|
1520
1447
|
const selectedFrontends = selectedAppType === "fullstack" ? await resolveFrontendTargets(options.frontends, useDefaults, onCancel) : [];
|
|
1521
|
-
let
|
|
1522
|
-
if (selectedAppType === "fullstack") {
|
|
1523
|
-
selectedTemplate = selectedTemplate ?? "custom";
|
|
1524
|
-
} else if (!selectedTemplate) {
|
|
1525
|
-
if (useDefaults) {
|
|
1526
|
-
selectedTemplate = "default";
|
|
1527
|
-
} else {
|
|
1528
|
-
const { template } = await prompts({
|
|
1529
|
-
type: "select",
|
|
1530
|
-
name: "template",
|
|
1531
|
-
message: cliM("cli_createTemplatePrompt"),
|
|
1532
|
-
hint: `
|
|
1533
|
-
${formatPromptDescription(cliM("cli_selectHint"))}`,
|
|
1534
|
-
choices: Object.values(PROJECT_TEMPLATES).map((t) => ({
|
|
1535
|
-
title: t.name,
|
|
1536
|
-
description: formatPromptDescription(t.description),
|
|
1537
|
-
value: t.name
|
|
1538
|
-
})),
|
|
1539
|
-
initial: 1
|
|
1540
|
-
}, { onCancel });
|
|
1541
|
-
selectedTemplate = template;
|
|
1542
|
-
}
|
|
1543
|
-
}
|
|
1544
|
-
let selectedFeatures = [];
|
|
1545
|
-
const appTypeDefaults = APP_TYPES[selectedAppType].defaultFeatures;
|
|
1546
|
-
if (selectedAppType === "fullstack") {
|
|
1547
|
-
selectedFeatures = resolveFeatureDependencies([...appTypeDefaults, ...options.features ?? ["feedback"]]);
|
|
1548
|
-
} else if (options.features) {
|
|
1549
|
-
selectedFeatures = resolveFeatureDependencies(options.features);
|
|
1550
|
-
} else if (selectedTemplate === "custom" && !options.features && useDefaults) {
|
|
1551
|
-
selectedFeatures = resolveFeatureDependencies(appTypeDefaults);
|
|
1552
|
-
} else if (selectedTemplate === "custom" && !options.features) {
|
|
1553
|
-
const featureChoices = SELECTABLE_FEATURES.map((id) => ({
|
|
1554
|
-
title: FEATURES[id].name,
|
|
1555
|
-
description: formatPromptDescription(FEATURES[id].description),
|
|
1556
|
-
value: id,
|
|
1557
|
-
selected: appTypeDefaults.includes(id)
|
|
1558
|
-
}));
|
|
1559
|
-
const { features } = await prompts({
|
|
1560
|
-
type: "multiselect",
|
|
1561
|
-
name: "features",
|
|
1562
|
-
message: cliM("cli_featuresPrompt"),
|
|
1563
|
-
choices: featureChoices,
|
|
1564
|
-
hint: `
|
|
1565
|
-
${formatPromptDescription(cliM("cli_multiselectHint"))}`,
|
|
1566
|
-
instructions: false
|
|
1567
|
-
}, { onCancel });
|
|
1568
|
-
selectedFeatures = features || [];
|
|
1569
|
-
selectedFeatures = resolveFeatureDependencies(selectedFeatures);
|
|
1570
|
-
} else {
|
|
1571
|
-
const templateFeatures = PROJECT_TEMPLATES[selectedTemplate]?.features || [];
|
|
1572
|
-
const merged = /* @__PURE__ */ new Set([...templateFeatures, ...appTypeDefaults]);
|
|
1573
|
-
selectedFeatures = resolveFeatureDependencies(Array.from(merged));
|
|
1574
|
-
}
|
|
1448
|
+
let selectedFeatures = selectedAppType === "mobile-app" ? ["iam", "crypto", "api-client", "capacitor"] : resolveFeatureDependencies(FULL_FEATURES);
|
|
1575
1449
|
selectedFeatures = selectedFeatures.filter((feature) => feature !== "deploy");
|
|
1576
1450
|
if (options.deploy !== false)
|
|
1577
1451
|
selectedFeatures.push("deploy");
|
|
1578
|
-
const moduleConfigs = selectedAppType === "fullstack" ? { ...buildDefaultModuleConfigs(selectedFeatures, projectName), ...options.moduleConfigs } : options.moduleConfigs ?? (useDefaults ? buildDefaultModuleConfigs(selectedFeatures, projectName) : await promptModuleConfigs(selectedFeatures, projectName));
|
|
1579
|
-
let addExamples = options.examples;
|
|
1580
|
-
if (addExamples === void 0 && useDefaults) {
|
|
1581
|
-
addExamples = true;
|
|
1582
|
-
} else if (addExamples === void 0) {
|
|
1583
|
-
const { examples } = await prompts({
|
|
1584
|
-
type: "confirm",
|
|
1585
|
-
active: cliM("cli_yes"),
|
|
1586
|
-
inactive: cliM("cli_no"),
|
|
1587
|
-
name: "examples",
|
|
1588
|
-
message: cliM("cli_createExamplesPrompt"),
|
|
1589
|
-
initial: true
|
|
1590
|
-
}, { onCancel });
|
|
1591
|
-
addExamples = examples;
|
|
1592
|
-
}
|
|
1452
|
+
const moduleConfigs = selectedAppType === "fullstack" || selectedAppType === "mobile-app" ? { ...buildDefaultModuleConfigs(selectedFeatures, projectName), ...options.moduleConfigs } : options.moduleConfigs ?? (useDefaults ? buildDefaultModuleConfigs(selectedFeatures, projectName) : await promptModuleConfigs(selectedFeatures, projectName));
|
|
1593
1453
|
const detected = await detectPackageManager(options.cwd ?? ".");
|
|
1594
1454
|
let packageManager = selectedAppType === "fullstack" || selectedAppType === "api" ? "pnpm" : options.packageManager;
|
|
1595
1455
|
if (!packageManager && useDefaults) {
|
|
@@ -1662,16 +1522,14 @@ ${formatPromptDescription(cliM("cli_selectHint"))}`,
|
|
|
1662
1522
|
name: projectName,
|
|
1663
1523
|
appType: selectedAppType,
|
|
1664
1524
|
frontends: selectedFrontends,
|
|
1665
|
-
template: selectedTemplate ?? "default",
|
|
1666
1525
|
features: selectedFeatures,
|
|
1667
1526
|
deploy: options.deploy !== false,
|
|
1668
1527
|
moduleConfigs,
|
|
1669
|
-
examples: addExamples ?? true,
|
|
1670
1528
|
install: install ?? true,
|
|
1671
1529
|
packageManager: packageManager || "pnpm",
|
|
1672
1530
|
git: git ?? true,
|
|
1673
1531
|
gitRemote: gitRemote ?? "",
|
|
1674
|
-
|
|
1532
|
+
defaults: useDefaults,
|
|
1675
1533
|
verbose: options.verbose ?? false,
|
|
1676
1534
|
cwd: options.cwd ?? "."
|
|
1677
1535
|
};
|
|
@@ -1999,7 +1857,7 @@ function generateEnvironmentReadme() {
|
|
|
1999
1857
|
| \u5F00\u53D1 / \u6D4B\u8BD5\u6A21\u5F0F / \u751F\u4EA7\u6A21\u5F0F\u9884\u89C8 | \`pnpm dev\` / \`dev:test\` / \`dev:prod\` |
|
|
2000
1858
|
| \u7C7B\u578B\u68C0\u67E5\uFF08check \u4E3A\u522B\u540D\uFF09 | \`pnpm typecheck\` |
|
|
2001
1859
|
| \u68C0\u67E5 / \u81EA\u52A8\u4FEE\u590D\u683C\u5F0F | \`pnpm lint\` / \`lint:fix\` |
|
|
2002
|
-
| \u6E90\u7801\u6784\u5EFA / \u955C\u50CF\u6784\u5EFA | \`pnpm build\` / \`pnpm build --target
|
|
1860
|
+
| \u6E90\u7801\u6784\u5EFA / \u955C\u50CF\u6784\u5EFA | \`pnpm build\` / \`pnpm build --target image\` |
|
|
2003
1861
|
| \u5355\u5143\u6D4B\u8BD5 / \u76D1\u542C / \u6D4F\u89C8\u5668\u9A8C\u6536 | \`pnpm test\` / \`test:watch\` / \`test:e2e\` |
|
|
2004
1862
|
| Node \u670D\u52A1\u6309\u73AF\u5883\u542F\u52A8\uFF08\u6784\u5EFA\u540E\uFF09 | \`pnpm start\` / \`start:test\` / \`start:prod\` |
|
|
2005
1863
|
|
|
@@ -2013,20 +1871,20 @@ function generateBuildDeploymentReadme(pm, appDir) {
|
|
|
2013
1871
|
|
|
2014
1872
|
\u7F51\u7AD9\u5730\u5740\u542B/yyy/\u7B49\u76EE\u5F55\u65F6\uFF0C\u5728\u6784\u5EFA\u8FDB\u7A0B\u663E\u5F0F\u8BBE\u7F6EPUBLIC_BASE_PATH=/yyy\uFF0C\u9ED8\u8BA4\u7A7A\u5B57\u7B26\u4E32\uFF1B\u4E0D\u4ECE\u79C1\u6709dotenv\u8BFB\u53D6\u3002SvelteKit\u7F16\u8BD1\u65F6\u56FA\u5B9Abase\uFF0CVite\u9ED8\u8BA4\u76F8\u5BF9\u8D44\u6E90\u4E0Ehash\u8DEF\u7531\uFF1BAPI\u5730\u5740\u72EC\u7ACB\u914D\u7F6E\u3002Docker\u7F51\u7AD9\u5236\u54C1\u7531SDK\u5904\u7406\u4EE3\u7406\u4E0E\u5065\u5EB7\u8DEF\u5F84\uFF0C\u4F7F\u7528\u5339\u914DCLI/Deploy\uFF1B\u6240\u6709\u90E8\u5206\u6784\u5EFA\u4FDD\u6301\u540C\u4E00\u76EE\u5F55\uFF0C\u5373\u4F7F\u53EA\u6784\u5EFAserv\u3002\u76EE\u5F55\u53D8\u5316\u4F7F\u7528\u5168\u91CF\u90E8\u7F72\uFF0C\u64CD\u4F5C\u548C\u9A8C\u8BC1\u89C1[\u7F51\u7AD9\u6302\u8F7D\u76EE\u5F55](.agents/skills/hai-build/paths.md)\u3002
|
|
2015
1873
|
|
|
2016
|
-
\u81EA\u5EFAS3\u7684\u5185\u90E8endpoint\u4E0E\u6D4F\u89C8\u5668publicEndpoint\u5206\u5F00\
|
|
1874
|
+
\u81EA\u5EFAS3\u7684\u5185\u90E8endpoint\u4E0E\u6D4F\u89C8\u5668publicEndpoint\u5206\u5F00\u3002publicEndpoint\u53EF\u8BBE\u7F6E\u4E3Ahttps://example.com/delivery/files\u7B49\u5E26\u76EE\u5F55\u5730\u5740\uFF0C\u4E0D\u542B\u6876\u540D\u3001\u51ED\u636E\u6216\u67E5\u8BE2\uFF1B\u8FD4\u56DEURL\u4FDD\u7559\u6876\u540D\uFF0CWeb Nginx\u6216SSR/API gateway\u53BB\u6389\u5165\u53E3\u524D\u7F00\uFF0C\u4FDD\u7559\u539F\u59CBS3\u8DEF\u5F84\u3001Host\u4E0E\u67E5\u8BE2\u540E\u76F4\u63A5\u8F6C\u53D1storage\uFF0C\u5927\u6587\u4EF6\u4E0D\u7ECF\u8FC7Serv\u3002\u5916\u5C42\u4EE3\u7406\u4FDD\u7559\u5B8C\u6574\u5165\u53E3\u8DEF\u5F84\uFF0C\u4E0D\u91CD\u590D\u53BB\u524D\u7F00\uFF1B\u5916\u90E8S3/CORS\u6309\u5B9E\u9645\u670D\u52A1\u914D\u7F6E\uFF0CLocal\u4E0D\u516C\u5F00\u79C1\u6709\u76EE\u5F55\u3002\u589E\u52A0\u7F51\u5173\u3001\u6539\u53D8\u6876\u6216\u516C\u5F00\u5730\u5740\u65F6\u5168\u91CF\u90E8\u7F72\uFF0C\u5E94\u7528\u589E\u91CF\u4E0E\u56DE\u6EDA\u4FDD\u6301\u539F\u914D\u7F6E\uFF1B\u8BE6\u89C1[\u5B58\u50A8\u4F7F\u7528\u89C4\u8303](.agents/skills/hai-storage/SKILL.md)\u3002
|
|
2017
1875
|
\u6BCF\u4E2A\u955C\u50CF\u5BF9\u5E94 images/ \u4E2D\u4E00\u4E2A Dockerfile\uFF0C\u6839 hai.json \u7684 build \u5206\u533A\u6307\u5B9A\u6587\u4EF6\uFF0Cdeploy \u5206\u533A\u4FDD\u5B58\u6574\u4E2A\u9879\u76EE\u7684\u90E8\u7F72\u914D\u7F6E\u3002
|
|
2018
|
-
\u4E0B\u8F7D\u3001\u5B89\u88C5\u548C\u6574\u7406\u751F\u4EA7\u4F9D\u8D56\u590D\u7528\u540C\u4E00\u4EFD\u5305\u7F13\u5B58\u4E0E SQLite \u9A71\u52A8\u7F13\u5B58\uFF0C\
|
|
1876
|
+
\u4E0B\u8F7D\u3001\u5B89\u88C5\u548C\u6574\u7406\u751F\u4EA7\u4F9D\u8D56\u590D\u7528\u540C\u4E00\u4EFD\u5305\u7F13\u5B58\u4E0E SQLite \u9A71\u52A8\u7F13\u5B58\uFF0C\u4F9D\u8D56\u5B89\u88C5\u5148\u4E8E\u6E90\u7801\u590D\u5236\u3002\u524D\u540E\u7AEF\u9ED8\u8BA4\u6700\u591A\u5E76\u884C\u6784\u5EFA\u4E24\u9879\uFF0C--concurrency 1\u53EF\u964D\u4F4E\u8D44\u6E90\u5360\u7528\u3002\u8FD0\u884C\u955C\u50CF\u53BB\u9664\u5E94\u7528\u81EA\u8EAB\u7684\u58F0\u660E\u6587\u4EF6\u3001\u6E90\u7801\u6620\u5C04\u548C\u8BF4\u660E\u6587\u4EF6\u3002
|
|
2019
1877
|
\u9ED8\u8BA4\u5BB9\u5668\u7528\u4E8E CPU \u8FD0\u884C\u6216\u8FDE\u63A5\u8FDC\u7A0B AI \u670D\u52A1\uFF0C\u4E0D\u989D\u5916\u4E0B\u8F7D GPU \u8FD0\u884C\u5E93\uFF1B\u9700\u8981\u4F7F\u7528\u90E8\u7F72\u673A\u5668\u4E0A\u7684 GPU \u65F6\uFF0C\u81EA\u884C\u5B9A\u5236\u955C\u50CF\u548C\u5BB9\u5668\u8FD0\u884C\u53C2\u6570\u3002
|
|
2020
1878
|
|
|
2021
1879
|
\`\`\`sh
|
|
2022
1880
|
${pm} build
|
|
2023
|
-
${pm} build --target
|
|
1881
|
+
${pm} build --target image
|
|
2024
1882
|
# \u6784\u5EFA\u548C\u90E8\u7F72\u4E00\u6B21\u5B8C\u6210
|
|
2025
1883
|
${pm} exec hai deploy ${appDir} --env test --build
|
|
2026
1884
|
# \u590D\u7528\u5DF2\u6709\u955C\u50CF\uFF1B\u53EA\u751F\u6210\u5B8C\u6574\u79BB\u7EBF\u5305\u53CA\u8DE8\u5E73\u53F0\u90E8\u7F72\u811A\u672C
|
|
2027
1885
|
${pm} exec hai deploy ${appDir} --env prod --bundle-only
|
|
2028
1886
|
# \u53EA\u66F4\u65B0\u524D\u7AEF\uFF08\u5DE5\u7A0B\u5B58\u5728 web \u955C\u50CF\u65F6\uFF09
|
|
2029
|
-
${pm} build --target
|
|
1887
|
+
${pm} build --target image --images web
|
|
2030
1888
|
${pm} exec hai deploy ${appDir} --env prod --services web
|
|
2031
1889
|
\`\`\`
|
|
2032
1890
|
|
|
@@ -2035,8 +1893,10 @@ ${pm} exec hai deploy ${appDir} --env prod --services web
|
|
|
2035
1893
|
\u5B8C\u6574\u79BB\u7EBF\u5305\u81EA\u52A8\u643A\u5E26\u81EA\u5EFA\u4E2D\u95F4\u4EF6\u955C\u50CF\uFF1B\u51C6\u5907\u4E2D\u95F4\u4EF6\u955C\u50CF\u9700\u8981\u6784\u5EFA\u673A\u6709\u53EF\u7528 Podman/Docker\uFF0C\u7F3A\u955C\u50CF\u65F6\u5728\u6253\u5305\u9636\u6BB5\u62C9\u53D6\u3002
|
|
2036
1894
|
\u670D\u52A1\u5668\u9700\u8981 Linux\u3001Docker/Podman\u3001Docker Compose v2 \u6216 podman-compose 1.6.0 \u53CA\u4EE5\u4E0A\u3001sh\u3001tar\u3001sha256sum\u3001awk\u3001curl\u3001util-linux flock \u548C\u652F\u6301 flock \u7684\u6587\u4EF6\u7CFB\u7EDF\uFF0C\u542F\u52A8\u9636\u6BB5\u4E0D\u8054\u7F51\u62C9\u53D6\u3002
|
|
2037
1895
|
\u56FA\u5B9A .deploy.lock \u5185\u6838\u9501\u8986\u76D6\u90E8\u7F72\u53CA\u6062\u590D\uFF1B\u7981\u6B62\u5220\u9664\u6216\u66FF\u6362\uFF0C\u6240\u6709\u6301\u6709\u8FDB\u7A0B\u9000\u51FA\u6216\u91CD\u542F\u540E\u81EA\u52A8\u91CA\u653E\u3002\u5F3A\u6740\u540E\u987B\u6838\u5BF9\u7ED3\u679C\u6587\u4EF6\u3001current \u548C\u5BB9\u5668\u72B6\u6001\uFF0C\u518D\u7528\u65B0\u5305\u6062\u590D\u3002
|
|
2038
|
-
\u6BCF\u6B21\u79BB\u7EBF\u4EA4\u4ED8\u751F\u6210\u72EC\u7ACB\u76EE\u5F55\uFF0C\u5916\u5C42\u4EC5\u542B
|
|
1896
|
+
\u6BCF\u6B21\u79BB\u7EBF\u4EA4\u4ED8\u751F\u6210\u72EC\u7ACB\u76EE\u5F55\uFF0C\u9ED8\u8BA4tar.gz\uFF08\u663E\u5F0F--output release.tar\u53EF\u4FDD\u7559\u672A\u538B\u7F29\uFF09\uFF0C\u5916\u5C42\u4EC5\u542B\u5F52\u6863\u3001deploy.ps1\uFF08Windows\uFF09\u3001deploy.sh\uFF08Linux/macOS\uFF09\u3001deploy-target.json\u3001read-target.awk\u3001deploy-local.sh \u548C README.txt\uFF1BCompose\u3001\u8FD0\u884C\u73AF\u5883\u548C\u6E05\u5355\u4EC5\u5728\u5F52\u6863\u5185\uFF0CREADME\u4E0D\u91CD\u590D\u3002\u4E2D\u95F4\u4EF6\u5F52\u6863\u6309\u955C\u50CF\u5185\u5BB9ID\u7F13\u5B58\u5E76\u5728\u590D\u7528\u524D\u9A8C\u8BC1\u6458\u8981\u3002\u6210\u529F\u540E\u6E05\u7406\u672C\u6B21incoming\u4E0A\u4F20\u4E0E\u89E3\u5305\u76EE\u5F55\uFF0Crelease\u4FDD\u7559\u6062\u590D\u914D\u7F6E\u548C\u72B6\u6001\uFF0C\u955C\u50CF\u7531\u5F15\u64CE\u4FDD\u5B58\u3002\u4EA4\u4E92\u5F0F\u6253\u5305\u5728\u6784\u5EFA\u548C\u4E2D\u95F4\u4EF6\u914D\u7F6E\u540E\u6700\u540E\u8BE2\u95EE\u662F\u5426\u4FDD\u5B58\u6216\u4FEE\u6539 SSH \u4FE1\u606F\uFF0C\u5199\u5165 .env.<\u73AF\u5883>.local \u5E76\u7528\u4E8E\u672C\u6B21\u548C\u540E\u7EED target\uFF1B--yes \u590D\u7528\u5DF2\u6709\u914D\u7F6E\uFF0C\u590D\u5236\u6574\u4E2A\u76EE\u5F55\u5373\u53EF\u3002\u811A\u672C\u6309\u5DF2\u4FDD\u5B58\u7684 SSH \u53C2\u6570\u4E0A\u4F20\u3001\u5BFC\u5165\u3001\u542F\u52A8\u5E76\u9A8C\u8BC1 HTTP\u6216HTTPS\u3002
|
|
1897
|
+
\u90E8\u7F72\u5411\u5BFC\u53EF\u9009\u5DF2\u6709PEM\u8BC1\u4E66\u4E0E\u79C1\u94A5\u8DEF\u5F84\uFF08\u76F8\u5BF9\u5DE5\u7A0B\u6839\uFF0C\u6821\u9A8C\u540E\u590D\u5236\u5230\u79C1\u6709\u5305\uFF09\u6216Let\u2019s Encrypt\u3002\u5B98\u65B9Certbot\u7533\u8BF7\u5E76\u6BCF12\u5C0F\u65F6\u68C0\u67E5\u7EED\u671F\uFF0CNginx\u81EA\u52A8\u91CD\u8F7D\uFF1B\u9700\u8981\u6761\u6B3E\u786E\u8BA4\u3001\u771F\u5B9E\u90AE\u7BB1\u3001\u516C\u7F51DNS\u300180\u7AEF\u53E3\u53CACA\u7F51\u7EDC\u3002HTTPS\u9ED8\u8BA4443\u3001HTTP\u8DF3\u8F6C\u5F00\u542F\uFF0C\u672A\u914D\u7F6E\u4ECD\u4E3AHTTP\uFF1B\u542F\u505C\u6216\u6539\u6A21\u5F0F/\u57DF\u540D/\u7AEF\u53E3/\u672C\u5730\u8BC1\u4E66\u987B\u5168\u91CF\u3002\u8BC1\u4E66\u72B6\u6001\u4FDD\u7559\u5728\u670D\u52A1\u5668\u5E76\u5907\u4EFD\uFF0C\u9694\u79BB\u76EE\u6807\u4F7F\u7528\u5DF2\u6709\u8BC1\u4E66\u3002--plan\u4E2D\u7684root\u8868\u793A\u5DE5\u7A0B\u6839\uFF0CserviceDir\u8868\u793A\u90E8\u7F72\u670D\u52A1\u6E90\u7801\u4E0E\u914D\u7F6E\u76EE\u5F55\u3002
|
|
2039
1898
|
\u5B8C\u5168\u9694\u79BB\u65F6\u590D\u5236\u6574\u4E2A\u4EA4\u4ED8\u76EE\u5F55\uFF0C\u8FD0\u884C sh deploy-local.sh \u81EA\u52A8\u89E3\u5305\u5E76\u90E8\u7F72\uFF1B\u53EF\u4F20\u5165\u90E8\u7F72\u76EE\u5F55\u8986\u76D6\u9ED8\u8BA4\u4F4D\u7F6E\u3002
|
|
1899
|
+
Node\u540E\u7AEF\u9ED8\u8BA4\u5728\u542F\u52A8\u65F6\u91CD\u8BFB\u5B9E\u9645\u6302\u8F7D\u7684releases/<\u540E\u7AEF\u53D1\u5E03ID>/.env.runtime\uFF0C\u4FEE\u6539\u540Erestart\u5BB9\u5668\u5373\u53EF\uFF1B\u65E0\u9700\u65B0\u5F00\u5173\u6216override\uFF0C\u65E7\u90E8\u7F72\u5148\u7528\u65B0\u7248CLI\u53D1\u5E03\u4E00\u6B21\u3002\u4EC5\u66F4\u65B0Web/\u4E2D\u95F4\u4EF6\u540E\uFF0C\u6309\u540E\u7AEF\u5BB9\u5668Mounts\u786E\u8BA4\u6E90\u6587\u4EF6\uFF0C\u4E0D\u80FD\u6839\u636Ecurrent\u731C\u8DEF\u5F84\u3002\u6258\u7BA1\u8FDE\u63A5middleware.env\u4ECD\u4F18\u5148\uFF1B\u7AEF\u53E3/\u6302\u8F7D/Node\u542F\u52A8\u53C2\u6570\u3001\u9759\u6001Web\u548C\u4E2D\u95F4\u4EF6\u6309\u539F\u6D41\u7A0B\u66F4\u65B0\u3002\u624B\u5DE5\u4FEE\u6539\u5148\u5907\u4EFD\uFF0C\u957F\u671F\u914D\u7F6E\u540C\u6B65\u6E90\u73AF\u5883\uFF0C\u4E0B\u6B21\u540E\u7AEF\u53D1\u5E03\u4F1A\u91CD\u65B0\u751F\u6210\u3002
|
|
2040
1900
|
\u90E8\u5206\u66F4\u65B0\u8981\u6C42\u5DF2\u6709\u6210\u529F\u7684\u5B8C\u6574\u90E8\u7F72\uFF0C\u4FDD\u7559\u672A\u9009\u670D\u52A1\u53CA\u6570\u636E\u5377\uFF1B\u5931\u8D25\u8FD4\u56DE\u975E\u96F6\u9000\u51FA\u7801\uFF0C\u5DF2\u6709\u90E8\u7F72\u5C1D\u8BD5\u6062\u590D\u9009\u4E2D\u670D\u52A1\u3002
|
|
2041
1901
|
\u4E2D\u95F4\u4EF6\u8FDE\u63A5\u9F50\u5168\u65F6\u4F7F\u7528 --skip-middleware \u8DF3\u8FC7\u914D\u7F6E\u5411\u5BFC\u3002\u5305\u4E2D\u5305\u542B\u79C1\u6709\u914D\u7F6E\uFF0C\u4E0D\u63D0\u4EA4 Git\u3002
|
|
2042
1902
|
`;
|
|
@@ -2045,7 +1905,7 @@ function generateReadme(name, appTypeLabel, pm, appType) {
|
|
|
2045
1905
|
const install = getInstallCommand(pm);
|
|
2046
1906
|
const staticWebsite = appType === "website" ? `### \u9759\u6001\u7F51\u7AD9\u4E0E GitHub Pages
|
|
2047
1907
|
|
|
2048
|
-
\u9ED8\u8BA4\u4F7F\u7528Node SSR\
|
|
1908
|
+
\u9ED8\u8BA4\u4F7F\u7528Node SSR\uFF0C\u5305\u542B\u5B8C\u6574\u6A21\u5757\u548C\u793A\u4F8B\u3002\u9759\u6001\u53D1\u5E03\u524D\u5148\u79FB\u9664\u670D\u52A1\u7AEF\u4E1A\u52A1\u4F9D\u8D56\u3001\u521D\u59CB\u5316\u53CAAPI/actions\uFF0C\u4FDD\u7559Core/UI\uFF1B\u5DF2\u6709\u52A8\u6001\u6A21\u5757\u4E0D\u80FD\u4EC5\u6253\u5F00\u5F00\u5173\u9759\u6001\u53D1\u5E03\u3002
|
|
2049
1909
|
|
|
2050
1910
|
\`HAI_BUILD_STATIC=1\`\u663E\u5F0F\u9884\u6E32\u67D3\uFF0C\`PUBLIC_BASE_PATH=/${name}\`\u6307\u5B9A\u9879\u76EE\u7AD9\u76EE\u5F55\uFF08\u65E0\u672B\u5C3E/\uFF0C\u6839\u7AD9\u7559\u7A7A\uFF09\uFF1B\u5728PowerShell\u8BBE\u7F6E\u672C\u6B21\u6784\u5EFA\u73AF\u5883\uFF1A
|
|
2051
1911
|
|
|
@@ -2472,7 +2332,7 @@ async function addModule(options) {
|
|
|
2472
2332
|
writeCliMessage(cliM("cli_addAvailableModules", { params: { modules: Object.keys(MODULE_MAP).join(", ") } }));
|
|
2473
2333
|
return;
|
|
2474
2334
|
}
|
|
2475
|
-
const { generateProjectAiSupport, resolveSkillNamesForPackages } = await import('./cli-skill-templates-
|
|
2335
|
+
const { generateProjectAiSupport, resolveSkillNamesForPackages } = await import('./cli-skill-templates-VV2XWYON.js');
|
|
2476
2336
|
if (moduleDef.packages.every((p) => project.haiPackages.includes(p))) {
|
|
2477
2337
|
if (moduleName === "deploy")
|
|
2478
2338
|
await ensureDeployConfig(cwd);
|
|
@@ -2679,9 +2539,9 @@ function staticCompileEnvironment(target) {
|
|
|
2679
2539
|
if (base && (!/^\/[\w.-]+(?:\/[\w.-]+)*$/.test(base) || base.split("/").some((segment) => segment === "." || segment === "..")))
|
|
2680
2540
|
throw new Error(cliM("cli_build_static_base"));
|
|
2681
2541
|
if (target !== "compile") {
|
|
2682
|
-
if (flag === "1" || base && target !== "
|
|
2542
|
+
if (flag === "1" || base && target !== "image")
|
|
2683
2543
|
throw new Error(cliM("cli_build_static_target"));
|
|
2684
|
-
return target === "
|
|
2544
|
+
return target === "image" && base ? { PUBLIC_BASE_PATH: base } : {};
|
|
2685
2545
|
}
|
|
2686
2546
|
return { ...flag ? { HAI_BUILD_STATIC: flag } : {}, ...base ? { PUBLIC_BASE_PATH: base } : {} };
|
|
2687
2547
|
}
|
|
@@ -2702,11 +2562,14 @@ function sourceCommit(cwd) {
|
|
|
2702
2562
|
async function buildCommand(options) {
|
|
2703
2563
|
const directory = resolve(options.cwd ?? process4.cwd());
|
|
2704
2564
|
const target = options.target ?? "compile";
|
|
2565
|
+
const concurrency = Number(options.concurrency ?? 2);
|
|
2566
|
+
if (![1, 2].includes(concurrency))
|
|
2567
|
+
throw new Error(cliM("cli_build_concurrency_invalid"));
|
|
2705
2568
|
const cwd = target === "compile" ? directory : dirname(projectConfigPath(directory));
|
|
2706
|
-
if (!["compile", "
|
|
2569
|
+
if (!["compile", "image", "desktop", "app"].includes(target))
|
|
2707
2570
|
throw new Error(cliM("cli_build_target_invalid"));
|
|
2708
2571
|
const compileEnvironment = staticCompileEnvironment(target);
|
|
2709
|
-
if (target !== "
|
|
2572
|
+
if (target !== "image" && (options.images !== void 0 || options.concurrency !== void 0 || options.runtime !== void 0 || options.network !== void 0 || options.sqlitePrebuildMirror !== void 0 || options.httpProxy === false) || !["desktop", "app"].includes(target) && (options.platform !== void 0 || options.arch !== void 0) || target === "app" && options.arch !== void 0) {
|
|
2710
2573
|
throw new Error(cliM("cli_build_options_invalid"));
|
|
2711
2574
|
}
|
|
2712
2575
|
if (target === "compile") {
|
|
@@ -2798,7 +2661,7 @@ async function buildCommand(options) {
|
|
|
2798
2661
|
try {
|
|
2799
2662
|
for (const context of new Set(images.map((image) => resolve(cwd, image.context ?? "."))))
|
|
2800
2663
|
prepareDependencyInputs(context);
|
|
2801
|
-
|
|
2664
|
+
const buildImage = async (image) => {
|
|
2802
2665
|
if (!["serv", "web"].includes(image.name))
|
|
2803
2666
|
throw new Error(cliM("cli_build_images_missing"));
|
|
2804
2667
|
const context = resolve(cwd, image.context ?? ".");
|
|
@@ -2840,7 +2703,15 @@ async function buildCommand(options) {
|
|
|
2840
2703
|
const hash = createHash("sha256");
|
|
2841
2704
|
for await (const chunk of createReadStream(join(staging, file)))
|
|
2842
2705
|
hash.update(chunk);
|
|
2843
|
-
|
|
2706
|
+
return { name: image.name, file, imageRef, sha256: hash.digest("hex"), ...image.containerPort ? { containerPort: image.containerPort } : {} };
|
|
2707
|
+
};
|
|
2708
|
+
for (let offset = 0; offset < images.length; offset += concurrency) {
|
|
2709
|
+
const results = await Promise.allSettled(images.slice(offset, offset + concurrency).map(buildImage));
|
|
2710
|
+
for (const result of results) {
|
|
2711
|
+
if (result.status === "rejected")
|
|
2712
|
+
throw result.reason;
|
|
2713
|
+
artifact.images.push(result.value);
|
|
2714
|
+
}
|
|
2844
2715
|
}
|
|
2845
2716
|
const output = join(parent, buildId);
|
|
2846
2717
|
writeFileSync(join(staging, "manifest.json"), `${JSON.stringify(artifact, null, 2)}
|
|
@@ -3224,7 +3095,7 @@ async function configureDeployMiddleware(module, scan, environment, config, yes
|
|
|
3224
3095
|
} else {
|
|
3225
3096
|
if (mode === "default" && ["aliyun-sms", "api"].includes(backendName))
|
|
3226
3097
|
throw new Error(cliM("cli_deploy_cloud_unsupported", { params: { backend: backend.label } }));
|
|
3227
|
-
const connection =
|
|
3098
|
+
const connection = backendName === current.backend ? { ...requirement.config } : requirement.configName === "reach" ? { name: requirement.config.name ?? requirement.service } : {};
|
|
3228
3099
|
for (const field of backend.fields) {
|
|
3229
3100
|
const previous2 = backendName === current.backend ? requirement.config[field.name] : void 0;
|
|
3230
3101
|
const initial = previous2 === void 0 ? field.defaultValue ?? "" : typeof previous2 === "object" ? JSON.stringify(previous2) : String(previous2);
|
|
@@ -3343,7 +3214,7 @@ async function configureDeployTarget(module, config, environment, nonInteractive
|
|
|
3343
3214
|
type: "docker-ssh",
|
|
3344
3215
|
ssh: { ...ssh, host, port, username, auth, password, identityFile: identityFile || void 0, knownHostsFile: knownHostsFile || void 0 },
|
|
3345
3216
|
remote: { ...remote, baseDir, runtime: runtime.value },
|
|
3346
|
-
expose: { type: "port", hostPort, containerPort }
|
|
3217
|
+
expose: { ...expose, type: "port", hostPort, containerPort }
|
|
3347
3218
|
};
|
|
3348
3219
|
} else {
|
|
3349
3220
|
provider = { type: "vercel", token: await input("HAI_DEPLOY_PROVIDER_TOKEN", void 0, { secret: true }) };
|
|
@@ -3357,6 +3228,57 @@ async function configureDeployTarget(module, config, environment, nonInteractive
|
|
|
3357
3228
|
config.provider = validated.data;
|
|
3358
3229
|
writeCliMessage(cliM("cli_deploy_target_saved"));
|
|
3359
3230
|
}
|
|
3231
|
+
async function configureDeployHttps(module, config, environment, nonInteractive, partial) {
|
|
3232
|
+
const provider = configObject(config.provider);
|
|
3233
|
+
if (provider.type !== "docker-ssh" || nonInteractive || partial)
|
|
3234
|
+
return;
|
|
3235
|
+
const expose = configObject(provider.expose);
|
|
3236
|
+
const previous = configObject(expose.https);
|
|
3237
|
+
const selection = await prompts({
|
|
3238
|
+
type: "select",
|
|
3239
|
+
name: "httpsMode",
|
|
3240
|
+
message: cliM("cli_deploy_https_mode"),
|
|
3241
|
+
choices: [
|
|
3242
|
+
{ title: cliM(previous.type ? "cli_deploy_https_keep" : "cli_deploy_https_off"), value: "keep" },
|
|
3243
|
+
{ title: cliM("cli_deploy_https_files"), value: "files" },
|
|
3244
|
+
{ title: "Let's Encrypt (Certbot)", value: "letsencrypt" },
|
|
3245
|
+
...previous.type ? [{ title: cliM("cli_deploy_https_off"), value: "off" }] : []
|
|
3246
|
+
]
|
|
3247
|
+
});
|
|
3248
|
+
if (selection.httpsMode === void 0)
|
|
3249
|
+
throw new Error(cliM("cli_deploy_cancelled"));
|
|
3250
|
+
if (selection.httpsMode === "keep")
|
|
3251
|
+
return;
|
|
3252
|
+
let https;
|
|
3253
|
+
if (selection.httpsMode !== "off") {
|
|
3254
|
+
const ask2 = async (name, initial) => {
|
|
3255
|
+
const answer = await prompts({ type: "text", name, message: cliM(`cli_deploy_${name}`), initial: initial === void 0 ? "" : String(initial), validate: (value) => !!value.trim() || cliM("cli_deploy_invalid_value") });
|
|
3256
|
+
if (answer[name] === void 0)
|
|
3257
|
+
throw new Error(cliM("cli_deploy_cancelled"));
|
|
3258
|
+
return String(answer[name]).trim();
|
|
3259
|
+
};
|
|
3260
|
+
https = { type: selection.httpsMode, domain: await ask2("httpsDomain", previous.domain), port: Number(await ask2("httpsPort", previous.port ?? 443)), redirectHttp: true };
|
|
3261
|
+
if (https.type === "files") {
|
|
3262
|
+
https.certificateFile = await ask2("httpsCertificate", previous.certificateFile);
|
|
3263
|
+
https.privateKeyFile = await ask2("httpsPrivateKey", previous.privateKeyFile);
|
|
3264
|
+
} else {
|
|
3265
|
+
writeCliMessage(cliM("cli_deploy_https_requirements"));
|
|
3266
|
+
https.email = await ask2("httpsEmail", previous.email);
|
|
3267
|
+
const consent = await prompts({ type: "confirm", name: "acceptTerms", message: cliM("cli_deploy_https_terms"), initial: previous.acceptTerms === true });
|
|
3268
|
+
if (consent.acceptTerms !== true)
|
|
3269
|
+
throw new Error(cliM("cli_deploy_cancelled"));
|
|
3270
|
+
https.acceptTerms = true;
|
|
3271
|
+
}
|
|
3272
|
+
}
|
|
3273
|
+
const parsed2 = module.ProviderConfigSchema.safeParse({ ...provider, expose: { ...expose, ...https?.type === "letsencrypt" ? { hostPort: 80 } : {}, https } });
|
|
3274
|
+
if (!parsed2.success)
|
|
3275
|
+
throw new Error(cliM("cli_deploy_invalid_value"));
|
|
3276
|
+
const saved = module.saveDeploymentEnvironment(environment, { HAI_DEPLOY_PROVIDER: JSON.stringify(parsed2.data) }, { private: true });
|
|
3277
|
+
if (!saved.success)
|
|
3278
|
+
throw new Error(saved.error.message);
|
|
3279
|
+
config.provider = parsed2.data;
|
|
3280
|
+
writeCliMessage(cliM("cli_deploy_https_saved"));
|
|
3281
|
+
}
|
|
3360
3282
|
async function deployCommand(options) {
|
|
3361
3283
|
const spinner = ora();
|
|
3362
3284
|
const cwd = options.cwd ?? process4.cwd();
|
|
@@ -3373,7 +3295,7 @@ async function deployCommand(options) {
|
|
|
3373
3295
|
throw new Error(cliM("cli_build_runtime_missing"));
|
|
3374
3296
|
if (options.artifactDir && options.build)
|
|
3375
3297
|
throw new Error(cliM("cli_deploy_build_artifact_conflict"));
|
|
3376
|
-
if (!options.build && (options.network !== void 0 || options.sqlitePrebuildMirror !== void 0 || options.httpProxy === false))
|
|
3298
|
+
if (!options.build && (options.concurrency !== void 0 || options.network !== void 0 || options.sqlitePrebuildMirror !== void 0 || options.httpProxy === false))
|
|
3377
3299
|
throw new Error(cliM("cli_deploy_build_options_required"));
|
|
3378
3300
|
let selectedServices = options.services?.split(",").map((name) => name.trim());
|
|
3379
3301
|
if (selectedServices && (!selectedServices.length || selectedServices.some((name) => !/^[a-z][a-z0-9-]*$/.test(name)) || new Set(selectedServices).size !== selectedServices.length))
|
|
@@ -3406,7 +3328,7 @@ async function deployCommand(options) {
|
|
|
3406
3328
|
localModule = localRequire.resolve("@h-ai/deploy");
|
|
3407
3329
|
} catch {
|
|
3408
3330
|
}
|
|
3409
|
-
deployModule = localModule ? await import(pathToFileURL(localModule).href) : await import('./src-
|
|
3331
|
+
deployModule = localModule ? await import(pathToFileURL(localModule).href) : await import('./src-IR5AU43K.js');
|
|
3410
3332
|
} catch {
|
|
3411
3333
|
writeCliError(chalk.red(cliM("cli_deploy_module_missing")));
|
|
3412
3334
|
writeCliMessage(chalk.cyan(" pnpm add -D @h-ai/deploy"));
|
|
@@ -3513,7 +3435,7 @@ async function deployCommand(options) {
|
|
|
3513
3435
|
const imageNames2 = selectedServices?.filter((name) => ["serv", "web"].includes(name));
|
|
3514
3436
|
if (!selectedServices || imageNames2?.length) {
|
|
3515
3437
|
writeCliMessage(cliM("cli_deploy_build_started"));
|
|
3516
|
-
await buildCommand({ cwd: buildRoot, target: "
|
|
3438
|
+
await buildCommand({ cwd: buildRoot, target: "image", images: imageNames2?.join(","), concurrency: options.concurrency, runtime: options.runtime, network: options.network, sqlitePrebuildMirror: options.sqlitePrebuildMirror, httpProxy: options.httpProxy });
|
|
3517
3439
|
}
|
|
3518
3440
|
}
|
|
3519
3441
|
if (selectedServices)
|
|
@@ -3537,6 +3459,7 @@ async function deployCommand(options) {
|
|
|
3537
3459
|
deployConfig.services = void 0;
|
|
3538
3460
|
if (options.bundleOnly)
|
|
3539
3461
|
await configureDeployTarget(deployModule, deployConfig, environment.data, options.yes, true);
|
|
3462
|
+
await configureDeployHttps(deployModule, deployConfig, environment.data, options.yes, selectedServices !== void 0);
|
|
3540
3463
|
for (const key of Object.keys(process4.env)) {
|
|
3541
3464
|
if (originalEnv[key] === void 0)
|
|
3542
3465
|
delete process4.env[key];
|
|
@@ -5119,7 +5042,7 @@ async function initProject(options) {
|
|
|
5119
5042
|
const { project, existing, missing } = result;
|
|
5120
5043
|
spinner.succeed(cliM("cli_initDetected", { params: { name: chalk.cyan(project.name) } }));
|
|
5121
5044
|
if (project.isHaiProject) {
|
|
5122
|
-
const { generateProjectAiSupport, resolveSkillNamesForPackages } = await import('./cli-skill-templates-
|
|
5045
|
+
const { generateProjectAiSupport, resolveSkillNamesForPackages } = await import('./cli-skill-templates-VV2XWYON.js');
|
|
5123
5046
|
const generatedFiles = await generateProjectAiSupport(cwd, resolveSkillNamesForPackages(project.haiPackages), {
|
|
5124
5047
|
overwriteSkills: false,
|
|
5125
5048
|
overwriteBridgeFiles: false
|
|
@@ -5368,34 +5291,19 @@ var VERSION = getCliVersion();
|
|
|
5368
5291
|
var cli = cac("hai");
|
|
5369
5292
|
cli.option("-v, --verbose", cliM("cli_help_verbose"));
|
|
5370
5293
|
cli.option("-C, --cwd <path>", cliM("cli_help_cwd"));
|
|
5371
|
-
cli.command("create [name]", cliM("cli_help_create")).option("--type <type>", cliM("cli_help_appType")).option("--frontends <targets>", cliM("cli_help_frontends")).option("
|
|
5294
|
+
cli.command("create [name]", cliM("cli_help_create")).option("--type <type>", cliM("cli_help_appType")).option("--frontends <targets>", cliM("cli_help_frontends")).option("--no-install", cliM("cli_help_noInstall")).option("--no-deploy", cliM("cli_help_noDeploy")).option("-p, --package-manager <pm>", cliM("cli_help_packageManager")).option("--no-git", cliM("cli_help_noGit")).option("--git-remote <url>", cliM("cli_help_gitRemote")).option("--defaults", cliM("cli_help_defaults")).action(async (name, options) => {
|
|
5372
5295
|
try {
|
|
5373
|
-
let features;
|
|
5374
|
-
if (options.features !== void 0) {
|
|
5375
|
-
try {
|
|
5376
|
-
if (typeof options.features !== "string")
|
|
5377
|
-
throw new Error(cliM("cli_createFeaturesInvalid"));
|
|
5378
|
-
features = parseCreateFeatures(options.features);
|
|
5379
|
-
} catch (error) {
|
|
5380
|
-
writeCliError(cliM("cli_createFailed"), error);
|
|
5381
|
-
process4.exitCode = 1;
|
|
5382
|
-
return;
|
|
5383
|
-
}
|
|
5384
|
-
}
|
|
5385
5296
|
const frontends = typeof options.frontends === "string" ? options.frontends.split(",").map((f) => f.trim()).filter(Boolean) : void 0;
|
|
5386
5297
|
await createProject({
|
|
5387
5298
|
name: name ?? "",
|
|
5388
5299
|
appType: options.type,
|
|
5389
5300
|
frontends,
|
|
5390
|
-
template: options.template,
|
|
5391
|
-
features,
|
|
5392
5301
|
deploy: options.deploy,
|
|
5393
|
-
examples: options.examples,
|
|
5394
5302
|
install: options.install,
|
|
5395
5303
|
packageManager: options.packageManager,
|
|
5396
5304
|
git: options.git,
|
|
5397
5305
|
gitRemote: options.gitRemote,
|
|
5398
|
-
|
|
5306
|
+
defaults: options.defaults,
|
|
5399
5307
|
verbose: options.verbose,
|
|
5400
5308
|
cwd: options.cwd
|
|
5401
5309
|
});
|
|
@@ -5441,7 +5349,7 @@ cli.command("generate [type] [name]", cliM("cli_help_generate")).option("-w, --w
|
|
|
5441
5349
|
process4.exit(1);
|
|
5442
5350
|
}
|
|
5443
5351
|
});
|
|
5444
|
-
cli.command("build", cliM("cli_help_build")).option("--target <target>", cliM("cli_help_target")).option("--images <names>", cliM("cli_help_images")).option("--runtime <runtime>", cliM("cli_help_buildRuntime")).option("--network <network>", cliM("cli_help_buildNetwork")).option("--platform <platform>", cliM("cli_help_platform")).option("--arch <arch>", cliM("cli_help_arch")).option("--sqlite-prebuild-mirror <url>", cliM("cli_help_sqliteMirror")).option("--no-http-proxy", cliM("cli_help_noHttpProxy")).action(async (options) => {
|
|
5352
|
+
cli.command("build", cliM("cli_help_build")).option("--target <target>", cliM("cli_help_target")).option("--images <names>", cliM("cli_help_images")).option("--concurrency <count>", cliM("cli_help_buildConcurrency")).option("--runtime <runtime>", cliM("cli_help_buildRuntime")).option("--network <network>", cliM("cli_help_buildNetwork")).option("--platform <platform>", cliM("cli_help_platform")).option("--arch <arch>", cliM("cli_help_arch")).option("--sqlite-prebuild-mirror <url>", cliM("cli_help_sqliteMirror")).option("--no-http-proxy", cliM("cli_help_noHttpProxy")).action(async (options) => {
|
|
5445
5353
|
try {
|
|
5446
5354
|
await buildCommand(options);
|
|
5447
5355
|
} catch (error) {
|
|
@@ -5457,7 +5365,7 @@ cli.command("upgrade <version>", cliM("cli_help_upgrade")).action(async (version
|
|
|
5457
5365
|
process4.exitCode = 1;
|
|
5458
5366
|
}
|
|
5459
5367
|
});
|
|
5460
|
-
cli.command("deploy [appDir]", cliM("cli_help_deploy")).option("--plan", cliM("cli_help_deployPlan")).option("--inspect-remote", cliM("cli_help_inspectRemote")).option("--env <environment>", cliM("cli_help_environment")).option("--env-dir <path>", cliM("cli_help_envDir")).option("-y, --yes", cliM("cli_help_deployYes")).option("--skip-middleware", cliM("cli_help_skipMiddleware")).option("--artifact-dir <path>", cliM("cli_help_artifactDir")).option("--services <names>", cliM("cli_help_services")).option("--build", cliM("cli_help_deployBuild")).option("--network <network>", cliM("cli_help_deployNetwork")).option("--sqlite-prebuild-mirror <url>", cliM("cli_help_deploySqliteMirror")).option("--no-http-proxy", cliM("cli_help_deployNoProxy")).option("--runtime <runtime>", cliM("cli_help_deployRuntime")).option("--bundle-only", cliM("cli_help_bundleOnly")).option("--output <path>", cliM("cli_help_bundleOutput")).action(async (appDir, options) => {
|
|
5368
|
+
cli.command("deploy [appDir]", cliM("cli_help_deploy")).option("--plan", cliM("cli_help_deployPlan")).option("--inspect-remote", cliM("cli_help_inspectRemote")).option("--env <environment>", cliM("cli_help_environment")).option("--env-dir <path>", cliM("cli_help_envDir")).option("-y, --yes", cliM("cli_help_deployYes")).option("--skip-middleware", cliM("cli_help_skipMiddleware")).option("--artifact-dir <path>", cliM("cli_help_artifactDir")).option("--services <names>", cliM("cli_help_services")).option("--build", cliM("cli_help_deployBuild")).option("--concurrency <count>", cliM("cli_help_buildConcurrency")).option("--network <network>", cliM("cli_help_deployNetwork")).option("--sqlite-prebuild-mirror <url>", cliM("cli_help_deploySqliteMirror")).option("--no-http-proxy", cliM("cli_help_deployNoProxy")).option("--runtime <runtime>", cliM("cli_help_deployRuntime")).option("--bundle-only", cliM("cli_help_bundleOnly")).option("--output <path>", cliM("cli_help_bundleOutput")).action(async (appDir, options) => {
|
|
5461
5369
|
try {
|
|
5462
5370
|
await deployCommand({
|
|
5463
5371
|
appDir,
|
|
@@ -5470,6 +5378,7 @@ cli.command("deploy [appDir]", cliM("cli_help_deploy")).option("--plan", cliM("c
|
|
|
5470
5378
|
artifactDir: options.artifactDir,
|
|
5471
5379
|
services: options.services,
|
|
5472
5380
|
build: options.build,
|
|
5381
|
+
concurrency: options.concurrency,
|
|
5473
5382
|
network: options.network,
|
|
5474
5383
|
sqlitePrebuildMirror: options.sqlitePrebuildMirror,
|
|
5475
5384
|
httpProxy: options.httpProxy,
|