@h-ai/cli 0.1.0-alpha.50 → 0.1.0-alpha.53

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 (34) hide show
  1. package/dist/index.js +155 -2
  2. package/dist/index.js.map +1 -1
  3. package/package.json +2 -2
  4. package/templates/apps/api/.dockerignore.hbs +14 -0
  5. package/templates/apps/api/Dockerfile.hbs +37 -0
  6. package/templates/apps/api/apps/{{projectName}}-service/README.md.hbs +6 -0
  7. package/templates/apps/api/apps/{{projectName}}-service/config/_core.yml.hbs +2 -2
  8. package/templates/apps/api/apps/{{projectName}}-service/config/_serv.yml.hbs +4 -4
  9. package/templates/apps/api/apps/{{projectName}}-service/data/.gitkeep.hbs +1 -0
  10. package/templates/apps/api/apps/{{projectName}}-service/package.json.hbs +2 -0
  11. package/templates/apps/api/package.json.hbs +4 -0
  12. package/templates/apps/fullstack/.dockerignore.hbs +16 -0
  13. package/templates/apps/fullstack/Dockerfile.hbs +58 -0
  14. package/templates/apps/fullstack/apps/{{projectName}}-web/index.html.hbs +2 -1
  15. package/templates/apps/fullstack/apps/{{projectName}}-web/public/runtime-config.js.hbs +1 -0
  16. package/templates/apps/fullstack/apps/{{projectName}}-web/tests/api.test.ts.hbs +27 -0
  17. package/templates/apps/fullstack/docker/19-runtime-config.envsh.hbs +5 -0
  18. package/templates/apps/fullstack/docker/nginx.conf.hbs +24 -0
  19. package/templates/apps/fullstack/docker/runtime-config.js.template.hbs +3 -0
  20. package/templates/apps/fullstack/package.json.hbs +6 -0
  21. package/templates/apps/fullstack/packages/{{projectName}}-serv/README.md.hbs +8 -0
  22. package/templates/apps/fullstack/packages/{{projectName}}-serv/config/_core.yml.hbs +2 -2
  23. package/templates/apps/fullstack/packages/{{projectName}}-serv/config/_serv.yml.hbs +3 -3
  24. package/templates/apps/fullstack/packages/{{projectName}}-serv/data/.gitkeep.hbs +1 -0
  25. package/templates/apps/fullstack/packages/{{projectName}}-serv/package.json.hbs +6 -0
  26. package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/api/api-client.ts.hbs +10 -1
  27. package/templates/base/.dockerignore.hbs +17 -0
  28. package/templates/base/.gitignore.hbs +2 -1
  29. package/templates/base/Dockerfile.hbs +62 -0
  30. package/templates/base/docker/nginx.conf.hbs +18 -0
  31. package/templates/base/package.json.hbs +16 -6
  32. package/templates/base/svelte.config.js.hbs +0 -8
  33. package/templates/skills/hai-ai/SKILL.md +4 -0
  34. package/templates/skills/hai-app-create/SKILL.md +15 -0
package/dist/index.js CHANGED
@@ -1110,7 +1110,7 @@ PORT=3000
1110
1110
  resolvedOptions.name
1111
1111
  );
1112
1112
  const appTypeLabel = APP_TYPES[resolvedOptions.appType].name;
1113
- const readmeContent = resolvedOptions.appType === "fullstack" ? generateFullstackReadme(resolvedOptions.name, resolvedOptions.packageManager, resolvedOptions.frontends) : resolvedOptions.appType === "api" ? generateApiWorkspaceReadme(resolvedOptions.name, resolvedOptions.packageManager) : generateReadme(resolvedOptions.name, appTypeLabel, resolvedOptions.packageManager);
1113
+ const readmeContent = resolvedOptions.appType === "fullstack" ? generateFullstackReadme(resolvedOptions.name, resolvedOptions.packageManager, resolvedOptions.frontends) : resolvedOptions.appType === "api" ? generateApiWorkspaceReadme(resolvedOptions.name, resolvedOptions.packageManager) : generateReadme(resolvedOptions.name, appTypeLabel, resolvedOptions.packageManager, resolvedOptions.appType);
1114
1114
  await writeFile(
1115
1115
  path2.join(projectPath, "README.md"),
1116
1116
  readmeContent
@@ -1612,8 +1612,25 @@ function normalizeChildOutput(output) {
1612
1612
  }
1613
1613
  return "";
1614
1614
  }
