@h-ai/cli 0.1.0-alpha.51 → 0.1.0-alpha.54

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 (54) hide show
  1. package/README.md +14 -0
  2. package/dist/{chunk-U4PA632G.js → chunk-FVDXJYMO.js} +34 -67
  3. package/dist/chunk-FVDXJYMO.js.map +1 -0
  4. package/dist/cli-config-templates-WGN2I23J.js +5 -0
  5. package/dist/{cli-config-templates-GSXKLGU5.js.map → cli-config-templates-WGN2I23J.js.map} +1 -1
  6. package/dist/index.js +299 -127
  7. package/dist/index.js.map +1 -1
  8. package/dist/{src-QNEJJ3BV.js → src-6ZBWDBUB.js} +127 -87
  9. package/dist/{src-QNEJJ3BV.js.map → src-6ZBWDBUB.js.map} +1 -1
  10. package/package.json +13 -4
  11. package/templates/apps/api/Dockerfile.hbs +6 -3
  12. package/templates/apps/api/apps/{{projectName}}-contract/package.json.hbs +1 -0
  13. package/templates/apps/api/apps/{{projectName}}-service/data/.gitkeep.hbs +1 -0
  14. package/templates/apps/api/apps/{{projectName}}-service/package.json.hbs +1 -0
  15. package/templates/apps/api/package.json.hbs +1 -0
  16. package/templates/apps/fullstack/.dockerignore.hbs +2 -0
  17. package/templates/apps/fullstack/Dockerfile.hbs +23 -36
  18. package/templates/apps/fullstack/apps/{{projectName}}-app/package.json.hbs +1 -0
  19. package/templates/apps/fullstack/apps/{{projectName}}-desktop/package.json.hbs +1 -0
  20. package/templates/apps/fullstack/apps/{{projectName}}-web/index.html.hbs +2 -1
  21. package/templates/apps/fullstack/apps/{{projectName}}-web/package.json.hbs +1 -0
  22. package/templates/apps/fullstack/apps/{{projectName}}-web/public/runtime-config.js.hbs +1 -0
  23. package/templates/apps/fullstack/apps/{{projectName}}-web/tests/api.test.ts.hbs +27 -0
  24. package/templates/apps/fullstack/docker/19-runtime-config.envsh.hbs +5 -0
  25. package/templates/apps/fullstack/docker/nginx.conf.hbs +6 -0
  26. package/templates/apps/fullstack/docker/runtime-config.js.template.hbs +3 -0
  27. package/templates/apps/fullstack/package.json.hbs +3 -4
  28. package/templates/apps/fullstack/packages/{{projectName}}-contract/package.json.hbs +1 -0
  29. package/templates/apps/fullstack/packages/{{projectName}}-serv/README.md.hbs +3 -2
  30. package/templates/apps/fullstack/packages/{{projectName}}-serv/data/.gitkeep.hbs +1 -0
  31. package/templates/apps/fullstack/packages/{{projectName}}-serv/package.json.hbs +1 -0
  32. package/templates/apps/fullstack/packages/{{projectName}}-shared/package.json.hbs +1 -0
  33. package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/api/api-client.ts.hbs +9 -1
  34. package/templates/base/.gitignore.hbs +2 -1
  35. package/templates/base/Dockerfile.hbs +10 -4
  36. package/templates/base/eslint.config.js.hbs +1 -0
  37. package/templates/base/package.json.hbs +3 -1
  38. package/templates/skills/hai-ai/SKILL.md +4 -0
  39. package/templates/skills/hai-ai/reference.md +5 -1
  40. package/templates/skills/hai-app-create/SKILL.md +3 -1
  41. package/templates/skills/hai-app-review/SKILL.md +6 -0
  42. package/templates/skills/hai-build/SKILL.md +8 -0
  43. package/templates/skills/hai-cache/SKILL.md +2 -0
  44. package/templates/skills/hai-capacitor/SKILL.md +2 -0
  45. package/templates/skills/hai-ci/SKILL.md +12 -0
  46. package/templates/skills/hai-core/SKILL.md +2 -0
  47. package/templates/skills/hai-crypto/SKILL.md +2 -0
  48. package/templates/skills/hai-deploy/SKILL.md +12 -0
  49. package/templates/skills/hai-iam/SKILL.md +6 -0
  50. package/templates/skills/hai-scheduler/SKILL.md +12 -0
  51. package/templates/skills/hai-ui/SKILL.md +4 -0
  52. package/dist/chunk-U4PA632G.js.map +0 -1
  53. package/dist/cli-config-templates-GSXKLGU5.js +0 -5
  54. package/templates/apps/fullstack/docker/fullstack-server.mjs.hbs +0 -194
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { generateConfigFile } from './chunk-U4PA632G.js';
2
+ import { generateConfigFile } from './chunk-FVDXJYMO.js';
3
3
  import './chunk-WUYLYCW2.js';
4
4
  import './chunk-2H7UOFLK.js';
5
5
  import process3 from 'process';
@@ -1076,7 +1076,7 @@ async function createProject(options) {
1076
1076
  });
1077
1077
  await generateFromTemplates(projectPath, context);
