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

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 (69) hide show
  1. package/README.en.md +13 -7
  2. package/README.ja.md +13 -7
  3. package/README.md +14 -8
  4. package/dist/{chunk-IPG3QNYK.js → chunk-FWN5B54L.js} +36 -4
  5. package/dist/chunk-FWN5B54L.js.map +1 -0
  6. package/dist/{chunk-47V5OT22.js → chunk-QYX6V7H2.js} +17 -43
  7. package/dist/chunk-QYX6V7H2.js.map +1 -0
  8. package/dist/cli-config-templates-3XSFIXBP.js +5 -0
  9. package/dist/{cli-config-templates-SJ2MBIR7.js.map → cli-config-templates-3XSFIXBP.js.map} +1 -1
  10. package/dist/{cli-skill-templates-A5VIR5J6.js → cli-skill-templates-TSYEQTCY.js} +3 -3
  11. package/dist/{cli-skill-templates-A5VIR5J6.js.map → cli-skill-templates-TSYEQTCY.js.map} +1 -1
  12. package/dist/index.js +273 -208
  13. package/dist/index.js.map +1 -1
  14. package/dist/{src-6PPBKQ6Q.js → src-RDQ2EIBT.js} +506 -488
  15. package/dist/src-RDQ2EIBT.js.map +1 -0
  16. package/package.json +8 -8
  17. package/templates/apps/api/.dockerignore.hbs +4 -5
  18. package/templates/apps/api/.gitignore.hbs +2 -1
  19. package/templates/apps/api/Dockerfile.hbs +2 -15
  20. package/templates/apps/api/apps/{{projectName}}-service/README.md.hbs +2 -2
  21. package/templates/apps/api/apps/{{projectName}}-service/package.json.hbs +0 -1
  22. package/templates/apps/api/hai.build.json.hbs +1 -0
  23. package/templates/apps/api/package.json.hbs +5 -8
  24. package/templates/apps/fullstack/.dockerignore.hbs +4 -5
  25. package/templates/apps/fullstack/.gitignore.hbs +2 -1
  26. package/templates/apps/fullstack/Dockerfile.hbs +3 -15
  27. package/templates/apps/fullstack/apps/{{projectName}}-app/README.md.hbs +0 -3
  28. package/templates/apps/fullstack/apps/{{projectName}}-app/package.json.hbs +0 -2
  29. package/templates/apps/fullstack/apps/{{projectName}}-app/vite.config.ts.hbs +2 -2
  30. package/templates/apps/fullstack/apps/{{projectName}}-desktop/README.md.hbs +3 -6
  31. package/templates/apps/fullstack/apps/{{projectName}}-desktop/package.json.hbs +2 -3
  32. package/templates/apps/fullstack/apps/{{projectName}}-desktop/src-tauri/src/lib.rs.hbs +73 -2
  33. package/templates/apps/fullstack/apps/{{projectName}}-desktop/src-tauri/tauri.conf.json.hbs +2 -2
  34. package/templates/apps/fullstack/apps/{{projectName}}-desktop/vite.config.ts.hbs +2 -2
  35. package/templates/apps/fullstack/apps/{{projectName}}-web/README.md.hbs +2 -6
  36. package/templates/apps/fullstack/apps/{{projectName}}-web/package.json.hbs +0 -2
  37. package/templates/apps/fullstack/apps/{{projectName}}-web/vite.config.ts.hbs +2 -2
  38. package/templates/apps/fullstack/e2e/fullstack.spec.ts.hbs +1 -0
  39. package/templates/apps/fullstack/hai.build.json.hbs +7 -0
  40. package/templates/apps/fullstack/package.json.hbs +5 -12
  41. package/templates/apps/fullstack/packages/{{projectName}}-serv/README.md.hbs +2 -2
  42. package/templates/apps/fullstack/packages/{{projectName}}-serv/package.json.hbs +0 -1
  43. package/templates/apps/fullstack/packages/{{projectName}}-shared/README.md.hbs +1 -1
  44. package/templates/apps/mobile-app/index.html.hbs +1 -0
  45. package/templates/apps/mobile-app/public/runtime-config.js +1 -0
  46. package/templates/apps/mobile-app/src/lib/api.ts.hbs +2 -1
  47. package/templates/apps/mobile-app/src/lib/app-config.ts.hbs +3 -2
  48. package/templates/base/.dockerignore.hbs +4 -5
  49. package/templates/base/.gitignore.hbs +2 -1
  50. package/templates/base/Dockerfile.hbs +5 -22
  51. package/templates/base/docker/nginx.conf.hbs +6 -0
  52. package/templates/base/hai.build.json.hbs +1 -0
  53. package/templates/base/package.json.hbs +13 -17
  54. package/templates/base/svelte.config.js.hbs +2 -0
  55. package/templates/base/vite.config.ts.hbs +6 -2
  56. package/templates/shared/.gitattributes +5 -0
  57. package/templates/shared/container-runtime-config.sh +30 -0
  58. package/templates/shared/run-e2e.mjs +65 -0
  59. package/templates/shared/vite-runtime-config.ts +54 -0
  60. package/templates/skills/hai-app-create/development.md +3 -1
  61. package/templates/skills/hai-app-create/reference.md +2 -2
  62. package/templates/skills/hai-build/SKILL.md +16 -1
  63. package/templates/skills/hai-deploy/SKILL.md +52 -260
  64. package/templates/skills/hai-deploy/middleware.md +8 -14
  65. package/templates/skills/hai-ui/reference.md +2 -0
  66. package/dist/chunk-47V5OT22.js.map +0 -1
  67. package/dist/chunk-IPG3QNYK.js.map +0 -1
  68. package/dist/cli-config-templates-SJ2MBIR7.js +0 -5
  69. package/dist/src-6PPBKQ6Q.js.map +0 -1
package/dist/index.js CHANGED
@@ -1,11 +1,11 @@
1
1
  #!/usr/bin/env node
2
- import { generateConfigFile, runtimeEnvironmentVariables, renderEnvironment, moduleEnvironmentVariables, parameterizeConfig } from './chunk-47V5OT22.js';
3
- import { cliM } from './chunk-IPG3QNYK.js';
2
+ import { generateConfigFile, runtimeEnvironmentVariables, renderEnvironment, moduleEnvironmentVariables, parameterizeConfig } from './chunk-QYX6V7H2.js';
3
+ import { cliM } from './chunk-FWN5B54L.js';
4
4
  import './chunk-2H7UOFLK.js';
5
- import process3 from 'process';
5
+ import process4 from 'process';
6
6
  import { cac } from 'cac';
7
7
  import chalk from 'chalk';
8
- import path2, { resolve, join } from 'path';
8
+ import path2, { resolve, join, dirname } from 'path';
9
9
  import { fileURLToPath, pathToFileURL } from 'url';
10
10
  import { wrapAnsi } from 'fast-wrap-ansi';
11
11
  import fse from 'fs-extra';
@@ -14,27 +14,27 @@ import { core } from '@h-ai/core';
14
14
  import ora from 'ora';
15
15
  import prompts from 'prompts';
16
16
  import { Buffer } from 'buffer';
17
- import { execSync, execFileSync } from 'child_process';
18
- import { randomBytes } from 'crypto';
17
+ import { execSync, execFileSync, spawn } from 'child_process';
18
+ import { randomBytes, createHash } from 'crypto';
19
19
  import { stringify, parseDocument } from 'yaml';
20
- import { existsSync, readFileSync, writeFileSync, mkdirSync } from 'fs';
20
+ import { readFileSync, mkdirSync, mkdtempSync, existsSync, createReadStream, writeFileSync, renameSync, rmSync } from 'fs';
21
21
  import { createRequire } from 'module';
22
22
 
23
23
  var CAMEL_CASE_SEPARATOR_REGEX = /[-_\s]+(.)?/g;
24
24
  var LEADING_CHAR_REGEX = /^./;
