@h-ai/cli 1.0.0-beta.6 → 1.0.0-beta.8

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 (53) hide show
  1. package/README.en.md +9 -9
  2. package/README.ja.md +9 -9
  3. package/README.md +9 -9
  4. package/dist/{chunk-4XVCGFBA.js → chunk-MEEFB4BI.js} +552 -11
  5. package/dist/chunk-MEEFB4BI.js.map +1 -0
  6. package/dist/{chunk-STWZQ3IP.js → chunk-SGWSVWZ5.js} +588 -11
  7. package/dist/chunk-SGWSVWZ5.js.map +1 -0
  8. package/dist/cli-config-templates-SVV3633D.js +5 -0
  9. package/dist/{cli-config-templates-YGPVNRX5.js.map → cli-config-templates-SVV3633D.js.map} +1 -1
  10. package/dist/index.js +97 -78
  11. package/dist/index.js.map +1 -1
  12. package/dist/metafile-esm.json +1 -1
  13. package/dist/{src-NXCCU6GO.js → src-IW4CY2IN.js} +3 -3
  14. package/dist/src-IW4CY2IN.js.map +1 -0
  15. package/package.json +16 -12
  16. package/templates/apps/api/apps/{{projectName}}-serv/config/_serv.yml.hbs +1 -1
  17. package/templates/apps/api/apps/{{projectName}}-serv/src/app.ts.hbs +6 -2
  18. package/templates/apps/api/apps/{{projectName}}-serv/tests/service.test.ts.hbs +29 -3
  19. package/templates/apps/fullstack/.gitignore.hbs +7 -2
  20. package/templates/apps/fullstack/apps/{{projectName}}-app/README.md.hbs +2 -0
  21. package/templates/apps/fullstack/apps/{{projectName}}-app/package.json.hbs +1 -1
  22. package/templates/apps/fullstack/packages/{{projectName}}-serv/README.md.hbs +2 -2
  23. package/templates/apps/mobile-app/messages/en-US.json +1 -1
  24. package/templates/apps/mobile-app/messages/ja-JP.json +1 -1
  25. package/templates/apps/mobile-app/messages/zh-CN.json +1 -1
  26. package/templates/apps/mobile-app/src/lib/api.ts.hbs +8 -5
  27. package/templates/base/.gitignore.hbs +7 -2
  28. package/templates/base/e2e/app.spec.ts.hbs +9 -4
  29. package/templates/base/package.json.hbs +7 -2
  30. package/templates/skills/hai-ai/SKILL.md +6 -2
  31. package/templates/skills/hai-ai/acp.md +145 -0
  32. package/templates/skills/hai-ai/guide.md +1 -1
  33. package/templates/skills/hai-ai/reference.md +1 -1
  34. package/templates/skills/hai-ai/resources.md +69 -0
  35. package/templates/skills/hai-api-client/reference.md +2 -0
  36. package/templates/skills/hai-app-create/development.md +1 -1
  37. package/templates/skills/hai-app-create/reference.md +1 -1
  38. package/templates/skills/hai-build/SKILL.md +1 -1
  39. package/templates/skills/hai-build/reference.md +1 -1
  40. package/templates/skills/hai-cache/reference.md +6 -2
  41. package/templates/skills/hai-ci/SKILL.md +2 -0
  42. package/templates/skills/hai-core/SKILL.md +1 -1
  43. package/templates/skills/hai-crypto/reference.md +2 -0
  44. package/templates/skills/hai-payment/reference.md +39 -17
  45. package/templates/skills/hai-reach/reference.md +15 -7
  46. package/templates/skills/hai-reldb/reference.md +5 -2
  47. package/templates/skills/hai-scheduler/reference.md +1 -1
  48. package/templates/skills/hai-storage/reference.md +8 -2
  49. package/templates/skills/hai-vecdb/reference.md +7 -2
  50. package/dist/chunk-4XVCGFBA.js.map +0 -1
  51. package/dist/chunk-STWZQ3IP.js.map +0 -1
  52. package/dist/cli-config-templates-YGPVNRX5.js +0 -5
  53. package/dist/src-NXCCU6GO.js.map +0 -1
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env node
2
+ export { generateConfigFile, generateEnvExample, generateProductionEnvironment } from './chunk-SGWSVWZ5.js';
3
+ import './chunk-ZSDEVXZ7.js';
4
+ //# sourceMappingURL=cli-config-templates-SVV3633D.js.map
5
+ //# sourceMappingURL=cli-config-templates-SVV3633D.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":[],"names":[],"mappings":"","file":"cli-config-templates-YGPVNRX5.js"}
1
+ {"version":3,"sources":[],"names":[],"mappings":"","file":"cli-config-templates-SVV3633D.js"}
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
- import { generateConfigFile, runtimeEnvironmentVariables, renderEnvironment, generateProductionEnvironment, moduleEnvironmentVariables, documentConfig, parameterizeConfig } from './chunk-STWZQ3IP.js';
2
+ import { generateConfigFile, runtimeEnvironmentVariables, renderEnvironment, generateProductionEnvironment, moduleEnvironmentVariables, documentConfig, parameterizeConfig } from './chunk-SGWSVWZ5.js';
3
3
  import { cliM } from './chunk-7DNIMG2H.js';