1078
1078
  if (resolvedOptions.appType !== "fullstack") {
1079
- const { generateConfigFile: generateConfigFile2, generateEnvExample } = await import('./cli-config-templates-GSXKLGU5.js');
1079
+ const { generateConfigFile: generateConfigFile2, generateEnvExample } = await import('./cli-config-templates-WGN2I23J.js');
1080
1080
  const configs = resolvedOptions.moduleConfigs;
1081
1081
  const isApiWorkspace = resolvedOptions.appType === "api";
1082
1082
  const configDir = isApiWorkspace ? path2.join(projectPath, "apps", `${resolvedOptions.name}-service`, "config") : path2.join(projectPath, "config");
@@ -1617,15 +1617,20 @@ function generateReadme(name, appTypeLabel, pm, appType) {
1617
1617
  const isStaticImage = appType === "mobile-app";
1618
1618
  const containerPort = isStaticImage ? 8080 : 3e3;
1619
1619
  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`;
1620
- const embeddedData = isStaticImage ? "" : `
1621
- \u5982\u9700\u628A\u5F53\u524D \`data/\`\uFF08\u4F8B\u5982\u9884\u7F6E SQLite \u6587\u4EF6\uFF09\u56FA\u5316\u4E3A\u955C\u50CF\u521D\u59CB\u5FEB\u7167\uFF1A
1622
-
1623
- \`\`\`bash
1620
+ const embeddedDataBuild = isStaticImage ? "" : `
1621
+ # \u5E26 data/ \u521D\u59CB\u6570\u636E\u6784\u5EFA\uFF08\u4E8C\u9009\u4E00\uFF09
1624
1622
  ${getRunCommand(pm, "docker:build:data")}
1625
- \`\`\`
1623
+ ${getRunCommand(pm, "podman:build:data")}`;
1624
+ 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
1626
1625
 
1627
- \u8FD0\u884C\u540E\u4EA7\u751F\u7684\u65B0\u6570\u636E\u4ECD\u5E94\u5199\u5165 \`/app/data\` volume\uFF1B\u914D\u7F6E\u6587\u4EF6\u53EF\u6302\u8F7D\u5230 \`/app/config\`\uFF0CYAML \u4E2D\u7684 \`\${ENV_NAME:default}\` \u53EF\u7531 \`--env-file\` \u6216 \`-e\` \u8986\u76D6\u3002
1628
- `;
1626
+ | \u53C2\u6570 | \u7528\u9014 | \u53EF\u9009\u6027 |
1627
+ | --- | --- | --- |
1628
+ | \`--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 |
1629
+ | \`--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 |
1630
+ | \`--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 |
1631
+
1632
+ - \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`;
1633
+ const testPath = isStaticImage ? "/health" : "/";
1629
1634
  return `# ${name}
1630
1635
 
1631
1636
  \u57FA\u4E8E hai Agent Framework \u6784\u5EFA\u7684${appTypeLabel}\u5E94\u7528\u3002
@@ -1645,13 +1650,31 @@ ${getRunCommand(pm, "i18n:compile")}
1645
1650
  ${getRunCommand(pm, "preview")}
1646
1651
  \`\`\`
1647
1652
 
1648
- ## Docker
1653
+ ## \u5BB9\u5668\u8FD0\u884C
1654
+
1655
+ ### \u6784\u5EFA\u955C\u50CF
1649
1656
 
1650
1657
  \`\`\`bash
1658
+ # \u666E\u901A\u6784\u5EFA\uFF08\u4E8C\u9009\u4E00\uFF09
1651
1659
  ${getRunCommand(pm, "docker:build")}
1652
- docker run --rm -p ${containerPort}:${containerPort}${runtimeOptions} ${name}:latest
1660
+ ${getRunCommand(pm, "podman:build")}${embeddedDataBuild}
1661
+ \`\`\`
1662
+
1663
+ ### \u8FD0\u884C\u955C\u50CF
1664
+
1665
+ \`\`\`bash
1666
+ # Docker\uFF1B\u4F7F\u7528 Podman \u65F6\u5C06 docker \u66FF\u6362\u4E3A podman
1667
+ docker run --rm -d --name ${name} -p ${containerPort}:${containerPort}${runtimeOptions} ${name}:latest
1653
1668
  \`\`\`
1654
- ${embeddedData}
1669
+
1670
+ ### \u6D4B\u8BD5\u955C\u50CF
1671
+
1672
+ \`\`\`bash
1673
+ curl --fail http://127.0.0.1:${containerPort}${testPath}
1674
+ docker stop ${name}
1675
+ \`\`\`
1676
+
1677
+ ${runtimeNotes}
1655
1678
 
1656
1679
  ## \u6587\u6863
1657
1680
 
@@ -1686,18 +1709,50 @@ ${pm} test
1686
1709
  ${pm} test:e2e
1687
1710
  \`\`\`
1688
1711
 
1689
- ## Docker
1712
+ ## \u5BB9\u5668\u8FD0\u884C
1713
+
1714
+ ### \u6784\u5EFA\u955C\u50CF
1690
1715
 
1691
1716
  \`\`\`bash
1717
+ # \u666E\u901A\u6784\u5EFA\uFF08\u4E8C\u9009\u4E00\uFF09
1692
1718
  ${pm} docker:build
1693
- docker run --rm -p 3000:3000 --env-file .env \\
1719
+ ${pm} podman:build
1720
+
1721
+ # \u5C06 apps/${name}-service/data \u4F5C\u4E3A\u955C\u50CF\u521D\u59CB\u6570\u636E\u6784\u5EFA\uFF08\u4E8C\u9009\u4E00\uFF09
1722
+ ${pm} docker:build:data
1723
+ ${pm} podman:build:data
1724
+ \`\`\`
1725
+
1726
+ ### \u8FD0\u884C\u955C\u50CF
1727
+
1728
+ \`\`\`bash
1729
+ # \u4F7F\u7528 Podman \u65F6\u5C06 docker \u66FF\u6362\u4E3A podman
1730
+ docker run --rm -d --name ${name} -p 3000:3000 --env-file .env \\
1694
1731
  --mount type=bind,source=/absolute/path/to/config,target=/app/config,readonly \\
1695
1732
  --mount type=volume,source=${name}-data,target=/app/data \\
1696
1733
  ${name}:latest
1697
1734
  \`\`\`
1698
1735
 
1736
+ ### \u6D4B\u8BD5\u955C\u50CF
1737
+
1738
+ \`\`\`bash
1739
+ curl --fail http://127.0.0.1:3000/health
1740
+ curl --fail http://127.0.0.1:3000/ready
1741
+ curl --fail --request POST http://127.0.0.1:3000/api/v1/app/info \\
1742
+ --header "Content-Type: application/json" --data '{}'
1743
+ docker stop ${name}
1744
+ \`\`\`
1745
+
1699
1746
  - \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
1700
- - \u5C06 SQLite/\u672C\u5730\u5B58\u50A8\u6587\u4EF6\u653E\u5728 \`apps/${name}-service/data\` \u540E\u8FD0\u884C \`${pm} docker:build:data\`\uFF0C\u53EF\u628A\u8BE5\u76EE\u5F55\u4F5C\u4E3A\u955C\u50CF\u521D\u59CB\u6570\u636E\uFF1B\u751F\u4EA7\u5199\u5165\u4ECD\u5EFA\u8BAE\u4F7F\u7528 \`/app/data\` volume\u3002
1747
+ - \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
1748
+
1749
+ \u4EE5\u4E0A\u4E09\u4E2A\u8FD0\u884C\u65F6\u53C2\u6570\u5747\u53EF\u6309\u9700\u5220\u9664\uFF1A
1750
+
1751
+ | \u53C2\u6570 | \u7528\u9014 | \u53EF\u9009\u6027 |
1752
+ | --- | --- | --- |
1753
+ | \`--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 |
1754
+ | \`--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 |
1755
+ | \`--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 |
1701
1756
 
1702
1757
  ## \u6269\u5C55\u5EFA\u8BAE
1703
1758
 
@@ -1715,6 +1770,20 @@ docker run --rm -p 3000:3000 --env-file .env \\
1715
1770
  }
1716
1771
  function generateFullstackReadme(name, pm, frontends) {
1717
1772
  const enabledFrontends = frontends.map((target) => FRONTEND_TARGETS[target].name).join(" / ");
1773
+ const webBuildCommands = frontends.includes("web") ? `
1774
+ # Web \u9759\u6001\u955C\u50CF\uFF08\u4E8C\u9009\u4E00\uFF09
1775
+ ${pm} docker:build:web
1776
+ ${pm} podman:build:web` : "";
1777
+ const webRunCommand = frontends.includes("web") ? `
1778
+
1779
+ # Web\uFF1BPUBLIC_API_BASE \u5728\u5BB9\u5668\u542F\u52A8\u65F6\u6CE8\u5165\uFF0C\u4E0D\u9700\u8981\u91CD\u65B0\u6784\u5EFA\u955C\u50CF
1780
+ docker run --rm -d --name ${name}-web -p 8080:8080 \\
1781
+ -e PUBLIC_API_BASE=https://api.example.com \\
1782
+ ${name}-web:latest` : "";
1783
+ const webTestCommands = frontends.includes("web") ? `curl --fail http://127.0.0.1:8080/health
1784
+ curl --fail http://127.0.0.1:8080/runtime-config.js
1785
+ ` : "";
1786
+ const containerNames = frontends.includes("web") ? `${name} ${name}-web` : name;
1718
1787
  return `# ${name}
1719
1788
 
1720
1789
  \u57FA\u4E8E hai Agent Framework \u6784\u5EFA\u7684\u524D\u540E\u7AEF\u5206\u79BB\u591A\u5305\u5DE5\u7A0B\u3002
@@ -1747,22 +1816,56 @@ ${pm} package
1747
1816
  ${pm} deploy
1748
1817
  \`\`\`
1749
1818
 
1750
- ## Docker
1819
+ ## \u5BB9\u5668\u8FD0\u884C
1820
+
1821
+ \u9ED8\u8BA4\u955C\u50CF\u76F4\u63A5\u8FD0\u884C API Service\uFF0C\u5916\u90E8\u7AEF\u53E3\u4E3A 3000\u3002
1751
1822
 
1752
- \u9ED8\u8BA4\u955C\u50CF\u628A Web \u4E0E API Service \u5408\u5E76\u4E3A\u540C\u6E90\u5165\u53E3\uFF0C\u5916\u90E8\u7AEF\u53E3\u4E3A 8080\uFF1A
1823
+ ### \u6784\u5EFA\u955C\u50CF
1753
1824
 
1754
1825
  \`\`\`bash
1826
+ # Service \u666E\u901A\u6784\u5EFA\uFF08\u4E8C\u9009\u4E00\uFF09
1755
1827
  ${pm} docker:build
1756
- docker run --rm -p 8080:8080 --env-file .env \\
1757
- --mount type=bind,source=/absolute/path/to/config,target=/app/service/config,readonly \\
1758
- --mount type=volume,source=${name}-data,target=/app/service/data \\
1828
+ ${pm} podman:build
1829
+
1830
+ # \u5C06 packages/${name}-serv/data \u4F5C\u4E3A\u955C\u50CF\u521D\u59CB\u6570\u636E\u6784\u5EFA\uFF08\u4E8C\u9009\u4E00\uFF09
1831
+ ${pm} docker:build:data
1832
+ ${pm} podman:build:data
1833
+ ${webBuildCommands}
1834
+ \`\`\`
1835
+
1836
+ ### \u8FD0\u884C\u955C\u50CF
1837
+
1838
+ \`\`\`bash
1839
+ # Service\uFF1B\u4F7F\u7528 Podman \u65F6\u5C06 docker \u66FF\u6362\u4E3A podman
1840
+ docker run --rm -d --name ${name} -p 3000:3000 --env-file .env \\
1841
+ --mount type=bind,source=/absolute/path/to/config,target=/app/config,readonly \\
1842
+ --mount type=volume,source=${name}-data,target=/app/data \\
1759
1843
  ${name}:latest
1844
+ ${webRunCommand}
1760
1845
  \`\`\`
1761
1846
 
1762
- - \u914D\u7F6E\u9ED8\u8BA4\u6765\u81EA \`packages/${name}-serv/config\`\uFF0C\u4E5F\u53EF\u6302\u8F7D\u5230 \`/app/service/config\`\uFF1BYAML \u4E2D\u7684 \`\${ENV_NAME:default}\` \u53EF\u7531 \`--env-file\` \u6216 \`-e\` \u6CE8\u5165\u3002
1763
- - \u5C06\u9884\u7F6E SQLite/\u672C\u5730\u5B58\u50A8\u653E\u5165 \`packages/${name}-serv/data\` \u540E\u8FD0\u884C \`${pm} docker:build:data\`\uFF0C\u53EF\u751F\u6210\u5E26\u521D\u59CB\u6570\u636E\u7684\u955C\u50CF\uFF1B\u6301\u4E45\u5316\u5199\u5165\u4ECD\u5E94\u4F7F\u7528 volume\u3002
1764
- - \`HAI_WEB_PORT\` \u63A7\u5236\u5408\u5E76\u955C\u50CF\u7684 Web \u7AEF\u53E3\uFF0C\`HAI_SERV_PORT\` \u63A7\u5236\u5BB9\u5668\u5185 Service \u7AEF\u53E3\u3002
1765
- - \u9700\u8981\u62C6\u5206\u90E8\u7F72\u65F6\uFF0C\u4F7F\u7528 \`${pm} docker:build:service\`${frontends.includes("web") ? ` \u4E0E \`${pm} docker:build:web -- --build-arg PUBLIC_API_BASE=https://api.example.com\`` : ""}\u3002
1847
+ ### \u6D4B\u8BD5\u955C\u50CF
1848
+
1849
+ \`\`\`bash
1850
+ curl --fail http://127.0.0.1:3000/health
1851
+ curl --fail http://127.0.0.1:3000/ready
1852
+ curl --fail --request POST http://127.0.0.1:3000/api/v1/app/info \\
1853
+ --header "Content-Type: application/json" --data '{}'
1854
+ ${webTestCommands}docker stop ${containerNames}
1855
+ \`\`\`
1856
+
1857
+ - \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
1858
+ - \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
1859
+ - Service \u7531 \`@h-ai/serv\` \u76F4\u63A5\u5BF9\u5916\u76D1\u542C\uFF0C\u4E0D\u989D\u5916\u542F\u52A8 Web \u8F6C\u53D1\u5C42\u3002
1860
+ ${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` : ""}
1861
+
1862
+ \u4EE5\u4E0A\u4E09\u4E2A Service \u8FD0\u884C\u65F6\u53C2\u6570\u5747\u53EF\u6309\u9700\u5220\u9664\uFF1A
1863
+
1864
+ | \u53C2\u6570 | \u7528\u9014 | \u53EF\u9009\u6027 |
1865
+ | --- | --- | --- |
1866
+ | \`--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 |
1867
+ | \`--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 |
1868
+ | \`--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 |
1766
1869
 
1767
1870
  \u524D\u7AEF\u5355\u72EC\u4EA4\u4ED8\u547D\u4EE4\uFF1A
1768
1871
 
@@ -2015,6 +2118,69 @@ async function addModule(options) {
2015
2118
  throw error;
2016
2119
  }
2017
2120
  }
2121
+
2122
+ // messages/en-US.json
2123
+ var en_US_default = {
2124
+ cli_invalidGenerateName: "Use a name starting with a letter, with only letters, digits, hyphens or underscores",
2125
+ cli_generateOutsideWorkspace: "Generated files must remain inside the workspace, including symbolic link targets",
2126
+ cli_generateExists: "File already exists: {path}. Use --force to overwrite it",
2127
+ cli_deploy_module_missing: "Deploy module not found. Install @h-ai/deploy first:",
2128
+ cli_deploy_load_credentials: "Loading credentials...",
2129
+ cli_deploy_credentials_failed: "Failed to load credentials: {message}",
2130
+ cli_deploy_credentials_loaded: "Loaded {count} credentials",
2131
+ cli_deploy_load_config: "Loading deploy config...",
2132
+ cli_deploy_config_missing: "Deploy config not found: {path}",
2133
+ cli_deploy_config_hint: "Run: hai add deploy to generate config template",
2134
+ cli_deploy_config_loaded: "Deploy config loaded",
2135
+ cli_deploy_scanning: "Scanning application...",
2136
+ cli_deploy_scan_failed: "Scan failed: {message}",
2137
+ cli_deploy_scanned: "Scanned: {name} (SvelteKit: {sveltekit}, Services: {services})",
2138
+ cli_deploy_none: "none",
2139
+ cli_deploy_initializing: "Initializing deploy module...",
2140
+ cli_deploy_init_failed: "Init failed: {message}",
2141
+ cli_deploy_initialized: "Deploy module initialized",
2142
+ cli_deploy_deploying: "Deploying application...",
2143
+ cli_deploy_failed: "Deploy failed: {message}",
2144
+ cli_deploy_success: "Deployment successful!",
2145
+ cli_deploy_url: " URL: {url}",
2146
+ cli_deploy_id: " ID: {id}",
2147
+ cli_deploy_env: " Env: {names}",
2148
+ cli_deploy_command_failed: "Deploy command failed"
2149
+ };
2150
+
2151
+ // messages/zh-CN.json
2152
+ var zh_CN_default = {
2153
+ cli_invalidGenerateName: "\u540D\u79F0\u5FC5\u987B\u4EE5\u5B57\u6BCD\u5F00\u5934\uFF0C\u4E14\u4EC5\u5305\u542B\u5B57\u6BCD\u3001\u6570\u5B57\u3001\u77ED\u6A2A\u7EBF\u6216\u4E0B\u5212\u7EBF",
2154
+ cli_generateOutsideWorkspace: "\u751F\u6210\u6587\u4EF6\u5FC5\u987B\u4F4D\u4E8E\u5DE5\u4F5C\u76EE\u5F55\u5185\uFF0C\u7B26\u53F7\u94FE\u63A5\u76EE\u6807\u4E5F\u4E0D\u5F97\u8D8A\u754C",
2155
+ cli_generateExists: "\u6587\u4EF6\u5DF2\u5B58\u5728\uFF1A{path}\u3002\u4F7F\u7528 --force \u624D\u53EF\u8986\u76D6",
2156
+ cli_deploy_module_missing: "\u672A\u627E\u5230\u90E8\u7F72\u6A21\u5757\uFF0C\u8BF7\u5148\u5B89\u88C5 @h-ai/deploy\uFF1A",
2157
+ cli_deploy_load_credentials: "\u6B63\u5728\u52A0\u8F7D\u51ED\u8BC1\u2026",
2158
+ cli_deploy_credentials_failed: "\u52A0\u8F7D\u51ED\u8BC1\u5931\u8D25\uFF1A{message}",
2159
+ cli_deploy_credentials_loaded: "\u5DF2\u52A0\u8F7D {count} \u9879\u51ED\u8BC1",
2160
+ cli_deploy_load_config: "\u6B63\u5728\u52A0\u8F7D\u90E8\u7F72\u914D\u7F6E\u2026",
2161
+ cli_deploy_config_missing: "\u672A\u627E\u5230\u90E8\u7F72\u914D\u7F6E\uFF1A{path}",
2162
+ cli_deploy_config_hint: "\u8FD0\u884C hai add deploy \u751F\u6210\u90E8\u7F72\u914D\u7F6E\u6A21\u677F",
2163
+ cli_deploy_config_loaded: "\u90E8\u7F72\u914D\u7F6E\u5DF2\u52A0\u8F7D",
2164
+ cli_deploy_scanning: "\u6B63\u5728\u626B\u63CF\u5E94\u7528\u2026",
2165
+ cli_deploy_scan_failed: "\u626B\u63CF\u5931\u8D25\uFF1A{message}",
2166
+ cli_deploy_scanned: "\u5DF2\u626B\u63CF\uFF1A{name}\uFF08SvelteKit\uFF1A{sveltekit}\uFF0C\u670D\u52A1\uFF1A{services}\uFF09",
2167
+ cli_deploy_none: "\u65E0",
2168
+ cli_deploy_initializing: "\u6B63\u5728\u521D\u59CB\u5316\u90E8\u7F72\u6A21\u5757\u2026",
2169
+ cli_deploy_init_failed: "\u521D\u59CB\u5316\u5931\u8D25\uFF1A{message}",
2170
+ cli_deploy_initialized: "\u90E8\u7F72\u6A21\u5757\u5DF2\u521D\u59CB\u5316",
2171
+ cli_deploy_deploying: "\u6B63\u5728\u90E8\u7F72\u5E94\u7528\u2026",
2172
+ cli_deploy_failed: "\u90E8\u7F72\u5931\u8D25\uFF1A{message}",
2173
+ cli_deploy_success: "\u90E8\u7F72\u6210\u529F\uFF01",
2174
+ cli_deploy_url: " \u5730\u5740\uFF1A{url}",
2175
+ cli_deploy_id: " \u6807\u8BC6\uFF1A{id}",
2176
+ cli_deploy_env: " \u73AF\u5883\u53D8\u91CF\uFF1A{names}",
2177
+ cli_deploy_command_failed: "\u90E8\u7F72\u547D\u4EE4\u5931\u8D25"
2178
+ };
2179
+
2180
+ // src/cli-i18n.ts
2181
+ var cliM = core.i18n.createMessageGetter({ "en-US": en_US_default, "zh-CN": zh_CN_default });
2182
+
2183
+ // src/commands/cli-deploy.ts
2018
2184
  async function deployCommand(options) {
2019
2185
  const spinner = ora();
2020
2186
  const cwd = options.cwd ?? process3.cwd();
@@ -2023,67 +2189,68 @@ async function deployCommand(options) {
2023
2189
  try {
2024
2190
  let deployModule;
2025
2191
  try {
2026
- deployModule = await import('./src-QNEJJ3BV.js');
2192
+ deployModule = await import('./src-6ZBWDBUB.js');
2027
2193
  } catch {
2028
- core.logger.error(chalk.red("Deploy module not found. Install @h-ai/deploy first:"));
2194
+ core.logger.error(chalk.red(cliM("cli_deploy_module_missing")));
2029
2195
  core.logger.info(chalk.cyan(" pnpm add @h-ai/deploy"));
2030
- return;
2196
+ throw new Error(cliM("cli_deploy_module_missing"));
2031
2197
  }
2032
2198
  const { deploy } = deployModule;
2033
- spinner.start("Loading credentials...");
2199
+ spinner.start(cliM("cli_deploy_load_credentials"));
2034
2200
  const credResult = deploy.credentials.load();
2035
2201
  if (!credResult.success) {
2036
- spinner.fail(chalk.red(`Failed to load credentials: ${credResult.error.message}`));
2037
- return;
2202
+ throw new Error(cliM("cli_deploy_credentials_failed", { params: { message: credResult.error.message } }));
2038
2203
  }
2039
- spinner.succeed(`Loaded ${credResult.data.length} credentials`);
2040
- spinner.start("Loading deploy config...");
2204
+ spinner.succeed(cliM("cli_deploy_credentials_loaded", { params: { count: credResult.data.length } }));
2205
+ spinner.start(cliM("cli_deploy_load_config"));
2041
2206
  const configPath = resolve(appDir, "config", "_deploy.yml");
2042
2207
  if (!existsSync(configPath)) {
2043
- spinner.fail(chalk.red(`Deploy config not found: ${configPath}`));
2044
- core.logger.info(chalk.cyan(" Run: hai add deploy to generate config template"));
2045
- return;
2208
+ spinner.fail(chalk.red(cliM("cli_deploy_config_missing", { params: { path: configPath } })));
2209
+ core.logger.info(chalk.cyan(cliM("cli_deploy_config_hint")));
2210
+ throw new Error(cliM("cli_deploy_config_missing", { params: { path: configPath } }));
2046
2211
  }
2047
2212
  const configContent = readFileSync(configPath, "utf-8");
2048
2213
  const rawConfig = interpolateEnvFallback(configContent);
2049
2214
  const deployConfig = parse(rawConfig);
2050
- spinner.succeed("Deploy config loaded");
2051
- spinner.start("Scanning application...");
2215
+ spinner.succeed(cliM("cli_deploy_config_loaded"));
2216
+ spinner.start(cliM("cli_deploy_scanning"));
2052
2217
  const scanResult = await deploy.scan(appDir);
2053
2218
  if (!scanResult.success) {
2054
- spinner.fail(chalk.red(`Scan failed: ${scanResult.error.message}`));
2055
- return;
2219
+ throw new Error(cliM("cli_deploy_scan_failed", { params: { message: scanResult.error.message } }));
2056
2220
  }
2057
2221
  const scan = scanResult.data;
2058
- spinner.succeed(`Scanned: ${scan.appName} (SvelteKit: ${scan.isSvelteKit}, Services: ${scan.requiredServices.join(", ") || "none"})`);
2059
- spinner.start("Initializing deploy module...");
2222
+ spinner.succeed(cliM("cli_deploy_scanned", { params: { name: scan.appName, sveltekit: String(scan.isSvelteKit), services: scan.requiredServices.join(", ") || cliM("cli_deploy_none") } }));
2223
+ spinner.start(cliM("cli_deploy_initializing"));
2060
2224
  const initResult = await deploy.init(deployConfig);
2061
2225
  if (!initResult.success) {
2062
- spinner.fail(chalk.red(`Init failed: ${initResult.error.message}`));
2063
- return;
2226
+ throw new Error(cliM("cli_deploy_init_failed", { params: { message: initResult.error.message } }));
2064
2227
  }
2065
2228
  closeDeploy = () => deploy.close();
2066
- spinner.succeed("Deploy module initialized");
2067
- spinner.start("Deploying application...");
2229
+ spinner.succeed(cliM("cli_deploy_initialized"));
2230
+ spinner.start(cliM("cli_deploy_deploying"));
2068
2231
  const deployResult = await deploy.deployApp(appDir, {
2069
2232
  projectName: options.projectName,
2070
2233
  skipProvision: options.skipProvision,
2071
2234
  skipBuild: options.skipBuild
2072
2235
  });
2073
2236
  if (!deployResult.success) {
2074
- spinner.fail(chalk.red(`Deploy failed: ${deployResult.error.message}`));
2075
- return;
2237
+ if (deployResult.error.ext?.recovery) {
2238
+ core.logger.warn(deployResult.error.suggestion ?? cliM("cli_deploy_command_failed"), {
2239
+ recovery: deployResult.error.ext.recovery
2240
+ });
2241
+ }
2242
+ throw new Error(cliM("cli_deploy_failed", { params: { message: deployResult.error.message } }));
2076
2243
  }
2077
- spinner.succeed(chalk.green("Deployment successful!"));
2244
+ spinner.succeed(chalk.green(cliM("cli_deploy_success")));
2078
2245
  core.logger.info("");
2079
- core.logger.info(chalk.cyan(` URL: ${deployResult.data.url}`));
2080
- core.logger.info(chalk.gray(` ID: ${deployResult.data.deploymentId}`));
2246
+ core.logger.info(chalk.cyan(cliM("cli_deploy_url", { params: { url: deployResult.data.url } })));
2247
+ core.logger.info(chalk.gray(cliM("cli_deploy_id", { params: { id: deployResult.data.deploymentId } })));
2081
2248
  if (deployResult.data.envVarsSet.length > 0) {
2082
- core.logger.info(chalk.gray(` Env: ${deployResult.data.envVarsSet.join(", ")}`));
2249
+ core.logger.info(chalk.gray(cliM("cli_deploy_env", { params: { names: deployResult.data.envVarsSet.join(", ") } })));
2083
2250
  }
2084
2251
  core.logger.info("");
2085
2252
  } catch (error) {
2086
- spinner.fail(chalk.red("Deploy command failed"));
2253
+ spinner.fail(chalk.red(cliM("cli_deploy_command_failed")));
2087
2254
  core.logger.error(error instanceof Error ? error.message : String(error));
2088
2255
  throw error;
2089
2256
  } finally {
@@ -2092,6 +2259,7 @@ async function deployCommand(options) {
2092
2259
  await closeDeploy();
2093
2260
  } catch (error) {
2094
2261
  core.logger.error("Failed to close deploy module", { error });
2262
+ process3.exitCode = 1;
2095
2263
  }
2096
2264
  }
2097
2265
  }
@@ -2141,24 +2309,41 @@ async function generate(options) {
2141
2309
  if (!generator) {
2142
2310
  throw new Error(`\u672A\u77E5\u7684\u751F\u6210\u5668\u7C7B\u578B: ${resolvedOptions.type}`);
2143
2311
  }
2312
+ if (!/^[a-z][a-z0-9]*(?:[-_][a-z0-9]+)*$/i.test(resolvedOptions.name))
2313
+ throw new Error(cliM("cli_invalidGenerateName"));
2144
2314
  const outputDir = path2.resolve(cwd, resolvedOptions.output || generator.defaultPath);
2145
2315
  const resolvedCwd = path2.resolve(cwd);
2146
2316
  if (!outputDir.startsWith(resolvedCwd + path2.sep) && outputDir !== resolvedCwd) {
2147
- core.logger.error(chalk.red(`\u8F93\u51FA\u8DEF\u5F84 "${resolvedOptions.output}" \u8D85\u51FA\u5DE5\u4F5C\u76EE\u5F55\u8303\u56F4`));
2148
- return;
2317
+ throw new Error(cliM("cli_generateOutsideWorkspace"));
2149
2318
  }
2150
2319
  const context = createTemplateContext(resolvedOptions.name, {
2151
2320
  projectName: project?.name
2152
2321
  });
2153
2322
  spinner.start(`\u751F\u6210 ${generator.description}...`);
2154
- const files = await generator.generate(resolvedOptions.name, context, outputDir);
2323
+ const files = await generator.generate(context, outputDir);
2324
+ const realCwd = await fse.realpath(resolvedCwd);
2325
+ for (const file of files) {
2326
+ if (!resolvedOptions.force && await fse.pathExists(file.path))
2327
+ throw new Error(cliM("cli_generateExists", { params: { path: file.path } }));
2328
+ let ancestor = file.path;
2329
+ while (!await fse.pathExists(ancestor))
2330
+ ancestor = path2.dirname(ancestor);
2331
+ const realAncestor = await fse.realpath(ancestor);
2332
+ const relative = path2.relative(realCwd, realAncestor);
2333
+ if (relative === ".." || relative.startsWith(`..${path2.sep}`) || path2.isAbsolute(relative))
2334
+ throw new Error(cliM("cli_generateOutsideWorkspace"));
2335
+ }
2336
+ for (const file of files) {
2337
+ await fse.ensureDir(path2.dirname(file.path));
2338
+ await fse.writeFile(file.path, file.content, { encoding: "utf-8", flag: resolvedOptions.force ? "w" : "wx" });
2339
+ }
2155
2340
  spinner.succeed();
2156
2341
  core.logger.info("", {});
2157
2342
  core.logger.info(chalk.green("\u2714 \u751F\u6210\u5B8C\u6210\uFF01"));
2158
2343
  core.logger.info("", {});
2159
2344
  core.logger.info("\u521B\u5EFA\u7684\u6587\u4EF6:");
2160
2345
  files.forEach((file) => {
2161
- core.logger.info(chalk.cyan(` ${path2.relative(cwd, file)}`));
2346
+ core.logger.info(chalk.cyan(` ${path2.relative(cwd, file.path)}`));
2162
2347
  });
2163
2348
  core.logger.info("", {});
2164
2349
  } catch (error) {
@@ -2202,76 +2387,72 @@ async function resolveGenerateOptions(options) {
2202
2387
  cwd: options.cwd ?? "."
2203
2388
  };
2204
2389
  }
2205
- async function generatePage(_name, context, outputDir) {
2390
+ async function generatePage(context, outputDir) {
2206
2391
  const files = [];
2207
2392
  const pageDir = path2.join(outputDir, context.kebabCase);
2208
- const pageContent = `<script lang="ts">
2209
- /**
2210
- * ${context.pascalCase} \u9875\u9762
2211
- */
2212
- </script>
2213
-
2393
+ const pageContent = `<!-- ${context.pascalCase} \u9875\u9762 -->
2214
2394
  <svelte:head>
2215
2395
  <title>${context.pascalCase}</title>
2216
2396
  </svelte:head>
2217
2397
 
2218
- <div class="container mx-auto p-4">
2219
- <h1 class="text-2xl font-bold mb-4">${context.pascalCase}</h1>
2220
-
2398
+ <div class='container mx-auto p-4'>
2399
+ <h1 class='text-2xl font-bold mb-4'>${context.pascalCase}</h1>
2400
+
2221
2401
  <!-- \u9875\u9762\u5185\u5BB9 -->
2222
2402
  </div>
2223
2403
  `;