1615
- function generateReadme(name, appTypeLabel, pm) {
1615
+ function generateReadme(name, appTypeLabel, pm, appType) {
1616
1616
  const install = getInstallCommand(pm);
1617
+ const isStaticImage = appType === "mobile-app";
1618
+ const containerPort = isStaticImage ? 8080 : 3e3;
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 embeddedDataBuild = isStaticImage ? "" : `
1621
+ # \u5E26 data/ \u521D\u59CB\u6570\u636E\u6784\u5EFA\uFF08\u4E8C\u9009\u4E00\uFF09
1622
+ ${getRunCommand(pm, "docker:build:data")}
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
1625
+
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" : "/";
1617
1634
  return `# ${name}
1618
1635
 
1619
1636
  \u57FA\u4E8E hai Agent Framework \u6784\u5EFA\u7684${appTypeLabel}\u5E94\u7528\u3002
@@ -1633,6 +1650,32 @@ ${getRunCommand(pm, "i18n:compile")}
1633
1650
  ${getRunCommand(pm, "preview")}
1634
1651
  \`\`\`
1635
1652
 
1653
+ ## \u5BB9\u5668\u8FD0\u884C
1654
+
1655
+ ### \u6784\u5EFA\u955C\u50CF
1656
+
1657
+ \`\`\`bash
1658
+ # \u666E\u901A\u6784\u5EFA\uFF08\u4E8C\u9009\u4E00\uFF09
1659
+ ${getRunCommand(pm, "docker:build")}
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
1668
+ \`\`\`
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}
1678
+
1636
1679
  ## \u6587\u6863
1637
1680
 
1638
1681
  - [hai Agent Framework](https://github.com/hai-series/hai-framework)
@@ -1666,6 +1709,51 @@ ${pm} test
1666
1709
  ${pm} test:e2e
1667
1710
  \`\`\`
1668
1711
 
1712
+ ## \u5BB9\u5668\u8FD0\u884C
1713
+
1714
+ ### \u6784\u5EFA\u955C\u50CF
1715
+
1716
+ \`\`\`bash
1717
+ # \u666E\u901A\u6784\u5EFA\uFF08\u4E8C\u9009\u4E00\uFF09
1718
+ ${pm} docker:build
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 \\
1731
+ --mount type=bind,source=/absolute/path/to/config,target=/app/config,readonly \\
1732
+ --mount type=volume,source=${name}-data,target=/app/data \\
1733
+ ${name}:latest
1734
+ \`\`\`
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
+
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
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 |
1756
+
1669
1757
  ## \u6269\u5C55\u5EFA\u8BAE
1670
1758
 
1671
1759
  - \u5728 \`${name}-contract\` \u4E2D\u7EF4\u62A4\u5E94\u7528\u7EA7 contract\uFF0C\u524D\u540E\u7AEF\u5171\u7528\u540C\u4E00\u4EFD\u7C7B\u578B\u4E0E\u8DEF\u5F84\u5B9A\u4E49\u3002
@@ -1682,6 +1770,20 @@ ${pm} test:e2e
1682
1770
  }
1683
1771
  function generateFullstackReadme(name, pm, frontends) {
1684
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;
1685
1787
  return `# ${name}
1686
1788
 
1687
1789
  \u57FA\u4E8E hai Agent Framework \u6784\u5EFA\u7684\u524D\u540E\u7AEF\u5206\u79BB\u591A\u5305\u5DE5\u7A0B\u3002
@@ -1714,6 +1816,57 @@ ${pm} package
1714
1816
  ${pm} deploy
1715
1817
  \`\`\`
1716
1818
 
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
1822
+
1823
+ ### \u6784\u5EFA\u955C\u50CF
1824
+
1825
+ \`\`\`bash
1826
+ # Service \u666E\u901A\u6784\u5EFA\uFF08\u4E8C\u9009\u4E00\uFF09
1827
+ ${pm} docker:build
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 \\
1843
+ ${name}:latest
1844
+ ${webRunCommand}
1845
+ \`\`\`
1846
+
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 |
1869
+
1717
1870
  \u524D\u7AEF\u5355\u72EC\u4EA4\u4ED8\u547D\u4EE4\uFF1A
1718
1871
 
1719
1872
  - Web\uFF1A\`${pm} --filter ${name}-web build\` / \`${pm} --filter ${name}-web package\`