4
- import { normalizeProjectName, resolveImageReference, resolveDownloadProxy } from './chunk-4XVCGFBA.js';
4
+ import { normalizeProjectName, resolveImageReference, resolveDownloadProxy } from './chunk-MEEFB4BI.js';
5
5
  import './chunk-ZSDEVXZ7.js';
6
6
  import process5 from 'process';
7
7
  import { cac } from 'cac';
@@ -14,7 +14,7 @@ import Handlebars2 from 'handlebars';
14
14
  import { parseEnv } from 'util';
15
15
  import ora from 'ora';
16
16
  import prompts from 'prompts';
17
- import { parse, stringify, parseAllDocuments } from 'yaml';
17
+ import { parse, stringify, parseDocument, parseAllDocuments } from 'yaml';
18
18
  import { Buffer } from 'buffer';
19
19
  import { execSync, execFileSync, spawn } from 'child_process';
20
20
  import { randomBytes, createECDH, randomUUID, createHash } from 'crypto';
@@ -133,11 +133,10 @@ Handlebars2.registerHelper("html", (value) => Handlebars2.escapeExpression(value
133
133
  Handlebars2.registerHelper("yaml", (key, value) => stringify({ [key]: value }).trim());
134
134
  Handlebars2.registerHelper("jsString", (value) => `'${JSON.stringify(value).slice(1, -1).replace(/\\"/g, '"').replace(/'/g, "\\'")}'`);
135
135
  Handlebars2.registerHelper("moduleConfig", function(moduleKey, enabled) {
136
- const generated = generateConfigFile(moduleKey, this.moduleConfigs).trimEnd();
136
+ const generated = generateConfigFile(moduleKey, this.moduleConfigs, Boolean(enabled)).trimEnd();
137
137
  const content = moduleKey === "feedback" ? generated.replace("HAI_FEEDBACK_APPID:my-app", `HAI_FEEDBACK_APPID:${this.projectName}`) : generated;
138
138
  const configured = this.appType === "fullstack" && moduleKey === "iam" ? content.replace("HAI_IAM_SEEDDEFAULTDATA:true", "HAI_IAM_SEEDDEFAULTDATA:false").replace("variant: card", "variant: split") : content;
139
- return enabled ? configured : `# # \u672A\u542F\u7528\uFF1A\u5B89\u88C5\u5BF9\u5E94\u6A21\u5757\u5E76\u5728\u670D\u52A1\u7AEF runtime \u4E2D\u521D\u59CB\u5316\uFF0C\u518D\u53D6\u6D88\u6240\u9700\u914D\u7F6E\u7684\u6CE8\u91CA\u3002
140
- ${configured.split("\n").map((line) => line ? `# ${line}` : "").join("\n")}`;
139
+ return configured;
141
140
  });
142
141
  var TEMPLATE_SKIP_IF_EMPTY_MARKER = "@skipIfEmpty";
143
142
  var NORMALIZED_RENDER_EXTENSIONS = /* @__PURE__ */ new Set([".ts", ".js", ".svelte", ".json", ".css"]);
@@ -460,7 +459,11 @@ function buildApiWorkspaceCatalogPackages(featureMap) {
460
459
  function buildAppHaiCatalogPackages(options) {
461
460
  const packageNames = ["@h-ai/cli"];
462
461
  if (options.isSvelteOnlyApp) {
463
- packageNames.push("@h-ai/api-client", "@h-ai/api-contract", "@h-ai/capacitor", "@h-ai/ui");
462
+ packageNames.push("@h-ai/api-client", "@h-ai/capacitor", "@h-ai/core", "@h-ai/ui");
463
+ if (options.featureMap.iam)
464
+ packageNames.push("@h-ai/api-contract");
465
+ if (options.featureMap.deploy)
466
+ packageNames.push("@h-ai/deploy");
464
467
  if (options.featureMap.crypto)
465
468
  packageNames.push("@h-ai/crypto");
466
469
  return pickCatalogPackages(packageNames, HAI_CATALOG_PACKAGES);
@@ -1228,7 +1231,14 @@ var FEATURES = {
1228
1231
  packages: ["@h-ai/capacitor"]
1229
1232
  }
1230
1233
  };
1231
- var FULL_FEATURES = ["iam", "db", "cache", "crypto", "ai", "storage", "audit", "reach", "feedback", "payment", "vecdb", "datapipe", "scheduler", "deploy", "api-client", "capacitor"];
1234
+ var TEMPLATE_FEATURES = {
1235
+ "admin": ["iam", "reach"],
1236
+ "website": [],
1237
+ "h5": ["iam", "reach"],
1238
+ "api": ["iam", "reach", "storage", "feedback"],
1239
+ "mobile-app": ["iam", "crypto", "api-client", "capacitor"],
1240
+ "fullstack": ["iam", "storage", "audit", "reach", "scheduler", "feedback"]
1241
+ };
1232
1242
  var PROJECT_NAME_REGEX = /^[a-z0-9-]+$/;
1233
1243
  async function createProject(options) {
1234
1244
  const spinner = ora();
@@ -1271,30 +1281,23 @@ async function createProject(options) {
1271
1281
  });
1272
1282
  await generateFromTemplates(projectPath, context);
1273
1283
  {
1274
- const { generateConfigFile: generateConfigFile2, generateEnvExample, generateProductionEnvironment: generateProductionEnvironment2 } = await import('./cli-config-templates-YGPVNRX5.js');
1284
+ const { generateConfigFile: generateConfigFile2, generateEnvExample, generateProductionEnvironment: generateProductionEnvironment2 } = await import('./cli-config-templates-SVV3633D.js');
1275
1285
  const configs = resolvedOptions.moduleConfigs;
1276
1286
  const isApiWorkspace = resolvedOptions.appType === "api";
1277
1287
  const isFullstack = resolvedOptions.appType === "fullstack";
1278
1288
  const configDir = isApiWorkspace ? path2.join(projectPath, "apps", `${resolvedOptions.name}-serv`, "config") : isFullstack ? path2.join(projectPath, "packages", `${resolvedOptions.name}-serv`, "config") : path2.join(projectPath, "config");
1279
1289
  const envExamplePath = isApiWorkspace ? path2.join(projectPath, "apps", `${resolvedOptions.name}-serv`, ".env.example") : path2.join(projectPath, ".env.example");
1280
- for (const featureId of resolvedOptions.features) {
1281
- const configKey = getFeatureConfigKey(featureId);
1282
- if (!context.isSvelteOnlyApp && configKey && configKey !== "deploy") {
1283
- const configPath = path2.join(configDir, `_${configKey}.yml`);
1284
- if (isFullstack && await fse.pathExists(configPath))
1285
- continue;
1286
- const baseConfig = generateConfigFile2(configKey, configs);
1287
- const renderedConfig = baseConfig;
1288
- await writeFile(configPath, configKey === "feedback" ? renderedConfig.replace("HAI_FEEDBACK_APPID:my-app", `HAI_FEEDBACK_APPID:${resolvedOptions.name}`) : renderedConfig);
1289
- }
1290
- }
1291
- if (!isApiWorkspace && !isFullstack) {
1292
- await writeFile(
1293
- path2.join(configDir, "_core.yml"),
1294
- generateConfigFile2("core", configs)
1295
- );
1296
- if (!context.isSvelteOnlyApp)
1297
- await writeFile(path2.join(configDir, "_kit.yml"), generateConfigFile2("kit", configs));
1290
+ const enabledConfigs = /* @__PURE__ */ new Set(["core", ...context.isSvelteOnlyApp ? [] : resolvedOptions.features.map(getFeatureConfigKey).filter(Boolean)]);
1291
+ if (isApiWorkspace || isFullstack)
1292
+ enabledConfigs.add("serv");
1293
+ else if (!context.isSvelteOnlyApp)
1294
+ enabledConfigs.add("kit");
1295
+ for (const configKey of ["core", "serv", "kit", "db", "cache", "iam", "storage", "audit", "reach", "ai", "vecdb", "payment", "scheduler", "feedback"]) {
1296
+ const configPath = path2.join(configDir, `_${configKey}.yml`);
1297
+ if ((isApiWorkspace || isFullstack) && await fse.pathExists(configPath))
1298
+ continue;
1299
+ const content = generateConfigFile2(configKey, configs, enabledConfigs.has(configKey));
1300
+ await writeFile(configPath, configKey === "feedback" ? content.replace("HAI_FEEDBACK_APPID:my-app", `HAI_FEEDBACK_APPID:${resolvedOptions.name}`) : content);
1298
1301
  }
1299
1302
  const configContents = {};
1300
1303
  for (const file of await fse.readdir(configDir)) {
@@ -1302,7 +1305,7 @@ async function createProject(options) {
1302
1305
  configContents[file.slice(1, -4)] = await fse.readFile(path2.join(configDir, file), "utf8");
1303
1306
  }
1304
1307
  const envFeatures = context.isSvelteOnlyApp ? [] : isFullstack ? [.../* @__PURE__ */ new Set([...resolvedOptions.features, "reach"])] : resolvedOptions.features;
1305
- const envContents = generateEnvExample(envFeatures, configs, isFullstack, configContents);
1308
+ const envContents = generateEnvExample(envFeatures, configs, true, configContents);
1306
1309
  let envExample = isApiWorkspace ? envContents : isFullstack ? `${envContents}
1307
1310
  # First-start administrator password; must satisfy the IAM password policy.
1308
1311
  HAI_ADMIN_DEFAULT_PASSWORD=
@@ -1452,7 +1455,7 @@ ${formatPromptDescription(cliM("cli_selectHint"))}`,
1452
1455
  }
1453
1456
  const selectedAppType = resolvedAppType || "admin";
1454
1457
  const selectedFrontends = selectedAppType === "fullstack" ? await resolveFrontendTargets(options.frontends, useDefaults, onCancel) : [];
1455
- let selectedFeatures = selectedAppType === "mobile-app" ? ["iam", "crypto", "api-client", "capacitor"] : resolveFeatureDependencies(FULL_FEATURES);
1458
+ let selectedFeatures = selectedAppType === "mobile-app" ? [...TEMPLATE_FEATURES[selectedAppType]] : resolveFeatureDependencies(TEMPLATE_FEATURES[selectedAppType]);
1456
1459
  selectedFeatures = selectedFeatures.filter((feature) => feature !== "deploy");
1457
1460
  if (options.deploy !== false)
1458
1461
  selectedFeatures.push("deploy");
@@ -1857,7 +1860,9 @@ function generateEnvironmentReadme() {
1857
1860
 
1858
1861
  \u8FD0\u884C\u53C2\u6570\u5728 \`.env.example\` \u4E2D\u9010\u9879\u8BF4\u660E\u7528\u9014\u3001\u679A\u4E3E\u548C\u8303\u56F4\u3002\`.env\` \u4E3A\u516C\u5171\u57FA\u7840\uFF0C\`.env.dev\` / \`.env.test\` / \`.env.prod\` \u4E3A\u73AF\u5883\u8986\u76D6\uFF0C\u5747\u63D0\u4EA4 Git\uFF1B\`.env.local\` \u4E3A\u672C\u673A\u79C1\u6709\u8986\u76D6\uFF0C\u4E0D\u63D0\u4EA4 Git\u3002\u4F18\u5148\u7EA7\u4E3A\u8FDB\u7A0B > \`.env.<mode>.local\` > \`.env.local\` > \`.env.<mode>\` > \`.env\` > YAML\u3002\u540C\u6765\u6E90\u5148\u7236 JSON \u540E\u7EC6\u9879\uFF0C\u9AD8\u6765\u6E90\u6574\u6BB5\u66FF\u6362\u5148\u6E05\u9664\u4F4E\u6765\u6E90\u7EC6\u9879\u3002YAML \u663E\u5F0F\u5F15\u7528 \`HAI_<\u914D\u7F6E\u540D>_<\u8DEF\u5F84>\` \u5E76\u4FDD\u7559\u672C\u5730\u56DE\u9000\uFF0CcamelCase \u4E0D\u62C6\u8BCD\u3002\u5B8C\u6574\u547D\u4EE4\u3001\u52A0\u8F7D\u4F18\u5148\u7EA7\u548C Provider \u5207\u6362\u89C1 [\u73AF\u5883\u914D\u7F6E](.agents/skills/hai-app-create/development.md#\u73AF\u5883\u914D\u7F6E)\u3002
1859
1862
 
1860
- \u751F\u4EA7 .env.prod \u6309\u5DF2\u542F\u7528\u6A21\u5757\u9009\u62E9\u5171\u4EAB PostgreSQL\uFF08\u660E\u786E\u9009\u62E9 MySQL \u65F6\u4FDD\u7559\uFF09\u3001Redis\u3001S3\u3001Qdrant\uFF0C\u9ED8\u8BA4\u4F7F\u7528 Compose \u5185\u90E8\u670D\u52A1\u540D db/cache/storage/vecdb\u3002\u5BC6\u7801\u3001S3 \u51ED\u636E\u3001\u6A21\u578B Key \u4E0E\u53D7\u4FDD\u62A4 Qdrant \u7684 Key \u53EA\u7531\u79C1\u6709\u73AF\u5883\u6CE8\u5165\uFF1B\u5BB9\u5668\u5916\u8FD0\u884C\u8986\u76D6\u5185\u90E8\u8FDE\u63A5\u5730\u5740\u3002\u751F\u4EA7 Reach \u5FC5\u987B\u6CE8\u5165\u771F\u5B9E HAI_REACH_PROVIDERS JSON\uFF0C\u516C\u5171\u7A7A\u6570\u7EC4\u4F1A\u62D2\u7EDD\u542F\u52A8\u3002AI \u81EA\u52A8\u8865\u5168 db+vecdb \u4F9D\u8D56\uFF0C\u5E76\u5728\u4E8C\u8005\u5C31\u7EEA\u540E\u521D\u59CB\u5316\uFF0C\u5931\u8D25\u9006\u5E8F\u6E05\u7406\u3002
1863
+ \u4F9D\u8D56\u53EA\u5305\u542B\u6A21\u677F\u5B9E\u9645\u4F7F\u7528\u7684\u6A21\u5757\uFF1Bconfig \u4E0E \`.env.example\` \u4FDD\u7559\u6240\u6709\u6A21\u5757\u7684\u5B8C\u6574\u53C2\u8003\uFF0C\u672A\u542F\u7528\u9879\u4EE5\u6CE8\u91CA\u63D0\u4F9B\uFF0C\u4E0D\u8FDB\u5165\u516C\u5171\u73AF\u5883\u9ED8\u8BA4\u503C\u3002\u65B0\u589E\u529F\u80FD\u65F6\u6DFB\u52A0\u4F9D\u8D56\u3001\u542F\u7528\u6240\u9700\u914D\u7F6E\uFF0C\u518D\u63A5\u5165\u521D\u59CB\u5316\u3001\u53CD\u5E8F\u5173\u95ED\u548C\u4E1A\u52A1\u4EE3\u7801\uFF1B\u5B89\u88C5\u4F9D\u8D56\u4E0D\u4F1A\u81EA\u52A8\u63A5\u5165\u6A21\u5757\u3002\u7EAF\u5BA2\u6237\u7AEF\u7684\u670D\u52A1\u7AEF\u914D\u7F6E\u4EC5\u4F9B\u540E\u7EED\u540E\u7AEF\u96C6\u6210\u53C2\u8003\uFF0C\u79D8\u5BC6\u4E0D\u5F97\u6CE8\u5165\u6D4F\u89C8\u5668\u3002
1864
+
1865
+ \u751F\u4EA7 .env.prod \u6309\u5DF2\u542F\u7528\u6A21\u5757\u9009\u62E9\u5171\u4EAB PostgreSQL\uFF08\u660E\u786E\u9009\u62E9 MySQL \u65F6\u4FDD\u7559\uFF09\u3001Redis\u3001S3\u3001Qdrant\uFF0C\u9ED8\u8BA4\u4F7F\u7528 Compose \u5185\u90E8\u670D\u52A1\u540D db/cache/storage/vecdb\u3002\u5BC6\u7801\u3001S3 \u51ED\u636E\u3001\u6A21\u578B Key \u4E0E\u53D7\u4FDD\u62A4 Qdrant \u7684 Key \u53EA\u7531\u79C1\u6709\u73AF\u5883\u6CE8\u5165\uFF1B\u5BB9\u5668\u5916\u8FD0\u884C\u8986\u76D6\u5185\u90E8\u8FDE\u63A5\u5730\u5740\u3002\u751F\u4EA7 Reach \u5FC5\u987B\u6CE8\u5165\u771F\u5B9E HAI_REACH_PROVIDERS JSON\uFF0C\u516C\u5171\u7A7A\u6570\u7EC4\u4F1A\u62D2\u7EDD\u542F\u52A8\u3002\u540E\u7EED\u901A\u8FC7 hai add ai \u8865\u9F50 db+vecdb \u4F9D\u8D56\u65F6\uFF0C\u8FD8\u987B\u5728\u5E94\u7528\u5165\u53E3\u63A5\u5165\u521D\u59CB\u5316\u548C\u53CD\u5E8F\u5173\u95ED\uFF0C\u4E0D\u80FD\u4EC5\u5B89\u88C5\u4F9D\u8D56\u3002
1861
1866
 
1862
1867
  | \u7528\u9014 | \u547D\u4EE4 |
1863
1868
  | --- | --- |
@@ -1913,7 +1918,7 @@ function generateReadme(name, appTypeLabel, pm, appType) {
1913
1918
  const install = getInstallCommand(pm);
1914
1919
  const staticWebsite = appType === "website" ? `### \u9759\u6001\u7F51\u7AD9\u4E0E GitHub Pages
1915
1920
 
1916
- \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
1921
+ \u9ED8\u8BA4\u4F7F\u7528Node SSR\uFF0C\u53EA\u5B89\u88C5\u5C55\u793A\u529F\u80FD\u6240\u9700\u7684Core/Kit/UI\uFF0C\u53EF\u76F4\u63A5\u5207\u6362\u9759\u6001\u53D1\u5E03\uFF1B\u5B8C\u6574\u6CE8\u91CA\u914D\u7F6E\u4E0D\u542F\u7528\u6A21\u5757\u3002\u540E\u7EED\u65B0\u589E\u7684\u670D\u52A1\u7AEF\u4E1A\u52A1\u4F9D\u8D56\u3001\u521D\u59CB\u5316\u53CAAPI/actions\u987B\u5148\u79FB\u9664\uFF0C\u5DF2\u6709\u52A8\u6001\u6A21\u5757\u4E0D\u80FD\u4EC5\u6253\u5F00\u5F00\u5173\u9759\u6001\u53D1\u5E03\u3002
1917
1922
 
1918
1923
  \`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
1919
1924
 
@@ -2103,7 +2108,7 @@ CLI \u5728\u6839\u76EE\u5F55 .env.dev.local\u3001.env.test.local\u3001.env.prod.
2103
2108
 
2104
2109
  ### \u914D\u7F6E\u6269\u5C55
2105
2110
 
2106
- \u670D\u52A1\u7AEF \`packages/${name}-serv/config/\` \u59CB\u7EC8\u4FDD\u7559 core\u3001serv\u3001db\u3001cache\u3001iam\u3001storage\u3001audit\u3001reach\u3001ai\u3001vecdb\u3001payment\u3001scheduler\u3001feedback \u5171 13 \u4EFD YAML\uFF1B\u6784\u5EFA\u90E8\u7F72\u914D\u7F6E\u7EDF\u4E00\u4F4D\u4E8E\u9879\u76EE\u6839 hai.json\u3002\u5DF2\u7528\u6A21\u5757\u4FDD\u7559\u5DE5\u4F5C\u9ED8\u8BA4\u503C\uFF0C\u672A\u542F\u7528\u6A21\u5757\u6574\u4EFD\u6CE8\u91CA\uFF0C\u53EF\u9009\u5B57\u6BB5\u548C\u66FF\u4EE3 Provider \u6309\u5757\u6CE8\u91CA\uFF1B\u6839 \`.env.example\` \u4E5F\u4FDD\u7559\u672A\u542F\u7528\u6A21\u5757\u7684\u51ED\u636E\u793A\u4F8B\u3002Reach \u9ED8\u8BA4\u4E3A\u5F00\u53D1\u63A7\u5236\u53F0\u901A\u77E5\uFF0Cfeedback \u4F7F\u7528 email \u522B\u540D\uFF1B\u751F\u4EA7\u6309 \`_reach.yml\` \u6CE8\u91CA\u66FF\u6362\u4E3A SMTP\u3001\u77ED\u4FE1\u6216 API Provider\uFF0C\u5E76\u4FDD\u6301\u540D\u79F0\u552F\u4E00\u3002
2111
+ \u670D\u52A1\u7AEF \`packages/${name}-serv/config/\` \u59CB\u7EC8\u4FDD\u7559 core\u3001serv\u3001kit\u3001db\u3001cache\u3001iam\u3001storage\u3001audit\u3001reach\u3001ai\u3001vecdb\u3001payment\u3001scheduler\u3001feedback \u5171 14 \u4EFD YAML\uFF1B\u6784\u5EFA\u90E8\u7F72\u914D\u7F6E\u7EDF\u4E00\u4F4D\u4E8E\u9879\u76EE\u6839 hai.json\u3002\u53EA\u5B89\u88C5\u7BA1\u7406\u540E\u53F0\u5B9E\u9645\u4F7F\u7528\u7684\u6A21\u5757\uFF1B\u5DF2\u7528\u6A21\u5757\u4FDD\u7559\u5DE5\u4F5C\u9ED8\u8BA4\u503C\uFF0C\u672A\u542F\u7528\u6A21\u5757\u6574\u4EFD\u6CE8\u91CA\uFF0C\u53EF\u9009\u5B57\u6BB5\u548C\u66FF\u4EE3 Provider \u6309\u5757\u6CE8\u91CA\uFF1B\u6839 \`.env.example\` \u4E5F\u4FDD\u7559\u672A\u542F\u7528\u6A21\u5757\u7684\u51ED\u636E\u793A\u4F8B\u3002\u65B0\u589E\u529F\u80FD\u65F6\u5B89\u88C5\u4F9D\u8D56\u5E76\u63A5\u5165\u521D\u59CB\u5316\u3001\u5173\u95ED\u53CA\u4E1A\u52A1\u4EE3\u7801\u3002Reach \u9ED8\u8BA4\u4E3A\u5F00\u53D1\u63A7\u5236\u53F0\u901A\u77E5\uFF0Cfeedback \u4F7F\u7528 email \u522B\u540D\uFF1B\u751F\u4EA7\u6309 \`_reach.yml\` \u6CE8\u91CA\u66FF\u6362\u4E3A SMTP\u3001\u77ED\u4FE1\u6216 API Provider\uFF0C\u5E76\u4FDD\u6301\u540D\u79F0\u552F\u4E00\u3002
2107
2112
 
2108
2113
  \u81EA\u52A8\u751F\u6210\u7684 \`.env\` \u5305\u542B\u5DF2\u542F\u7528\u6A21\u5757\u7684\u73AF\u5883\u53C2\u6570\uFF1BYAML \u663E\u5F0F\u5F15\u7528\u76F8\u540C\u53D8\u91CF\u5E76\u4FDD\u7559\u672C\u5730\u56DE\u9000\u3002\u7F16\u8F91\u73AF\u5883\u53C2\u6570\u4F18\u5148\u4FEE\u6539\u5BF9\u5E94 \`.env\`\uFF1B\u672A\u542F\u7528\u6A21\u5757\u4ECD\u4FDD\u7559\u6CE8\u91CA\u3002
2109
2114
 
@@ -2244,7 +2249,7 @@ async function ensureDeployConfig(cwd) {
2244
2249
  }
2245
2250
  var MODULE_MAP = {
2246
2251
  // iam 依赖 crypto(password hash)/ db(user 存储)/ cache(session 缓存)
2247
- "iam": { packages: ["@h-ai/iam"], deps: ["crypto", "db", "cache"], configKey: "iam", get description() {
2252
+ "iam": { packages: ["@h-ai/iam"], configKey: "iam", get description() {
2248
2253
  return cliM("cli_featureIamName");
2249
2254
  } },
2250
2255
  // 'db' 和 'reldb' 都映射到 @h-ai/reldb,兼容用户习惯(hai add db 和 hai add reldb 均可)
@@ -2257,13 +2262,13 @@ var MODULE_MAP = {
2257
2262
  "cache": { packages: ["@h-ai/cache"], configKey: "cache", get description() {
2258
2263
  return cliM("cli_featureCacheName");
2259
2264
  } },
2260
- "ai": { packages: ["@h-ai/ai"], deps: ["db", "vecdb"], configKey: "ai", get description() {
2265
+ "ai": { packages: ["@h-ai/ai"], configKey: "ai", get description() {
2261
2266
  return cliM("cli_featureAiName");
2262
2267
  } },
2263
2268
  "storage": { packages: ["@h-ai/storage"], configKey: "storage", get description() {
2264
2269
  return cliM("cli_featureStorageName");
2265
2270
  } },
2266
- "crypto": { packages: ["@h-ai/crypto"], deps: ["db"], get description() {
2271
+ "crypto": { packages: ["@h-ai/crypto"], get description() {
2267
2272
  return cliM("cli_featureCryptoName");
2268
2273
  } },
2269
2274
  "kit": { packages: ["@h-ai/kit"], get description() {
@@ -2284,13 +2289,13 @@ var MODULE_MAP = {
2284
2289
  "reach": { packages: ["@h-ai/reach"], configKey: "reach", get description() {
2285
2290
  return cliM("cli_featureReachName");
2286
2291
  } },
2287
- "feedback": { packages: ["@h-ai/feedback"], deps: ["crypto", "db", "cache", "storage", "iam"], configKey: "feedback", get description() {
2292
+ "feedback": { packages: ["@h-ai/feedback"], configKey: "feedback", get description() {
2288
2293
  return cliM("cli_featureFeedbackName");
2289
2294
  } },
2290
- "scheduler": { packages: ["@h-ai/scheduler"], deps: ["db"], configKey: "scheduler", get description() {
2295
+ "scheduler": { packages: ["@h-ai/scheduler"], configKey: "scheduler", get description() {
2291
2296
  return cliM("cli_featureSchedulerName");
2292
2297
  } },
2293
- "audit": { packages: ["@h-ai/audit"], deps: ["db"], configKey: "audit", get description() {
2298
+ "audit": { packages: ["@h-ai/audit"], configKey: "audit", get description() {
2294
2299
  return cliM("cli_featureAuditName");
2295
2300
  } },
2296
2301
  "payment": { packages: ["@h-ai/payment"], configKey: "payment", get description() {
@@ -2323,25 +2328,62 @@ async function addModule(options) {
2323
2328
  writeCliMessage(chalk.green(cliM("cli_addAllInstalled")));
2324
2329
  return;
2325
2330
  }
2326
- const { selected } = await prompts({
2331
+ const { selected: selected2 } = await prompts({
2327
2332
  type: "select",
2328
2333
  hint: cliM("cli_selectHint"),
2329
2334
  name: "selected",
2330
2335
  message: cliM("cli_addModulePrompt"),
2331
2336
  choices
2332
2337
  });
2333
- if (!selected)
2338
+ if (!selected2)
2334
2339
  return;
2335
- moduleName = selected;
2340
+ moduleName = selected2;
2336
2341
  }
2337
- const moduleDef = MODULE_MAP[moduleName];
2338
- if (!moduleDef) {
2342
+ const baseDefinition = MODULE_MAP[moduleName];
2343
+ if (!baseDefinition) {
2339
2344
  writeCliError(chalk.red(cliM("cli_addUnknownModule", { params: { module: moduleName } })));
2340
2345
  writeCliMessage(cliM("cli_addAvailableModules", { params: { modules: Object.keys(MODULE_MAP).join(", ") } }));
2341
2346
  return;
2342
2347
  }
2348
+ const selected = baseDefinition.configKey === "db" ? "db" : moduleName;
2349
+ const moduleDef = { ...baseDefinition, deps: resolveFeatureDependencies([selected]).filter((feature) => feature !== selected) };
2343
2350
  const { generateProjectAiSupport, resolveSkillNamesForPackages } = await import('./cli-skill-templates-S2YH642Z.js');
2344
- if (moduleDef.packages.every((p) => project.haiPackages.includes(p))) {
2351
+ const allPackages = new Set(moduleDef.packages);
2352
+ for (const dep of moduleDef.deps) {
2353
+ for (const name of MODULE_MAP[dep]?.packages ?? [])
2354
+ allPackages.add(name);
2355
+ }
2356
+ const pkgPath = path2.join(cwd, "package.json");
2357
+ const pkg = await fse.readJson(pkgPath);
2358
+ if (!pkg.dependencies)
2359
+ pkg.dependencies = {};
2360
+ const existingHaiSpecifier = Object.entries(pkg.dependencies).find(([pkgName]) => pkgName.startsWith("@h-ai/"))?.[1];
2361
+ const depVersion = existingHaiSpecifier === "catalog:" ? "catalog:" : getCliVersion();
2362
+ let catalogUpdated = false;
2363
+ if (depVersion === "catalog:") {
2364
+ let workspaceRoot = path2.resolve(cwd);
2365
+ while (!await fileExists(path2.join(workspaceRoot, "pnpm-workspace.yaml"))) {
2366
+ const parent = path2.dirname(workspaceRoot);
2367
+ if (parent === workspaceRoot) {
2368
+ workspaceRoot = path2.resolve(cwd);
2369
+ break;
2370
+ }
2371
+ workspaceRoot = parent;
2372
+ }
2373
+ const workspacePath = path2.join(workspaceRoot, "pnpm-workspace.yaml");
2374
+ const document = parseDocument(await fileExists(workspacePath) ? await fse.readFile(workspacePath, "utf8") : "packages: []\n");
2375
+ const currentVersion = document.getIn(["catalog", "@h-ai/core"]);
2376
+ const version = typeof currentVersion === "string" && /^[~^]?\d+\.\d+\.\d+/.test(currentVersion) ? currentVersion : getCliVersion();
2377
+ const dependencies = { ...pkg.dependencies, ...pkg.devDependencies };
2378
+ const names = [.../* @__PURE__ */ new Set([...allPackages, ...Object.keys(dependencies).filter((name) => name.startsWith("@h-ai/") && dependencies[name] === "catalog:")])].filter((name) => !document.hasIn(["catalog", name]));
2379
+ for (const name of names)
2380
+ document.setIn(["catalog", name], version);
2381
+ if (names.length > 0) {
2382
+ await writeFile(workspacePath, document.toString());
2383
+ catalogUpdated = true;
2384
+ }
2385
+ }
2386
+ if (!catalogUpdated && [...allPackages].every((p) => project.haiPackages.includes(p))) {
2345
2387
  if (moduleName === "deploy")
2346
2388
  await ensureDeployConfig(cwd);
2347
2389
  const generatedFiles2 = await generateProjectAiSupport(
@@ -2362,24 +2404,7 @@ async function addModule(options) {
2362
2404
  }
2363
2405
  return;
2364
2406
  }
2365
- const allPackages = new Set(moduleDef.packages);
2366
- if (moduleDef.deps) {
2367
- for (const dep of moduleDef.deps) {
2368
- const depDef = MODULE_MAP[dep];
2369
- if (depDef) {
2370
- for (const pkg2 of depDef.packages) {
2371
- allPackages.add(pkg2);
2372
- }
2373
- }
2374
- }
2375
- }
2376
2407
  spinner.start(cliM("cli_addUpdateManifest"));
2377
- const pkgPath = path2.join(cwd, "package.json");
2378
- const pkg = await fse.readJson(pkgPath);
2379
- if (!pkg.dependencies)
2380
- pkg.dependencies = {};
2381
- const existingHaiSpecifier = Object.entries(pkg.dependencies).find(([pkgName]) => pkgName.startsWith("@h-ai/"))?.[1];
2382
- const depVersion = existingHaiSpecifier === "catalog:" ? "catalog:" : getCliVersion();
2383
2408
  for (const pkgName of allPackages) {
2384
2409
  if (pkgName === "@h-ai/deploy") {
2385
2410
  pkg.devDependencies ??= {};
@@ -2404,16 +2429,14 @@ async function addModule(options) {
2404
2429
  writeCliMessage(chalk.gray(cliM("cli_addConfigExists", { params: { config: moduleDef.configKey } })));
2405
2430
  }
2406
2431
  }
2407
- if (moduleDef.deps) {
2408
- for (const dep of moduleDef.deps) {
2409
- const depDef = MODULE_MAP[dep];
2410
- if (depDef?.configKey) {
2411
- const depConfigPath = path2.join(cwd, "config", `_${depDef.configKey}.yml`);
2412
- if (!await fileExists(depConfigPath)) {
2413
- const content = generateConfigFile(depDef.configKey);
2414
- await writeFile(depConfigPath, content);
2415
- writeCliMessage(chalk.gray(cliM("cli_addDependencyConfig", { params: { config: depDef.configKey } })));
2416
- }
2432
+ for (const dep of moduleDef.deps) {
2433
+ const depDef = MODULE_MAP[dep];
2434
+ if (depDef?.configKey) {
2435
+ const depConfigPath = path2.join(cwd, "config", `_${depDef.configKey}.yml`);
2436
+ if (!await fileExists(depConfigPath)) {
2437
+ const content = generateConfigFile(depDef.configKey);
2438
+ await writeFile(depConfigPath, content);
2439
+ writeCliMessage(chalk.gray(cliM("cli_addDependencyConfig", { params: { config: depDef.configKey } })));
2417
2440
  }
2418
2441
  }
2419
2442
  }
@@ -2435,7 +2458,7 @@ async function addModule(options) {
2435
2458
  const previousProduction = await fileExists(productionPath) ? await fse.readFile(productionPath, "utf8") : "";
2436
2459
  const configured = new Set([...previousProduction.matchAll(/^([A-Z][A-Z0-9_]*)=/gm)].map((match) => match[1]));
2437
2460
  const previousValues = parseEnv(previousProduction);
2438
- const production = generateProductionEnvironment([moduleName, ...moduleDef.deps ?? []], previousValues.HAI_DB_TYPE === "mysql" ? { db: { type: "mysql", database: previousValues.HAI_DB_DATABASE } } : void 0).split("\n").filter((line) => !/^[A-Z][A-Z0-9_]*=/.test(line) || !configured.has(line.split("=")[0]));
2461
+ const production = generateProductionEnvironment([selected, ...moduleDef.deps], previousValues.HAI_DB_TYPE === "mysql" ? { db: { type: "mysql", database: previousValues.HAI_DB_DATABASE } } : void 0).split("\n").filter((line) => !/^[A-Z][A-Z0-9_]*=/.test(line) || !configured.has(line.split("=")[0]));
2439
2462
  await writeFile(productionPath, `${previousProduction.trimEnd()}
2440
2463
 
2441
2464
  ${production.join("\n")}`);
@@ -2765,11 +2788,7 @@ function staticCompileEnvironment(target) {
2765
2788
  function sourceCommit(cwd) {
2766
2789
  try {
2767
2790
  const options = { cwd, encoding: "utf8", stdio: "pipe", windowsHide: true };
2768
- const root = execFileSync("git", ["rev-parse", "--show-toplevel"], options).trim();
2769
- const manifest = relative(root, join(cwd, "package.json")).replaceAll("\\", "/");
2770
- if (manifest.startsWith("../"))
2771
- return "unknown";
2772
- execFileSync("git", ["ls-files", "--error-unmatch", "--", manifest], { ...options, cwd: root });
2791
+ execFileSync("git", ["--literal-pathspecs", "ls-files", "--error-unmatch", "--", "package.json"], options);
2773
2792
  const commit = execFileSync("git", ["rev-parse", "HEAD"], options).trim();
2774
2793
  return /^(?:[a-f\d]{40}|[a-f\d]{64})$/i.test(commit) ? commit : "unknown";
2775
2794
  } catch {
@@ -3317,7 +3336,7 @@ async function deployCommand(options) {
3317
3336
  localModule = localRequire.resolve("@h-ai/deploy");
3318
3337
  } catch {
3319
3338
  }
3320
- deployModule = localModule ? await import(pathToFileURL(localModule).href) : await import('./src-NXCCU6GO.js');
3339
+ deployModule = localModule ? await import(pathToFileURL(localModule).href) : await import('./src-IW4CY2IN.js');
3321
3340
  } catch {
3322
3341
  writeCliError(chalk.red(cliM("cli_deploy_module_missing")));
3323
3342
  writeCliMessage(chalk.cyan(" pnpm add -D @h-ai/deploy"));