2224
2404
  const pagePath = path2.join(pageDir, "+page.svelte");
2225
- await writeFile(pagePath, pageContent);
2226
- files.push(pagePath);
2405
+ files.push({ path: pagePath, content: pageContent });
2227
2406
  const serverContent = `/**
2228
2407
  * ${context.pascalCase} \u9875\u9762\u670D\u52A1\u7AEF
2229
2408
  */
2230
- import type { PageServerLoad, Actions } from './$types'
2409
+ import type { Actions, PageServerLoad } from './$types'
2231
2410
 
2232
- export const load: PageServerLoad = async ({ locals }) => {
2411
+ export const load: PageServerLoad = async () => {
2233
2412
  return {
2234
2413
  // \u9875\u9762\u6570\u636E
2235
2414
  }
2236
2415
  }
2237
2416
 
2238
2417
  export const actions: Actions = {
2239
- default: async ({ request, locals }) => {
2418
+ default: async () => {
2240
2419
  // \u8868\u5355\u5904\u7406
2241
2420
  },
2242
2421
  }
2243
2422
  `;
2244
2423
  const serverPath = path2.join(pageDir, "+page.server.ts");
2245
- await writeFile(serverPath, serverContent);
2246
- files.push(serverPath);
2424
+ files.push({ path: serverPath, content: serverContent });
2247
2425
  return files;