25
25
  function writeCliMessage(message) {
26
- process3.stdout.write(`${message}
26
+ process4.stdout.write(`${message}
27
27
  `);
28
28
  }
29
29
  function writeCliError(message, error) {
30
- process3.stderr.write(`${message}
30
+ process4.stderr.write(`${message}
31
31
  `);
32
32
  if (error !== void 0)
33
- process3.stderr.write(`${error instanceof Error ? error.message : String(error)}
33
+ process4.stderr.write(`${error instanceof Error ? error.message : String(error)}
34
34
  `);
35
35
  }
36
36
  function formatPromptDescription(description) {
37
- return wrapAnsi(description, Math.max(1, (process3.stdout.columns || 80) - 32), { hard: true });
37
+ return wrapAnsi(description, Math.max(1, (process4.stdout.columns || 80) - 32), { hard: true });
38
38
  }
39
39
  function toCamelCase(str) {
40
40
  return str.replace(CAMEL_CASE_SEPARATOR_REGEX, (_, c) => c ? c.toUpperCase() : "").replace(LEADING_CHAR_REGEX, (c) => c.toLowerCase());
@@ -208,6 +208,7 @@ var HAI_CATALOG_PACKAGE_NAMES = [
208
208
  "@h-ai/audit",
209
209
  "@h-ai/cache",
210
210
  "@h-ai/capacitor",
211
+ "@h-ai/cli",
211
212
  "@h-ai/core",
212
213
  "@h-ai/crypto",
213
214
  "@h-ai/datapipe",
@@ -388,6 +389,7 @@ function buildFullstackPackageSpecifiers(useCatalogProtocol) {
388
389
  function buildApiWorkspaceCatalogPackages(featureMap) {
389
390
  const packageNames = [
390
391
  ...API_WORKSPACE_EXTERNAL_CATALOG_PACKAGE_NAMES,
392
+ "@h-ai/cli",
391
393
  "@h-ai/api-client",
392
394
  "@h-ai/api-contract",
393
395
  "@h-ai/core",
@@ -422,7 +424,7 @@ function buildApiWorkspaceCatalogPackages(featureMap) {
422
424
  return pickCatalogPackages(packageNames, FULLSTACK_CATALOG_PACKAGES);
423
425
  }
424
426
  function buildAppHaiCatalogPackages(options) {
425
- const packageNames = [];
427
+ const packageNames = ["@h-ai/cli"];
426
428
  if (options.isSvelteOnlyApp) {
427
429
  packageNames.push("@h-ai/api-client", "@h-ai/api-contract", "@h-ai/capacitor", "@h-ai/ui");
428
430
  return pickCatalogPackages(packageNames, HAI_CATALOG_PACKAGES);
@@ -469,6 +471,7 @@ function buildAppHaiCatalogPackages(options) {
469
471
  function buildFullstackCatalogPackages(frontends, deploy = false) {
470
472
  const packageNames = [
471
473
  ...FULLSTACK_ROOT_CATALOG_PACKAGE_NAMES,
474
+ "@h-ai/cli",
472
475
  ...FULLSTACK_CONTRACT_CATALOG_PACKAGE_NAMES,
473
476
  ...FULLSTACK_SERV_CATALOG_PACKAGE_NAMES,
474
477
  ...FULLSTACK_SHARED_CATALOG_PACKAGE_NAMES
@@ -772,6 +775,12 @@ async function generateFromTemplates(projectPath, context) {
772
775
  }
773
776
  }
774
777
  await renderDynamicFiles(root, projectPath, context);
778
+ await fse.copy(path2.join(root, "shared", "run-e2e.mjs"), path2.join(projectPath, "scripts", "run-e2e.mjs"));
779
+ await fse.copy(path2.join(root, "shared", ".gitattributes"), path2.join(projectPath, ".gitattributes"));
780
+ if (context.isSvelteOnlyApp) {
781
+ 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"));
783
+ }
775
784
  if (context.isSvelteOnlyApp || context.appType === "fullstack") {
776
785
  const navigationDir = context.appType === "fullstack" ? path2.join(projectPath, "packages", `${context.projectName}-shared`, "src", "lib", "navigation") : path2.join(projectPath, "src", "lib", "navigation");
777
786
  await fse.copy(path2.join(root, "shared", "hash-router.svelte.ts"), path2.join(navigationDir, "hash-router.svelte.ts"));
@@ -1132,7 +1141,7 @@ async function createProject(options) {
1132
1141
  });
1133
1142
  await generateFromTemplates(projectPath, context);
1134
1143
  {
1135
- const { generateConfigFile: generateConfigFile2, generateEnvExample } = await import('./cli-config-templates-SJ2MBIR7.js');
1144
+ const { generateConfigFile: generateConfigFile2, generateEnvExample } = await import('./cli-config-templates-3XSFIXBP.js');
1136
1145
  const configs = resolvedOptions.moduleConfigs;
1137
1146
  const isApiWorkspace = resolvedOptions.appType === "api";
1138
1147
  const isFullstack = resolvedOptions.appType === "fullstack";
@@ -1206,7 +1215,7 @@ HAI_ADMIN_DEFAULT_PASSWORD=${password}
1206
1215
  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");
1207
1216
  }
1208
1217
  }
1209
- const { generateSkillFiles } = await import('./cli-skill-templates-A5VIR5J6.js');
1218
+ const { generateSkillFiles } = await import('./cli-skill-templates-TSYEQTCY.js');
1210
1219
  await generateSkillFiles(
1211
1220
  projectPath,
1212
1221
  resolvedOptions.features,
@@ -1253,7 +1262,7 @@ async function resolveOptions(options) {
1253
1262
  writeCliMessage("");
1254
1263
  const onCancel = () => {
1255
1264
  writeCliMessage(chalk.red("\n\u5DF2\u53D6\u6D88"));
1256
- process3.exit(1);
1265
+ process4.exit(1);
1257
1266
  };
1258
1267
  let projectName = options.name;
1259
1268
  if (!projectName && useDefaults) {
@@ -1509,7 +1518,7 @@ async function promptModuleConfigs(features, projectName) {
1509
1518
  const configs = {};
1510
1519
  const onCancel = () => {
1511
1520
  writeCliMessage(chalk.red("\n\u5DF2\u53D6\u6D88"));
1512
- process3.exit(1);
1521
+ process4.exit(1);
1513
1522
  };
1514
1523
  writeCliMessage("");
1515
1524
  writeCliMessage(chalk.bold(" \u2699 \u6A21\u5757\u914D\u7F6E"));
@@ -1749,7 +1758,7 @@ function generateEnvironmentReadme() {
1749
1758
  | \u5F00\u53D1 / \u6D4B\u8BD5\u6A21\u5F0F / \u751F\u4EA7\u6A21\u5F0F\u9884\u89C8 | \`pnpm dev\` / \`dev:test\` / \`dev:prod\` |
1750
1759
  | \u7C7B\u578B\u68C0\u67E5\uFF08check \u4E3A\u522B\u540D\uFF09 | \`pnpm typecheck\` |
1751
1760
  | \u68C0\u67E5 / \u81EA\u52A8\u4FEE\u590D\u683C\u5F0F | \`pnpm lint\` / \`lint:fix\` |
1752
- | \u6784\u5EFA / \u751F\u4EA7\u6A21\u5F0F\u6784\u5EFA | \`pnpm build\` / \`build:prod\` |
1761
+ | \u6E90\u7801\u6784\u5EFA / \u955C\u50CF\u6784\u5EFA | \`pnpm build\` / \`pnpm build --target docker\` |
1753
1762
  | \u5355\u5143\u6D4B\u8BD5 / \u76D1\u542C / \u6D4F\u89C8\u5668\u9A8C\u6536 | \`pnpm test\` / \`test:watch\` / \`test:e2e\` |
1754
1763
  | Node \u670D\u52A1\u6309\u73AF\u5883\u542F\u52A8\uFF08\u6784\u5EFA\u540E\uFF09 | \`pnpm start\` / \`start:test\` / \`start:prod\` |
1755
1764
 
@@ -1757,23 +1766,6 @@ Vite \u5BA2\u6237\u7AEF\u4F7F\u7528 \`preview\` \u9884\u89C8\u9759\u6001\u4EA7\u
1757
1766
  }
1758
1767
  function generateReadme(name, appTypeLabel, pm, appType) {
1759
1768
  const install = getInstallCommand(pm);
1760
- const isStaticImage = appType === "mobile-app";
1761
- const containerPort = isStaticImage ? 8080 : 3e3;
1762
- const runtimeOptions = isStaticImage ? "" : ` --env-file .env --mount type=bind,source=/absolute/path/to/config,target=/app/config,readonly --mount type=volume,source=${name}-data,target=/app/data`;
1763
- const embeddedDataBuild = isStaticImage ? "" : `
1764
- # \u5E26 data/ \u521D\u59CB\u6570\u636E\u6784\u5EFA\uFF08\u4E8C\u9009\u4E00\uFF09
1765
- ${getRunCommand(pm, "docker:build:data")}
1766
- ${getRunCommand(pm, "podman:build:data")}`;
1767
- const runtimeNotes = isStaticImage ? "- \u8BE5\u6A21\u677F\u751F\u6210\u9759\u6001 Web \u955C\u50CF\uFF0C\u4E0D\u5305\u542B\u53EF\u5199\u6570\u636E\u5E93\u76EE\u5F55\u3002" : `\u4EE5\u4E0A\u4E09\u4E2A\u8FD0\u884C\u65F6\u53C2\u6570\u5747\u53EF\u6309\u9700\u5220\u9664\uFF1A
1768
-
1769
- | \u53C2\u6570 | \u7528\u9014 | \u53EF\u9009\u6027 |
1770
- | --- | --- | --- |
1771
- | \`--env-file .env\` | \u6279\u91CF\u4F20\u5165\u73AF\u5883\u53D8\u91CF\uFF1B\u4E5F\u53EF\u6539\u7528\u591A\u4E2A \`-e NAME=value\`\u3002 | \u53EF\u9009\uFF1B\u5220\u9664\u540E\u4F7F\u7528\u955C\u50CF\u548C YAML \u9ED8\u8BA4\u503C\u3002 |
1772
- | \`--mount type=bind,...target=/app/config,readonly\` | \u7528\u5BBF\u4E3B\u673A\u914D\u7F6E\u76EE\u5F55\u8986\u76D6\u955C\u50CF\u5185 \`/app/config\`\uFF1B\u5FC5\u987B\u628A \`source\` \u6539\u6210\u771F\u5B9E\u7EDD\u5BF9\u8DEF\u5F84\uFF0C\`readonly\` \u9632\u6B62\u5BB9\u5668\u4FEE\u6539\u914D\u7F6E\u3002 | \u53EF\u9009\uFF1B\u5220\u9664\u540E\u4F7F\u7528\u955C\u50CF\u81EA\u5E26\u914D\u7F6E\u3002 |
1773
- | \`--mount type=volume,...target=/app/data\` | \u521B\u5EFA\u6216\u590D\u7528 named volume\uFF0C\u6301\u4E45\u5316 SQLite \u548C\u672C\u5730\u5B58\u50A8\u3002 | \u53EF\u9009\u4F46\u751F\u4EA7\u73AF\u5883\u5EFA\u8BAE\u4FDD\u7559\uFF1B\u5220\u9664\u540E\u6570\u636E\u53EA\u5199\u5165\u5BB9\u5668\u5C42\uFF0C\u4F7F\u7528 \`--rm\` \u5220\u9664\u5BB9\u5668\u65F6\u4F1A\u4E22\u5931\u3002 |
1774
-
1775
- - \u5E26\u6570\u636E\u6784\u5EFA\u4F1A\u628A\u5F53\u524D \`data/\`\uFF08\u4F8B\u5982\u9884\u7F6E SQLite \u6587\u4EF6\uFF09\u56FA\u5316\u4E3A\u955C\u50CF\u521D\u59CB\u5FEB\u7167\uFF1B\u8FD0\u884C\u540E\u4EA7\u751F\u7684\u65B0\u6570\u636E\u4ECD\u5E94\u5199\u5165 \`/app/data\` volume\u3002`;
1776
- const testPath = isStaticImage ? "/health" : "/";
1777
1769
  return `# ${name}
1778
1770
 
1779
1771
  \u57FA\u4E8E hai Agent Framework \u6784\u5EFA\u7684${appTypeLabel}\u5E94\u7528\u3002
@@ -1799,31 +1791,20 @@ ${getRunCommand(pm, "i18n:compile")}
1799
1791
  ${getRunCommand(pm, "preview")}
1800
1792
  \`\`\`
1801
1793
 
1802
- ## \u5BB9\u5668\u8FD0\u884C
1803
-
1804
- ### \u6784\u5EFA\u955C\u50CF
1805
-
1806
- \`\`\`bash
1807
- # \u666E\u901A\u6784\u5EFA\uFF08\u4E8C\u9009\u4E00\uFF09
1808
- ${getRunCommand(pm, "docker:build")}
1809
- ${getRunCommand(pm, "podman:build")}${embeddedDataBuild}
1810
- \`\`\`
1811
-
1812
- ### \u8FD0\u884C\u955C\u50CF
1794
+ ## \u6784\u5EFA\u4E0E\u90E8\u7F72
1813
1795
 
1814
1796
  \`\`\`bash
1815
- # Docker\uFF1B\u4F7F\u7528 Podman \u65F6\u5C06 docker \u66FF\u6362\u4E3A podman
1816
- docker run --rm -d --name ${name} -p ${containerPort}:${containerPort}${runtimeOptions} ${name}:latest
1817
- \`\`\`
1818
-
1819
- ### \u6D4B\u8BD5\u955C\u50CF
1820
-
1821
- \`\`\`bash
1822
- curl --fail http://127.0.0.1:${containerPort}${testPath}
1823
- docker stop ${name}
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
1824
1805
  \`\`\`
1825
1806
 
1826
- ${runtimeNotes}
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
1827
1808
 
1828
1809
  ${generateAiDevelopmentReadme()}## \u6587\u6863
1829
1810
 
@@ -1860,50 +1841,20 @@ ${pm} test
1860
1841
  ${pm} test:e2e
1861
1842
  \`\`\`
1862
1843
 
1863
- ## \u5BB9\u5668\u8FD0\u884C
1864
-
1865
- ### \u6784\u5EFA\u955C\u50CF
1866
-
1867
- \`\`\`bash
1868
- # \u666E\u901A\u6784\u5EFA\uFF08\u4E8C\u9009\u4E00\uFF09
1869
- ${pm} docker:build
1870
- ${pm} podman:build
1871
-
1872
- # \u5C06 apps/${name}-service/data \u4F5C\u4E3A\u955C\u50CF\u521D\u59CB\u6570\u636E\u6784\u5EFA\uFF08\u4E8C\u9009\u4E00\uFF09
1873
- ${pm} docker:build:data
1874
- ${pm} podman:build:data
1875
- \`\`\`
1876
-
1877
- ### \u8FD0\u884C\u955C\u50CF
1844
+ ## \u6784\u5EFA\u4E0E\u90E8\u7F72
1878
1845
 
1879
1846
  \`\`\`bash
1880
- # \u4F7F\u7528 Podman \u65F6\u5C06 docker \u66FF\u6362\u4E3A podman
1881
- docker run --rm -d --name ${name} -p 3000:3000 --env-file .env \\
1882
- --mount type=bind,source=/absolute/path/to/config,target=/app/config,readonly \\
1883
- --mount type=volume,source=${name}-data,target=/app/data \\
1884
- ${name}:latest
1885
- \`\`\`
1886
-
1887
- ### \u6D4B\u8BD5\u955C\u50CF
1888
-
1889
- \`\`\`bash
1890
- curl --fail http://127.0.0.1:3000/health
1891
- curl --fail http://127.0.0.1:3000/ready
1892
- curl --fail --request POST http://127.0.0.1:3000/api/v1/app/info \\
1893
- --header "Content-Type: application/json" --data '{}'
1894
- docker stop ${name}
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
1895
1855
  \`\`\`
1896
1856
 
1897
- - \u914D\u7F6E\u9ED8\u8BA4\u6765\u81EA \`apps/${name}-service/config\`\uFF1B\u8FD0\u884C\u65F6\u53EF\u53EA\u8BFB\u6302\u8F7D\u5230 \`/app/config\` \u8986\u76D6\uFF0C\u5E76\u7528 YAML \u7684 \`\${ENV_NAME:default}\` \u5360\u4F4D\u7B26\u63A5\u6536\u73AF\u5883\u53D8\u91CF\u3002
1898
- - \u5E26\u6570\u636E\u6784\u5EFA\u4F1A\u628A \`apps/${name}-service/data\` \u56FA\u5316\u4E3A\u955C\u50CF\u521D\u59CB\u6570\u636E\uFF1B\u751F\u4EA7\u5199\u5165\u4ECD\u5EFA\u8BAE\u4F7F\u7528 \`/app/data\` volume\u3002
1899
-
1900
- \u4EE5\u4E0A\u4E09\u4E2A\u8FD0\u884C\u65F6\u53C2\u6570\u5747\u53EF\u6309\u9700\u5220\u9664\uFF1A
1901
-
1902
- | \u53C2\u6570 | \u7528\u9014 | \u53EF\u9009\u6027 |
1903
- | --- | --- | --- |
1904
- | \`--env-file .env\` | \u6279\u91CF\u4F20\u5165\u73AF\u5883\u53D8\u91CF\uFF1B\u4E5F\u53EF\u6539\u7528\u591A\u4E2A \`-e NAME=value\`\u3002 | \u53EF\u9009\uFF1B\u5220\u9664\u540E\u4F7F\u7528\u955C\u50CF\u548C YAML \u9ED8\u8BA4\u503C\u3002 |
1905
- | \`--mount type=bind,...target=/app/config,readonly\` | \u7528\u5BBF\u4E3B\u673A\u914D\u7F6E\u76EE\u5F55\u8986\u76D6\u955C\u50CF\u5185 \`/app/config\`\uFF1B\u5FC5\u987B\u628A \`source\` \u6539\u6210\u771F\u5B9E\u7EDD\u5BF9\u8DEF\u5F84\uFF0C\`readonly\` \u9632\u6B62\u5BB9\u5668\u4FEE\u6539\u914D\u7F6E\u3002 | \u53EF\u9009\uFF1B\u5220\u9664\u540E\u4F7F\u7528\u955C\u50CF\u81EA\u5E26\u914D\u7F6E\u3002 |
1906
- | \`--mount type=volume,...target=/app/data\` | \u521B\u5EFA\u6216\u590D\u7528 named volume\uFF0C\u6301\u4E45\u5316 SQLite \u548C\u672C\u5730\u5B58\u50A8\u3002 | \u53EF\u9009\u4F46\u751F\u4EA7\u73AF\u5883\u5EFA\u8BAE\u4FDD\u7559\uFF1B\u5220\u9664\u540E\u6570\u636E\u53EA\u5199\u5165\u5BB9\u5668\u5C42\uFF0C\u4F7F\u7528 \`--rm\` \u5220\u9664\u5BB9\u5668\u65F6\u4F1A\u4E22\u5931\u3002 |
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
1907
1858
 
1908
1859
  ## \u6269\u5C55\u5EFA\u8BAE
1909
1860
 
@@ -1921,20 +1872,6 @@ ${generateAiDevelopmentReadme()}## \u6587\u6863
1921
1872
  }
1922
1873
  function generateFullstackReadme(name, pm, frontends) {
1923
1874
  const enabledFrontends = frontends.map((target) => FRONTEND_TARGETS[target].name).join(" / ");
1924
- const webBuildCommands = frontends.includes("web") ? `
1925
- # Web \u9759\u6001\u955C\u50CF\uFF08\u4E8C\u9009\u4E00\uFF09
1926
- ${pm} docker:build:web
1927
- ${pm} podman:build:web` : "";
1928
- const webRunCommand = frontends.includes("web") ? `
1929
-
1930
- # Web\uFF1BPUBLIC_API_BASE \u5728\u5BB9\u5668\u542F\u52A8\u65F6\u6CE8\u5165\uFF0C\u4E0D\u9700\u8981\u91CD\u65B0\u6784\u5EFA\u955C\u50CF
1931
- docker run --rm -d --name ${name}-web -p 8080:8080 \\
1932
- -e PUBLIC_API_BASE=https://api.example.com \\
1933
- ${name}-web:latest` : "";
1934
- const webTestCommands = frontends.includes("web") ? `curl --fail http://127.0.0.1:8080/health
1935
- curl --fail http://127.0.0.1:8080/runtime-config.js
1936
- ` : "";
1937
- const containerNames = frontends.includes("web") ? `${name} ${name}-web` : name;
1938
1875
  return `# ${name}
1939
1876
 
1940
1877
  \u57FA\u4E8E hai Agent Framework \u6784\u5EFA\u7684\u524D\u540E\u7AEF\u5206\u79BB\u591A\u5305\u5DE5\u7A0B\u3002
@@ -1981,77 +1918,34 @@ CLI \u5DF2\u5728\u6839\u76EE\u5F55\u751F\u6210 \`.env.local\`\uFF0C\u5176\u4E2D
1981
1918
 
1982
1919
  ${generateEnvironmentReadme()}
1983
1920
 
1984
- ## \u5F00\u53D1\u3001\u7F16\u8BD1\u4E0E\u6253\u5305
1921
+ ## \u5F00\u53D1\u4E0E\u6784\u5EFA
1985
1922
 
1986
1923
  \`\`\`bash
1987
1924
  ${pm} install
1988
1925
  ${pm} dev
1989
-
1990
- # \u4E00\u952E\u7F16\u8BD1\uFF1A\u7C7B\u578B\u68C0\u67E5 + \u5168\u5DE5\u4F5C\u533A\u6784\u5EFA
1991
- ${pm} compile
1992
-
1993
- # \u4E00\u952E\u6253\u5305\uFF1AWeb build\u3001App Capacitor sync\u3001Desktop Tauri build\uFF08\u6309\u542F\u7528\u524D\u7AEF\u6267\u884C\uFF09
1994
- ${pm} package
1995
-
1996
- # \u90E8\u7F72\u5165\u53E3\uFF1A\u9ED8\u8BA4\u6267\u884C package\uFF0C\u53EF\u66FF\u6362\u4E3A\u56E2\u961F\u81EA\u5DF1\u7684 CI/CD \u53D1\u5E03\u811A\u672C
1997
- ${pm} deploy
1998
- \`\`\`
1999
-
2000
- ## \u5BB9\u5668\u8FD0\u884C
2001
-
2002
- \u9ED8\u8BA4\u955C\u50CF\u76F4\u63A5\u8FD0\u884C API Service\uFF0C\u5916\u90E8\u7AEF\u53E3\u4E3A 3000\u3002
2003
-
2004
- ### \u6784\u5EFA\u955C\u50CF
2005
-
2006
- \`\`\`bash
2007
- # Service \u666E\u901A\u6784\u5EFA\uFF08\u4E8C\u9009\u4E00\uFF09
2008
- ${pm} docker:build
2009
- ${pm} podman:build
2010
-
2011
- # \u5C06 packages/${name}-serv/data \u4F5C\u4E3A\u955C\u50CF\u521D\u59CB\u6570\u636E\u6784\u5EFA\uFF08\u4E8C\u9009\u4E00\uFF09
2012
- ${pm} docker:build:data
2013
- ${pm} podman:build:data
2014
- ${webBuildCommands}
1926
+ ${pm} typecheck
1927
+ ${pm} build
1928
+ ${pm} test
1929
+ ${pm} test:e2e
1930
+ ${frontends.includes("desktop") ? `${pm} build --target desktop --platform windows # Windows \u4E3B\u673A\uFF1BLinux/macOS \u5728\u5BF9\u5E94\u4E3B\u673A\u6784\u5EFA` : ""}
2015
1931
  \`\`\`
2016
1932
 
2017
- ### \u8FD0\u884C\u955C\u50CF
2018
-
2019
- \`\`\`bash
2020
- # Service\uFF1B\u4F7F\u7528 Podman \u65F6\u5C06 docker \u66FF\u6362\u4E3A podman
2021
- docker run --rm -d --name ${name} -p 3000:3000 --env-file .env --env-file .env.prod --env-file .env.local \\
2022
- --mount type=bind,source=/absolute/path/to/config,target=/app/config,readonly \\
2023
- --mount type=volume,source=${name}-data,target=/app/data \\
2024
- ${name}:latest
2025
- ${webRunCommand}
2026
- \`\`\`
1933
+ 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
2027
1934
 
2028
- ### \u6D4B\u8BD5\u955C\u50CF
1935
+ ## \u6784\u5EFA\u4E0E\u90E8\u7F72
2029
1936
 
2030
1937
  \`\`\`bash
2031
- curl --fail http://127.0.0.1:3000/health
2032
- curl --fail http://127.0.0.1:3000/ready
2033
- ${webTestCommands}docker stop ${containerNames}
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
2034
1946
  \`\`\`
2035
1947
 
2036
- - \u4E0A\u4F8B\u6309\u516C\u5171\u57FA\u7840\u3001\u751F\u4EA7\u73AF\u5883\u3001\u672C\u673A\u79C1\u6709\u6587\u4EF6\u7684\u987A\u5E8F\u6CE8\u5165\u53D8\u91CF\uFF1B\u8FD0\u884C\u524D\u5728\u76EE\u6807\u673A\u5668\u521B\u5EFA \`.env.local\` \u5E76\u8BBE\u7F6E\u81EA\u5DF1\u7684\u7BA1\u7406\u5458\u5BC6\u7801\u3002\u90E8\u7F72\u5E73\u53F0\u4E5F\u53EF\u76F4\u63A5\u6CE8\u5165\u540C\u540D\u53D8\u91CF\u3002
2037
- - \u914D\u7F6E\u9ED8\u8BA4\u6765\u81EA \`packages/${name}-serv/config\`\uFF0C\u4E5F\u53EF\u6302\u8F7D\u5230 \`/app/config\`\uFF1BYAML \u4E2D\u7684 \`\${ENV_NAME:default}\` \u53EF\u7531 \`--env-file\` \u6216 \`-e\` \u6CE8\u5165\u3002
2038
- - \u5E26\u6570\u636E\u6784\u5EFA\u4F1A\u628A \`packages/${name}-serv/data\` \u56FA\u5316\u4E3A\u955C\u50CF\u521D\u59CB\u6570\u636E\uFF1B\u6301\u4E45\u5316\u5199\u5165\u4ECD\u5E94\u4F7F\u7528 volume\u3002
2039
- - Service \u7531 \`@h-ai/serv\` \u76F4\u63A5\u5BF9\u5916\u76D1\u542C\uFF0C\u4E0D\u989D\u5916\u542F\u52A8 Web \u8F6C\u53D1\u5C42\u3002
2040
- ${frontends.includes("web") ? `- Web \u4F7F\u7528\u72EC\u7ACB\u9759\u6001\u955C\u50CF\u5E76\u652F\u6301\u4E00\u6B21\u6784\u5EFA\u3001\u591A\u73AF\u5883\u90E8\u7F72\uFF1B\u6784\u5EFA\u53EA\u9700\u8FD0\u884C \`${pm} docker:build:web\`\uFF0C\u542F\u52A8\u5BB9\u5668\u65F6\u901A\u8FC7 \`-e PUBLIC_API_BASE=https://api.example.com\` \u6307\u5411 Service \u5BF9\u5916\u5730\u5740\u3002\u672A\u8BBE\u7F6E\u65F6\u9ED8\u8BA4\u4F7F\u7528 Web \u9875\u9762\u81EA\u8EAB origin\u3002` : ""}
2041
-
2042
- \u4EE5\u4E0A\u4E09\u4E2A Service \u8FD0\u884C\u65F6\u53C2\u6570\u5747\u53EF\u6309\u9700\u5220\u9664\uFF1A
2043
-
2044
- | \u53C2\u6570 | \u7528\u9014 | \u53EF\u9009\u6027 |
2045
- | --- | --- | --- |
2046
- | \`--env-file .env\` | \u6279\u91CF\u4F20\u5165\u73AF\u5883\u53D8\u91CF\uFF1B\u4E5F\u53EF\u6539\u7528\u591A\u4E2A \`-e NAME=value\`\u3002 | \u53EF\u9009\uFF1B\u5220\u9664\u540E\u4F7F\u7528\u955C\u50CF\u548C YAML \u9ED8\u8BA4\u503C\u3002 |
2047
- | \`--mount type=bind,...target=/app/config,readonly\` | \u7528\u5BBF\u4E3B\u673A\u914D\u7F6E\u76EE\u5F55\u8986\u76D6\u955C\u50CF\u5185 \`/app/config\`\uFF1B\u5FC5\u987B\u628A \`source\` \u6539\u6210\u771F\u5B9E\u7EDD\u5BF9\u8DEF\u5F84\uFF0C\`readonly\` \u9632\u6B62\u5BB9\u5668\u4FEE\u6539\u914D\u7F6E\u3002 | \u53EF\u9009\uFF1B\u5220\u9664\u540E\u4F7F\u7528\u955C\u50CF\u81EA\u5E26\u914D\u7F6E\u3002 |
2048
- | \`--mount type=volume,...target=/app/data\` | \u521B\u5EFA\u6216\u590D\u7528 named volume\uFF0C\u6301\u4E45\u5316 SQLite \u548C\u672C\u5730\u5B58\u50A8\u3002 | \u53EF\u9009\u4F46\u751F\u4EA7\u73AF\u5883\u5EFA\u8BAE\u4FDD\u7559\uFF1B\u5220\u9664\u540E\u6570\u636E\u53EA\u5199\u5165\u5BB9\u5668\u5C42\uFF0C\u4F7F\u7528 \`--rm\` \u5220\u9664\u5BB9\u5668\u65F6\u4F1A\u4E22\u5931\u3002 |
2049
-
2050
- \u524D\u7AEF\u5355\u72EC\u4EA4\u4ED8\u547D\u4EE4\uFF1A
2051
-
2052
- - Web\uFF1A\`${pm} --filter ${name}-web build\` / \`${pm} --filter ${name}-web package\`
2053
- - App\uFF1A\`${pm} --filter ${name}-app package\`\uFF0CAndroid/iOS \u53EF\u7528 \`cap:build:android:debug\` / \`cap:build:android:release\` / \`cap:build:ios\`
2054
- - Desktop\uFF1A\`${pm} --filter ${name}-desktop tauri:dev\` / \`${pm} --filter ${name}-desktop package\`
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
2055
1949
 
2056
1950
  ## i18n \u4E0E shared \u534F\u540C
2057
1951
 
@@ -2195,7 +2089,7 @@ async function addModule(options) {
2195
2089
  core.logger.info(`\u53EF\u7528\u6A21\u5757: ${Object.keys(MODULE_MAP).join(", ")}`);
2196
2090
  return;
2197
2091
  }
2198
- const { generateProjectAiSupport, resolveSkillNamesForPackages } = await import('./cli-skill-templates-A5VIR5J6.js');
2092
+ const { generateProjectAiSupport, resolveSkillNamesForPackages } = await import('./cli-skill-templates-TSYEQTCY.js');
2199
2093
  if (moduleDef.packages.every((p) => project.haiPackages.includes(p))) {
2200
2094
  const generatedFiles2 = await generateProjectAiSupport(
2201
2095
  cwd,
@@ -2301,6 +2195,155 @@ async function addModule(options) {
2301
2195
  throw error;
2302
2196
  }
2303
2197
  }
2198
+ async function run(command, args, cwd, quiet = false) {
2199
+ await new Promise((resolve3, reject) => {
2200
+ const env = Object.fromEntries(Object.entries(process4.env).filter(([key]) => !/^(?:HAI_|PUBLIC_|VITE_)/.test(key)));
2201
+ env.NODE_ENV = "production";
2202
+ if (env.CI === "1" || env.CI === "0")
2203
+ env.CI = env.CI === "1" ? "true" : "false";
2204
+ const child = spawn(command, args, { cwd, env, windowsHide: true, stdio: quiet ? "ignore" : "inherit", shell: false });
2205
+ child.once("error", reject);
2206
+ child.once("exit", (code) => code === 0 ? resolve3() : reject(new Error(cliM("cli_build_failed", { params: { command, code: String(code) } }))));
2207
+ });
2208
+ }
2209
+ async function pnpm(args, cwd) {
2210
+ const entry = process4.env.npm_execpath;
2211
+ if (entry && /(?:^|[/\\])pnpm(?:-native)?(?:\.exe|\.[cm]?js)?$/i.test(entry))
2212
+ return /\.[cm]?js$/i.test(entry) ? run(process4.execPath, [entry, ...args], cwd) : run(entry, args, cwd);
2213
+ if (process4.platform === "win32") {
2214
+ const directories = (process4.env.PATH ?? "").split(";");
2215
+ for (const directory of directories) {
2216
+ const executable = join(directory, "pnpm.exe");
2217
+ if (existsSync(executable))
2218
+ return run(executable, args, cwd);
2219
+ for (const relative of ["node_modules/pnpm/bin/pnpm.cjs", "pnpm.cjs"]) {
2220
+ const candidate = join(directory, relative);
2221
+ if (existsSync(candidate))
2222
+ return run(process4.execPath, [candidate, ...args], cwd);
2223
+ }
2224
+ }
2225
+ throw new Error(cliM("cli_build_use_pnpm"));
2226
+ }
2227
+ return run("pnpm", args, cwd);
2228
+ }
2229
+ async function buildCommand(options) {
2230
+ const cwd = resolve(options.cwd ?? process4.cwd());
2231
+ const target = options.target ?? "compile";
2232
+ if (!["compile", "docker", "desktop"].includes(target))
2233
+ 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)) {
2235
+ throw new Error(cliM("cli_build_options_invalid"));
2236
+ }
2237
+ if (target === "compile") {
2238
+ await pnpm(["run", "compile"], cwd);
2239
+ return;
2240
+ }
2241
+ const metadata = JSON.parse(readFileSync(join(cwd, "package.json"), "utf8"));
2242
+ const appVersion = metadata.version;
2243
+ if (typeof appVersion !== "string" || appVersion.length > 80 || !/^(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)(?:-[0-9a-z-]+(?:\.[0-9a-z-]+)*)?(?:\+[0-9a-z-]+(?:\.[0-9a-z-]+)*)?$/i.test(appVersion))
2244
+ throw new Error(cliM("cli_build_version_invalid"));
2245
+ const config = JSON.parse(readFileSync(join(cwd, "hai.build.json"), "utf8"));
2246
+ if (target === "desktop") {
2247
+ const platforms = { win32: "windows", linux: "linux", darwin: "macos" };
2248
+ const host = platforms[process4.platform];
2249
+ const platform = options.platform ?? host;
2250
+ const arch = options.arch ?? process4.arch;
2251
+ if (!platform || platform !== host || !["x64", "arm64"].includes(arch))
2252
+ throw new Error(cliM("cli_build_native_host"));
2253
+ if (!config.desktop?.length)
2254
+ throw new Error(cliM("cli_build_desktop_missing"));
2255
+ const rustTarget = `${arch === "x64" ? "x86_64" : "aarch64"}-${platform === "windows" ? "pc-windows-msvc" : platform === "macos" ? "apple-darwin" : "unknown-linux-gnu"}`;
2256
+ const bundles = platform === "windows" ? "nsis" : platform === "macos" ? "app,dmg" : "deb,appimage";
2257
+ for (const directory of config.desktop)
2258
+ 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 } }));
2260
+ return;
2261
+ }
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))) {
2263
+ throw new Error(cliM("cli_build_images_missing"));
2264
+ }
2265
+ const candidates = options.runtime && options.runtime !== "auto" ? [options.runtime] : ["podman", "docker"];
2266
+ if (candidates.some((runtime2) => !["podman", "docker"].includes(runtime2)))
2267
+ throw new Error(cliM("cli_build_runtime_missing"));
2268
+ let runtime;
2269
+ for (const candidate of candidates) {
2270
+ try {
2271
+ await run(candidate, ["version"], cwd, true);
2272
+ runtime = candidate;
2273
+ break;
2274
+ } catch {
2275
+ }
2276
+ }
2277
+ if (!runtime)
2278
+ throw new Error(cliM("cli_build_runtime_missing"));
2279
+ if (options.sqlitePrebuildMirror) {
2280
+ const url = new URL(options.sqlitePrebuildMirror);
2281
+ if (!["http:", "https:"].includes(url.protocol) || url.username || url.password || url.search || url.hash)
2282
+ throw new Error(cliM("cli_build_mirror_invalid"));
2283
+ }
2284
+ const buildId = `${appVersion}-${Date.now()}-${randomBytes(3).toString("hex")}`;
2285
+ const artifact = { version: 2, appVersion, buildId, images: [] };
2286
+ const parent = join(cwd, ".hai/build/docker");
2287
+ mkdirSync(parent, { recursive: true });
2288
+ const staging = mkdtempSync(join(parent, ".building-"));
2289
+ const built = [];
2290
+ try {
2291
+ for (const image of config.images) {
2292
+ if (!["app", "web"].includes(image.name))
2293
+ throw new Error(cliM("cli_build_images_missing"));
2294
+ const context = resolve(cwd, image.context ?? ".");
2295
+ const imageRef = `localhost/hai-${basenameSafe(cwd)}-${image.name}:${buildId.toLowerCase().replaceAll("+", "_")}`;
2296
+ const args = ["build", "-f", resolve(context, image.dockerfile ?? "Dockerfile"), "-t", imageRef];
2297
+ if (options.network !== void 0)
2298
+ args.push("--network", options.network);
2299
+ const ignore = `${resolve(context, image.dockerfile ?? "Dockerfile")}.dockerignore`;
2300
+ if (runtime === "podman" && existsSync(ignore))
2301
+ args.push("--ignorefile", ignore);
2302
+ if (runtime === "podman") {
2303
+ args.push("--format", "docker", "--jobs", "1", ...options.httpProxy === false ? ["--http-proxy=false"] : []);
2304
+ if (options.httpProxy !== false) {
2305
+ for (const key of ["HTTP_PROXY", "HTTPS_PROXY", "FTP_PROXY", "NO_PROXY", "http_proxy", "https_proxy", "ftp_proxy", "no_proxy"]) {
2306
+ if (process4.env[key])
2307
+ args.push("--build-arg", `${key}=${process4.env[key]}`);
2308
+ }
2309
+ }
2310
+ }
2311
+ if (image.target)
2312
+ args.push("--target", image.target);
2313
+ if (options.sqlitePrebuildMirror)
2314
+ args.push("--build-arg", `SQLITE_PREBUILD_MIRROR=${options.sqlitePrebuildMirror}`);
2315
+ built.push(imageRef);
2316
+ await run(runtime, [...args, context], cwd);
2317
+ const file = `${image.name}-${appVersion}.tar`;
2318
+ await run(runtime, ["save", ...runtime === "podman" ? ["--format", "docker-archive"] : [], "-o", join(staging, file), imageRef], cwd);
2319
+ const hash = createHash("sha256");
2320
+ for await (const chunk of createReadStream(join(staging, file)))
2321
+ hash.update(chunk);
2322
+ artifact.images.push({ name: image.name, file, imageRef, sha256: hash.digest("hex"), ...image.containerPort ? { containerPort: image.containerPort } : {} });
2323
+ }
2324
+ const output = join(parent, buildId);
2325
+ writeFileSync(join(staging, "manifest.json"), `${JSON.stringify(artifact, null, 2)}
2326
+ `);
2327
+ renameSync(staging, output);
2328
+ writeFileSync(join(parent, `latest-${buildId}`), `${buildId}
2329
+ `);
2330
+ renameSync(join(parent, `latest-${buildId}`), join(parent, "latest"));
2331
+ core.logger.info(cliM("cli_build_ready", { params: { path: output, runtime } }));
2332
+ } catch (error) {
2333
+ for (const imageRef of built) {
2334
+ try {
2335
+ await run(runtime, ["image", "rm", imageRef], cwd, true);
2336
+ } catch {
2337
+ }
2338
+ }
2339
+ throw error;
2340
+ } finally {
2341
+ rmSync(staging, { recursive: true, force: true });
2342
+ }
2343
+ }
2344
+ function basenameSafe(directory) {
2345
+ return directory.slice(dirname(directory).length + 1).toLowerCase().replace(/[^a-z0-9-]/g, "-").replace(/^-+/, "") || "app";
2346
+ }
2304
2347
  function displayDeployValue(name, value) {
2305
2348
  if (/PASSWORD|SECRET|TOKEN|APIKEY|PRIVATEKEY|ACCESSKEY|AUTHORIZATION/i.test(name.replace(/[^a-z0-9]/gi, "")) || /(?:^|_)PASS$/i.test(name) || value.includes("PRIVATE KEY"))
2306
2349
  return cliM("cli_deploy_secret");
@@ -2542,15 +2585,21 @@ async function configureDeployMiddleware(module, scan, environment, config, yes
2542
2585
  function configObject(value) {
2543
2586
  return value && typeof value === "object" && !Array.isArray(value) ? value : {};
2544
2587
  }
2545
- async function configureDeployTarget(module, config, environment, nonInteractive) {
2588
+ async function configureDeployTarget(module, config, environment, nonInteractive, bundleOnly) {
2589
+ if (bundleOnly) {
2590
+ const existing = configObject(config.provider);
2591
+ if (existing.type && existing.type !== "docker-ssh")
2592
+ throw new Error(cliM("cli_deploy_bundle_provider"));
2593
+ config.provider = { type: "docker-ssh", remote: { runtime: "auto" }, expose: { hostPort: 3e3 }, ...existing };
2594
+ }
2546
2595
  const current = module.ProviderConfigSchema.safeParse(config.provider);
2547
- if (current.success) {
2596
+ if (current.success && (bundleOnly || current.data.type !== "docker-ssh" || current.data.ssh)) {
2548
2597
  config.provider = current.data;
2549
- core.logger.info(cliM("cli_deploy_target_reused", { params: { target: current.data.type === "docker-ssh" ? `${current.data.ssh.username}@${current.data.ssh.host}:${current.data.ssh.port}` : "Vercel" } }));
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" } }));
2550
2599
  return;
2551
2600
  }
2552
2601
  if (nonInteractive)
2553
- throw new Error(cliM("cli_deploy_target_missing", { params: { fields: current.error.issues.map((issue) => `provider.${issue.path.join(".")}`).join(", ") } }));
2602
+ throw new Error(cliM("cli_deploy_target_missing", { params: { fields: current.success ? "provider.ssh" : current.error.issues.map((issue) => `provider.${issue.path.join(".")}`).join(", ") } }));
2554
2603
  const original = configObject(config.provider);
2555
2604
  const platform = await prompts({
2556
2605
  type: "select",
@@ -2610,21 +2659,21 @@ async function configureDeployTarget(module, config, environment, nonInteractive
2610
2659
  }
2611
2660
  async function deployCommand(options) {
2612
2661
  const spinner = ora();
2613
- const cwd = options.cwd ?? process3.cwd();
2662
+ const cwd = options.cwd ?? process4.cwd();
2614
2663
  let appDir = resolve(cwd, options.appDir ?? ".");
2615
2664
  let closeDeploy = null;
2616
- const originalEnv = { ...process3.env };
2665
+ const originalEnv = { ...process4.env };
2617
2666
  try {
2618
2667
  let environmentName = options.env;
2619
2668
  if (!environmentName) {
2620
- if (!process3.stdin.isTTY)
2669
+ if (!process4.stdin.isTTY)
2621
2670
  throw new Error(cliM("cli_deploy_environment_hint"));
2622
2671
  const answer = await prompts({ type: "text", name: "environment", message: cliM("cli_deploy_environment"), initial: "prod" });
2623
2672
  environmentName = answer.environment;
2624
2673
  if (!environmentName)
2625
2674
  throw new Error(cliM("cli_deploy_cancelled"));
2626
2675
  }
2627
- if (!options.yes && !process3.stdin.isTTY)
2676
+ if (!options.yes && !process4.stdin.isTTY)
2628
2677
  throw new Error(cliM("cli_deploy_environment_hint"));
2629
2678
  let deployModule;
2630
2679
  try {
@@ -2634,7 +2683,7 @@ async function deployCommand(options) {
2634
2683
  localModule = localRequire.resolve("@h-ai/deploy");
2635
2684
  } catch {
2636
2685
  }
2637
- deployModule = localModule ? await import(pathToFileURL(localModule).href) : await import('./src-6PPBKQ6Q.js');
2686
+ deployModule = localModule ? await import(pathToFileURL(localModule).href) : await import('./src-RDQ2EIBT.js');
2638
2687
  } catch {
2639
2688
  core.logger.error(chalk.red(cliM("cli_deploy_module_missing")));
2640
2689
  core.logger.info(chalk.cyan(" pnpm add @h-ai/deploy"));
@@ -2649,7 +2698,7 @@ async function deployCommand(options) {
2649
2698
  const environment = deployModule.loadDeploymentEnvironment({ environment: environmentName, envDir: scan.envDir ?? appDir });
2650
2699
  if (!environment.success)
2651
2700
  throw new Error(environment.error.message);
2652
- Object.assign(process3.env, environment.data.values);
2701
+ Object.assign(process4.env, environment.data.values);
2653
2702
  spinner.start(cliM("cli_deploy_load_credentials"));
2654
2703
  const credResult = deploy.credentials.load();
2655
2704
  if (!credResult.success) {
@@ -2663,14 +2712,14 @@ async function deployCommand(options) {
2663
2712
  core.logger.info(chalk.cyan(cliM("cli_deploy_config_hint")));
2664
2713
  throw new Error(cliM("cli_deploy_config_missing", { params: { path: configPath } }));
2665
2714
  }
2666
- const loadedConfig = deployModule.loadDeploymentConfig("deploy", configPath, process3.env);
2715
+ const loadedConfig = deployModule.loadDeploymentConfig("deploy", configPath, process4.env);
2667
2716
  if (!loadedConfig.success)
2668
2717
  throw new Error(loadedConfig.error.message);
2669
2718
  const deployConfig = loadedConfig.data;
2670
2719
  spinner.succeed(cliM("cli_deploy_config_loaded"));
2671
- await configureDeployTarget(deployModule, deployConfig, environment.data, options.yes);
2720
+ await configureDeployTarget(deployModule, deployConfig, environment.data, options.yes, options.bundleOnly);
2672
2721
  spinner.succeed(cliM("cli_deploy_scanned", { params: { name: scan.appName, sveltekit: String(scan.isSvelteKit), services: scan.requiredServices.join(", ") || cliM("cli_deploy_none") } }));
2673
- let skipMiddleware = options.skipMiddleware || options.skipProvision;
2722
+ let skipMiddleware = options.skipMiddleware;
2674
2723
  if (!skipMiddleware && !options.yes && scan.requiredServices.length) {
2675
2724
  const action = await prompts({
2676
2725
  type: "select",
@@ -2687,15 +2736,15 @@ async function deployCommand(options) {
2687
2736
  const serviceModes = skipMiddleware ? Object.fromEntries(scan.requiredServices.map((service) => [service, "keep"])) : await configureDeployMiddleware(deployModule, scan, environment.data, deployConfig, options.yes);
2688
2737
  if (skipMiddleware)
2689
2738
  deployConfig.services = void 0;
2690
- for (const key of Object.keys(process3.env)) {
2739
+ for (const key of Object.keys(process4.env)) {
2691
2740
  if (originalEnv[key] === void 0)
2692
- delete process3.env[key];
2741
+ delete process4.env[key];
2693
2742
  }
2694
- Object.assign(process3.env, originalEnv);
2743
+ Object.assign(process4.env, originalEnv);
2695
2744
  const refreshed = deployModule.loadDeploymentEnvironment({ environment: environment.data.environment, envDir: environment.data.envDir });
2696
2745
  if (!refreshed.success)
2697
2746
  throw new Error(refreshed.error.message);
2698
- Object.assign(process3.env, refreshed.data.values);
2747
+ Object.assign(process4.env, refreshed.data.values);
2699
2748
  spinner.start(cliM("cli_deploy_initializing"));
2700
2749
  const validatedConfig = deployModule.DeployConfigSchema.safeParse(deployConfig);
2701
2750
  if (!validatedConfig.success)
@@ -2709,8 +2758,10 @@ async function deployCommand(options) {
2709
2758
  spinner.start(cliM("cli_deploy_deploying"));
2710
2759
  const deployResult = await deploy.deployApp(appDir, {
2711
2760
  projectName: options.projectName,
2712
- skipProvision: skipMiddleware,
2713
- skipBuild: options.skipBuild,
2761
+ skipMiddleware,
2762
+ artifactDir: options.artifactDir ? resolve(cwd, options.artifactDir) : void 0,
2763
+ bundleOnly: options.bundleOnly,
2764
+ output: options.output ? resolve(cwd, options.output) : void 0,
2714
2765
  environment: environment.data.environment,
2715
2766
  envDir: environment.data.envDir,
2716
2767
  serviceModes
@@ -2723,12 +2774,12 @@ async function deployCommand(options) {
2723
2774
  }
2724
2775
  throw new Error(cliM("cli_deploy_failed", { params: { message: deployResult.error.message } }));
2725
2776
  }
2726
- spinner.succeed(chalk.green(cliM("cli_deploy_success")));
2727
- for (const key of Object.keys(process3.env)) {
2777
+ spinner.succeed(chalk.green(cliM(deployResult.data.status === "packaged" ? "cli_deploy_bundle_ready" : "cli_deploy_success")));
2778
+ for (const key of Object.keys(process4.env)) {
2728
2779
  if (originalEnv[key] === void 0)
2729
- delete process3.env[key];
2780
+ delete process4.env[key];
2730
2781
  }
2731
- Object.assign(process3.env, originalEnv);
2782
+ Object.assign(process4.env, originalEnv);
2732
2783
  const completedEnvironment = deployModule.loadDeploymentEnvironment({ environment: environment.data.environment, envDir: environment.data.envDir });
2733
2784
  if (!completedEnvironment.success)
2734
2785
  throw new Error(completedEnvironment.error.message);
@@ -2740,7 +2791,12 @@ async function deployCommand(options) {
2740
2791
  core.logger.info(cliM("cli_deploy_saved", { params: { name: requirement.envPrefix, value: displayDeployValue(requirement.envPrefix, value) } }));
2741
2792
  }
2742
2793
  core.logger.info("");
2743
- core.logger.info(chalk.cyan(cliM("cli_deploy_url", { params: { url: deployResult.data.url } })));
2794
+ if (deployResult.data.bundlePath)
2795
+ core.logger.info(cliM("cli_deploy_bundle_path", { params: { path: deployResult.data.bundlePath } }));
2796
+ if (deployResult.data.status === "packaged")
2797
+ core.logger.info(deployResult.data.instructions ?? "");
2798
+ else
2799
+ core.logger.info(chalk.cyan(cliM("cli_deploy_url", { params: { url: deployResult.data.url } })));
2744
2800
  core.logger.info(chalk.gray(cliM("cli_deploy_id", { params: { id: deployResult.data.deploymentId } })));
2745
2801
  if (deployResult.data.envVarsSet.length > 0) {
2746
2802
  core.logger.info(chalk.gray(cliM("cli_deploy_env", { params: { names: deployResult.data.envVarsSet.join(", ") } })));
@@ -2756,14 +2812,14 @@ async function deployCommand(options) {
2756
2812
  await closeDeploy();
2757
2813
  } catch (error) {
2758
2814
  core.logger.error("Failed to close deploy module", { error });
2759
- process3.exitCode = 1;
2815
+ process4.exitCode = 1;
2760
2816
  }
2761
2817
  }
2762
- for (const key of Object.keys(process3.env)) {
2818
+ for (const key of Object.keys(process4.env)) {
2763
2819
  if (originalEnv[key] === void 0)
2764
- delete process3.env[key];
2820
+ delete process4.env[key];
2765
2821
  }
2766
- Object.assign(process3.env, originalEnv);
2822
+ Object.assign(process4.env, originalEnv);
2767
2823
  }
2768
2824
  }
2769
2825
  var GENERATORS = {
@@ -3107,7 +3163,7 @@ async function initProject(options) {
3107
3163
  const { project, existing, missing } = result;
3108
3164
  spinner.succeed(`\u68C0\u6D4B\u5230\u9879\u76EE: ${chalk.cyan(project.name)}`);
3109
3165
  if (project.isHaiProject) {
3110
- const { generateProjectAiSupport, resolveSkillNamesForPackages } = await import('./cli-skill-templates-A5VIR5J6.js');
3166
+ const { generateProjectAiSupport, resolveSkillNamesForPackages } = await import('./cli-skill-templates-TSYEQTCY.js');
3111
3167
  const generatedFiles = await generateProjectAiSupport(cwd, resolveSkillNamesForPackages(project.haiPackages), {
3112
3168
  overwriteSkills: false,
3113
3169
  overwriteBridgeFiles: false
@@ -3211,7 +3267,7 @@ cli.command("create [name]", "\u521B\u5EFA\u65B0\u7684 hai \u9879\u76EE").option
3211
3267
  cwd: options.cwd
3212
3268
  });
3213
3269
  } catch {
3214
- process3.exitCode = 1;
3270
+ process4.exitCode = 1;
3215
3271
  }
3216
3272
  });
3217
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) => {
@@ -3223,7 +3279,7 @@ cli.command("add [module]", "\u5411\u73B0\u6709\u9879\u76EE\u6DFB\u52A0\u6A21\u5
3223
3279
  cwd: options.cwd
3224
3280
  });
3225
3281
  } catch {
3226
- process3.exit(1);
3282
+ process4.exit(1);
3227
3283
  }
3228
3284
  });
3229
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) => {
@@ -3234,7 +3290,7 @@ cli.command("init", "\u521D\u59CB\u5316/\u6821\u9A8C\u9879\u76EE\u914D\u7F6E").o
3234
3290
  cwd: options.cwd
3235
3291
  });
3236
3292
  } catch {
3237
- process3.exit(1);
3293
+ process4.exit(1);
3238
3294
  }
3239
3295
  });
3240
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) => {
@@ -3248,10 +3304,18 @@ cli.command("generate [type] [name]", "\u751F\u6210\u4EE3\u7801").alias("g").opt
3248
3304
  cwd: options.cwd
3249
3305
  });
3250
3306
  } catch {
3251
- process3.exit(1);
3307
+ process4.exit(1);
3252
3308
  }
3253
3309
  });
3254
- 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\u4F7F\u7528\u5E94\u7528\u76EE\u5F55\u540D\uFF09").option("--skip-provision", "\u914D\u7F6E\u5DF2\u5B8C\u5907\uFF0C\u8DF3\u8FC7\u4E2D\u95F4\u4EF6\u914D\u7F6E\u4E0E\u5F00\u901A\uFF0C\u76F4\u63A5\u90E8\u7F72").option("--skip-middleware", "\u914D\u7F6E\u5DF2\u5B8C\u5907\uFF0C\u8DF3\u8FC7\u5168\u90E8\u4E2D\u95F4\u4EF6\u5904\u7406\uFF0C\u76F4\u63A5\u90E8\u7F72").option("--skip-build", "\u8DF3\u8FC7\u6784\u5EFA").action(async (appDir, options) => {
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) => {
3311
+ try {
3312
+ await buildCommand(options);
3313
+ } catch (error) {
3314
+ writeCliMessage(error instanceof Error ? error.message : String(error));
3315
+ process4.exitCode = 1;
3316
+ }
3317
+ });
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) => {
3255
3319
  try {
3256
3320
  await deployCommand({
3257
3321
  appDir,
@@ -3259,14 +3323,15 @@ cli.command("deploy [appDir]", "\u90E8\u7F72\u5E94\u7528\u5230\u4E91\u5E73\u53F0
3259
3323
  envDir: options.envDir,
3260
3324
  yes: options.yes,
3261
3325
  projectName: options.projectName,
3262
- skipProvision: options.skipProvision,
3263
3326
  skipMiddleware: options.skipMiddleware,
3264
- skipBuild: options.skipBuild,
3327
+ artifactDir: options.artifactDir,
3328
+ bundleOnly: options.bundleOnly,
3329
+ output: options.output,
3265
3330
  verbose: options.verbose,
3266
3331
  cwd: options.cwd
3267
3332
  });
3268
3333
  } catch {
3269
- process3.exit(1);
3334
+ process4.exit(1);
3270
3335
  }
3271
3336
  });
3272
3337
  cli.command("g:page <name>", "\u751F\u6210\u9875\u9762").action(async (name, options) => {
@@ -3281,7 +3346,7 @@ cli.command("g:api <name>", "\u751F\u6210 API").action(async (name, options) =>
3281
3346
  cli.command("g:model <name>", "\u751F\u6210\u6570\u636E\u6A21\u578B").action(async (name, options) => {
3282
3347
  await generate({ type: "model", name, cwd: options.cwd });
3283
3348
  });
3284
- cli.version(VERSION);
3349
+ cli.version(VERSION, "-V, --version");
3285
3350
  cli.help();
3286
3351
  var parsed = cli.parse();
3287
3352
  if (!cli.matchedCommand && !parsed.options.help && !parsed.options.version) {