@h-ai/cli 0.1.0-alpha.66 → 0.1.0-alpha.67

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 (125) hide show
  1. package/README.en.md +66 -257
  2. package/README.ja.md +66 -257
  3. package/README.md +66 -257
  4. package/dist/{chunk-QYX6V7H2.js → chunk-7DSURTJ4.js} +22 -79
  5. package/dist/chunk-7DSURTJ4.js.map +1 -0
  6. package/dist/{chunk-FWN5B54L.js → chunk-AOQ4N657.js} +182 -10
  7. package/dist/chunk-AOQ4N657.js.map +1 -0
  8. package/dist/cli-config-templates-6AHNUGBN.js +5 -0
  9. package/dist/{cli-config-templates-3XSFIXBP.js.map → cli-config-templates-6AHNUGBN.js.map} +1 -1
  10. package/dist/{cli-skill-templates-TSYEQTCY.js → cli-skill-templates-FC3QRKDO.js} +3 -3
  11. package/dist/{cli-skill-templates-TSYEQTCY.js.map → cli-skill-templates-FC3QRKDO.js.map} +1 -1
  12. package/dist/index.js +435 -247
  13. package/dist/index.js.map +1 -1
  14. package/dist/{src-RDQ2EIBT.js → src-2V6VP4I5.js} +553 -119
  15. package/dist/src-2V6VP4I5.js.map +1 -0
  16. package/package.json +8 -8
  17. package/templates/README.md +48 -145
  18. package/templates/apps/admin/src/routes/admin/+layout.svelte.hbs +1 -1
  19. package/templates/apps/api/.dockerignore.hbs +14 -7
  20. package/templates/apps/api/.gitignore.hbs +7 -0
  21. package/templates/apps/api/apps/{{projectName}}-contract/README.md.hbs +23 -2
  22. package/templates/apps/api/apps/{{projectName}}-service/README.md.hbs +28 -7
  23. package/templates/apps/api/apps/{{projectName}}-service/package.json.hbs +1 -2
  24. package/templates/apps/api/hai.build.json.hbs +1 -1
  25. package/templates/apps/api/images/app.Dockerfile.hbs +35 -0
  26. package/templates/apps/api/package.json.hbs +5 -0
  27. package/templates/apps/fullstack/.dockerignore.hbs +14 -9
  28. package/templates/apps/fullstack/.gitignore.hbs +7 -0
  29. package/templates/apps/fullstack/apps/{{projectName}}-app/README.md.hbs +23 -5
  30. package/templates/apps/fullstack/apps/{{projectName}}-app/index.html.hbs +1 -0
  31. package/templates/apps/fullstack/apps/{{projectName}}-app/public/runtime-config.js.hbs +4 -0
  32. package/templates/apps/fullstack/apps/{{projectName}}-app/vite.config.ts.hbs +1 -1
  33. package/templates/apps/fullstack/apps/{{projectName}}-desktop/README.md.hbs +22 -4
  34. package/templates/apps/fullstack/apps/{{projectName}}-miniapp/README.md.hbs +16 -6
  35. package/templates/apps/fullstack/apps/{{projectName}}-web/README.md.hbs +23 -5
  36. package/templates/apps/fullstack/apps/{{projectName}}-web/vite.config.ts.hbs +1 -1
  37. package/templates/apps/fullstack/e2e/fullstack.spec.ts.hbs +11 -1
  38. package/templates/apps/fullstack/hai.build.json.hbs +2 -2
  39. package/templates/apps/fullstack/images/app.Dockerfile.hbs +36 -0
  40. package/templates/apps/fullstack/images/web.Dockerfile.hbs +34 -0
  41. package/templates/apps/fullstack/package.json.hbs +2 -0
  42. package/templates/apps/fullstack/packages/{{projectName}}-contract/README.md.hbs +23 -2
  43. package/templates/apps/fullstack/packages/{{projectName}}-serv/README.md.hbs +27 -6
  44. package/templates/apps/fullstack/packages/{{projectName}}-shared/.gitignore.hbs +7 -0
  45. package/templates/apps/fullstack/packages/{{projectName}}-shared/README.md.hbs +32 -11
  46. package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/components/AppShell.svelte.hbs +2 -2
  47. package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/pages/settings.svelte.hbs +3 -3
  48. package/templates/apps/fullstack/packages/{{projectName}}-shared/tests/api-client.test.ts.hbs +2 -2
  49. package/templates/apps/h5/src/routes/+layout.svelte.hbs +2 -2
  50. package/templates/apps/mobile-app/src/App.svelte.hbs +2 -2
  51. package/templates/apps/mobile-app/src/lib/pages/home.svelte.hbs +2 -2
  52. package/templates/apps/website/src/routes/+layout.svelte.hbs +3 -3
  53. package/templates/apps/website/src/routes/services/+page.svelte.hbs +1 -1
  54. package/templates/base/.dockerignore.hbs +14 -8
  55. package/templates/base/.gitignore.hbs +7 -0
  56. package/templates/base/e2e/app.spec.ts.hbs +10 -0
  57. package/templates/base/hai.build.json.hbs +1 -1
  58. package/templates/base/{Dockerfile.hbs → images/app.Dockerfile.hbs} +19 -17
  59. package/templates/base/package.json.hbs +8 -3
  60. package/templates/base/vite.config.ts.hbs +3 -1
  61. package/templates/shared/.gitattributes +6 -0
  62. package/templates/skills/AGENTS.md +3 -3
  63. package/templates/skills/bridges/admin/AGENTS.md +1 -1
  64. package/templates/skills/bridges/api/AGENTS.md +1 -1
  65. package/templates/skills/bridges/fullstack/AGENTS.md.hbs +1 -1
  66. package/templates/skills/bridges/h5/AGENTS.md +1 -1
  67. package/templates/skills/bridges/mobile-app/AGENTS.md +1 -1
  68. package/templates/skills/bridges/website/AGENTS.md +1 -1
  69. package/templates/skills/hai-api-client/SKILL.md +14 -161
  70. package/templates/skills/hai-api-client/reference.md +166 -0
  71. package/templates/skills/hai-api-contract/SKILL.md +15 -273
  72. package/templates/skills/hai-api-contract/reference.md +278 -0
  73. package/templates/skills/hai-app-create/SKILL.md +2 -2
  74. package/templates/skills/hai-app-create/development.md +4 -4
  75. package/templates/skills/hai-app-create/reference.md +5 -4
  76. package/templates/skills/hai-app-review/SKILL.md +1 -1
  77. package/templates/skills/hai-app-tests/SKILL.md +4 -3
  78. package/templates/skills/hai-app-tests/reference.md +2 -2
  79. package/templates/skills/hai-app-tests/validation.md +31 -0
  80. package/templates/skills/hai-audit/SKILL.md +17 -142
  81. package/templates/skills/hai-audit/reference.md +145 -0
  82. package/templates/skills/hai-build/SKILL.md +14 -8
  83. package/templates/skills/hai-build/reference.md +10 -9
  84. package/templates/skills/hai-cache/SKILL.md +15 -182
  85. package/templates/skills/hai-cache/reference.md +187 -0
  86. package/templates/skills/hai-capacitor/SKILL.md +4 -3
  87. package/templates/skills/hai-capacitor/reference.md +6 -4
  88. package/templates/skills/hai-ci/SKILL.md +2 -0
  89. package/templates/skills/hai-core/SKILL.md +3 -3
  90. package/templates/skills/hai-crypto/SKILL.md +16 -252
  91. package/templates/skills/hai-crypto/reference.md +256 -0
  92. package/templates/skills/hai-datapipe/SKILL.md +15 -196
  93. package/templates/skills/hai-datapipe/reference.md +202 -0
  94. package/templates/skills/hai-deploy/SKILL.md +20 -55
  95. package/templates/skills/hai-deploy/middleware.md +5 -11
  96. package/templates/skills/hai-framework-sync/SKILL.md +2 -2
  97. package/templates/skills/hai-framework-sync/sync.md +1 -1
  98. package/templates/skills/hai-iam/SKILL.md +1 -3
  99. package/templates/skills/hai-kit/SKILL.md +12 -167
  100. package/templates/skills/hai-kit/reference.md +172 -0
  101. package/templates/skills/hai-payment/SKILL.md +19 -251
  102. package/templates/skills/hai-payment/reference.md +256 -0
  103. package/templates/skills/hai-pr-review/SKILL.md +4 -4
  104. package/templates/skills/hai-reach/SKILL.md +2 -2
  105. package/templates/skills/hai-reldb/SKILL.md +1 -1
  106. package/templates/skills/hai-scheduler/SKILL.md +15 -195
  107. package/templates/skills/hai-scheduler/reference.md +202 -0
  108. package/templates/skills/hai-serv/SKILL.md +2 -2
  109. package/templates/skills/hai-spec-implement/SKILL.md +1 -1
  110. package/templates/skills/hai-spec-implement/workflow.md +3 -3
  111. package/templates/skills/hai-storage/SKILL.md +17 -238
  112. package/templates/skills/hai-storage/reference.md +243 -0
  113. package/templates/skills/hai-ui/SKILL.md +1 -1
  114. package/templates/skills/hai-ui/reference.md +9 -5
  115. package/templates/skills/hai-vecdb/SKILL.md +2 -2
  116. package/dist/chunk-FWN5B54L.js.map +0 -1
  117. package/dist/chunk-QYX6V7H2.js.map +0 -1
  118. package/dist/cli-config-templates-3XSFIXBP.js +0 -5
  119. package/dist/src-RDQ2EIBT.js.map +0 -1
  120. package/templates/apps/api/Dockerfile.hbs +0 -35
  121. package/templates/apps/fullstack/Dockerfile.hbs +0 -57
  122. /package/templates/apps/fullstack/{docker → images/web}/19-runtime-config.envsh.hbs +0 -0
  123. /package/templates/apps/fullstack/{docker → images/web}/nginx.conf.hbs +0 -0
  124. /package/templates/apps/fullstack/{docker → images/web}/runtime-config.js.template.hbs +0 -0
  125. /package/templates/base/{docker → images/web}/nginx.conf.hbs +0 -0
package/dist/index.js CHANGED
@@ -1,24 +1,24 @@
1
1
  #!/usr/bin/env node
2
- import { generateConfigFile, runtimeEnvironmentVariables, renderEnvironment, moduleEnvironmentVariables, parameterizeConfig } from './chunk-QYX6V7H2.js';
3
- import { cliM } from './chunk-FWN5B54L.js';
2
+ import { generateConfigFile, runtimeEnvironmentVariables, renderEnvironment, moduleEnvironmentVariables, parameterizeConfig } from './chunk-7DSURTJ4.js';
3
+ import { cliM } from './chunk-AOQ4N657.js';
4
4
  import './chunk-2H7UOFLK.js';
5
5
  import process4 from 'process';
6
6
  import { cac } from 'cac';
7
7
  import chalk from 'chalk';
8
- import path2, { resolve, join, dirname } from 'path';
8
+ import path2, { resolve, join, dirname, relative } from 'path';
9
9
  import { fileURLToPath, pathToFileURL } from 'url';
10
10
  import { wrapAnsi } from 'fast-wrap-ansi';
11
11
  import fse from 'fs-extra';
12
12
  import Handlebars2 from 'handlebars';
13
- import { core } from '@h-ai/core';
14
13
  import ora from 'ora';
15
14
  import prompts from 'prompts';
16
15
  import { Buffer } from 'buffer';
17
16
  import { execSync, execFileSync, spawn } from 'child_process';
18
17
  import { randomBytes, createHash } from 'crypto';
19
- import { stringify, parseDocument } from 'yaml';
20
- import { readFileSync, mkdirSync, mkdtempSync, existsSync, createReadStream, writeFileSync, renameSync, rmSync } from 'fs';
18
+ import { stringify, parseDocument, isScalar, isMap } from 'yaml';
19
+ import { readFileSync, existsSync, mkdirSync, mkdtempSync, createReadStream, writeFileSync, renameSync, rmSync, readdirSync, copyFileSync } from 'fs';
21
20
  import { createRequire } from 'module';
21
+ import { core } from '@h-ai/core';
22
22
 
23
23
  var CAMEL_CASE_SEPARATOR_REGEX = /[-_\s]+(.)?/g;
24
24
  var LEADING_CHAR_REGEX = /^./;