2248
2426
  }
2249
- async function generateComponent(_name, context, outputDir) {
2427
+ async function generateComponent(context, outputDir) {
2250
2428
  const files = [];
2251
- const componentContent = `<script lang="ts">
2429
+ const componentContent = `<script lang='ts'>
2430
+ import type { Snippet } from 'svelte'
2431
+
2252
2432
  /**
2253
2433
  * ${context.pascalCase} \u7EC4\u4EF6
2254
2434
  */
2255
-
2435
+
2256
2436
  interface Props {
2257
2437
  /** \u81EA\u5B9A\u4E49 class */
2258
2438
  class?: string
2439
+ /** \u5B50\u5185\u5BB9\uFF08Svelte 5 snippet\uFF09 */
2440
+ children?: Snippet
2259
2441
  }
2260
-
2261
- let { class: className = '' }: Props = $props()
2442
+
2443
+ const { class: className = '', children }: Props = $props()
2262
2444
  </script>
2263
2445
 
2264
2446
  <div class={className}>
2265
2447
  <!-- \u7EC4\u4EF6\u5185\u5BB9 -->
2266
- <slot />
2448
+ {@render children?.()}
2267
2449
  </div>
2268
2450
  `;
2269
2451
  const componentPath = path2.join(outputDir, `${context.pascalCase}.svelte`);
2270
- await writeFile(componentPath, componentContent);
2271
- files.push(componentPath);
2452
+ files.push({ path: componentPath, content: componentContent });
2272
2453
  return files;
2273
2454
  }
2274
- async function generateApi(_name, context, outputDir) {
2455
+ async function generateApi(context, outputDir) {
2275
2456
  const files = [];
2276
2457
  const apiDir = path2.join(outputDir, context.kebabCase);
2277
2458
  const serverContent = `/**
@@ -2279,44 +2460,35 @@ async function generateApi(_name, context, outputDir) {
2279
2460
  */
2280
2461
  import type { RequestHandler } from './$types'
2281
2462
  import { kit } from '@h-ai/kit'
2463
+ import { z } from 'zod'
2464
+
2465
+ /** \u793A\u4F8B\u8F93\u5165\uFF1B\u6DFB\u52A0\u4E1A\u52A1\u903B\u8F91\u524D\u6269\u5C55\u5B57\u6BB5\u548C\u7EA6\u675F\u3002 */
2466
+ const InputSchema = z.object({
2467
+ /** \u4E1A\u52A1\u540D\u79F0\uFF08\u975E\u7A7A\uFF09 */
2468
+ name: z.string().min(1),
2469
+ })
2282
2470
 
2283
2471
  /**
2284
2472
  * GET ${context.kebabCase}
2285
2473
  */
2286
- export const GET: RequestHandler = async ({ locals }) => {
2287
- try {
2288
- const { requestId } = locals
2289
-
2290
- return kit.response.ok({ message: 'Hello from ${context.kebabCase}' }, requestId)
2291
- }
2292
- catch (error) {
2293
- return kit.response.internalError()
2294
- }
2295
- }
2474
+ export const GET: RequestHandler = kit.handler(async ({ locals }) => {
2475
+ return kit.response.ok({ items: [] }, locals.requestId)
2476
+ })
2296
2477
 
2297
2478
  /**
2298
2479
  * POST ${context.kebabCase}
2299
2480
  */
2300
- export const POST: RequestHandler = async ({ request, locals }) => {
2301
- try {
2302
- const { requestId } = locals
2303
- const body = await request.json()
2304
-
2305
- // \u5904\u7406\u8BF7\u6C42
2306
-
2307
- return kit.response.ok({ success: true }, requestId)
2308
- }
2309
- catch (error) {
2310
- return kit.response.internalError()
2311
- }
2312
- }
2481
+ export const POST: RequestHandler = kit.handler(async ({ request, locals }) => {
2482
+ const body = await kit.validate.body(request, InputSchema)
2483
+ // \u6821\u9A8C\u901A\u8FC7\u540E\u518D\u6267\u884C\u4E1A\u52A1\u64CD\u4F5C\uFF1B\u793A\u4F8B\u4EC5\u56DE\u4F20\u6570\u636E\uFF0C\u4E0D\u6267\u884C\u5199\u5165\u3002
2484
+ return kit.response.ok(body, locals.requestId)
2485
+ })
2313
2486
  `;
2314
2487
  const serverPath = path2.join(apiDir, "+server.ts");
2315
- await writeFile(serverPath, serverContent);
2316
- files.push(serverPath);
2488
+ files.push({ path: serverPath, content: serverContent });
2317
2489
  return files;
2318
2490
  }
2319
- async function generateModel(_name, context, outputDir) {
2491
+ async function generateModel(context, outputDir) {
2320
2492
  const files = [];
2321
2493
  const modelContent = `/**
2322
2494
  * ${context.pascalCase} \u6A21\u578B
@@ -2327,9 +2499,13 @@ import { z } from 'zod'
2327
2499
  * ${context.pascalCase} Schema
2328
2500
  */
2329
2501
  export const ${context.camelCase}Schema = z.object({
2502
+ /** \u8BB0\u5F55\u552F\u4E00\u6807\u8BC6 */
2330
2503
  id: z.string(),
2504
+ /** \u663E\u793A\u540D\u79F0 */
2331
2505
  name: z.string().min(1),
2506
+ /** \u521B\u5EFA\u65F6\u95F4 */
2332
2507
  createdAt: z.date(),
2508
+ /** \u6700\u540E\u4FEE\u6539\u65F6\u95F4 */
2333
2509
  updatedAt: z.date(),
2334
2510
  })
2335
2511
 
@@ -2355,39 +2531,35 @@ export type Create${context.pascalCase} = z.infer<typeof create${context.pascalC
2355
2531
  export type Update${context.pascalCase} = z.infer<typeof update${context.pascalCase}Schema>
2356
2532
  `;
2357
2533
  const modelPath = path2.join(outputDir, `${context.kebabCase}.ts`);
2358
- await writeFile(modelPath, modelContent);
2359
- files.push(modelPath);
2534
+ files.push({ path: modelPath, content: modelContent });
2360
2535
  return files;
2361
2536
  }
2362
- async function generateMigration(_name, context, outputDir) {
2537
+ async function generateMigration(context, outputDir) {
2363
2538
  const files = [];
2364
2539
  const timestamp = Date.now();
2365
2540
  const migrationContent = `/**
2366
2541
  * \u8FC1\u79FB: ${context.pascalCase}
2367
2542
  * \u65F6\u95F4: ${(/* @__PURE__ */ new Date()).toISOString()}
2368
2543
  */
2369
- import type { MigrationFn } from '@h-ai/reldb'
2544
+ import { reldb } from '@h-ai/reldb'
2370
2545
 
2371
- export const up: MigrationFn = async (db) => {
2372
- // \u5347\u7EA7\u903B\u8F91
2373
- await db.run(\`
2374
- CREATE TABLE IF NOT EXISTS ${context.snakeCase} (
2375
- id TEXT PRIMARY KEY,
2376
- name TEXT NOT NULL,
2377
- created_at INTEGER NOT NULL,
2378
- updated_at INTEGER NOT NULL
2379
- )
2380
- \`)
2546
+ /** \u5347\u7EA7\uFF1A\u8C03\u7528\u524D\u521D\u59CB\u5316 reldb\uFF0C\u8C03\u7528\u65B9\u5FC5\u987B\u68C0\u67E5\u8FD4\u56DE\u7684 HaiResult\u3002 */
2547
+ export function up() {
2548
+ return reldb.ddl.createTable('hai_app_${context.snakeCase}', {
2549
+ id: { type: 'TEXT', primaryKey: true },
2550
+ name: { type: 'TEXT', notNull: true },
2551
+ created_at: { type: 'TIMESTAMP', notNull: true },
2552
+ updated_at: { type: 'TIMESTAMP', notNull: true },
2553
+ })
2381
2554
  }
2382
2555
 
2383
- export const down: MigrationFn = async (db) => {
2384
- // \u56DE\u6EDA\u903B\u8F91
2385
- await db.run('DROP TABLE IF EXISTS ${context.snakeCase}')
2556
+ /** \u56DE\u6EDA\u4F1A\u5220\u9664\u6574\u5F20\u8868\uFF1B\u7531\u5E94\u7528\u786E\u8BA4\u6570\u636E\u53EF\u5220\u9664\u540E\u663E\u5F0F\u8C03\u7528\u3002 */
2557
+ export function down() {
2558
+ return reldb.ddl.dropTable('hai_app_${context.snakeCase}')
2386
2559
  }
2387
2560
  `;
2388
2561
  const migrationPath = path2.join(outputDir, `${timestamp}_${context.snakeCase}.ts`);
2389
- await writeFile(migrationPath, migrationContent);
2390
- files.push(migrationPath);
2562
+ files.push({ path: migrationPath, content: migrationContent });
2391
2563
  return files;
2392
2564
  }
2393
2565
  var MODULE_CONFIG_MAP = {