@h-ai/cli 1.0.0-beta.2 → 1.0.0-beta.4

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.
Files changed (58) hide show
  1. package/README.en.md +20 -8
  2. package/README.ja.md +20 -8
  3. package/README.md +20 -8
  4. package/dist/{chunk-MUHRBJSW.js → chunk-K6RWTTHD.js} +546 -97
  5. package/dist/chunk-K6RWTTHD.js.map +1 -0
  6. package/dist/{chunk-AKV2CQZV.js → chunk-SWEU7KUE.js} +71 -59
  7. package/dist/chunk-SWEU7KUE.js.map +1 -0
  8. package/dist/{chunk-EF7WOFWZ.js → chunk-YVNNMC4H.js} +364 -136
  9. package/dist/chunk-YVNNMC4H.js.map +1 -0
  10. package/dist/cli-config-templates-LUJGDQNW.js +5 -0
  11. package/dist/{cli-config-templates-HHFHNXBN.js.map → cli-config-templates-LUJGDQNW.js.map} +1 -1
  12. package/dist/{cli-skill-templates-PN4FLGBC.js → cli-skill-templates-VV2XWYON.js} +3 -3
  13. package/dist/{cli-skill-templates-PN4FLGBC.js.map → cli-skill-templates-VV2XWYON.js.map} +1 -1
  14. package/dist/index.js +117 -208
  15. package/dist/index.js.map +1 -1
  16. package/dist/metafile-esm.json +1 -1
  17. package/dist/{src-TEBHR5I5.js → src-IR5AU43K.js} +3 -3
  18. package/dist/src-IR5AU43K.js.map +1 -0
  19. package/package.json +11 -11
  20. package/templates/apps/api/.dockerignore.hbs +10 -0
  21. package/templates/apps/api/apps/{{projectName}}-serv/config/_serv.yml.hbs +20 -1
  22. package/templates/apps/api/apps/{{projectName}}-serv/src/app.ts.hbs +6 -1
  23. package/templates/apps/api/apps/{{projectName}}-serv/src/lib/server/init.ts.hbs +1 -1
  24. package/templates/apps/api/images/serv.Dockerfile.hbs +3 -2
  25. package/templates/apps/fullstack/.dockerignore.hbs +10 -0
  26. package/templates/apps/fullstack/apps/{{projectName}}-web/README.md.hbs +1 -1
  27. package/templates/apps/fullstack/e2e/fullstack.spec.ts.hbs +16 -2
  28. package/templates/apps/fullstack/images/serv.Dockerfile.hbs +6 -2
  29. package/templates/apps/fullstack/images/web.Dockerfile.hbs +6 -1
  30. package/templates/apps/fullstack/packages/{{projectName}}-serv/README.md.hbs +1 -1
  31. package/templates/apps/fullstack/packages/{{projectName}}-serv/config/_reach.yml.hbs +10 -7
  32. package/templates/apps/fullstack/packages/{{projectName}}-serv/config/_serv.yml.hbs +14 -7
  33. package/templates/apps/fullstack/packages/{{projectName}}-serv/package.json.hbs +6 -0
  34. package/templates/apps/fullstack/packages/{{projectName}}-serv/src/app-server.ts.hbs +1 -1
  35. package/templates/apps/fullstack/packages/{{projectName}}-serv/tests/app-server.test.ts.hbs +7 -1
  36. package/templates/apps/fullstack/packages/{{projectName}}-serv/tests/security.test.ts.hbs +1 -1
  37. package/templates/apps/fullstack/pnpm-workspace.yaml.hbs +1 -0
  38. package/templates/apps/website/scripts/static-website.mjs.hbs +3 -3
  39. package/templates/base/.dockerignore.hbs +10 -0
  40. package/templates/base/images/web.Dockerfile.hbs +4 -2
  41. package/templates/base/package.json.hbs +1 -1
  42. package/templates/base/src/hooks.server.ts.hbs +9 -7
  43. package/templates/features/payment/routes/api/v1/payment/create/+server.ts +3 -1
  44. package/templates/skills/hai-ai/guide.md +5 -3
  45. package/templates/skills/hai-app-create/development.md +5 -3
  46. package/templates/skills/hai-app-create/reference.md +1 -1
  47. package/templates/skills/hai-build/SKILL.md +5 -5
  48. package/templates/skills/hai-build/paths.md +1 -1
  49. package/templates/skills/hai-build/reference.md +1 -1
  50. package/templates/skills/hai-core/request-access.md +1 -1
  51. package/templates/skills/hai-deploy/SKILL.md +15 -3
  52. package/templates/skills/hai-deploy/middleware.md +4 -2
  53. package/templates/skills/hai-serv/reference.md +5 -8
  54. package/dist/chunk-AKV2CQZV.js.map +0 -1
  55. package/dist/chunk-EF7WOFWZ.js.map +0 -1
  56. package/dist/chunk-MUHRBJSW.js.map +0 -1
  57. package/dist/cli-config-templates-HHFHNXBN.js +0 -5
  58. package/dist/src-TEBHR5I5.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-EF7WOFWZ.js';
