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