@@ -29,6 +29,8 @@ function writeCliMessage(message) {
29
29
  function writeCliError(message, error) {
30
30
  process4.stderr.write(`${message}
31
31
  `);
32
+ if (error && typeof error === "object" && "error" in error)
33
+ error = error.error;
32
34
  if (error !== void 0)
33
35
  process4.stderr.write(`${error instanceof Error ? error.message : String(error)}
34
36
  `);
@@ -133,56 +135,56 @@ var CAPACITOR_SKIPPED_SERVER_FILES = /* @__PURE__ */ new Set([
133
135
  "src/lib/server/init.ts"
134
136
  ]);
135
137
  var VERSIONS = {
136
- antfuEslintConfig: "^9.5.1",
137
- capacitorSecureStorage: "^8.0.1",
138
+ antfuEslintConfig: "9.5.1",
139
+ capacitorSecureStorage: "8.0.1",
138
140
  // UI / Icon
139
- bitsUi: "^2.19.5",
140
- iconifyJsonTabler: "^1.2.42",
141
- iconifyTailwind: "^1.2.3",
141
+ bitsUi: "2.19.5",
142
+ iconifyJsonTabler: "1.2.42",
143
+ iconifyTailwind: "1.2.3",
142
144
  // Capacitor
143
- capacitorAndroid: "^8.5.3",
144
- capacitorApp: "^8.1.2",
145
- capacitorCamera: "^8.2.5",
146
- capacitorCli: "^8.5.3",
147
- capacitorCore: "^8.5.3",
148
- capacitorDevice: "^8.0.3",
149
- capacitorIos: "^8.5.3",
150
- capacitorPreferences: "^8.0.1",
151
- capacitorPushNotifications: "^8.1.3",
152
- capacitorStatusBar: "^8.0.4",
145
+ capacitorAndroid: "8.5.3",
146
+ capacitorApp: "8.1.2",
147
+ capacitorCamera: "8.2.5",
148
+ capacitorCli: "8.5.3",
149
+ capacitorCore: "8.5.3",
150
+ capacitorDevice: "8.0.3",
151
+ capacitorIos: "8.5.3",
152
+ capacitorPreferences: "8.0.1",
153
+ capacitorPushNotifications: "8.1.3",
154
+ capacitorStatusBar: "8.0.4",
153
155
  // Tailwind / DaisyUI
154
- daisyui: "^5.7.47",
155
- tailwindcss: "^4.3.3",
156
- tailwindcssVite: "^4.3.3",
156
+ daisyui: "5.7.47",
157
+ tailwindcss: "4.3.3",
158
+ tailwindcssVite: "4.3.3",
157
159
  // i18n
158
- inlangParaglide: "^2.27.0",
159
- inlangMessageFormat: "^4.5.0",
160
+ inlangParaglide: "2.27.0",
161
+ inlangMessageFormat: "4.5.0",
160
162
  // Svelte / Vite
161
- svelte: "^5.57.2",
162
- svelteKit: "^2.70.3",
163
- svelteAdapterNode: "^5.5.7",
164
- svelteCheck: "^4.7.6",
165
- sveltePlugin: "^7.3.1",
163
+ svelte: "5.57.2",
164
+ svelteKit: "2.70.3",
165
+ svelteAdapterNode: "5.5.7",
166
+ svelteCheck: "4.7.6",
167
+ sveltePlugin: "7.3.1",
166
168
  // Build / Test
167
- vite: "^8.3.4",
168
- vitest: "^4.1.11",
169
- tsup: "^8.5.1",
170
- rimraf: "^6.1.3",
169
+ vite: "8.3.4",
170
+ vitest: "4.1.11",
171
+ tsup: "8.5.1",
172
+ rimraf: "6.1.3",
171
173
  // ESLint
172
- eslint: "^10.12.0",
173
- eslintPluginFormat: "^2.0.1",
174
- eslintPluginSvelte: "^3.23.1",
175
- svelteEslintParser: "^1.9.0",
174
+ eslint: "10.12.0",
175
+ eslintPluginFormat: "2.0.1",
176
+ eslintPluginSvelte: "3.23.1",
177
+ svelteEslintParser: "1.9.0",
176
178
  // Node / TS
177
- nodeTypes: "^26.6.5",
179
+ nodeTypes: "26.6.5",
178
180
  // Svelte / ESLint / tsup 继续使用稳定的 JavaScript 编译 API。
179
- typescript: "^6.0.3",
181
+ typescript: "6.0.3",
180
182
  // E2E
181
- playwright: "^1.64.0",
183
+ playwright: "1.64.0",
182
184
  // Validation
183
- zod: "^4.6.5",
185
+ zod: "4.6.5",
184
186
  // Tauri
185
- tauriCli: "^2.12.1"
187
+ tauriCli: "2.12.1"
186
188
  };
187
189
  var FRONTEND_PORTS_DEFAULT = {
188
190
  web: 4173,
@@ -580,6 +582,7 @@ function buildTemplateContext(options) {
580
582
  for (const f of options.features) {
581
583
  featureMap[f] = true;
582
584
  }
585
+ featureMap.deploy = options.deploy !== false;
583
586
  if (options.appType === "fullstack") {
584
587
  for (const feature of ["iam", "db", "cache", "crypto", "storage", "audit"]) featureMap[feature] = true;
585
588
  }
@@ -776,10 +779,12 @@ async function generateFromTemplates(projectPath, context) {
776
779
  }
777
780
  await renderDynamicFiles(root, projectPath, context);
778
781
  await fse.copy(path2.join(root, "shared", "run-e2e.mjs"), path2.join(projectPath, "scripts", "run-e2e.mjs"));
782
+ await fse.ensureDir(path2.join(projectPath, "images", "dependencies"));
783
+ await fse.writeFile(path2.join(projectPath, "images", "dependencies", ".gitkeep"), "");
779
784
  await fse.copy(path2.join(root, "shared", ".gitattributes"), path2.join(projectPath, ".gitattributes"));
780
785
  if (context.isSvelteOnlyApp) {
781
786
  await fse.copy(path2.join(root, "shared", "vite-runtime-config.ts"), path2.join(projectPath, "scripts", "vite-runtime-config.ts"));
782
- await fse.copy(path2.join(root, "shared", "container-runtime-config.sh"), path2.join(projectPath, "docker", "19-runtime-config.sh"));
787
+ await fse.copy(path2.join(root, "shared", "container-runtime-config.sh"), path2.join(projectPath, "images", "web", "19-runtime-config.sh"));
783
788
  }
784
789
  if (context.isSvelteOnlyApp || context.appType === "fullstack") {
785
790
  const navigationDir = context.appType === "fullstack" ? path2.join(projectPath, "packages", `${context.projectName}-shared`, "src", "lib", "navigation") : path2.join(projectPath, "src", "lib", "navigation");
@@ -1062,7 +1067,6 @@ var SELECTABLE_FEATURES = [
1062
1067
  "vecdb",
1063
1068
  "datapipe",
1064
1069
  "scheduler",
1065
- "deploy",
1066
1070
  "api-client",
1067
1071
  "capacitor"
1068
1072
  ];
@@ -1135,13 +1139,14 @@ async function createProject(options) {
1135
1139
  name: resolvedOptions.name,
1136
1140
  appType: resolvedOptions.appType,
1137
1141
  features: resolvedOptions.features,
1142
+ deploy: resolvedOptions.deploy,
1138
1143
  frontends: resolvedOptions.frontends,
1139
1144
  moduleConfigs: resolvedOptions.moduleConfigs,
1140
1145
  packageManager: resolvedOptions.packageManager
1141
1146
  });
1142
1147
  await generateFromTemplates(projectPath, context);
1143
1148
  {
1144
- const { generateConfigFile: generateConfigFile2, generateEnvExample } = await import('./cli-config-templates-3XSFIXBP.js');
1149
+ const { generateConfigFile: generateConfigFile2, generateEnvExample } = await import('./cli-config-templates-6AHNUGBN.js');
1145
1150
  const configs = resolvedOptions.moduleConfigs;
1146
1151
  const isApiWorkspace = resolvedOptions.appType === "api";
1147
1152
  const isFullstack = resolvedOptions.appType === "fullstack";
@@ -1215,7 +1220,7 @@ HAI_ADMIN_DEFAULT_PASSWORD=${password}
1215
1220
  await writeFile(path2.join(envDirectory, ".env.local"), "# \u672C\u673A\u79C1\u6709\u914D\u7F6E\uFF0C\u4E0D\u63D0\u4EA4 Git\uFF1B\u8986\u76D6\u516C\u5171\u57FA\u7840\u548C\u73AF\u5883\u914D\u7F6E\u3002\n");
1216
1221
  }
1217
1222
  }
1218
- const { generateSkillFiles } = await import('./cli-skill-templates-TSYEQTCY.js');
1223
+ const { generateSkillFiles } = await import('./cli-skill-templates-FC3QRKDO.js');
1219
1224
  await generateSkillFiles(
1220
1225
  projectPath,
1221
1226
  resolvedOptions.features,
@@ -1226,7 +1231,7 @@ HAI_ADMIN_DEFAULT_PASSWORD=${password}
1226
1231
  const readmeContent = resolvedOptions.appType === "fullstack" ? generateFullstackReadme(resolvedOptions.name, resolvedOptions.packageManager, resolvedOptions.frontends) : resolvedOptions.appType === "api" ? generateApiWorkspaceReadme(resolvedOptions.name, resolvedOptions.packageManager) : generateReadme(resolvedOptions.name, appTypeLabel, resolvedOptions.packageManager, resolvedOptions.appType);
1227
1232
  await writeFile(
1228
1233
  path2.join(projectPath, "README.md"),
1229
- readmeContent
1234
+ 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
1230
1235
  );
1231
1236
  spinner.succeed();
1232
1237
  if (resolvedOptions.git) {
@@ -1328,7 +1333,7 @@ async function resolveOptions(options) {
1328
1333
  let selectedFeatures = [];
1329
1334
  const appTypeDefaults = APP_TYPES[selectedAppType].defaultFeatures;
1330
1335
  if (selectedAppType === "fullstack") {
1331
- selectedFeatures = resolveFeatureDependencies([...appTypeDefaults, ...options.features?.includes("deploy") ? ["deploy"] : []]);
1336
+ selectedFeatures = resolveFeatureDependencies(appTypeDefaults);
1332
1337
  } else if (selectedTemplate === "custom" && !options.features && useDefaults) {
1333
1338
  selectedFeatures = resolveFeatureDependencies(appTypeDefaults);
1334
1339
  } else if (selectedTemplate === "custom" && !options.features) {
@@ -1355,6 +1360,9 @@ async function resolveOptions(options) {
1355
1360
  const merged = /* @__PURE__ */ new Set([...templateFeatures, ...appTypeDefaults]);
1356
1361
  selectedFeatures = resolveFeatureDependencies(Array.from(merged));
1357
1362
  }
1363
+ selectedFeatures = selectedFeatures.filter((feature) => feature !== "deploy");
1364
+ if (options.deploy !== false)
1365
+ selectedFeatures.push("deploy");
1358
1366
  const moduleConfigs = selectedAppType === "fullstack" ? { ...buildDefaultModuleConfigs(selectedFeatures, projectName), ...options.moduleConfigs } : options.moduleConfigs ?? (useDefaults ? buildDefaultModuleConfigs(selectedFeatures, projectName) : await promptModuleConfigs(selectedFeatures, projectName));
1359
1367
  let addExamples = options.examples;
1360
1368
  if (addExamples === void 0 && useDefaults) {
@@ -1436,6 +1444,7 @@ async function resolveOptions(options) {
1436
1444
  frontends: selectedFrontends,
1437
1445
  template: selectedTemplate ?? "default",
1438
1446
  features: selectedFeatures,
1447
+ deploy: options.deploy !== false,
1439
1448
  moduleConfigs,
1440
1449
  examples: addExamples ?? true,
1441
1450
  install: install ?? true,
@@ -1732,24 +1741,20 @@ function normalizeChildOutput(output) {
1732
1741
  return "";
1733
1742
  }
1734
1743
  function generateAiDevelopmentReadme() {
1735
- return `## AI \u8F85\u52A9\u5F00\u53D1
1744
+ return `### AI \u8F85\u52A9\u5F00\u53D1
1736
1745
 
1737
1746
  \u4ECE\u672C\u9879\u76EE\u6839\u76EE\u5F55\u6253\u5F00\u6240\u7528\u7F16\u7801\u5DE5\u5177\uFF0C\u5148\u8BFB\u53D6 [AGENTS.md](./AGENTS.md)\u3002Claude Code \u901A\u8FC7 [CLAUDE.md](./CLAUDE.md) \u52A0\u8F7D\u9879\u76EE\u89C4\u5219\uFF1BGitHub Copilot \u4F7F\u7528 [.github/copilot-instructions.md](./.github/copilot-instructions.md)\uFF0COpenCode \u4F7F\u7528 AGENTS.md \u548C [opencode.json](./opencode.json)\u3002
1738
1747
 
1739
- \u4E1A\u52A1\u4EE5\u7528\u6237\u5F53\u524D\u660E\u786E\u8981\u6C42\u53CA\u5176\u6307\u5B9A\u7684\u9700\u6C42/\u8BBE\u8BA1\u6587\u6863\u4E3A\u51C6\u3002\u6A21\u677F AGENTS\u3001README\u3001skills\u3001\u751F\u6210\u4EE3\u7801\u4E0E\u6D4B\u8BD5\u4E2D\u7684\u89D2\u8272/\u6743\u9650\u3001\u6D41\u7A0B\u3001\u9650\u989D\u548C\u67B6\u6784\u662F\u53EF\u8C03\u6574\u7684\u521D\u59CB\u65B9\u6848\uFF1B\u5BA1\u67E5\u5C06\u5DEE\u5F02\u5217\u4E3A\u6A21\u677F\u9002\u914D\u9879\uFF0C\u5B9E\u65BD\u6309\u6388\u6743\u540C\u6B65\u51B2\u7A81\u4EE3\u7801\u3001\u6D4B\u8BD5\u4E0E\u8FC7\u671F\u89C4\u5219\uFF0C\u4E0D\u8981\u6C42\u628A\u9700\u6C42\u6539\u56DE\u6A21\u677F\u3002\u6846\u67B6 API \u4EE5\u5F53\u524D\u5B89\u88C5\u7248\u672C\u516C\u5F00\u5951\u7EA6\u4E3A\u51C6\uFF0C\u670D\u52A1\u7AEF\u6388\u6743\u3001\u8D44\u6E90\u9694\u79BB\u3001\u8F93\u5165\u6821\u9A8C\u548C\u51ED\u636E\u4FDD\u62A4\u4ECD\u9700\u843D\u5B9E\u3002
1740
-
1741
- Codex\u3001Cursor\u3001Copilot\u3001OpenCode \u4ECE .agents/skills/ \u53D1\u73B0\u5171\u4EAB\u6280\u80FD\uFF1BClaude Code \u901A\u8FC7 CLAUDE.md \u5F15\u7528 AGENTS.md\uFF0C\u5E76\u6309\u9879\u76EE\u6307\u5F15\u8BFB\u53D6 .agents/skills/ \u7684\u540C\u4E00\u4EFD\u6B63\u6587\u3002\u9700\u6C42\u6216\u8BBE\u8BA1\u6587\u6863\u5728\u751F\u6210\u524D\u4F7F\u7528 [\u6587\u6863\u5BA1\u67E5 Skill](./.agents/skills/hai-spec-review/SKILL.md) \u4E25\u683C\u68C0\u67E5\u5B8C\u5907\u6027\u3001\u6B67\u4E49\u3001\u6280\u672F\u6B63\u786E\u6027\u548C\u547D\u540D\u4E00\u81F4\u6027\uFF0C\u95EE\u9898\u89E3\u51B3\u540E\u518D\u751F\u6210\u3002\u666E\u901A\u65B0\u589E\u3001\u4FEE\u6539\u3001\u4FEE\u590D\u4EFB\u52A1\u4F7F\u7528 [\u5E94\u7528\u5F00\u53D1 Skill](./.agents/skills/hai-app-create/SKILL.md) \u548C [\u5F00\u53D1\u89C4\u8303\u4E0E\u6846\u67B6\u80FD\u529B\u8DEF\u7531](./.agents/skills/hai-app-create/development.md)\uFF1B\u5177\u4F53\u6A21\u5757\u518D\u6309\u9700\u8BFB\u53D6\uFF0C\u4E0D\u80FD\u628A Skill \u5DF2\u4EA4\u4ED8\u5F53\u6210\u4F9D\u8D56\u5DF2\u5B89\u88C5\u6216\u914D\u7F6E\u5DF2\u542F\u7528\u3002
1748
+ \u751F\u6210\u7684\u9875\u9762\u548C\u4E1A\u52A1\u89C4\u5219\u662F\u8D77\u70B9\uFF0C\u53EF\u4EE5\u6309\u9700\u6C42\u8C03\u6574\u3002\u628A\u529F\u80FD\u3001\u6743\u9650\u548C\u9A8C\u6536\u6761\u4EF6\u544A\u8BC9 AI\uFF0C\u518D\u8BA9\u5B83\u8BFB\u53D6\u5BF9\u5E94\u6280\u80FD\u4E0E\u5F53\u524D\u5B89\u88C5\u7248\u672C\u7684 API\u3002
1742
1749
 
1743
- \u6839\u636E\u6587\u6863\u89C4\u5212\u3001\u5B9E\u65BD\u3001\u8FD4\u4FEE\u6216\u6309\u5DF2\u63D0\u4EA4\u6587\u6863\u5DEE\u5F02\u540C\u6B65\u5B9E\u73B0\u65F6\uFF0C\u4F7F\u7528 [\u6587\u6863\u5B9E\u65BD Skill](./.agents/skills/hai-spec-implement/SKILL.md)\uFF1A\u7EF4\u62A4 docs/task-list.md \u4E0E docs/tasks/\uFF0C\u8BB0\u5F55\u5DF2\u9A8C\u6536\u6587\u6863\u548C\u5B9E\u73B0\u7684 Git SHA\uFF0C\u6BD4\u8F83\u56FA\u5B9A\u76EE\u6807\u7248\u672C\u540E\u66F4\u65B0\u539F\u4EFB\u52A1\u6216\u65B0\u5EFA\u4EFB\u52A1\uFF0C\u6BCF\u9879\u72EC\u7ACB\u5206\u652F TDD\u3001\u9A8C\u8BC1\u5E76\u672C\u5730\u5408\u5E76\uFF1B\u5168\u90E8\u5DEE\u5F02\u4E0E\u6574\u4F53\u9A8C\u6536\u901A\u8FC7\u540E\u624D\u63A8\u8FDB\u57FA\u7EBF\u3002\u8FD4\u4FEE\u6CBF\u7528\u539F\u4EFB\u52A1\u4E0E\u5206\u652F\u3002\u4EC5\u89C4\u5212\u4E0D\u5F00\u59CB\u5B9E\u73B0\uFF0C\u6307\u5B9A\u4EFB\u52A1\u5148\u6838\u9A8C\u4F9D\u8D56\u3002
1750
+ \u666E\u901A\u529F\u80FD\u5F00\u53D1\u4F7F\u7528 [\u5E94\u7528\u5F00\u53D1 Skill](./.agents/skills/hai-app-create/SKILL.md)\u3002\u9700\u6C42\u6587\u6863\u8F83\u591A\u65F6\uFF0C\u5148\u7528 [\u6587\u6863\u5BA1\u67E5 Skill](./.agents/skills/hai-spec-review/SKILL.md) \u627E\u51FA\u7F3A\u5931\u548C\u77DB\u76FE\uFF0C\u518D\u7528 [\u6587\u6863\u5B9E\u65BD Skill](./.agents/skills/hai-spec-implement/SKILL.md) \u7EF4\u62A4 docs/task-list.md \u4E0E docs/tasks/\uFF0C\u9010\u9879\u5B9E\u73B0\u3001\u6D4B\u8BD5\u548C\u4EA4\u4ED8\u3002
1744
1751
 
1745
- \u4F18\u5148\u590D\u7528\u9002\u7528\u7684 hai-framework \u516C\u5F00 API \u4E0E\u5DF2\u6709\u4E1A\u52A1\u5B9E\u73B0\u3002\u5B8C\u6210\u65F6\u8BF4\u660E\u590D\u7528\u7684\u80FD\u529B\u3001\u76F8\u5173\u80FD\u529B\u672A\u91C7\u7528\u7684\u539F\u56E0\uFF0C\u4EE5\u53CA\u5B89\u5168\u8FB9\u754C\u548C\u7C7B\u578B\u3001\u914D\u7F6E\u3001\u6587\u6848\u3001\u8C03\u7528\u65B9\u7684\u540C\u6B65\u60C5\u51B5\u3002\u8D28\u91CF\u9A8C\u8BC1\u6309\u5F53\u524D package.json \u6267\u884C typecheck \u2192 lint \u2192 build \u2192 test\uFF1BUI\u3001\u8DEF\u7531\u4E0E\u7AEF\u5230\u7AEF\u6D41\u7A0B\u53E6\u8FD0\u884C test:e2e\uFF0C\u672A\u8FD0\u884C\u9879\u8BF4\u660E\u539F\u56E0\u3002\u5B89\u5168\u4E0E\u8D28\u91CF\u89C4\u5219\u65E2\u8981\u6309\u89C4\u8303\u5BA1\u67E5\uFF0C\u4E5F\u8981\u8FD0\u884C\u95E8\u7981\u9A8C\u8BC1\u3002
1746
-
1747
- \u5DF2\u6709 hai \u9879\u76EE\u53EF\u8FD0\u884C hai init \u8865\u9F50\u7F3A\u5931\u7684 AI \u652F\u6301\u6587\u4EF6\u4E0E\u53C2\u8003\u8D44\u6E90\uFF1B\u5DF2\u6709 AGENTS.md\u3001Skill \u548C\u81EA\u5B9A\u4E49\u89C4\u5219\u4F1A\u4FDD\u7559\uFF0C\u5347\u7EA7\u65F6\u9700\u5C06\u4E0A\u8FF0\u9700\u6C42\u4F18\u5148\u89C4\u5219\u3001\u666E\u901A\u5F00\u53D1\u5165\u53E3\u4E0E\u89C4\u8303\u663E\u5F0F\u5408\u5E76\u5230\u65E7\u6307\u5F15\uFF0C\u5E76\u4FEE\u6B63\u5DF2\u88AB\u9700\u6C42\u66FF\u4EE3\u7684\u6A21\u677F\u7EA6\u675F\u3002
1752
+ \u5DF2\u6709\u9879\u76EE\u53EF\u8FD0\u884C hai init \u8865\u9F50\u7F3A\u5931\u7684 AI \u5165\u53E3\uFF1B\u5B83\u4FDD\u7559\u5DF2\u6709\u81EA\u5B9A\u4E49\u5185\u5BB9\u3002\u5347\u7EA7\u6846\u67B6\u4F7F\u7528 [\u6846\u67B6\u5347\u7EA7 Skill](./.agents/skills/hai-framework-sync/SKILL.md)\uFF0C\u540C\u65F6\u6838\u5BF9\u4F9D\u8D56\u3001\u914D\u7F6E\u3001\u4EE3\u7801\u548C\u6280\u80FD\u3002
1748
1753
 
1749
1754
  `;
1750
1755
  }
1751
1756
  function generateEnvironmentReadme() {
1752
- return `## \u5206\u73AF\u5883\u914D\u7F6E
1757
+ return `### \u5206\u73AF\u5883\u914D\u7F6E
1753
1758
 
1754
1759
  \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\u73AF\u5883 > \`.env.local\` > \u73AF\u5883\u6587\u4EF6 > \`.env\`\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
1755
1760
 
@@ -1764,17 +1769,57 @@ function generateEnvironmentReadme() {
1764
1769
 
1765
1770
  Vite \u5BA2\u6237\u7AEF\u4F7F\u7528 \`preview\` \u9884\u89C8\u9759\u6001\u4EA7\u7269\uFF0CNode \u670D\u52A1\u4F7F\u7528 \`start\`\uFF1B\u7EAF contract/shared \u5E93\u53EA\u63D0\u4F9B\u9002\u7528\u7684\u6784\u5EFA\u548C\u8D28\u91CF\u547D\u4EE4\u3002API workspace \u7684\u73AF\u5883\u6587\u4EF6\u4F4D\u4E8E \`apps/<name>-service\`\uFF0Cfullstack \u4F4D\u4E8E\u5DE5\u7A0B\u6839\u3002`;
1766
1771
  }
1772
+ function generateBuildDeploymentReadme(pm, appDir) {
1773
+ return `### \u6784\u5EFA\u4E0E\u90E8\u7F72
1774
+
1775
+ \u521B\u5EFA\u65F6\u9ED8\u8BA4\u5305\u542B\u90E8\u7F72\u5DE5\u5177\uFF1B\u4E0D\u9700\u8981\u65F6\u4F7F\u7528 hai create --no-deploy\u3002
1776
+ \u6BCF\u4E2A\u955C\u50CF\u5BF9\u5E94 images/ \u4E2D\u4E00\u4E2A Dockerfile\uFF0Chai.build.json \u7528 dockerfile \u6307\u5B9A\u6587\u4EF6\u3002
1777
+ \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
1778
+ \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
1779
+
1780
+ \`\`\`sh
1781
+ ${pm} build
1782
+ ${pm} build --target docker
1783
+ # \u6784\u5EFA\u548C\u90E8\u7F72\u4E00\u6B21\u5B8C\u6210
1784
+ ${pm} exec hai deploy ${appDir} --env test --build
1785
+ # \u590D\u7528\u5DF2\u6709\u955C\u50CF\uFF1B\u53EA\u751F\u6210\u5B8C\u6574\u79BB\u7EBF\u5305\u53CA\u8DE8\u5E73\u53F0\u90E8\u7F72\u811A\u672C
1786
+ ${pm} exec hai deploy ${appDir} --env prod --bundle-only
1787
+ # \u53EA\u66F4\u65B0\u524D\u7AEF\uFF08\u5DE5\u7A0B\u5B58\u5728 web \u955C\u50CF\u65F6\uFF09
1788
+ ${pm} build --target docker --images web
1789
+ ${pm} exec hai deploy ${appDir} --env prod --services web
1790
+ \`\`\`
1791
+
1792
+ \u6784\u5EFA\u4E0D\u8BFB\u53D6\u90E8\u7F72\u73AF\u5883\uFF1B\u4EA7\u7269\u4F4D\u4E8E .hai/build/docker/<buildId>\uFF0C\u7528 --artifact-dir \u7CBE\u786E\u9009\u62E9\u3002
1793
+ \u5E94\u7528\u6839 package.json.version \u9A71\u52A8\u7248\u672C\uFF0C\u540C\u7248\u672C\u5141\u8BB8\u91CD\u65B0\u6784\u5EFA\u548C\u53D1\u5E03\uFF1B\u6E05\u5355\u8BB0\u5F55\u72EC\u7ACB\u6784\u5EFA\u7F16\u53F7\u4E0E\u6821\u9A8C\u548C\u3002
1794
+ \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
1795
+ \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\uFF0C\u542F\u52A8\u9636\u6BB5\u4E0D\u8054\u7F51\u62C9\u53D6\u3002
1796
+ tar \u65C1\u7684\u90E8\u7F72\u76EE\u5F55\u63D0\u4F9B deploy.ps1\uFF08Windows\uFF09\u548C deploy.sh\uFF08Linux/macOS\uFF09\uFF0C\u6309\u5DF2\u4FDD\u5B58\u7684 SSH \u53C2\u6570\u4E0A\u4F20\u3001\u5BFC\u5165\u3001\u542F\u52A8\u5E76\u9A8C\u8BC1 HTTP\u3002
1797
+ \u5B8C\u5168\u9694\u79BB\u65F6\u624B\u5DE5\u642C\u8FD0 tar\uFF0C\u72EC\u7ACB\u76EE\u5F55\u89E3\u5305\u540E\u6267\u884C sh apply.sh\uFF0C\u6216\u4F20\u5165\u90E8\u7F72\u76EE\u5F55\u8986\u76D6\u9ED8\u8BA4\u4F4D\u7F6E\u3002
1798
+ \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
1799
+ \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
1800
+ `;
1801
+ }
1767
1802
  function generateReadme(name, appTypeLabel, pm, appType) {
1768
1803
  const install = getInstallCommand(pm);
1769
1804
  return `# ${name}
1770
1805
 
1806
+ ## \u7B80\u4ECB
1807
+
1771
1808
  \u57FA\u4E8E hai Agent Framework \u6784\u5EFA\u7684${appTypeLabel}\u5E94\u7528\u3002
1772
1809
 
1773
- ## \u9875\u9762\u5E03\u5C40\u4E0E\u8DEF\u7531
1810
+ ## \u6838\u5FC3\u529F\u80FD\u6E05\u5355
1811
+
1812
+ - \u5F00\u53D1\u670D\u52A1\u5668\u3001\u6E90\u7801\u7F16\u8BD1\u4E0E\u6D4F\u89C8\u5668\u6D4B\u8BD5\u3002
1813
+ - \u6309\u6240\u9009\u6A21\u5757\u63D0\u4F9B\u9875\u9762\u548C\u670D\u52A1\u914D\u7F6E\u3002
1814
+ - \u72EC\u7ACB Dockerfile \u4E0E\u955C\u50CF\u5F52\u6863\uFF0C\u4FBF\u4E8E\u4EA4\u4ED8\u3002
1815
+
1816
+ ## \u4F7F\u7528\u5165\u95E8
1817
+
1818
+ ### \u9875\u9762\u5E03\u5C40\u4E0E\u8DEF\u7531
1774
1819
 
1775
1820
  \u9875\u9762\u7EDF\u4E00\u7EE7\u627F\u6839\u5E03\u5C40\u7684 header/footer\u3002${appType === "mobile-app" ? "\u9875\u9762\u653E src/lib/pages/**/*.svelte\uFF0Chash \u8DEF\u7531\u81EA\u52A8\u53D1\u73B0\uFF0C\u5237\u65B0\u4E0E\u524D\u8FDB\u540E\u9000\u6062\u590D\u5F53\u524D\u9875\u3002" : "\u4F7F\u7528 SvelteKit \u81EA\u52A8\u6587\u4EF6\u8DEF\u7531\uFF0C\u65B0\u589E +page.svelte \u65E0\u9700\u7EF4\u62A4\u8DEF\u7531\u8868\uFF1B\u5237\u65B0\u5B9A\u4F4D\u4E0E\u670D\u52A1\u7AEF\u9274\u6743\u4FDD\u6301\u4E00\u81F4\u3002"}
1776
1821
 
1777
- ## \u5F00\u53D1
1822
+ ### \u5F00\u53D1
1778
1823
 
1779
1824
  \`\`\`bash
1780
1825
  ${install}
@@ -1783,46 +1828,57 @@ ${getRunCommand(pm, "dev")}
1783
1828
 
1784
1829
  ${generateEnvironmentReadme()}
1785
1830
 
1786
- ## \u6784\u5EFA
1831
+ ### \u6784\u5EFA
1787
1832
 
1788
1833
  \`\`\`bash
1789
1834
  ${getRunCommand(pm, "build")}
1790
- ${getRunCommand(pm, "i18n:compile")}
1835
+ ${getRunCommand(pm, "paraglide:compile")}
1791
1836
  ${getRunCommand(pm, "preview")}
1792
1837
  \`\`\`
1793
1838
 
1794
- ## \u6784\u5EFA\u4E0E\u90E8\u7F72
1795
-
1796
- \`\`\`bash
1797
- # \u9ED8\u8BA4\u53EA\u7F16\u8BD1\u6E90\u7801\uFF1B\u4E0D\u9009\u62E9\u90E8\u7F72\u73AF\u5883
1798
- ${pm} build
1799
- # \u6784\u5EFA\u5E76\u5BFC\u51FA API/Web \u955C\u50CF\uFF0Cauto \u4F18\u5148 Podman\u3001\u5176\u6B21 Docker
1800
- ${pm} build --target docker
1801
- # \u9009\u62E9\u73AF\u5883\u3001\u4E2D\u95F4\u4EF6\u5E76\u4E0A\u4F20\u4E00\u4E2A Compose tar\uFF1B\u4E0D\u4F1A\u91CD\u65B0\u6784\u5EFA
1802
- ${pm} exec hai deploy . --env test
1803
- # \u65E0\u6CD5\u8FDE\u63A5\u670D\u52A1\u5668\u65F6\u53EA\u751F\u6210\u5305\uFF0C\u6309\u8F93\u51FA\u8BF4\u660E\u624B\u5DE5\u642C\u8FD0\u548C\u542F\u52A8
1804
- ${pm} exec hai deploy . --env prod --bundle-only
1805
- \`\`\`
1806
-
1807
- \u955C\u50CF\u6784\u5EFA\u914D\u7F6E\u89C1 \`hai.build.json\`\u3002\u6784\u5EFA\u4EA7\u7269\u4FDD\u5B58\u5728 \`.hai/build/docker/<buildId>\`\uFF0C\u53EF\u7528 \`--artifact-dir\` \u9009\u62E9\u540C\u4E00\u4EFD\u4EA7\u7269\u90E8\u7F72\u5230 test/prod\u3002\u90E8\u7F72\u5305\u542B\u79C1\u6709\u914D\u7F6E\uFF0C\u4E0D\u80FD\u63D0\u4EA4 Git\u3002\u73AF\u5883\u5DF2\u914D\u7F6E\u5B8C\u5907\u53EF\u52A0 \`--skip-middleware\`\u3002\u670D\u52A1\u8FD0\u884C\u6570\u636E\u7531 volume \u6301\u4E45\u5316\uFF0C\u955C\u50CF\u4E0D\u6253\u5305\u672C\u673A data\u3002API/Web \u8054\u5408\u90E8\u7F72\u516C\u5F00 Web \u5165\u53E3\u5E76\u4EE3\u7406\u540C\u6E90 API\uFF1B\u6CA1\u6709 Web \u4EA7\u7269\u65F6\u53EA\u90E8\u7F72\u670D\u52A1\u3002
1839
+ ${generateBuildDeploymentReadme(pm, `.`)}
1808
1840
 
1809
- ${generateAiDevelopmentReadme()}## \u6587\u6863
1841
+ ${generateAiDevelopmentReadme()}### \u6587\u6863
1810
1842
 
1811
1843
  - [hai Agent Framework](https://github.com/hai-series/hai-framework)
1812
1844
  - [Svelte 5](https://svelte.dev/)
1845
+
1846
+ ## \u5F00\u53D1\u6D4B\u8BD5
1847
+
1848
+ \`\`\`sh
1849
+ ${getRunCommand(pm, "typecheck")}
1850
+ ${getRunCommand(pm, "lint")}
1851
+ ${getRunCommand(pm, "build")}
1852
+ ${getRunCommand(pm, "test")}
1853
+ ${getRunCommand(pm, "test:e2e")}
1854
+ \`\`\`
1855
+
1856
+ ## \u8BB8\u53EF\u8BC1
1857
+
1858
+ Apache-2.0
1813
1859
  `;
1814
1860
  }
1815
1861
  function generateApiWorkspaceReadme(name, pm) {
1816
1862
  return `# ${name}
1817
1863
 
1818
- \u57FA\u4E8E hai Agent Framework \u6784\u5EFA\u7684 typed API workspace\u3002
1864
+ ## \u7B80\u4ECB
1865
+
1866
+ \u540E\u7AEF API \u5DE5\u7A0B\uFF0C\u63A5\u53E3\u5B9A\u4E49\u4E0E\u670D\u52A1\u5B9E\u73B0\u5206\u5305\u7EF4\u62A4\uFF0C\u5BA2\u6237\u7AEF\u53EF\u590D\u7528\u540C\u4E00\u4EFD\u63A5\u53E3\u7C7B\u578B\u3002
1819
1867
 
1820
- ## \u5DE5\u7A0B\u7ED3\u6784
1868
+ ## \u6838\u5FC3\u529F\u80FD\u6E05\u5355
1869
+
1870
+ - \u5171\u4EAB contract\u3001\u8F93\u5165\u8F93\u51FA\u6821\u9A8C\u4E0E\u670D\u52A1 procedures\u3002
1871
+ - \u6A21\u5757\u914D\u7F6E\u3001\u5065\u5EB7\u68C0\u67E5\u548C HTTP \u670D\u52A1\u3002
1872
+ - \u72EC\u7ACB\u955C\u50CF\u6784\u5EFA\u4E0E\u79BB\u7EBF\u4EA4\u4ED8\u3002
1873
+
1874
+ ## \u4F7F\u7528\u5165\u95E8
1875
+
1876
+ ### \u5DE5\u7A0B\u7ED3\u6784
1821
1877
 
1822
1878
  - \`apps/${name}-contract\`\uFF1A\u5171\u4EAB API contract\u3001Zod schema \u4E0E\u7C7B\u578B
1823
1879
  - \`apps/${name}-service\`\uFF1A\u540E\u7AEF API service\uFF0C\u57FA\u4E8E \`@h-ai/serv\` \u66B4\u9732 HTTP \u5E94\u7528
1824
1880
 
1825
- ## \u5F00\u53D1
1881
+ ### \u5F00\u53D1
1826
1882
 
1827
1883
  \`\`\`bash
1828
1884
  ${pm} install
@@ -1831,52 +1887,55 @@ ${pm} dev
1831
1887
 
1832
1888
  ${generateEnvironmentReadme()}
1833
1889
 
1834
- ## \u8D28\u91CF\u95E8\u7981
1835
-
1836
- \`\`\`bash
1837
- ${pm} typecheck
1838
- ${pm} lint
1839
- ${pm} build
1840
- ${pm} test
1841
- ${pm} test:e2e
1842
- \`\`\`
1843
-
1844
- ## \u6784\u5EFA\u4E0E\u90E8\u7F72
1845
-
1846
- \`\`\`bash
1847
- # \u9ED8\u8BA4\u53EA\u7F16\u8BD1\u6E90\u7801\uFF1B\u4E0D\u9009\u62E9\u90E8\u7F72\u73AF\u5883
1848
- ${pm} build
1849
- # \u6784\u5EFA\u5E76\u5BFC\u51FA API/Web \u955C\u50CF\uFF0Cauto \u4F18\u5148 Podman\u3001\u5176\u6B21 Docker
1850
- ${pm} build --target docker
1851
- # \u9009\u62E9\u73AF\u5883\u3001\u4E2D\u95F4\u4EF6\u5E76\u4E0A\u4F20\u4E00\u4E2A Compose tar\uFF1B\u4E0D\u4F1A\u91CD\u65B0\u6784\u5EFA
1852
- ${pm} exec hai deploy apps/${name}-service --env test
1853
- # \u65E0\u6CD5\u8FDE\u63A5\u670D\u52A1\u5668\u65F6\u53EA\u751F\u6210\u5305\uFF0C\u6309\u8F93\u51FA\u8BF4\u660E\u624B\u5DE5\u642C\u8FD0\u548C\u542F\u52A8
1854
- ${pm} exec hai deploy apps/${name}-service --env prod --bundle-only
1855
- \`\`\`
1856
1890
 
1857
- \u955C\u50CF\u6784\u5EFA\u914D\u7F6E\u89C1 \`hai.build.json\`\u3002\u6784\u5EFA\u4EA7\u7269\u4FDD\u5B58\u5728 \`.hai/build/docker/<buildId>\`\uFF0C\u53EF\u7528 \`--artifact-dir\` \u9009\u62E9\u540C\u4E00\u4EFD\u4EA7\u7269\u90E8\u7F72\u5230 test/prod\u3002\u90E8\u7F72\u5305\u542B\u79C1\u6709\u914D\u7F6E\uFF0C\u4E0D\u80FD\u63D0\u4EA4 Git\u3002\u73AF\u5883\u5DF2\u914D\u7F6E\u5B8C\u5907\u53EF\u52A0 \`--skip-middleware\`\u3002\u670D\u52A1\u8FD0\u884C\u6570\u636E\u7531 volume \u6301\u4E45\u5316\uFF0C\u955C\u50CF\u4E0D\u6253\u5305\u672C\u673A data\u3002API/Web \u8054\u5408\u90E8\u7F72\u516C\u5F00 Web \u5165\u53E3\u5E76\u4EE3\u7406\u540C\u6E90 API\uFF1B\u6CA1\u6709 Web \u4EA7\u7269\u65F6\u53EA\u90E8\u7F72\u670D\u52A1\u3002
1891
+ ${generateBuildDeploymentReadme(pm, `apps/${name}-service`)}
1858
1892
 
1859
- ## \u6269\u5C55\u5EFA\u8BAE
1893
+ ### \u6269\u5C55\u5EFA\u8BAE
1860
1894
 
1861
1895
  - \u5728 \`${name}-contract\` \u4E2D\u7EF4\u62A4\u5E94\u7528\u7EA7 contract\uFF0C\u524D\u540E\u7AEF\u5171\u7528\u540C\u4E00\u4EFD\u7C7B\u578B\u4E0E\u8DEF\u5F84\u5B9A\u4E49\u3002
1862
1896
  - \u5728 \`${name}-service\` \u4E2D\u6269\u5C55 procedures\u3001\u6A21\u5757\u521D\u59CB\u5316\u4E0E\u914D\u7F6E\u3002
1863
1897
  - \u9700\u8981 IAM / storage / ai \u65F6\uFF0C\u4F18\u5148\u590D\u7528 \`@h-ai/api-contract\` \u4E0E \`@h-ai/serv/features/*\`\u3002
1864
1898
 
1865
- ${generateAiDevelopmentReadme()}## \u6587\u6863
1899
+ ${generateAiDevelopmentReadme()}### \u6587\u6863
1866
1900
 
1867
1901
  - [hai Agent Framework](https://github.com/hai-series/hai-framework)
1868
1902
  - [@h-ai/serv](./.agents/skills/hai-serv/SKILL.md)
1869
1903
  - [@h-ai/api-contract](./.agents/skills/hai-api-contract/SKILL.md)
1870
1904
  - [@h-ai/api-client](./.agents/skills/hai-api-client/SKILL.md)
1905
+
1906
+ ## \u5F00\u53D1\u6D4B\u8BD5
1907
+
1908
+ \`\`\`bash
1909
+ ${pm} typecheck
1910
+ ${pm} lint
1911
+ ${pm} build
1912
+ ${pm} test
1913
+ ${pm} test:e2e
1914
+ \`\`\`
1915
+
1916
+ ## \u8BB8\u53EF\u8BC1
1917
+
1918
+ Apache-2.0
1871
1919
  `;
1872
1920
  }
1873
1921
  function generateFullstackReadme(name, pm, frontends) {
1874
1922
  const enabledFrontends = frontends.map((target) => FRONTEND_TARGETS[target].name).join(" / ");
1875
1923
  return `# ${name}
1876
1924
 
1877
- \u57FA\u4E8E hai Agent Framework \u6784\u5EFA\u7684\u524D\u540E\u7AEF\u5206\u79BB\u591A\u5305\u5DE5\u7A0B\u3002
1925
+ ## \u7B80\u4ECB
1926
+
1927
+ \u524D\u540E\u7AEF\u5206\u79BB\u5DE5\u7A0B\uFF0CAPI \u4E0E Web\u3001\u79FB\u52A8\u7AEF\u3001\u684C\u9762\u7AEF\u5171\u4EAB\u63A5\u53E3\u7C7B\u578B\u548C\u7BA1\u7406\u754C\u9762\uFF0C\u53EF\u6309\u5B9E\u9645\u4E1A\u52A1\u8C03\u6574\u3002
1928
+
1929
+ ## \u6838\u5FC3\u529F\u80FD\u6E05\u5355
1878
1930
 
1879
- ## \u5DE5\u7A0B\u7ED3\u6784
1931
+ - API \u670D\u52A1\u3001\u5171\u4EAB\u754C\u9762\u4E0E\u6309\u9700\u751F\u6210\u7684\u5BA2\u6237\u7AEF\u3002
1932
+ - \u7528\u6237\u7BA1\u7406\u3001\u89D2\u8272\u3001\u5BA1\u8BA1\u548C\u4E2A\u4EBA\u8BBE\u7F6E\u793A\u4F8B\u3002
1933
+ - \u767B\u5F55\u3001\u4E3B\u9898\u3001\u8BED\u8A00\u3001\u65B0\u624B\u5F15\u5BFC\u548C\u5E2E\u52A9\u4E2D\u5FC3\u3002
1934
+ - API/Web \u72EC\u7ACB Dockerfile \u4E0E\u955C\u50CF\u5F52\u6863\u3002
1935
+
1936
+ ## \u4F7F\u7528\u5165\u95E8
1937
+
1938
+ ### \u5DE5\u7A0B\u7ED3\u6784
1880
1939
 
1881
1940
  - \`packages/${name}-contract\`\uFF1A\u524D\u540E\u7AEF\u5171\u4EAB API \u5951\u7EA6\u3001Zod Schema \u4E0E\u7C7B\u578B
1882
1941
  - \`packages/${name}-serv\`\uFF1A\u540E\u7AEF API Service\uFF08\u901A\u8FC7 \`@h-ai/serv\` \u66B4\u9732\u6700\u5C0F HTTP App \u62BD\u8C61\uFF0C\u4E0D\u76F4\u63A5\u66B4\u9732 Hono\uFF09
@@ -1888,11 +1947,11 @@ function generateFullstackReadme(name, pm, frontends) {
1888
1947
 
1889
1948
  \u5DF2\u542F\u7528\u524D\u7AEF\uFF1A${enabledFrontends || "Web / App / Desktop"}
1890
1949
 
1891
- ## \u9875\u9762\u5E03\u5C40\u4E0E\u8DEF\u7531
1950
+ ### \u9875\u9762\u5E03\u5C40\u4E0E\u8DEF\u7531
1892
1951
 
1893
1952
  Web/App/Desktop \u5171\u4EAB AppShell \u7684 header/footer\u3002\u65B0\u589E\u9875\u9762\u653E shared/src/lib/pages/**/*.svelte\uFF0C\u7531 HashRouter \u81EA\u52A8\u53D1\u73B0\u5E76\u6309\u9700\u52A0\u8F7D\uFF1B\u83DC\u5355\u53EA\u7EF4\u62A4\u5165\u53E3\u3002\u5730\u5740\u4F7F\u7528 #/\u9875\u9762\u8DEF\u5F84\uFF0C\u652F\u6301\u5237\u65B0\u3001\u67E5\u8BE2\u4E32\u4E0E\u6D4F\u89C8\u5668\u524D\u8FDB\u540E\u9000\uFF1BAPI \u6743\u9650\u4ECD\u7531\u670D\u52A1\u7AEF\u6821\u9A8C\u3002
1894
1953
 
1895
- ## \u9ED8\u8BA4\u7BA1\u7406\u529F\u80FD
1954
+ ### \u9ED8\u8BA4\u7BA1\u7406\u529F\u80FD
1896
1955
 
1897
1956
  \u4EE5\u4E0B\u63CF\u8FF0\u53EF\u8FD0\u884C\u793A\u4F8B\u7684\u521D\u59CB\u4E1A\u52A1\uFF0C\u53EF\u6309\u5B9E\u9645\u9700\u6C42\u8C03\u6574\u6216\u79FB\u9664\uFF0C\u5E76\u540C\u6B65\u4EE3\u7801\u3001\u6570\u636E\u3001\u9274\u6743\u3001\u754C\u9762\u3001\u6D4B\u8BD5\u548C\u6587\u6863\u3002
1898
1957
 
@@ -1906,7 +1965,7 @@ CLI \u5DF2\u5728\u6839\u76EE\u5F55\u751F\u6210 \`.env.local\`\uFF0C\u5176\u4E2D
1906
1965
 
1907
1966
  \u5E94\u7528\u663E\u793A\u540D\u79F0\u3001\u7248\u672C\u3001\u73AF\u5883\u3001\u9ED8\u8BA4\u8BED\u8A00\u548C\u65E5\u5FD7\u8BBE\u7F6E\u6765\u81EA\u540E\u7AEF \`config/_core.yml\`\uFF0C\u524D\u7AEF\u901A\u8FC7 \`app.info\` \u83B7\u53D6\u5B89\u5168\u5143\u4FE1\u606F\u3002\u9ED8\u8BA4 SQLite\u3001\u672C\u5730\u5B58\u50A8\u4E0E\u5185\u5B58\u7F13\u5B58\u9002\u5408\u5355\u5B9E\u4F8B\uFF0C\u7BA1\u7406\u5199\u5165\u4EC5\u652F\u6301\u5355\u5199\u5B9E\u4F8B\uFF1B\u591A\u5B9E\u4F8B\u8BFB\u53D6\u53EF\u4F7F\u7528\u5171\u4EAB\u6570\u636E\u5E93\u3001Redis cache \u548C S3 \u5B58\u50A8\uFF0C\u4F46\u7BA1\u7406\u5199\u8BF7\u6C42\u987B\u7EDF\u4E00\u8DEF\u7531\u5230\u540C\u4E00\u5199\u5B9E\u4F8B\u3002\u53E6\u9700\u4F1A\u8BDD\u7C98\u6027\u8DEF\u7531\u4F7F\u8BF7\u6C42\u547D\u4E2D\u540C\u4E00\u8FDB\u7A0B\u5185\u4F20\u8F93\u79C1\u94A5\uFF0C\u670D\u52A1\u91CD\u542F\u540E\u5BA2\u6237\u7AEF\u91CD\u65B0\u534F\u5546\u3002\u751F\u4EA7\u9700\u663E\u5F0F\u914D\u7F6E\u5141\u8BB8\u7684 CORS Origin\u3002\u4E1A\u52A1\u63D0\u4EA4\u540E\u7684\u5BA1\u8BA1\u5199\u5165\u91C7\u7528\u6700\u5927\u52AA\u529B\uFF0C\u5931\u8D25\u8BB0\u5F55\u7ED3\u6784\u5316\u9519\u8BEF\u65E5\u5FD7\uFF0C\u4FDD\u7559\u5DF2\u5B8C\u6210\u4E1A\u52A1\u7684\u539F\u59CB\u7ED3\u679C\u3002
1908
1967
 
1909
- ## \u914D\u7F6E\u6269\u5C55
1968
+ ### \u914D\u7F6E\u6269\u5C55
1910
1969
 
1911
1970
  \u670D\u52A1\u7AEF \`packages/${name}-serv/config/\` \u59CB\u7EC8\u4FDD\u7559 core\u3001serv\u3001db\u3001cache\u3001iam\u3001storage\u3001audit\u3001reach\u3001ai\u3001vecdb\u3001payment\u3001scheduler\u3001deploy \u5171 13 \u4EFD YAML\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\uFF0C\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
1912
1971
 
@@ -1918,7 +1977,7 @@ CLI \u5DF2\u5728\u6839\u76EE\u5F55\u751F\u6210 \`.env.local\`\uFF0C\u5176\u4E2D
1918
1977
 
1919
1978
  ${generateEnvironmentReadme()}
1920
1979
 
1921
- ## \u5F00\u53D1\u4E0E\u6784\u5EFA
1980
+ ### \u5F00\u53D1\u4E0E\u6784\u5EFA
1922
1981
 
1923
1982
  \`\`\`bash
1924
1983
  ${pm} install
@@ -1932,22 +1991,9 @@ ${frontends.includes("desktop") ? `${pm} build --target desktop --platform windo
1932
1991
 
1933
1992
  Tauri \u9700\u8981\u76EE\u6807\u7CFB\u7EDF\u7684 Rust target\u3001SDK \u548C\u6253\u5305\u5DE5\u5177\u3002\u6D4B\u8BD5\u4EA7\u751F\u7684\u4E34\u65F6\u5DE5\u7A0B\u3001\u622A\u56FE\u3001\u62A5\u544A\u3001\u5BB9\u5668\u3001\u955C\u50CF\u4E0E\u8FDB\u7A0B\u5E94\u5728 finally/teardown \u4E2D\u81EA\u52A8\u6E05\u7406\uFF0C\u5931\u8D25\u4E5F\u6267\u884C\u3002
1934
1993
 
1935
- ## \u6784\u5EFA\u4E0E\u90E8\u7F72
1936
-
1937
- \`\`\`bash
1938
- # \u9ED8\u8BA4\u53EA\u7F16\u8BD1\u6E90\u7801\uFF1B\u4E0D\u9009\u62E9\u90E8\u7F72\u73AF\u5883
1939
- ${pm} build
1940
- # \u6784\u5EFA\u5E76\u5BFC\u51FA API/Web \u955C\u50CF\uFF0Cauto \u4F18\u5148 Podman\u3001\u5176\u6B21 Docker
1941
- ${pm} build --target docker
1942
- # \u9009\u62E9\u73AF\u5883\u3001\u4E2D\u95F4\u4EF6\u5E76\u4E0A\u4F20\u4E00\u4E2A Compose tar\uFF1B\u4E0D\u4F1A\u91CD\u65B0\u6784\u5EFA
1943
- ${pm} exec hai deploy packages/${name}-serv --env test
1944
- # \u65E0\u6CD5\u8FDE\u63A5\u670D\u52A1\u5668\u65F6\u53EA\u751F\u6210\u5305\uFF0C\u6309\u8F93\u51FA\u8BF4\u660E\u624B\u5DE5\u642C\u8FD0\u548C\u542F\u52A8
1945
- ${pm} exec hai deploy packages/${name}-serv --env prod --bundle-only
1946
- \`\`\`
1947
-
1948
- \u955C\u50CF\u6784\u5EFA\u914D\u7F6E\u89C1 \`hai.build.json\`\u3002\u6784\u5EFA\u4EA7\u7269\u4FDD\u5B58\u5728 \`.hai/build/docker/<buildId>\`\uFF0C\u53EF\u7528 \`--artifact-dir\` \u9009\u62E9\u540C\u4E00\u4EFD\u4EA7\u7269\u90E8\u7F72\u5230 test/prod\u3002\u90E8\u7F72\u5305\u542B\u79C1\u6709\u914D\u7F6E\uFF0C\u4E0D\u80FD\u63D0\u4EA4 Git\u3002\u73AF\u5883\u5DF2\u914D\u7F6E\u5B8C\u5907\u53EF\u52A0 \`--skip-middleware\`\u3002\u670D\u52A1\u8FD0\u884C\u6570\u636E\u7531 volume \u6301\u4E45\u5316\uFF0C\u955C\u50CF\u4E0D\u6253\u5305\u672C\u673A data\u3002API/Web \u8054\u5408\u90E8\u7F72\u516C\u5F00 Web \u5165\u53E3\u5E76\u4EE3\u7406\u540C\u6E90 API\uFF1B\u6CA1\u6709 Web \u4EA7\u7269\u65F6\u53EA\u90E8\u7F72\u670D\u52A1\u3002
1994
+ ${generateBuildDeploymentReadme(pm, `packages/${name}-serv`)}
1949
1995
 
1950
- ## i18n \u4E0E shared \u534F\u540C
1996
+ ### i18n \u4E0E shared \u534F\u540C
1951
1997
 
1952
1998
  - shared\uFF1A\`packages/${name}-shared/project.inlang\` + \`messages/\`\uFF0C\u53EA\u653E\u8DE8\u7AEF\u5171\u4EAB\u7EC4\u4EF6\u6587\u6848\u3002
1953
1999
  - web/app/desktop\uFF1A\u6BCF\u4E2A\u5E94\u7528\u90FD\u6709\u81EA\u5DF1\u7684 \`project.inlang\` + \`messages/\`\uFF0C\u7528\u4E8E\u672C\u7AEF\u9875\u9762\u4E0E\u4EA7\u54C1\u6587\u6848\u3002
@@ -1955,13 +2001,20 @@ ${pm} exec hai deploy packages/${name}-serv --env prod --bundle-only
1955
2001
  - \u65B0\u589E locale \u65F6\uFF0Cshared \u4E0E\u6240\u6709\u542F\u7528\u524D\u7AEF\u7684 \`locales\` \u5FC5\u987B\u540C\u6B65\uFF1B\u8BED\u8A00\u5207\u6362\u5668\u4F1A\u540C\u65F6\u66F4\u65B0 shared runtime \u4E0E\u5BBF\u4E3B\u5E94\u7528 runtime\u3002
1956
2002
  - \`${pm} i18n:compile\` \u4F1A\u7EDF\u4E00\u7F16\u8BD1 shared \u4E0E\u6BCF\u4E2A\u542F\u7528\u524D\u7AEF\u7684 Paraglide \u8F93\u51FA\uFF1B\u5404\u524D\u7AEF\u4E5F\u53EF\u5355\u72EC\u8FD0\u884C \`${pm} --filter ${name}-web paraglide:compile\` \u7B49\u547D\u4EE4\u3002
1957
2003
 
1958
- ## \u81EA\u5B9A\u4E49\u5EFA\u8BAE
2004
+ ### \u81EA\u5B9A\u4E49\u5EFA\u8BAE
1959
2005
 
1960
2006
  - \u6539\u5168\u5C40 Shell/\u4E3B\u9898/\u8BED\u8A00\u5207\u6362\uFF1A\u7F16\u8F91 \`packages/${name}-shared\`\u3002
1961
- - \u6539\u67D0\u4E2A\u524D\u7AEF\u9875\u9762\u548C\u6587\u6848\uFF1A\u7F16\u8F91\u5BF9\u5E94 \`apps/${name}-*/src/routes\` \u4E0E \`apps/${name}-*/messages\`\u3002
2007
+ - \u6539\u67D0\u4E2A\u524D\u7AEF\u9875\u9762\u548C\u6587\u6848\uFF1A\u7F16\u8F91\u5171\u4EAB \`packages/${name}-shared/src/lib/pages\`\u3001\u672C\u7AEF\u5165\u53E3\u4E0E \`apps/${name}-*/messages\`\u3002
1962
2008
  - \u6539\u4E3B\u9898\u5217\u8868\uFF1A\u8C03\u6574\u811A\u624B\u67B6\u914D\u7F6E\u4E2D\u7684 \`fullstack.theme.themes\`\uFF0C\u6216\u76F4\u63A5\u7F16\u8F91\u5404\u7AEF \`src/app.css\` \u7684 daisyUI \`themes\`\u3002
1963
2009
 
1964
- ## \u8D28\u91CF\u95E8\u7981
2010
+
2011
+ ${generateAiDevelopmentReadme()}### \u6587\u6863
2012
+
2013
+ - [hai Agent Framework](https://github.com/hai-series/hai-framework)
2014
+ - [@h-ai/serv](./.agents/skills/hai-serv/SKILL.md)
2015
+ - [@h-ai/ui](./.agents/skills/hai-ui/SKILL.md)
2016
+
2017
+ ## \u5F00\u53D1\u6D4B\u8BD5
1965
2018
 
1966
2019
  \`\`\`bash
1967
2020
  ${pm} typecheck
@@ -1971,11 +2024,9 @@ ${pm} test
1971
2024
  ${pm} test:e2e
1972
2025
  \`\`\`
1973
2026
 
1974
- ${generateAiDevelopmentReadme()}## \u6587\u6863
2027
+ ## \u8BB8\u53EF\u8BC1
1975
2028
 
1976
- - [hai Agent Framework](https://github.com/hai-series/hai-framework)
1977
- - [@h-ai/serv](./.agents/skills/hai-serv/SKILL.md)
1978
- - [@h-ai/ui](./.agents/skills/hai-ui/SKILL.md)
2029
+ Apache-2.0
1979
2030
  `;
1980
2031
  }
1981
2032
  function printCompletionMessage(options) {
@@ -2059,7 +2110,7 @@ async function addModule(options) {
2059
2110
  try {
2060
2111
  const project = await detectProject(cwd);
2061
2112
  if (!project) {
2062
- core.logger.error(chalk.red("\u672A\u68C0\u6D4B\u5230\u9879\u76EE\uFF08\u627E\u4E0D\u5230 package.json\uFF09"));
2113
+ writeCliError(chalk.red("\u672A\u68C0\u6D4B\u5230\u9879\u76EE\uFF08\u627E\u4E0D\u5230 package.json\uFF09"));
2063
2114
  return;
2064
2115
  }
2065
2116
  let moduleName = options.module;
@@ -2070,7 +2121,7 @@ async function addModule(options) {
2070
2121
  value: key
2071
2122
  }));
2072
2123
  if (choices.length === 0) {
2073
- core.logger.info(chalk.green("\u6240\u6709\u6A21\u5757\u5DF2\u5B89\u88C5\uFF01"));
2124
+ writeCliMessage(chalk.green("\u6240\u6709\u6A21\u5757\u5DF2\u5B89\u88C5\uFF01"));
2074
2125
  return;
2075
2126
  }
2076
2127
  const { selected } = await prompts({
@@ -2085,11 +2136,11 @@ async function addModule(options) {
2085
2136
  }
2086
2137
  const moduleDef = MODULE_MAP[moduleName];
2087
2138
  if (!moduleDef) {
2088
- core.logger.error(chalk.red(`\u672A\u77E5\u6A21\u5757: ${moduleName}`));
2089
- core.logger.info(`\u53EF\u7528\u6A21\u5757: ${Object.keys(MODULE_MAP).join(", ")}`);
2139
+ writeCliError(chalk.red(`\u672A\u77E5\u6A21\u5757: ${moduleName}`));
2140
+ writeCliMessage(`\u53EF\u7528\u6A21\u5757: ${Object.keys(MODULE_MAP).join(", ")}`);
2090
2141
  return;
2091
2142
  }
2092
- const { generateProjectAiSupport, resolveSkillNamesForPackages } = await import('./cli-skill-templates-TSYEQTCY.js');
2143
+ const { generateProjectAiSupport, resolveSkillNamesForPackages } = await import('./cli-skill-templates-FC3QRKDO.js');
2093
2144
  if (moduleDef.packages.every((p) => project.haiPackages.includes(p))) {
2094
2145
  const generatedFiles2 = await generateProjectAiSupport(
2095
2146
  cwd,
@@ -2100,12 +2151,12 @@ async function addModule(options) {
2100
2151
  }
2101
2152
  );
2102
2153
  if (generatedFiles2.length > 0) {
2103
- core.logger.info(chalk.yellow(`\u6A21\u5757 ${moduleName} \u5DF2\u5B89\u88C5\uFF0C\u5DF2\u8865\u5168 AI \u652F\u6301\u6587\u4EF6`));
2154
+ writeCliMessage(chalk.yellow(`\u6A21\u5757 ${moduleName} \u5DF2\u5B89\u88C5\uFF0C\u5DF2\u8865\u5168 AI \u652F\u6301\u6587\u4EF6`));
2104
2155
  for (const generatedFile of generatedFiles2) {
2105
- core.logger.info(chalk.gray(` + ${generatedFile} (AI support)`));
2156
+ writeCliMessage(chalk.gray(` + ${generatedFile} (AI support)`));
2106
2157
  }
2107
2158
  } else {
2108
- core.logger.info(chalk.yellow(`\u6A21\u5757 ${moduleName} \u5DF2\u5B89\u88C5`));
2159
+ writeCliMessage(chalk.yellow(`\u6A21\u5757 ${moduleName} \u5DF2\u5B89\u88C5`));
2109
2160
  }
2110
2161
  return;
2111
2162
  }
@@ -2128,7 +2179,10 @@ async function addModule(options) {
2128
2179
  const existingHaiSpecifier = Object.entries(pkg.dependencies).find(([pkgName]) => pkgName.startsWith("@h-ai/"))?.[1];
2129
2180
  const depVersion = existingHaiSpecifier === "catalog:" ? "catalog:" : getCliVersion();
2130
2181
  for (const pkgName of allPackages) {
2131
- if (!pkg.dependencies[pkgName]) {
2182
+ if (pkgName === "@h-ai/deploy") {
2183
+ pkg.devDependencies ??= {};
2184
+ pkg.devDependencies[pkgName] ??= depVersion;
2185
+ } else if (!pkg.dependencies[pkgName]) {
2132
2186
  pkg.dependencies[pkgName] = depVersion;
2133
2187
  }
2134
2188
  }
@@ -2143,7 +2197,7 @@ async function addModule(options) {
2143
2197
  await writeFile(configPath, content);
2144
2198
  spinner.succeed();
2145
2199
  } else {
2146
- core.logger.info(chalk.gray(`\u914D\u7F6E\u6587\u4EF6\u5DF2\u5B58\u5728: config/_${moduleDef.configKey}.yml`));
2200
+ writeCliMessage(chalk.gray(`\u914D\u7F6E\u6587\u4EF6\u5DF2\u5B58\u5728: config/_${moduleDef.configKey}.yml`));
2147
2201
  }
2148
2202
  }
2149
2203
  if (moduleDef.deps) {
@@ -2154,7 +2208,7 @@ async function addModule(options) {
2154
2208
  if (!await fileExists(depConfigPath)) {
2155
2209
  const content = generateConfigFile(depDef.configKey);
2156
2210
  await writeFile(depConfigPath, content);
2157
- core.logger.info(chalk.gray(` + config/_${depDef.configKey}.yml (\u4F9D\u8D56)`));
2211
+ writeCliMessage(chalk.gray(` + config/_${depDef.configKey}.yml (\u4F9D\u8D56)`));
2158
2212
  }
2159
2213
  }
2160
2214
  }
@@ -2170,7 +2224,7 @@ async function addModule(options) {
2170
2224
  );
2171
2225
  if (generatedFiles.length > 0) {
2172
2226
  for (const generatedFile of generatedFiles) {
2173
- core.logger.info(chalk.gray(` + ${generatedFile} (AI support)`));
2227
+ writeCliMessage(chalk.gray(` + ${generatedFile} (AI support)`));
2174
2228
  }
2175
2229
  }
2176
2230
  const doInstall = options.install !== false;
@@ -2181,17 +2235,17 @@ async function addModule(options) {
2181
2235
  execSync2(`${pm} install`, { cwd, stdio: "ignore" });
2182
2236
  spinner.succeed();
2183
2237
  }
2184
- core.logger.info("");
2185
- core.logger.info(chalk.green(`\u2714 \u6A21\u5757 ${moduleName} \u6DFB\u52A0\u6210\u529F\uFF01`));
2186
- core.logger.info("");
2187
- core.logger.info("\u5DF2\u6DFB\u52A0\u7684\u5305:");
2238
+ writeCliMessage("");
2239
+ writeCliMessage(chalk.green(`\u2714 \u6A21\u5757 ${moduleName} \u6DFB\u52A0\u6210\u529F\uFF01`));
2240
+ writeCliMessage("");
2241
+ writeCliMessage("\u5DF2\u6DFB\u52A0\u7684\u5305:");
2188
2242
  for (const pkg2 of allPackages) {
2189
- core.logger.info(chalk.cyan(` ${pkg2}`));
2243
+ writeCliMessage(chalk.cyan(` ${pkg2}`));
2190
2244
  }
2191
- core.logger.info("");
2245
+ writeCliMessage("");
2192
2246
  } catch (error) {
2193
2247
  spinner.fail();
2194
- core.logger.error(chalk.red("\u6DFB\u52A0\u6A21\u5757\u5931\u8D25:"), { error });
2248
+ writeCliError(chalk.red("\u6DFB\u52A0\u6A21\u5757\u5931\u8D25:"), { error });
2195
2249
  throw error;
2196
2250
  }
2197
2251
  }
@@ -2216,8 +2270,8 @@ async function pnpm(args, cwd) {
2216
2270
  const executable = join(directory, "pnpm.exe");
2217
2271
  if (existsSync(executable))
2218
2272
  return run(executable, args, cwd);
2219
- for (const relative of ["node_modules/pnpm/bin/pnpm.cjs", "pnpm.cjs"]) {
2220
- const candidate = join(directory, relative);
2273
+ for (const relative2 of ["node_modules/pnpm/bin/pnpm.cjs", "pnpm.cjs"]) {
2274
+ const candidate = join(directory, relative2);
2221
2275
  if (existsSync(candidate))
2222
2276
  return run(process4.execPath, [candidate, ...args], cwd);
2223
2277
  }
@@ -2231,7 +2285,7 @@ async function buildCommand(options) {
2231
2285
  const target = options.target ?? "compile";
2232
2286
  if (!["compile", "docker", "desktop"].includes(target))
2233
2287
  throw new Error(cliM("cli_build_target_invalid"));
2234
- if (target !== "docker" && (options.runtime !== void 0 || options.network !== void 0 || options.sqlitePrebuildMirror !== void 0 || options.httpProxy === false) || target !== "desktop" && (options.platform !== void 0 || options.arch !== void 0)) {
2288
+ if (target !== "docker" && (options.images !== void 0 || options.runtime !== void 0 || options.network !== void 0 || options.sqlitePrebuildMirror !== void 0 || options.httpProxy === false) || target !== "desktop" && (options.platform !== void 0 || options.arch !== void 0)) {
2235
2289
  throw new Error(cliM("cli_build_options_invalid"));
2236
2290
  }
2237
2291
  if (target === "compile") {
@@ -2256,12 +2310,20 @@ async function buildCommand(options) {
2256
2310
  const bundles = platform === "windows" ? "nsis" : platform === "macos" ? "app,dmg" : "deb,appimage";
2257
2311
  for (const directory of config.desktop)
2258
2312
  await pnpm(["exec", "tauri", "build", "--target", rustTarget, "--bundles", bundles, "--config", JSON.stringify({ version: appVersion })], resolve(cwd, directory));
2259
- core.logger.info(cliM("cli_build_native_ready", { params: { target: rustTarget } }));
2313
+ writeCliMessage(cliM("cli_build_native_ready", { params: { target: rustTarget } }));
2260
2314
  return;
2261
2315
  }
2262
- if (!Array.isArray(config.images) || !config.images.length || config.images.filter((image) => image.name === "app").length !== 1 || config.images.length > 2 || new Set(config.images.map((image) => image.name)).size !== config.images.length || config.images.some((image) => !["app", "web"].includes(image.name) || image.containerPort !== void 0 && (!Number.isInteger(image.containerPort) || image.containerPort < 1 || image.containerPort > 65535))) {
2316
+ if (!Array.isArray(config.images) || !config.images.length || config.images.some((image) => !image || typeof image !== "object") || config.images.filter((image) => image.name === "app").length !== 1 || config.images.length > 2 || new Set(config.images.map((image) => image.name)).size !== config.images.length || config.images.some((image) => !["app", "web"].includes(image.name) || typeof image.dockerfile !== "string" || !image.dockerfile || "target" in image || image.containerPort !== void 0 && (!Number.isInteger(image.containerPort) || image.containerPort < 1 || image.containerPort > 65535))) {
2263
2317
  throw new Error(cliM("cli_build_images_missing"));
2264
2318
  }
2319
+ const selected = options.images?.split(",").map((name) => name.trim());
2320
+ if (selected && (selected.some((name) => !config.images.some((image) => image.name === name)) || new Set(selected).size !== selected.length))
2321
+ throw new Error(cliM("cli_build_selection_invalid"));
2322
+ const images = config.images.filter((image) => !selected || selected.includes(image.name));
2323
+ for (const image of images) {
2324
+ if (!existsSync(resolve(cwd, image.dockerfile)))
2325
+ throw new Error(cliM("cli_build_dockerfile_missing", { params: { path: image.dockerfile } }));
2326
+ }
2265
2327
  const candidates = options.runtime && options.runtime !== "auto" ? [options.runtime] : ["podman", "docker"];
2266
2328
  if (candidates.some((runtime2) => !["podman", "docker"].includes(runtime2)))
2267
2329
  throw new Error(cliM("cli_build_runtime_missing"));
@@ -2282,21 +2344,27 @@ async function buildCommand(options) {
2282
2344
  throw new Error(cliM("cli_build_mirror_invalid"));
2283
2345
  }
2284
2346
  const buildId = `${appVersion}-${Date.now()}-${randomBytes(3).toString("hex")}`;
2285
- const artifact = { version: 2, appVersion, buildId, images: [] };
2347
+ const artifact = { version: 3, appVersion, buildId, partial: images.length !== config.images.length, hasWeb: config.images.some((image) => image.name === "web"), images: [] };
2348
+ for (const image of config.images) {
2349
+ if (image.containerPort !== void 0)
2350
+ artifact[image.name === "app" ? "appContainerPort" : "webContainerPort"] = image.containerPort;
2351
+ }
2286
2352
  const parent = join(cwd, ".hai/build/docker");
2287
2353
  mkdirSync(parent, { recursive: true });
2288
2354
  const staging = mkdtempSync(join(parent, ".building-"));
2289
2355
  const built = [];
2290
2356
  try {
2291
- for (const image of config.images) {
2357
+ for (const context of new Set(images.map((image) => resolve(cwd, image.context ?? "."))))
2358
+ prepareDependencyInputs(context);
2359
+ for (const image of images) {
2292
2360
  if (!["app", "web"].includes(image.name))
2293
2361
  throw new Error(cliM("cli_build_images_missing"));
2294
2362
  const context = resolve(cwd, image.context ?? ".");
2295
2363
  const imageRef = `localhost/hai-${basenameSafe(cwd)}-${image.name}:${buildId.toLowerCase().replaceAll("+", "_")}`;
2296
- const args = ["build", "-f", resolve(context, image.dockerfile ?? "Dockerfile"), "-t", imageRef];
2364
+ const args = ["build", "-f", resolve(cwd, image.dockerfile), "-t", imageRef];
2297
2365
  if (options.network !== void 0)
2298
2366
  args.push("--network", options.network);
2299
- const ignore = `${resolve(context, image.dockerfile ?? "Dockerfile")}.dockerignore`;
2367
+ const ignore = `${resolve(cwd, image.dockerfile)}.dockerignore`;
2300
2368
  if (runtime === "podman" && existsSync(ignore))
2301
2369
  args.push("--ignorefile", ignore);
2302
2370
  if (runtime === "podman") {
@@ -2308,8 +2376,6 @@ async function buildCommand(options) {
2308
2376
  }
2309
2377
  }
2310
2378
  }
2311
- if (image.target)
2312
- args.push("--target", image.target);
2313
2379
  if (options.sqlitePrebuildMirror)
2314
2380
  args.push("--build-arg", `SQLITE_PREBUILD_MIRROR=${options.sqlitePrebuildMirror}`);
2315
2381
  built.push(imageRef);
@@ -2328,7 +2394,7 @@ async function buildCommand(options) {
2328
2394
  writeFileSync(join(parent, `latest-${buildId}`), `${buildId}
2329
2395
  `);
2330
2396
  renameSync(join(parent, `latest-${buildId}`), join(parent, "latest"));
2331
- core.logger.info(cliM("cli_build_ready", { params: { path: output, runtime } }));
2397
+ writeCliMessage(cliM("cli_build_ready", { params: { path: output, runtime } }));
2332
2398
  } catch (error) {
2333
2399
  for (const imageRef of built) {
2334
2400
  try {
@@ -2341,6 +2407,42 @@ async function buildCommand(options) {
2341
2407
  rmSync(staging, { recursive: true, force: true });
2342
2408
  }
2343
2409
  }
2410
+ function prepareDependencyInputs(cwd) {
2411
+ const destination = join(cwd, "images", "dependencies");
2412
+ mkdirSync(destination, { recursive: true });
2413
+ const tracking = join(destination, ".managed.json");
2414
+ const previous = existsSync(tracking) ? JSON.parse(readFileSync(tracking, "utf8")) : [];
2415
+ const files = [];
2416
+ const walk = (directory) => {
2417
+ for (const entry of readdirSync(directory, { withFileTypes: true })) {
2418
+ const source = join(directory, entry.name);
2419
+ if (source === destination || ["node_modules", ".git", ".hai", ".turbo", ".svelte-kit", ".pnpm-store", ".cache", "dist", "build", "data", "tmp", "test-results", "playwright-report"].includes(entry.name))
2420
+ continue;
2421
+ if (entry.isDirectory()) {
2422
+ walk(source);
2423
+ continue;
2424
+ }
2425
+ if (!entry.isFile() || !(entry.name === "package.json" || entry.name === ".pnpmfile.cjs" || /\.(?:tgz|patch)$/.test(entry.name)))
2426
+ continue;
2427
+ const file = relative(cwd, source);
2428
+ mkdirSync(dirname(join(destination, file)), { recursive: true });
2429
+ const target = join(destination, file);
2430
+ if (!existsSync(target) || !readFileSync(source).equals(readFileSync(target)))
2431
+ copyFileSync(source, target);
2432
+ files.push(file);
2433
+ }
2434
+ };
2435
+ walk(cwd);
2436
+ for (const file of previous) {
2437
+ const target = resolve(destination, file);
2438
+ if (!files.includes(file) && !relative(destination, target).startsWith(".."))
2439
+ rmSync(target, { force: true });
2440
+ }
2441
+ const content = `${JSON.stringify(files)}
2442
+ `;
2443
+ if (!existsSync(tracking) || readFileSync(tracking, "utf8") !== content)
2444
+ writeFileSync(tracking, content);
2445
+ }
2344
2446
  function basenameSafe(directory) {
2345
2447
  return directory.slice(dirname(directory).length + 1).toLowerCase().replace(/[^a-z0-9-]/g, "-").replace(/^-+/, "") || "app";
2346
2448
  }
@@ -2464,10 +2566,10 @@ function ensureConfigNode(appDir, requirement, backend) {
2464
2566
  if (document.errors.length)
2465
2567
  throw new Error(cliM("cli_deploy_invalid_value"));
2466
2568
  if (requirement.configPath.length && !document.hasIn(requirement.configPath))
2467
- document.setIn(requirement.configPath, requirement.configName === "reach" ? { name: requirement.config.name ?? requirement.service, type: backend } : {});
2569
+ document.setIn(requirement.configPath, document.createNode(requirement.configName === "reach" ? { name: requirement.config.name ?? requirement.service, type: backend } : {}));
2468
2570
  if (requirement.service === "ldap") {
2469
2571
  if (!document.hasIn(["login"]))
2470
- document.setIn(["login"], {});
2572
+ document.set("login", document.createNode({}));
2471
2573
  if (!document.hasIn(["login", "ldap"]))
2472
2574
  document.setIn(["login", "ldap"], `\${HAI_IAM_LOGIN_LDAP:false}`);
2473
2575
  }
@@ -2490,7 +2592,7 @@ async function configureDeployMiddleware(module, scan, environment, config, yes
2490
2592
  const current = choices.find((item) => item.backend === requirement.backend);
2491
2593
  if (!current)
2492
2594
  throw new Error(cliM("cli_deploy_cloud_unsupported", { params: { backend: requirement.backend } }));
2493
- core.logger.info(cliM("cli_deploy_middleware", { params: { service: requirement.service, backend: current.label, purpose: module.describeMiddlewareBackend(current), source: requirement.source } }));
2595
+ writeCliMessage(cliM("cli_deploy_middleware", { params: { service: requirement.service, backend: current.label, purpose: module.describeMiddlewareBackend(current), source: requirement.source } }));
2494
2596
  const canKeep = canKeepMiddleware(requirement, current, environment);
2495
2597
  const mode = yes ? canKeep ? "keep" : "default" : await ask({
2496
2598
  type: "select",
@@ -2529,8 +2631,11 @@ async function configureDeployMiddleware(module, scan, environment, config, yes
2529
2631
  if (deployment.errors.length)
2530
2632
  throw new Error(cliM("cli_deploy_invalid_value"));
2531
2633
  if (!deployment.hasIn(["container", "s3Engine"])) {
2532
- if (!deployment.hasIn(["container"]))
2533
- deployment.setIn(["container"], {});
2634
+ const node = deployment.get("container", true);
2635
+ if (node === void 0 || isScalar(node) && node.value === null)
2636
+ deployment.set("container", deployment.createNode({}));
2637
+ else if (!isMap(node))
2638
+ throw new Error(cliM("cli_deploy_yaml_node", { params: { path: deploymentFile, field: "container" } }));
2534
2639
  deployment.setIn(["container", "s3Engine"], `\${HAI_DEPLOY_CONTAINER_S3ENGINE:rustfs}`);
2535
2640
  writeFileSync(deploymentFile, deployment.toString());
2536
2641
  }
@@ -2544,9 +2649,10 @@ async function configureDeployMiddleware(module, scan, environment, config, yes
2544
2649
  for (const name of CLOUD_FIELDS[backend.provisioner]) {
2545
2650
  const key = `HAI_DEPLOY_SERVICES_${requirement.service.toUpperCase()}_${name.toUpperCase()}`;
2546
2651
  const initial = String(priorConfig[name] ?? environment.values[key] ?? "");
2547
- const value = yes ? initial : String(await ask({ type: /key|token|secret/i.test(name) ? "password" : "text", name: "value", message: key, initial, validate: (input) => Boolean(input) || cliM("cli_deploy_required") }));
2652
+ const label = `${backend.label} \u2014 ${cliM(`cli_deploy_cloud_input_${name}`)}`;
2653
+ const value = yes ? initial : String(await ask({ type: /key|token|secret/i.test(name) ? "password" : "text", name: "value", message: label, initial, validate: (input) => Boolean(input) || cliM("cli_deploy_required") }));
2548
2654
  if (!value)
2549
- throw new Error(`${key}: ${cliM("cli_deploy_required")}`);
2655
+ throw new Error(`${label}: ${cliM("cli_deploy_required")}`);
2550
2656
  cloud[name] = value;
2551
2657
  updates[key] = value;
2552
2658
  }
@@ -2574,7 +2680,7 @@ async function configureDeployMiddleware(module, scan, environment, config, yes
2574
2680
  if (!saved.success)
2575
2681
  throw new Error(saved.error.message);
2576
2682
  for (const [name, value] of Object.entries(updates)) {
2577
- core.logger.info(cliM("cli_deploy_saved", { params: { name, value: displayDeployValue(name, value) } }));
2683
+ writeCliMessage(cliM("cli_deploy_saved", { params: { name, value: displayDeployValue(name, value) } }));
2578
2684
  }
2579
2685
  }
2580
2686
  config.services = Object.keys(services).length ? services : void 0;
@@ -2595,7 +2701,7 @@ async function configureDeployTarget(module, config, environment, nonInteractive
2595
2701
  const current = module.ProviderConfigSchema.safeParse(config.provider);
2596
2702
  if (current.success && (bundleOnly || current.data.type !== "docker-ssh" || current.data.ssh)) {
2597
2703
  config.provider = current.data;
2598
- core.logger.info(cliM("cli_deploy_target_reused", { params: { target: current.data.type === "docker-ssh" ? current.data.ssh ? `${current.data.ssh.username}@${current.data.ssh.host}:${current.data.ssh.port}` : cliM("cli_deploy_bundle_local") : "Vercel" } }));
2704
+ writeCliMessage(cliM("cli_deploy_target_reused", { params: { target: current.data.type === "docker-ssh" ? current.data.ssh ? `${current.data.ssh.username}@${current.data.ssh.host}:${current.data.ssh.port}` : cliM("cli_deploy_bundle_local") : "Vercel" } }));
2599
2705
  return;
2600
2706
  }
2601
2707
  if (nonInteractive)
@@ -2614,9 +2720,9 @@ async function configureDeployTarget(module, config, environment, nonInteractive
2614
2720
  const answer = await prompts({
2615
2721
  type: options.secret ? "password" : "text",
2616
2722
  name: "value",
2617
- message: cliM("cli_deploy_target_field", { params: { name } }),
2723
+ message: cliM(`cli_deploy_input${name.split("_").at(-1)}`),
2618
2724
  initial: options.secret ? void 0 : initial === void 0 ? "" : String(initial),
2619
- validate: (value) => options.port ? /^\d+$/.test(value) && Number(value) >= 1 && Number(value) <= 65535 || cliM("cli_deploy_invalid_value") : options.optional || !!value.trim() || cliM("cli_deploy_invalid_value")
2725
+ validate: (value) => options.port ? /^\d+$/.test(value) && Number(value) >= 1 && Number(value) <= 65535 || cliM("cli_deploy_port_invalid") : options.optional || !!value.trim() || cliM("cli_deploy_invalid_value")
2620
2726
  });
2621
2727
  if (answer.value === void 0)
2622
2728
  throw new Error(cliM("cli_deploy_cancelled"));
@@ -2624,7 +2730,7 @@ async function configureDeployTarget(module, config, environment, nonInteractive
2624
2730
  };
2625
2731
  let provider;
2626
2732
  if (platform.value === "docker-ssh") {
2627
- core.logger.info(cliM("cli_deploy_target_ssh_hint"));
2733
+ writeCliMessage(cliM("cli_deploy_target_ssh_hint"));
2628
2734
  const ssh = configObject(original.ssh);
2629
2735
  const remote = configObject(original.remote);
2630
2736
  const expose = configObject(original.expose);
@@ -2655,15 +2761,27 @@ async function configureDeployTarget(module, config, environment, nonInteractive
2655
2761
  if (!saved.success)
2656
2762
  throw new Error(saved.error.message);
2657
2763
  config.provider = validated.data;
2658
- core.logger.info(cliM("cli_deploy_target_saved"));
2764
+ writeCliMessage(cliM("cli_deploy_target_saved"));
2659
2765
  }
2660
2766
  async function deployCommand(options) {
2661
2767
  const spinner = ora();
2662
2768
  const cwd = options.cwd ?? process4.cwd();
2663
2769
  let appDir = resolve(cwd, options.appDir ?? ".");
2664
2770
  let closeDeploy = null;
2771
+ let restoreProjectLogger;
2665
2772
  const originalEnv = { ...process4.env };
2773
+ const previousLogLevel = core.logger.getLevel();
2774
+ core.logger.setLevel("fatal");
2666
2775
  try {
2776
+ if (options.runtime && !["auto", "podman", "docker"].includes(options.runtime))
2777
+ throw new Error(cliM("cli_build_runtime_missing"));
2778
+ if (options.artifactDir && options.build)
2779
+ throw new Error(cliM("cli_deploy_build_artifact_conflict"));
2780
+ if (!options.build && (options.network !== void 0 || options.sqlitePrebuildMirror !== void 0 || options.httpProxy === false))
2781
+ throw new Error(cliM("cli_deploy_build_options_required"));
2782
+ let selectedServices = options.services?.split(",").map((name) => name.trim());
2783
+ if (selectedServices && (!selectedServices.length || selectedServices.some((name) => !/^[a-z][a-z0-9-]*$/.test(name)) || new Set(selectedServices).size !== selectedServices.length))
2784
+ throw new Error(cliM("cli_build_selection_invalid"));
2667
2785
  let environmentName = options.env;
2668
2786
  if (!environmentName) {
2669
2787
  if (!process4.stdin.isTTY)
@@ -2678,15 +2796,24 @@ async function deployCommand(options) {
2678
2796
  let deployModule;
2679
2797
  try {
2680
2798
  const localRequire = createRequire(resolve(appDir, "package.json"));
2799
+ try {
2800
+ const { core: projectCore } = await import(pathToFileURL(localRequire.resolve("@h-ai/core")).href);
2801
+ if (projectCore !== core) {
2802
+ const level = projectCore.logger.getLevel();
2803
+ projectCore.logger.setLevel("fatal");
2804
+ restoreProjectLogger = () => projectCore.logger.setLevel(level);
2805
+ }
2806
+ } catch {
2807
+ }
2681
2808
  let localModule;
2682
2809
  try {
2683
2810
  localModule = localRequire.resolve("@h-ai/deploy");
2684
2811
  } catch {
2685
2812
  }
2686
- deployModule = localModule ? await import(pathToFileURL(localModule).href) : await import('./src-RDQ2EIBT.js');
2813
+ deployModule = localModule ? await import(pathToFileURL(localModule).href) : await import('./src-2V6VP4I5.js');
2687
2814
  } catch {
2688
- core.logger.error(chalk.red(cliM("cli_deploy_module_missing")));
2689
- core.logger.info(chalk.cyan(" pnpm add @h-ai/deploy"));
2815
+ writeCliError(chalk.red(cliM("cli_deploy_module_missing")));
2816
+ writeCliMessage(chalk.cyan(" pnpm add -D @h-ai/deploy"));
2690
2817
  throw new Error(cliM("cli_deploy_module_missing"));
2691
2818
  }
2692
2819
  const { deploy } = deployModule;
@@ -2695,6 +2822,15 @@ async function deployCommand(options) {
2695
2822
  throw new Error(cliM("cli_deploy_scan_failed", { params: { message: scanResult.error.message } }));
2696
2823
  const scan = scanResult.data;
2697
2824
  appDir = scan.appDir ?? appDir;
2825
+ let buildRoot = appDir;
2826
+ while (!existsSync(resolve(buildRoot, "hai.build.json")) && dirname(buildRoot) !== buildRoot)
2827
+ buildRoot = dirname(buildRoot);
2828
+ const buildFile = resolve(buildRoot, "hai.build.json");
2829
+ const buildConfig = existsSync(buildFile) ? JSON.parse(readFileSync(buildFile, "utf8")) : {};
2830
+ const names = [...buildConfig.images?.map((image) => image.name) ?? ["app", "web"], ...(scan.middlewares ?? []).filter((item) => !["sqlite", "local", "memory", "disabled", "console", "lancedb"].includes(item.backend)).map((item) => item.service)];
2831
+ const available = [...new Set(names)];
2832
+ if (selectedServices?.some((name) => !available.includes(name)))
2833
+ throw new Error(cliM("cli_deploy_services_invalid", { params: { services: available.join(", ") } }));
2698
2834
  const environment = deployModule.loadDeploymentEnvironment({ environment: environmentName, envDir: scan.envDir ?? appDir });
2699
2835
  if (!environment.success)
2700
2836
  throw new Error(environment.error.message);
@@ -2709,7 +2845,7 @@ async function deployCommand(options) {
2709
2845
  const configPath = resolve(appDir, "config", "_deploy.yml");
2710
2846
  if (!existsSync(configPath)) {
2711
2847
  spinner.fail(chalk.red(cliM("cli_deploy_config_missing", { params: { path: configPath } })));
2712
- core.logger.info(chalk.cyan(cliM("cli_deploy_config_hint")));
2848
+ writeCliMessage(chalk.cyan(cliM("cli_deploy_config_hint")));
2713
2849
  throw new Error(cliM("cli_deploy_config_missing", { params: { path: configPath } }));
2714
2850
  }
2715
2851
  const loadedConfig = deployModule.loadDeploymentConfig("deploy", configPath, process4.env);
@@ -2717,9 +2853,51 @@ async function deployCommand(options) {
2717
2853
  throw new Error(loadedConfig.error.message);
2718
2854
  const deployConfig = loadedConfig.data;
2719
2855
  spinner.succeed(cliM("cli_deploy_config_loaded"));
2856
+ if (!options.build && !selectedServices && configObject(deployConfig.provider).type === "docker-ssh") {
2857
+ let artifactDirectory = options.artifactDir ? resolve(cwd, options.artifactDir) : resolve(buildRoot, ".hai/build/docker");
2858
+ if (existsSync(resolve(artifactDirectory, "latest"))) {
2859
+ const pointer = readFileSync(resolve(artifactDirectory, "latest"), "utf8").trim();
2860
+ if (!/^[a-z0-9][\w.+-]*$/i.test(pointer))
2861
+ throw new Error(cliM("cli_deploy_artifact_invalid"));
2862
+ artifactDirectory = resolve(artifactDirectory, pointer);
2863
+ }
2864
+ if (existsSync(resolve(artifactDirectory, "manifest.json"))) {
2865
+ try {
2866
+ const artifact = await deployModule.readDeploymentArtifact(artifactDirectory);
2867
+ if (artifact.partial)
2868
+ selectedServices = artifact.images.map((image) => image.name);
2869
+ } catch {
2870
+ throw new Error(cliM("cli_deploy_artifact_invalid"));
2871
+ }
2872
+ }
2873
+ }
2720
2874
  await configureDeployTarget(deployModule, deployConfig, environment.data, options.yes, options.bundleOnly);
2875
+ if (configObject(deployConfig.provider).type !== "docker-ssh" && (selectedServices || options.build))
2876
+ throw new Error(cliM("cli_deploy_bundle_provider"));
2877
+ if (!options.yes && !selectedServices && configObject(deployConfig.provider).type === "docker-ssh") {
2878
+ if (existsSync(buildFile)) {
2879
+ if (available.length > 1) {
2880
+ const choice = await prompts({ type: "multiselect", name: "value", message: cliM("cli_deploy_choose_services"), choices: available.map((name) => ({ title: name, value: name, selected: true })), min: 1 });
2881
+ if (!Array.isArray(choice.value) || choice.value.length === 0)
2882
+ throw new Error(cliM("cli_deploy_cancelled"));
2883
+ if (choice.value.length !== available.length)
2884
+ selectedServices = choice.value;
2885
+ }
2886
+ }
2887
+ }
2721
2888
  spinner.succeed(cliM("cli_deploy_scanned", { params: { name: scan.appName, sveltekit: String(scan.isSvelteKit), services: scan.requiredServices.join(", ") || cliM("cli_deploy_none") } }));
2722
- let skipMiddleware = options.skipMiddleware;
2889
+ if (options.build) {
2890
+ if (!existsSync(buildFile))
2891
+ throw new Error(cliM("cli_build_images_missing"));
2892
+ const imageNames = selectedServices?.filter((name) => ["app", "web"].includes(name));
2893
+ if (!selectedServices || imageNames?.length) {
2894
+ writeCliMessage(cliM("cli_deploy_build_started"));
2895
+ await buildCommand({ cwd: buildRoot, target: "docker", images: imageNames?.join(","), runtime: options.runtime, network: options.network, sqlitePrebuildMirror: options.sqlitePrebuildMirror, httpProxy: options.httpProxy });
2896
+ }
2897
+ }
2898
+ if (selectedServices)
2899
+ writeCliMessage(cliM("cli_deploy_services", { params: { services: selectedServices.join(", ") } }));
2900
+ let skipMiddleware = options.skipMiddleware || selectedServices?.every((name) => ["app", "web"].includes(name));
2723
2901
  if (!skipMiddleware && !options.yes && scan.requiredServices.length) {
2724
2902
  const action = await prompts({
2725
2903
  type: "select",
@@ -2732,8 +2910,8 @@ async function deployCommand(options) {
2732
2910
  skipMiddleware = action.value === "skip";
2733
2911
  }
2734
2912
  if (skipMiddleware)
2735
- core.logger.info(cliM("cli_deploy_middleware_skipped"));
2736
- const serviceModes = skipMiddleware ? Object.fromEntries(scan.requiredServices.map((service) => [service, "keep"])) : await configureDeployMiddleware(deployModule, scan, environment.data, deployConfig, options.yes);
2913
+ writeCliMessage(cliM("cli_deploy_middleware_skipped"));
2914
+ const serviceModes = skipMiddleware ? Object.fromEntries(scan.requiredServices.map((service) => [service, "keep"])) : await configureDeployMiddleware(deployModule, { ...scan, middlewares: scan.middlewares?.filter((item) => !selectedServices || selectedServices.includes(item.service)) }, environment.data, deployConfig, options.yes);
2737
2915
  if (skipMiddleware)
2738
2916
  deployConfig.services = void 0;
2739
2917
  for (const key of Object.keys(process4.env)) {
@@ -2748,7 +2926,7 @@ async function deployCommand(options) {
2748
2926
  spinner.start(cliM("cli_deploy_initializing"));
2749
2927
  const validatedConfig = deployModule.DeployConfigSchema.safeParse(deployConfig);
2750
2928
  if (!validatedConfig.success)
2751
- throw new Error(cliM("cli_deploy_init_failed", { params: { message: cliM("cli_deploy_invalid_value") } }));
2929
+ throw new Error(cliM("cli_deploy_config_invalid", { params: { path: configPath, fields: validatedConfig.error.issues.map((issue) => issue.path.join(".")).join(", ") } }));
2752
2930
  const initResult = await deploy.init(validatedConfig.data);
2753
2931
  if (!initResult.success) {
2754
2932
  throw new Error(cliM("cli_deploy_init_failed", { params: { message: initResult.error.message } }));
@@ -2758,6 +2936,9 @@ async function deployCommand(options) {
2758
2936
  spinner.start(cliM("cli_deploy_deploying"));
2759
2937
  const deployResult = await deploy.deployApp(appDir, {
2760
2938
  projectName: options.projectName,
2939
+ selectedServices,
2940
+ runtime: options.runtime,
2941
+ onProgress: (stage) => writeCliMessage(cliM(`cli_deploy_stage_${stage}`)),
2761
2942
  skipMiddleware,
2762
2943
  artifactDir: options.artifactDir ? resolve(cwd, options.artifactDir) : void 0,
2763
2944
  bundleOnly: options.bundleOnly,
@@ -2768,9 +2949,7 @@ async function deployCommand(options) {
2768
2949
  });
2769
2950
  if (!deployResult.success) {
2770
2951
  if (deployResult.error.ext?.recovery) {
2771
- core.logger.warn(deployResult.error.suggestion ?? cliM("cli_deploy_command_failed"), {
2772
- recovery: deployResult.error.ext.recovery
2773
- });
2952
+ writeCliMessage(deployResult.error.suggestion ?? cliM("cli_deploy_command_failed"));
2774
2953
  }
2775
2954
  throw new Error(cliM("cli_deploy_failed", { params: { message: deployResult.error.message } }));
2776
2955
  }
@@ -2788,30 +2967,30 @@ async function deployCommand(options) {
2788
2967
  continue;
2789
2968
  const value = completedEnvironment.data.values[requirement.envPrefix];
2790
2969
  if (value !== void 0)
2791
- core.logger.info(cliM("cli_deploy_saved", { params: { name: requirement.envPrefix, value: displayDeployValue(requirement.envPrefix, value) } }));
2970
+ writeCliMessage(cliM("cli_deploy_saved", { params: { name: requirement.envPrefix, value: displayDeployValue(requirement.envPrefix, value) } }));
2792
2971
  }
2793
- core.logger.info("");
2972
+ writeCliMessage("");
2794
2973
  if (deployResult.data.bundlePath)
2795
- core.logger.info(cliM("cli_deploy_bundle_path", { params: { path: deployResult.data.bundlePath } }));
2974
+ writeCliMessage(cliM("cli_deploy_bundle_path", { params: { path: deployResult.data.bundlePath } }));
2796
2975
  if (deployResult.data.status === "packaged")
2797
- core.logger.info(deployResult.data.instructions ?? "");
2976
+ writeCliMessage(deployResult.data.instructions ?? "");
2798
2977
  else
2799
- core.logger.info(chalk.cyan(cliM("cli_deploy_url", { params: { url: deployResult.data.url } })));
2800
- core.logger.info(chalk.gray(cliM("cli_deploy_id", { params: { id: deployResult.data.deploymentId } })));
2978
+ writeCliMessage(chalk.cyan(cliM("cli_deploy_url", { params: { url: deployResult.data.url } })));
2979
+ writeCliMessage(chalk.gray(cliM("cli_deploy_id", { params: { id: deployResult.data.deploymentId } })));
2801
2980
  if (deployResult.data.envVarsSet.length > 0) {
2802
- core.logger.info(chalk.gray(cliM("cli_deploy_env", { params: { names: deployResult.data.envVarsSet.join(", ") } })));
2981
+ writeCliMessage(chalk.gray(cliM("cli_deploy_env", { params: { names: deployResult.data.envVarsSet.join(", ") } })));
2803
2982
  }
2804
- core.logger.info("");
2983
+ writeCliMessage("");
2805
2984
  } catch (error) {
2806
2985
  spinner.fail(chalk.red(cliM("cli_deploy_command_failed")));
2807
- core.logger.error(error instanceof Error ? error.message : String(error));
2986
+ writeCliError(error instanceof Error ? error.message : String(error));
2808
2987
  throw error;
2809
2988
  } finally {
2810
2989
  if (closeDeploy) {
2811
2990
  try {
2812
2991
  await closeDeploy();
2813
2992
  } catch (error) {
2814
- core.logger.error("Failed to close deploy module", { error });
2993
+ writeCliError("Failed to close deploy module", error);
2815
2994
  process4.exitCode = 1;
2816
2995
  }
2817
2996
  }
@@ -2820,6 +2999,8 @@ async function deployCommand(options) {
2820
2999
  delete process4.env[key];
2821
3000
  }
2822
3001
  Object.assign(process4.env, originalEnv);
3002
+ core.logger.setLevel(previousLogLevel);
3003
+ restoreProjectLogger?.();
2823
3004
  }
2824
3005
  }
2825
3006
  var GENERATORS = {
@@ -2855,7 +3036,7 @@ async function generate(options) {
2855
3036
  try {
2856
3037
  const project = await detectProject(cwd);
2857
3038
  if (!project?.isHaiProject) {
2858
- core.logger.warn(chalk.yellow("\u8B66\u544A: \u5F53\u524D\u76EE\u5F55\u4E0D\u662F hai \u9879\u76EE"));
3039
+ writeCliMessage(chalk.yellow("\u8B66\u544A: \u5F53\u524D\u76EE\u5F55\u4E0D\u662F hai \u9879\u76EE"));
2859
3040
  }
2860
3041
  const resolvedOptions = await resolveGenerateOptions(options);
2861
3042
  const generator = GENERATORS[resolvedOptions.type];
@@ -2882,8 +3063,8 @@ async function generate(options) {
2882
3063
  while (!await fse.pathExists(ancestor))
2883
3064
  ancestor = path2.dirname(ancestor);
2884
3065
  const realAncestor = await fse.realpath(ancestor);
2885
- const relative = path2.relative(realCwd, realAncestor);
2886
- if (relative === ".." || relative.startsWith(`..${path2.sep}`) || path2.isAbsolute(relative))
3066
+ const relative2 = path2.relative(realCwd, realAncestor);
3067
+ if (relative2 === ".." || relative2.startsWith(`..${path2.sep}`) || path2.isAbsolute(relative2))
2887
3068
  throw new Error(cliM("cli_generateOutsideWorkspace"));
2888
3069
  }
2889
3070
  for (const file of files) {
@@ -2891,17 +3072,17 @@ async function generate(options) {
2891
3072
  await fse.writeFile(file.path, file.content, { encoding: "utf-8", flag: resolvedOptions.force ? "w" : "wx" });
2892
3073
  }
2893
3074
  spinner.succeed();
2894
- core.logger.info("", {});
2895
- core.logger.info(chalk.green("\u2714 \u751F\u6210\u5B8C\u6210\uFF01"));
2896
- core.logger.info("", {});
2897
- core.logger.info("\u521B\u5EFA\u7684\u6587\u4EF6:");
3075
+ writeCliMessage("");
3076
+ writeCliMessage(chalk.green("\u2714 \u751F\u6210\u5B8C\u6210\uFF01"));
3077
+ writeCliMessage("");
3078
+ writeCliMessage("\u521B\u5EFA\u7684\u6587\u4EF6:");
2898
3079
  files.forEach((file) => {
2899
- core.logger.info(chalk.cyan(` ${path2.relative(cwd, file.path)}`));
3080
+ writeCliMessage(chalk.cyan(` ${path2.relative(cwd, file.path)}`));
2900
3081
  });
2901
- core.logger.info("", {});
3082
+ writeCliMessage("");
2902
3083
  } catch (error) {
2903
3084
  spinner.fail();
2904
- core.logger.error(chalk.red("\u751F\u6210\u5931\u8D25:"), { error });
3085
+ writeCliError(chalk.red("\u751F\u6210\u5931\u8D25:"), { error });
2905
3086
  throw error;
2906
3087
  }
2907
3088
  }
@@ -3163,39 +3344,39 @@ async function initProject(options) {
3163
3344
  const { project, existing, missing } = result;
3164
3345
  spinner.succeed(`\u68C0\u6D4B\u5230\u9879\u76EE: ${chalk.cyan(project.name)}`);
3165
3346
  if (project.isHaiProject) {
3166
- const { generateProjectAiSupport, resolveSkillNamesForPackages } = await import('./cli-skill-templates-TSYEQTCY.js');
3347
+ const { generateProjectAiSupport, resolveSkillNamesForPackages } = await import('./cli-skill-templates-FC3QRKDO.js');
3167
3348
  const generatedFiles = await generateProjectAiSupport(cwd, resolveSkillNamesForPackages(project.haiPackages), {
3168
3349
  overwriteSkills: false,
3169
3350
  overwriteBridgeFiles: false
3170
3351
  });
3171
3352
  for (const generatedFile of generatedFiles) {
3172
- core.logger.info(chalk.gray(` + ${generatedFile}`));
3353
+ writeCliMessage(chalk.gray(` + ${generatedFile}`));
3173
3354
  }
3174
3355
  }
3175
- core.logger.info("");
3176
- core.logger.info(chalk.bold("\u9879\u76EE\u72B6\u6001:"));
3177
- core.logger.info(` \u540D\u79F0: ${chalk.cyan(project.name)}`);
3178
- core.logger.info(` \u7248\u672C: ${project.version}`);
3179
- core.logger.info(` \u5DF2\u5B89\u88C5\u6A21\u5757: ${project.haiPackages.length > 0 ? project.haiPackages.map((p) => chalk.cyan(p)).join(", ") : chalk.gray("\u65E0")}`);
3180
- core.logger.info("");
3356
+ writeCliMessage("");
3357
+ writeCliMessage(chalk.bold("\u9879\u76EE\u72B6\u6001:"));
3358
+ writeCliMessage(` \u540D\u79F0: ${chalk.cyan(project.name)}`);
3359
+ writeCliMessage(` \u7248\u672C: ${project.version}`);
3360
+ writeCliMessage(` \u5DF2\u5B89\u88C5\u6A21\u5757: ${project.haiPackages.length > 0 ? project.haiPackages.map((p) => chalk.cyan(p)).join(", ") : chalk.gray("\u65E0")}`);
3361
+ writeCliMessage("");
3181
3362
  if (existing.length > 0) {
3182
- core.logger.info(chalk.bold("\u5DF2\u6709\u914D\u7F6E:"));
3363
+ writeCliMessage(chalk.bold("\u5DF2\u6709\u914D\u7F6E:"));
3183
3364
  for (const key of existing) {
3184
- core.logger.info(chalk.green(` \u2714 config/_${key}.yml`));
3365
+ writeCliMessage(chalk.green(` \u2714 config/_${key}.yml`));
3185
3366
  }
3186
3367
  }
3187
3368
  if (missing.length === 0 && !options.force) {
3188
- core.logger.info("");
3189
- core.logger.info(chalk.green("\u2714 \u6240\u6709\u914D\u7F6E\u6587\u4EF6\u5DF2\u5C31\u7EEA\uFF01"));
3369
+ writeCliMessage("");
3370
+ writeCliMessage(chalk.green("\u2714 \u6240\u6709\u914D\u7F6E\u6587\u4EF6\u5DF2\u5C31\u7EEA\uFF01"));
3190
3371
  return;
3191
3372
  }
3192
3373
  if (missing.length > 0) {
3193
- core.logger.info("");
3194
- core.logger.info(chalk.bold("\u7F3A\u5931\u914D\u7F6E:"));
3374
+ writeCliMessage("");
3375
+ writeCliMessage(chalk.bold("\u7F3A\u5931\u914D\u7F6E:"));
3195
3376
  for (const item of missing) {
3196
- core.logger.info(chalk.yellow(` \u2718 config/_${item.configKey}.yml (${item.label})`));
3377
+ writeCliMessage(chalk.yellow(` \u2718 config/_${item.configKey}.yml (${item.label})`));
3197
3378
  }
3198
- core.logger.info("");
3379
+ writeCliMessage("");
3199
3380
  spinner.start("\u751F\u6210\u7F3A\u5931\u914D\u7F6E\u6587\u4EF6...");
3200
3381
  let generated = 0;
3201
3382
  for (const item of missing) {
@@ -3207,7 +3388,7 @@ async function initProject(options) {
3207
3388
  spinner.succeed(`\u5DF2\u751F\u6210 ${generated} \u4E2A\u914D\u7F6E\u6587\u4EF6`);
3208
3389
  }
3209
3390
  if (options.force && existing.length > 0) {
3210
- core.logger.info("");
3391
+ writeCliMessage("");
3211
3392
  spinner.start("\u5F3A\u5236\u91CD\u65B0\u751F\u6210\u6240\u6709\u914D\u7F6E\u6587\u4EF6...");
3212
3393
  let regenerated = 0;
3213
3394
  for (const pkg of project.haiPackages) {
@@ -3228,16 +3409,16 @@ async function initProject(options) {
3228
3409
  await writeFile(coreConfigPath, content);
3229
3410
  spinner.succeed();
3230
3411
  }
3231
- core.logger.info("");
3232
- core.logger.info(chalk.green("\u2714 \u9879\u76EE\u914D\u7F6E\u521D\u59CB\u5316\u5B8C\u6210\uFF01"));
3233
- core.logger.info("");
3234
- core.logger.info(chalk.gray("\u4E0B\u4E00\u6B65:"));
3235
- core.logger.info(chalk.gray(" 1. \u7F16\u8F91 config/ \u4E2D\u7684\u914D\u7F6E\u6587\u4EF6"));
3236
- core.logger.info(chalk.gray(" 2. \u8FD0\u884C pnpm dev \u542F\u52A8\u5F00\u53D1\u670D\u52A1"));
3237
- core.logger.info("");
3412
+ writeCliMessage("");
3413
+ writeCliMessage(chalk.green("\u2714 \u9879\u76EE\u914D\u7F6E\u521D\u59CB\u5316\u5B8C\u6210\uFF01"));
3414
+ writeCliMessage("");
3415
+ writeCliMessage(chalk.gray("\u4E0B\u4E00\u6B65:"));
3416
+ writeCliMessage(chalk.gray(" 1. \u7F16\u8F91 config/ \u4E2D\u7684\u914D\u7F6E\u6587\u4EF6"));
3417
+ writeCliMessage(chalk.gray(" 2. \u8FD0\u884C pnpm dev \u542F\u52A8\u5F00\u53D1\u670D\u52A1"));
3418
+ writeCliMessage("");
3238
3419
  } catch (error) {
3239
3420
  spinner.fail();
3240
- core.logger.error(chalk.red("\u521D\u59CB\u5316\u5931\u8D25:"), { error });
3421
+ writeCliError(chalk.red("\u521D\u59CB\u5316\u5931\u8D25:"), { error });
3241
3422
  throw error;
3242
3423
  }
3243
3424
  }
@@ -3245,9 +3426,9 @@ async function initProject(options) {
3245
3426
  // src/cli-main.ts
3246
3427
  var VERSION = getCliVersion();
3247
3428
  var cli = cac("hai");
3248
- cli.option("-v, --verbose", "\u663E\u793A\u8BE6\u7EC6\u8F93\u51FA");
3249
- cli.option("-C, --cwd <path>", "\u5DE5\u4F5C\u76EE\u5F55");
3250
- cli.command("create [name]", "\u521B\u5EFA\u65B0\u7684 hai \u9879\u76EE").option("--type <type>", "\u5E94\u7528\u7C7B\u578B (admin, website, h5, api, mobile-app, fullstack)").option("--frontends <targets>", "\u524D\u540E\u7AEF\u5206\u79BB\u5DE5\u7A0B\u524D\u7AEF\u76EE\u6807 (\u9017\u53F7\u5206\u9694: web,app,miniapp,desktop)").option("-t, --template <template>", "\u9879\u76EE\u6A21\u677F (minimal, default, full, custom)").option("-f, --features <features>", "\u529F\u80FD\u5217\u8868 (\u9017\u53F7\u5206\u9694: iam,db,cache,ai,storage,crypto)").option("--examples", "\u6DFB\u52A0\u793A\u4F8B\u4EE3\u7801").option("--no-examples", "\u4E0D\u6DFB\u52A0\u793A\u4F8B\u4EE3\u7801").option("--no-install", "\u4E0D\u5B89\u88C5\u4F9D\u8D56").option("-p, --package-manager <pm>", "\u5305\u7BA1\u7406\u5668 (pnpm, npm, yarn)").option("--no-git", "\u4E0D\u521D\u59CB\u5316 Git").option("--git-remote <url>", "Git \u8FDC\u7A0B\u4ED3\u5E93\u5730\u5740").option("-y, --yes", "\u4F7F\u7528\u9ED8\u8BA4\u914D\u7F6E\uFF0C\u8DF3\u8FC7\u4EA4\u4E92").action(async (name, options) => {
3429
+ cli.option("-v, --verbose", cliM("cli_help_verbose"));
3430
+ cli.option("-C, --cwd <path>", cliM("cli_help_cwd"));
3431
+ 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) => {
3251
3432
  try {
3252
3433
  const features = typeof options.features === "string" ? options.features.split(",").map((f) => f.trim()) : void 0;
3253
3434
  const frontends = typeof options.frontends === "string" ? options.frontends.split(",").map((f) => f.trim()).filter(Boolean) : void 0;
@@ -3257,6 +3438,7 @@ cli.command("create [name]", "\u521B\u5EFA\u65B0\u7684 hai \u9879\u76EE").option
3257
3438
  frontends,
3258
3439
  template: options.template,
3259
3440
  features,
3441
+ deploy: options.deploy,
3260
3442
  examples: options.examples,
3261
3443
  install: options.install,
3262
3444
  packageManager: options.packageManager,
@@ -3270,7 +3452,7 @@ cli.command("create [name]", "\u521B\u5EFA\u65B0\u7684 hai \u9879\u76EE").option
3270
3452
  process4.exitCode = 1;
3271
3453
  }
3272
3454
  });
3273
- cli.command("add [module]", "\u5411\u73B0\u6709\u9879\u76EE\u6DFB\u52A0\u6A21\u5757").option("--no-install", "\u4E0D\u5B89\u88C5\u4F9D\u8D56").action(async (module, options) => {
3455
+ cli.command("add [module]", cliM("cli_help_add")).option("--no-install", cliM("cli_help_noInstall")).action(async (module, options) => {
3274
3456
  try {
3275
3457
  await addModule({
3276
3458
  module,
@@ -3282,7 +3464,7 @@ cli.command("add [module]", "\u5411\u73B0\u6709\u9879\u76EE\u6DFB\u52A0\u6A21\u5
3282
3464
  process4.exit(1);
3283
3465
  }
3284
3466
  });
3285
- cli.command("init", "\u521D\u59CB\u5316/\u6821\u9A8C\u9879\u76EE\u914D\u7F6E").option("-f, --force", "\u5F3A\u5236\u91CD\u65B0\u751F\u6210\u914D\u7F6E").action(async (options) => {
3467
+ cli.command("init", cliM("cli_help_init")).option("-f, --force", cliM("cli_help_forceConfig")).action(async (options) => {
3286
3468
  try {
3287
3469
  await initProject({
3288
3470
  force: options.force,
@@ -3293,7 +3475,7 @@ cli.command("init", "\u521D\u59CB\u5316/\u6821\u9A8C\u9879\u76EE\u914D\u7F6E").o
3293
3475
  process4.exit(1);
3294
3476
  }
3295
3477
  });
3296
- cli.command("generate [type] [name]", "\u751F\u6210\u4EE3\u7801").alias("g").option("-o, --output <path>", "\u8F93\u51FA\u8DEF\u5F84").option("-f, --force", "\u8986\u76D6\u73B0\u6709\u6587\u4EF6").action(async (type, name, options) => {
3478
+ cli.command("generate [type] [name]", cliM("cli_help_generate")).alias("g").option("-o, --output <path>", cliM("cli_help_output")).option("-f, --force", cliM("cli_help_force")).action(async (type, name, options) => {
3297
3479
  try {
3298
3480
  await generate({
3299
3481
  type,
@@ -3307,7 +3489,7 @@ cli.command("generate [type] [name]", "\u751F\u6210\u4EE3\u7801").alias("g").opt
3307
3489
  process4.exit(1);
3308
3490
  }
3309
3491
  });
3310
- cli.command("build", "\u6784\u5EFA\u6E90\u7801\u3001Docker \u955C\u50CF\u6216 Tauri \u5B89\u88C5\u5305\uFF08\u4E0D\u533A\u5206\u90E8\u7F72\u73AF\u5883\uFF09").option("--target <target>", "\u4EA7\u7269\u7C7B\u578B\uFF1Acompile\uFF08\u9ED8\u8BA4\uFF09/ docker / desktop").option("--runtime <runtime>", "\u672C\u5730\u5BB9\u5668\u5F15\u64CE\uFF1Aauto\uFF08\u4F18\u5148 Podman\uFF09/ podman / docker").option("--network <network>", "\u5BB9\u5668\u6784\u5EFA\u7F51\u7EDC\uFF08\u9ED8\u8BA4\u5F15\u64CE\u8BBE\u7F6E\uFF09\uFF0C\u4F8B\u5982 rootless Podman \u7684 pasta").option("--platform <platform>", "Tauri \u76EE\u6807\u7CFB\u7EDF\uFF1Awindows / linux / macos\uFF08\u5728\u5BF9\u5E94\u7CFB\u7EDF\u6784\u5EFA\uFF09").option("--arch <arch>", "Tauri \u76EE\u6807\u67B6\u6784\uFF1Ax64 / arm64").option("--sqlite-prebuild-mirror <url>", "SQLite \u9884\u7F16\u8BD1\u4E8C\u8FDB\u5236\u955C\u50CF\u6839\u5730\u5740").option("--no-http-proxy", "Podman \u6784\u5EFA\u4E0D\u7EE7\u627F\u672C\u673A\u4EE3\u7406").action(async (options) => {
3492
+ 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) => {
3311
3493
  try {
3312
3494
  await buildCommand(options);
3313
3495
  } catch (error) {
@@ -3315,7 +3497,7 @@ cli.command("build", "\u6784\u5EFA\u6E90\u7801\u3001Docker \u955C\u50CF\u6216 Ta
3315
3497
  process4.exitCode = 1;
3316
3498
  }
3317
3499
  });
3318
- cli.command("deploy [appDir]", "\u90E8\u7F72\u5E94\u7528\u5230\u4E91\u5E73\u53F0").option("--env <environment>", "\u90E8\u7F72\u73AF\u5883\uFF08test / prod \u7B49\uFF0C\u5FC5\u987B\u5B58\u5728\u5BF9\u5E94\u73AF\u5883\u6587\u4EF6\uFF09").option("--env-dir <path>", "\u73AF\u5883\u6587\u4EF6\u6240\u5728\u76EE\u5F55\uFF08\u9ED8\u8BA4\u81EA\u52A8\u67E5\u627E\uFF09").option("-y, --yes", "\u6309\u5F53\u524D\u540E\u7AEF\u9ED8\u8BA4\u914D\u7F6E\u90E8\u7F72\uFF0C\u76EE\u6807\u73AF\u5883\u5DF2\u6709\u5B8C\u6574\u914D\u7F6E\u65F6\u4FDD\u6301\u4E0D\u53D8").option("--project-name <name>", "\u9879\u76EE\u540D\u79F0\uFF08\u9ED8\u8BA4\u670D\u52A1\u5305\u540D\uFF0C\u7F3A\u5931\u65F6\u4E3A\u76EE\u5F55\u540D\uFF1B\u9644\u52A0\u73AF\u5883\u540E\u7F00\uFF09").option("--skip-middleware", "\u914D\u7F6E\u5DF2\u5B8C\u5907\uFF0C\u8DF3\u8FC7\u5168\u90E8\u4E2D\u95F4\u4EF6\u5904\u7406\uFF0C\u76F4\u63A5\u90E8\u7F72").option("--artifact-dir <path>", "\u5DF2\u6709\u955C\u50CF\u6784\u5EFA\u4EA7\u7269\u76EE\u5F55\uFF08\u542B manifest.json\uFF09").option("--bundle-only", "\u53EA\u751F\u6210 Compose \u90E8\u7F72 tar\uFF0C\u624B\u5DE5\u642C\u8FD0\uFF1B\u4E0D\u8FDE\u63A5 SSH").option("--output <path>", "\u90E8\u7F72 tar \u8F93\u51FA\u8DEF\u5F84\uFF0C\u81EA\u52A8\u8865\u5E94\u7528\u7248\u672C\u540E\u7F00").action(async (appDir, options) => {
3500
+ cli.command("deploy [appDir]", cliM("cli_help_deploy")).option("--env <environment>", cliM("cli_help_environment")).option("--env-dir <path>", cliM("cli_help_envDir")).option("-y, --yes", cliM("cli_help_deployYes")).option("--project-name <name>", cliM("cli_help_projectName")).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) => {
3319
3501
  try {
3320
3502
  await deployCommand({
3321
3503
  appDir,
@@ -3325,25 +3507,31 @@ cli.command("deploy [appDir]", "\u90E8\u7F72\u5E94\u7528\u5230\u4E91\u5E73\u53F0
3325
3507
  projectName: options.projectName,
3326
3508
  skipMiddleware: options.skipMiddleware,
3327
3509
  artifactDir: options.artifactDir,
3510
+ services: options.services,
3511
+ build: options.build,
3512
+ network: options.network,
3513
+ sqlitePrebuildMirror: options.sqlitePrebuildMirror,
3514
+ httpProxy: options.httpProxy,
3515
+ runtime: options.runtime,
3328
3516
  bundleOnly: options.bundleOnly,
3329
3517
  output: options.output,
3330
3518
  verbose: options.verbose,
3331
3519
  cwd: options.cwd
3332
3520
  });
3333
3521
  } catch {
3334
- process4.exit(1);
3522
+ process4.exitCode = 1;
3335
3523
  }
3336
3524
  });
3337
- cli.command("g:page <name>", "\u751F\u6210\u9875\u9762").action(async (name, options) => {
3525
+ cli.command("g:page <name>", cliM("cli_help_generatePage")).action(async (name, options) => {
3338
3526
  await generate({ type: "page", name, cwd: options.cwd });
3339
3527
  });
3340
- cli.command("g:component <name>", "\u751F\u6210\u7EC4\u4EF6").action(async (name, options) => {
3528
+ cli.command("g:component <name>", cliM("cli_help_generateComponent")).action(async (name, options) => {
3341
3529
  await generate({ type: "component", name, cwd: options.cwd });
3342
3530
  });
3343
- cli.command("g:api <name>", "\u751F\u6210 API").action(async (name, options) => {
3531
+ cli.command("g:api <name>", cliM("cli_help_generateApi")).action(async (name, options) => {
3344
3532
  await generate({ type: "api", name, cwd: options.cwd });
3345
3533
  });
3346
- cli.command("g:model <name>", "\u751F\u6210\u6570\u636E\u6A21\u578B").action(async (name, options) => {
3534
+ cli.command("g:model <name>", cliM("cli_help_generateModel")).action(async (name, options) => {
3347
3535
  await generate({ type: "model", name, cwd: options.cwd });
3348
3536
  });
3349
3537
  cli.version(VERSION, "-V, --version");