3
- import { cliM } from './chunk-AKV2CQZV.js';
4
- import { external_exports, normalizeProjectName } from './chunk-MUHRBJSW.js';
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 SELECTABLE_FEATURES = [
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-HHFHNXBN.js');
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-PN4FLGBC.js');
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 docker\n\n\u672C\u9879\u76EE\u521B\u5EFA\u65F6\u5173\u95ED\u4E86\u90E8\u7F72\u5DE5\u5177\u3002\n") : readmeContent
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.yes ?? false;
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 selectedTemplate = options.template;
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
- yes: useDefaults,
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 docker\` |
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
 
@@ -2015,18 +1873,18 @@ function generateBuildDeploymentReadme(pm, appDir) {
2015
1873
 
2016
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\u907F\u514D\u91CD\u590D\u4E0B\u8F7D\u3002
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 docker
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 docker --images web
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 tar\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 tar \u5185\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\u3002
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\u3002\u7EAF\u5C55\u793A\u7AD9\u70B9\u521B\u5EFA\u65F6\u4F7F\u7528 \`--type website --features none\`\uFF0C\u4FDD\u7559Core/UI\u548C\u9ED8\u8BA4\u90E8\u7F72\u5F00\u53D1\u5DE5\u5177\uFF0C\u65E0\u987B\u9009\u62E9deploy\u518D\u5173\u95ED\u3002\u5DF2\u6709\u52A8\u6001\u6A21\u5757\u4E0D\u80FD\u4EC5\u6253\u5F00\u5F00\u5173\u9759\u6001\u53D1\u5E03\u3002
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-PN4FLGBC.js');
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 !== "docker")
2542
+ if (flag === "1" || base && target !== "image")
2683
2543
  throw new Error(cliM("cli_build_static_target"));
2684
- return target === "docker" && base ? { PUBLIC_BASE_PATH: base } : {};
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", "docker", "desktop", "app"].includes(target))
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 !== "docker" && (options.images !== 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) {
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
- for (const image of images) {
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
- artifact.images.push({ name: image.name, file, imageRef, sha256: hash.digest("hex"), ...image.containerPort ? { containerPort: image.containerPort } : {} });
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 = mode === "custom" && backendName === current.backend ? { ...requirement.config } : requirement.configName === "reach" ? { name: requirement.config.name ?? requirement.service } : {};
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-TEBHR5I5.js');
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: "docker", images: imageNames2?.join(","), runtime: options.runtime, network: options.network, sqlitePrebuildMirror: options.sqlitePrebuildMirror, httpProxy: options.httpProxy });
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-PN4FLGBC.js');
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("-t, --template <template>", cliM("cli_help_template")).option("-f, --features <features>", cliM("cli_help_features")).option("--examples", cliM("cli_help_examples")).option("--no-examples", cliM("cli_help_noExamples")).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("-y, --yes", cliM("cli_help_yes")).action(async (name, options) => {
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
- yes: options.yes,
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,