@h-ai/cli 0.1.0-alpha.63 → 0.1.0-alpha.65
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 +20 -4
- package/README.ja.md +20 -4
- package/README.md +28 -12
- package/dist/chunk-47V5OT22.js +5639 -0
- package/dist/chunk-47V5OT22.js.map +1 -0
- package/dist/{chunk-X3RSYNBG.js → chunk-IPG3QNYK.js} +62 -4
- package/dist/chunk-IPG3QNYK.js.map +1 -0
- package/dist/cli-config-templates-SJ2MBIR7.js +5 -0
- package/dist/{cli-config-templates-QRF6B5T4.js.map → cli-config-templates-SJ2MBIR7.js.map} +1 -1
- package/dist/{cli-skill-templates-EZFW7D43.js → cli-skill-templates-A5VIR5J6.js} +3 -3
- package/dist/{cli-skill-templates-EZFW7D43.js.map → cli-skill-templates-A5VIR5J6.js.map} +1 -1
- package/dist/index.js +524 -66
- package/dist/index.js.map +1 -1
- package/dist/{src-DUGLX4YB.js → src-6PPBKQ6Q.js} +666 -290
- package/dist/src-6PPBKQ6Q.js.map +1 -0
- package/package.json +9 -7
- package/templates/apps/admin/src/routes/+layout.svelte.hbs +4 -2
- package/templates/apps/api/.dockerignore.hbs +5 -0
- package/templates/apps/api/.gitignore.hbs +4 -4
- package/templates/apps/api/Dockerfile.hbs +11 -1
- package/templates/apps/api/apps/{{projectName}}-contract/package.json.hbs +2 -0
- package/templates/apps/api/apps/{{projectName}}-service/config/_core.yml.hbs +2 -2
- package/templates/apps/api/apps/{{projectName}}-service/config/_serv.yml.hbs +4 -4
- package/templates/apps/api/apps/{{projectName}}-service/package.json.hbs +9 -2
- package/templates/apps/api/package.json.hbs +9 -0
- package/templates/apps/fullstack/.dockerignore.hbs +5 -3
- package/templates/apps/fullstack/.gitignore.hbs +22 -9
- package/templates/apps/fullstack/Dockerfile.hbs +11 -1
- package/templates/apps/fullstack/apps/{{projectName}}-app/package.json.hbs +6 -0
- package/templates/apps/fullstack/apps/{{projectName}}-app/vite.config.ts.hbs +7 -3
- package/templates/apps/fullstack/apps/{{projectName}}-desktop/package.json.hbs +5 -0
- package/templates/apps/fullstack/apps/{{projectName}}-desktop/vite.config.ts.hbs +7 -3
- package/templates/apps/fullstack/apps/{{projectName}}-web/package.json.hbs +6 -0
- package/templates/apps/fullstack/apps/{{projectName}}-web/vite.config.ts.hbs +7 -3
- package/templates/apps/fullstack/e2e/fullstack.spec.ts.hbs +93 -1
- package/templates/apps/fullstack/package.json.hbs +12 -0
- package/templates/apps/fullstack/packages/{{projectName}}-contract/README.md.hbs +2 -2
- package/templates/apps/fullstack/packages/{{projectName}}-contract/package.json.hbs +2 -0
- package/templates/apps/fullstack/packages/{{projectName}}-contract/src/admin-contract.ts.hbs +1 -1
- package/templates/apps/fullstack/packages/{{projectName}}-contract/tests/contract.test.ts.hbs +8 -14
- package/templates/apps/fullstack/packages/{{projectName}}-serv/README.md.hbs +6 -4
- package/templates/apps/fullstack/packages/{{projectName}}-serv/config/_reach.yml.hbs +8 -8
- package/templates/apps/fullstack/packages/{{projectName}}-serv/config/_serv.yml.hbs +3 -3
- package/templates/apps/fullstack/packages/{{projectName}}-serv/package.json.hbs +9 -2
- package/templates/apps/fullstack/packages/{{projectName}}-serv/src/app-server.ts.hbs +4 -4
- package/templates/apps/fullstack/packages/{{projectName}}-shared/README.md.hbs +6 -2
- package/templates/apps/fullstack/packages/{{projectName}}-shared/package.json.hbs +2 -0
- package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/components/AdminWorkspace.svelte.hbs +19 -13
- package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/components/AppShell.svelte.hbs +1 -1
- package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/pages/settings.svelte.hbs +46 -0
- package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/stores/management-store.svelte.ts.hbs +17 -3
- package/templates/apps/fullstack/packages/{{projectName}}-shared/tests/management-store.test.ts.hbs +15 -5
- package/templates/apps/fullstack/playwright.config.ts.hbs +6 -1
- package/templates/apps/fullstack/pnpm-workspace.yaml.hbs +0 -1
- package/templates/apps/h5/src/routes/+layout.svelte.hbs +13 -13
- package/templates/apps/mobile-app/src/App.svelte.hbs +46 -117
- package/templates/apps/mobile-app/src/lib/pages/home.svelte.hbs +58 -0
- package/templates/apps/mobile-app/src/lib/pages/profile.svelte.hbs +13 -0
- package/templates/apps/mobile-app/src/lib/pages/tasks.svelte.hbs +16 -0
- package/templates/base/.dockerignore.hbs +5 -0
- package/templates/base/.gitignore.hbs +18 -4
- package/templates/base/Dockerfile.hbs +17 -1
- package/templates/base/e2e/app.spec.ts.hbs +14 -0
- package/templates/base/package.json.hbs +14 -2
- package/templates/base/tests/scaffold.test.ts.hbs +9 -3
- package/templates/base/vite.config.ts.hbs +6 -4
- package/templates/shared/hash-router.svelte.ts +83 -0
- package/templates/skills/AGENTS.md +15 -1
- package/templates/skills/bridges/admin/AGENTS.md +17 -1
- package/templates/skills/bridges/api/AGENTS.md +17 -3
- package/templates/skills/bridges/fullstack/AGENTS.md.hbs +35 -11
- package/templates/skills/bridges/h5/AGENTS.md +17 -1
- package/templates/skills/bridges/mobile-app/AGENTS.md +19 -3
- package/templates/skills/bridges/website/AGENTS.md +17 -1
- package/templates/skills/hai-ai/reference.md +2 -2
- package/templates/skills/hai-app-create/SKILL.md +5 -3
- package/templates/skills/hai-app-create/development.md +126 -2
- package/templates/skills/hai-app-create/reference.md +2 -2
- package/templates/skills/hai-app-review/SKILL.md +2 -2
- package/templates/skills/hai-app-tests/SKILL.md +2 -1
- package/templates/skills/hai-app-tests/reference.md +1 -1
- package/templates/skills/hai-core/SKILL.md +2 -1
- package/templates/skills/hai-deploy/SKILL.md +42 -34
- package/templates/skills/hai-deploy/middleware.md +94 -0
- package/templates/skills/hai-framework-sync/SKILL.md +1 -1
- package/templates/skills/hai-framework-sync/sync.md +1 -1
- package/templates/skills/hai-reach/reference.md +8 -8
- package/templates/skills/hai-spec-implement/SKILL.md +12 -9
- package/templates/skills/hai-spec-implement/changes.md +67 -0
- package/templates/skills/hai-spec-implement/templates.md +25 -3
- package/templates/skills/hai-spec-implement/workflow.md +7 -6
- package/templates/skills/hai-spec-review/SKILL.md +14 -5
- package/templates/skills/hai-spec-review/checklist.md +4 -3
- package/templates/skills/hai-storage/SKILL.md +2 -0
- package/templates/skills/hai-ui/SKILL.md +4 -0
- package/dist/chunk-WW2MIO6T.js +0 -992
- package/dist/chunk-WW2MIO6T.js.map +0 -1
- package/dist/chunk-X3RSYNBG.js.map +0 -1
- package/dist/cli-config-templates-QRF6B5T4.js +0 -5
- package/dist/src-DUGLX4YB.js.map +0 -1
- package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/components/ProfileSettings.svelte.hbs +0 -57
- /package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/{components/AuditManagement.svelte.hbs → pages/audit.svelte.hbs} +0 -0
- /package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/{components/RoleManagement.svelte.hbs → pages/roles.svelte.hbs} +0 -0
- /package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/{components/UserManagement.svelte.hbs → pages/users.svelte.hbs} +0 -0
package/dist/index.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { generateConfigFile } from './chunk-
|
|
3
|
-
import { cliM } from './chunk-
|
|
2
|
+
import { generateConfigFile, runtimeEnvironmentVariables, renderEnvironment, moduleEnvironmentVariables, parameterizeConfig } from './chunk-47V5OT22.js';
|
|
3
|
+
import { cliM } from './chunk-IPG3QNYK.js';
|
|
4
4
|
import './chunk-2H7UOFLK.js';
|
|
5
|
-
import
|
|
5
|
+
import process3 from 'process';
|
|
6
6
|
import { cac } from 'cac';
|
|
7
7
|
import chalk from 'chalk';
|
|
8
|
-
import path2, { resolve } from 'path';
|
|
9
|
-
import { fileURLToPath } from 'url';
|
|
8
|
+
import path2, { resolve, join } from 'path';
|
|
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';
|
|
@@ -16,24 +16,25 @@ import prompts from 'prompts';
|
|
|
16
16
|
import { Buffer } from 'buffer';
|
|
17
17
|
import { execSync, execFileSync } from 'child_process';
|
|
18
18
|
import { randomBytes } from 'crypto';
|
|
19
|
-
import { stringify,
|
|
20
|
-
import { existsSync, readFileSync } from 'fs';
|
|
19
|
+
import { stringify, parseDocument } from 'yaml';
|
|
20
|
+
import { existsSync, readFileSync, writeFileSync, mkdirSync } from 'fs';
|
|
21
|
+
import { createRequire } from 'module';
|
|
21
22
|
|
|
22
23
|
var CAMEL_CASE_SEPARATOR_REGEX = /[-_\s]+(.)?/g;
|
|
23
24
|
var LEADING_CHAR_REGEX = /^./;
|
|
24
25
|
function writeCliMessage(message) {
|
|
25
|
-
|
|
26
|
+
process3.stdout.write(`${message}
|
|
26
27
|
`);
|
|
27
28
|
}
|
|
28
29
|
function writeCliError(message, error) {
|
|
29
|
-
|
|
30
|
+
process3.stderr.write(`${message}
|
|
30
31
|
`);
|
|
31
32
|
if (error !== void 0)
|
|
32
|
-
|
|
33
|
+
process3.stderr.write(`${error instanceof Error ? error.message : String(error)}
|
|
33
34
|
`);
|
|
34
35
|
}
|
|
35
36
|
function formatPromptDescription(description) {
|
|
36
|
-
return wrapAnsi(description, Math.max(1, (
|
|
37
|
+
return wrapAnsi(description, Math.max(1, (process3.stdout.columns || 80) - 32), { hard: true });
|
|
37
38
|
}
|
|
38
39
|
function toCamelCase(str) {
|
|
39
40
|
return str.replace(CAMEL_CASE_SEPARATOR_REGEX, (_, c) => c ? c.toUpperCase() : "").replace(LEADING_CHAR_REGEX, (c) => c.toLowerCase());
|
|
@@ -121,7 +122,7 @@ Handlebars2.registerHelper("yaml", (key, value) => stringify({ [key]: value }).t
|
|
|
121
122
|
Handlebars2.registerHelper("jsString", (value) => `'${JSON.stringify(value).slice(1, -1).replace(/\\"/g, '"').replace(/'/g, "\\'")}'`);
|
|
122
123
|
Handlebars2.registerHelper("moduleConfig", function(moduleKey, enabled) {
|
|
123
124
|
const content = generateConfigFile(moduleKey, this.moduleConfigs).trimEnd();
|
|
124
|
-
const configured = this.appType === "fullstack" && moduleKey === "iam" ? content.replace("
|
|
125
|
+
const configured = this.appType === "fullstack" && moduleKey === "iam" ? content.replace("HAI_IAM_SEEDDEFAULTDATA:true", "HAI_IAM_SEEDDEFAULTDATA:false").replace("variant: card", "variant: split") : content;
|
|
125
126
|
return enabled ? configured : `# # \u672A\u542F\u7528\uFF1A\u5B89\u88C5\u5BF9\u5E94\u6A21\u5757\u5E76\u5728\u670D\u52A1\u7AEF runtime \u4E2D\u521D\u59CB\u5316\uFF0C\u518D\u53D6\u6D88\u6240\u9700\u914D\u7F6E\u7684\u6CE8\u91CA\u3002
|
|
126
127
|
${configured.split("\n").map((line) => line ? `# ${line}` : "").join("\n")}`;
|
|
127
128
|
});
|
|
@@ -465,7 +466,7 @@ function buildAppHaiCatalogPackages(options) {
|
|
|
465
466
|
packageNames.push("@h-ai/capacitor");
|
|
466
467
|
return pickCatalogPackages(packageNames, HAI_CATALOG_PACKAGES);
|
|
467
468
|
}
|
|
468
|
-
function buildFullstackCatalogPackages(frontends) {
|
|
469
|
+
function buildFullstackCatalogPackages(frontends, deploy = false) {
|
|
469
470
|
const packageNames = [
|
|
470
471
|
...FULLSTACK_ROOT_CATALOG_PACKAGE_NAMES,
|
|
471
472
|
...FULLSTACK_CONTRACT_CATALOG_PACKAGE_NAMES,
|
|
@@ -478,6 +479,8 @@ function buildFullstackCatalogPackages(frontends) {
|
|
|
478
479
|
packageNames.push(...FULLSTACK_APP_CATALOG_PACKAGE_NAMES);
|
|
479
480
|
if (frontends.includes("desktop"))
|
|
480
481
|
packageNames.push(...FULLSTACK_DESKTOP_CATALOG_PACKAGE_NAMES);
|
|
482
|
+
if (deploy)
|
|
483
|
+
packageNames.push("@h-ai/deploy");
|
|
481
484
|
return pickCatalogPackages(packageNames, FULLSTACK_CATALOG_PACKAGES);
|
|
482
485
|
}
|
|
483
486
|
var FEATURE_ROUTE_DIRS = {
|
|
@@ -594,7 +597,7 @@ function buildTemplateContext(options) {
|
|
|
594
597
|
const selectedFrontends = options.frontends ?? DEFAULT_FULLSTACK_FRONTENDS;
|
|
595
598
|
let haiCatalogPackages;
|
|
596
599
|
if (isFullstack) {
|
|
597
|
-
haiCatalogPackages = buildFullstackCatalogPackages(selectedFrontends);
|
|
600
|
+
haiCatalogPackages = buildFullstackCatalogPackages(selectedFrontends, featureMap.deploy);
|
|
598
601
|
} else if (isApi) {
|
|
599
602
|
haiCatalogPackages = buildApiWorkspaceCatalogPackages(featureMap);
|
|
600
603
|
} else {
|
|
@@ -769,6 +772,10 @@ async function generateFromTemplates(projectPath, context) {
|
|
|
769
772
|
}
|
|
770
773
|
}
|
|
771
774
|
await renderDynamicFiles(root, projectPath, context);
|
|
775
|
+
if (context.isSvelteOnlyApp || context.appType === "fullstack") {
|
|
776
|
+
const navigationDir = context.appType === "fullstack" ? path2.join(projectPath, "packages", `${context.projectName}-shared`, "src", "lib", "navigation") : path2.join(projectPath, "src", "lib", "navigation");
|
|
777
|
+
await fse.copy(path2.join(root, "shared", "hash-router.svelte.ts"), path2.join(navigationDir, "hash-router.svelte.ts"));
|
|
778
|
+
}
|
|
772
779
|
if (context.hasI18n) {
|
|
773
780
|
const i18nDir = path2.join(root, "i18n");
|
|
774
781
|
if (await fse.pathExists(i18nDir)) {
|
|
@@ -839,7 +846,8 @@ async function renderHbsInDir(dir, destRoot, context) {
|
|
|
839
846
|
continue;
|
|
840
847
|
}
|
|
841
848
|
const destPath = path2.join(destRoot, outRelPath);
|
|
842
|
-
const
|
|
849
|
+
const configMatch = /(?:^|[/\\])config[/\\]_([^/\\]+)\.ya?ml$/.exec(outRelPath);
|
|
850
|
+
const normalizedRendered = normalizeRenderedContent(configMatch ? parameterizeConfig(configMatch[1], rendered) : rendered, outRelPath);
|
|
843
851
|
await fse.ensureDir(path2.dirname(destPath));
|
|
844
852
|
await fse.writeFile(destPath, normalizedRendered, "utf-8");
|
|
845
853
|
}
|
|
@@ -1124,7 +1132,7 @@ async function createProject(options) {
|
|
|
1124
1132
|
});
|
|
1125
1133
|
await generateFromTemplates(projectPath, context);
|
|
1126
1134
|
{
|
|
1127
|
-
const { generateConfigFile: generateConfigFile2, generateEnvExample } = await import('./cli-config-templates-
|
|
1135
|
+
const { generateConfigFile: generateConfigFile2, generateEnvExample } = await import('./cli-config-templates-SJ2MBIR7.js');
|
|
1128
1136
|
const configs = resolvedOptions.moduleConfigs;
|
|
1129
1137
|
const isApiWorkspace = resolvedOptions.appType === "api";
|
|
1130
1138
|
const isFullstack = resolvedOptions.appType === "fullstack";
|
|
@@ -1145,26 +1153,60 @@ async function createProject(options) {
|
|
|
1145
1153
|
generateConfigFile2("core", configs)
|
|
1146
1154
|
);
|
|
1147
1155
|
}
|
|
1148
|
-
const
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1156
|
+
const configContents = {};
|
|
1157
|
+
for (const file of await fse.readdir(configDir)) {
|
|
1158
|
+
if (/^_.*\.yml$/.test(file))
|
|
1159
|
+
configContents[file.slice(1, -4)] = await fse.readFile(path2.join(configDir, file), "utf8");
|
|
1160
|
+
}
|
|
1161
|
+
const envFeatures = resolvedOptions.features;
|
|
1162
|
+
const envContents = generateEnvExample(envFeatures, configs, isFullstack, configContents);
|
|
1163
|
+
let envExample = isApiWorkspace ? envContents : isFullstack ? `${envContents}
|
|
1164
|
+
# First-start administrator password; must satisfy the IAM password policy.
|
|
1155
1165
|
HAI_ADMIN_DEFAULT_PASSWORD=
|
|
1156
1166
|
|
|
1157
|
-
#
|
|
1167
|
+
# Public API origin; never include credentials.
|
|
1158
1168
|
PUBLIC_API_BASE=http://127.0.0.1:3000
|
|
1159
|
-
` :
|
|
1169
|
+
` : context.isSvelteOnlyApp ? `${envContents}
|
|
1170
|
+
# Public API URL including /api/v1; never include credentials.
|
|
1171
|
+
PUBLIC_API_BASE=http://10.0.2.2:3000/api/v1
|
|
1172
|
+
` : envContents;
|
|
1173
|
+
const runtimeVariables = /* @__PURE__ */ new Map();
|
|
1174
|
+
for (const file of await fse.readdir(projectPath, { recursive: true, encoding: "utf8" })) {
|
|
1175
|
+
if (!/\.(?:ts|js|mjs|svelte|envsh)$/.test(file))
|
|
1176
|
+
continue;
|
|
1177
|
+
for (const variable of runtimeEnvironmentVariables(await fse.readFile(path2.join(projectPath, file), "utf8")))
|
|
1178
|
+
runtimeVariables.set(variable.name, variable);
|
|
1179
|
+
}
|
|
1180
|
+
const existingNames = new Set([...envExample.matchAll(/^(?:#\s*)?([A-Z][A-Z0-9_]*)=/gm)].map((match) => match[1]));
|
|
1181
|
+
const runtimeExample = renderEnvironment([...runtimeVariables.values()].filter((variable) => !existingNames.has(variable.name)));
|
|
1182
|
+
if (runtimeExample)
|
|
1183
|
+
envExample += `
|
|
1184
|
+
# \u53EF\u9009\u8FD0\u884C\u5165\u53E3\uFF08\u6309\u7528\u9014\u6CE8\u5165\uFF0C\u4E0D\u8FDB\u5165\u516C\u5171\u9ED8\u8BA4\u503C\uFF09\uFF1A
|
|
1185
|
+
${runtimeExample.split("\n").map((line) => line && !line.startsWith("#") ? `# ${line}` : line).join("\n")}
|
|
1186
|
+
`;
|
|
1160
1187
|
await writeFile(envExamplePath, envExample);
|
|
1188
|
+
const envDirectory = path2.dirname(envExamplePath);
|
|
1189
|
+
const sharedVariables = envExample.split("\n").filter((line) => /^[A-Z][A-Z0-9_]*=/.test(line));
|
|
1190
|
+
await writeFile(path2.join(envDirectory, ".env"), `# \u516C\u5171\u57FA\u7840\u914D\u7F6E\uFF08\u53EF\u63D0\u4EA4\uFF09\uFF1B\u5B8C\u6574\u5B57\u6BB5\u8BF4\u660E\u89C1 .env.example\u3002
|
|
1191
|
+
${sharedVariables.join("\n")}
|
|
1192
|
+
`);
|
|
1193
|
+
for (const [mode, environment] of [["dev", "development"], ["test", "test"], ["prod", "production"]]) {
|
|
1194
|
+
await writeFile(path2.join(envDirectory, `.env.${mode}`), `# \u516C\u5171 ${mode} \u73AF\u5883\u914D\u7F6E\uFF1B\u672C\u673A\u79C1\u6709\u503C\u653E .env.local\uFF0C\u8FDB\u7A0B\u73AF\u5883\u53D8\u91CF\u4F18\u5148\u3002
|
|
1195
|
+
HAI_CORE_ENV=${environment}
|
|
1196
|
+
HAI_CORE_DEBUG=false
|
|
1197
|
+
HAI_CORE_LOGGING_LEVEL=${mode === "test" ? "warn" : mode === "dev" ? "debug" : "info"}
|
|
1198
|
+
`);
|
|
1199
|
+
}
|
|
1161
1200
|
if (isFullstack) {
|
|
1162
1201
|
const password = `Aa1!${randomBytes(24).toString("base64url")}`;
|
|
1163
|
-
await fse.writeFile(path2.join(projectPath, ".env"),
|
|
1164
|
-
|
|
1202
|
+
await fse.writeFile(path2.join(projectPath, ".env.local"), `# \u672C\u673A\u79C1\u6709\u914D\u7F6E\uFF0C\u4E0D\u63D0\u4EA4 Git\u3002
|
|
1203
|
+
HAI_ADMIN_DEFAULT_PASSWORD=${password}
|
|
1204
|
+
`, { mode: 384 });
|
|
1205
|
+
} else {
|
|
1206
|
+
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");
|
|
1165
1207
|
}
|
|
1166
1208
|
}
|
|
1167
|
-
const { generateSkillFiles } = await import('./cli-skill-templates-
|
|
1209
|
+
const { generateSkillFiles } = await import('./cli-skill-templates-A5VIR5J6.js');
|
|
1168
1210
|
await generateSkillFiles(
|
|
1169
1211
|
projectPath,
|
|
1170
1212
|
resolvedOptions.features,
|
|
@@ -1211,7 +1253,7 @@ async function resolveOptions(options) {
|
|
|
1211
1253
|
writeCliMessage("");
|
|
1212
1254
|
const onCancel = () => {
|
|
1213
1255
|
writeCliMessage(chalk.red("\n\u5DF2\u53D6\u6D88"));
|
|
1214
|
-
|
|
1256
|
+
process3.exit(1);
|
|
1215
1257
|
};
|
|
1216
1258
|
let projectName = options.name;
|
|
1217
1259
|
if (!projectName && useDefaults) {
|
|
@@ -1277,7 +1319,7 @@ async function resolveOptions(options) {
|
|
|
1277
1319
|
let selectedFeatures = [];
|
|
1278
1320
|
const appTypeDefaults = APP_TYPES[selectedAppType].defaultFeatures;
|
|
1279
1321
|
if (selectedAppType === "fullstack") {
|
|
1280
|
-
selectedFeatures = resolveFeatureDependencies(appTypeDefaults);
|
|
1322
|
+
selectedFeatures = resolveFeatureDependencies([...appTypeDefaults, ...options.features?.includes("deploy") ? ["deploy"] : []]);
|
|
1281
1323
|
} else if (selectedTemplate === "custom" && !options.features && useDefaults) {
|
|
1282
1324
|
selectedFeatures = resolveFeatureDependencies(appTypeDefaults);
|
|
1283
1325
|
} else if (selectedTemplate === "custom" && !options.features) {
|
|
@@ -1467,7 +1509,7 @@ async function promptModuleConfigs(features, projectName) {
|
|
|
1467
1509
|
const configs = {};
|
|
1468
1510
|
const onCancel = () => {
|
|
1469
1511
|
writeCliMessage(chalk.red("\n\u5DF2\u53D6\u6D88"));
|
|
1470
|
-
|
|
1512
|
+
process3.exit(1);
|
|
1471
1513
|
};
|
|
1472
1514
|
writeCliMessage("");
|
|
1473
1515
|
writeCliMessage(chalk.bold(" \u2699 \u6A21\u5757\u914D\u7F6E"));
|
|
@@ -1685,16 +1727,34 @@ function generateAiDevelopmentReadme() {
|
|
|
1685
1727
|
|
|
1686
1728
|
\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
|
|
1687
1729
|
|
|
1730
|
+
\u4E1A\u52A1\u4EE5\u7528\u6237\u5F53\u524D\u660E\u786E\u8981\u6C42\u53CA\u5176\u6307\u5B9A\u7684\u9700\u6C42/\u8BBE\u8BA1\u6587\u6863\u4E3A\u51C6\u3002\u6A21\u677F AGENTS\u3001README\u3001skills\u3001\u751F\u6210\u4EE3\u7801\u4E0E\u6D4B\u8BD5\u4E2D\u7684\u89D2\u8272/\u6743\u9650\u3001\u6D41\u7A0B\u3001\u9650\u989D\u548C\u67B6\u6784\u662F\u53EF\u8C03\u6574\u7684\u521D\u59CB\u65B9\u6848\uFF1B\u5BA1\u67E5\u5C06\u5DEE\u5F02\u5217\u4E3A\u6A21\u677F\u9002\u914D\u9879\uFF0C\u5B9E\u65BD\u6309\u6388\u6743\u540C\u6B65\u51B2\u7A81\u4EE3\u7801\u3001\u6D4B\u8BD5\u4E0E\u8FC7\u671F\u89C4\u5219\uFF0C\u4E0D\u8981\u6C42\u628A\u9700\u6C42\u6539\u56DE\u6A21\u677F\u3002\u6846\u67B6 API \u4EE5\u5F53\u524D\u5B89\u88C5\u7248\u672C\u516C\u5F00\u5951\u7EA6\u4E3A\u51C6\uFF0C\u670D\u52A1\u7AEF\u6388\u6743\u3001\u8D44\u6E90\u9694\u79BB\u3001\u8F93\u5165\u6821\u9A8C\u548C\u51ED\u636E\u4FDD\u62A4\u4ECD\u9700\u843D\u5B9E\u3002
|
|
1731
|
+
|
|
1688
1732
|
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
|
|
1689
1733
|
|
|
1690
|
-
\u6839\u636E\u6587\u6863\u89C4\u5212\u3001\u5B9E\u65BD\
|
|
1734
|
+
\u6839\u636E\u6587\u6863\u89C4\u5212\u3001\u5B9E\u65BD\u3001\u8FD4\u4FEE\u6216\u6309\u5DF2\u63D0\u4EA4\u6587\u6863\u5DEE\u5F02\u540C\u6B65\u5B9E\u73B0\u65F6\uFF0C\u4F7F\u7528 [\u6587\u6863\u5B9E\u65BD Skill](./.agents/skills/hai-spec-implement/SKILL.md)\uFF1A\u7EF4\u62A4 docs/task-list.md \u4E0E docs/tasks/\uFF0C\u8BB0\u5F55\u5DF2\u9A8C\u6536\u6587\u6863\u548C\u5B9E\u73B0\u7684 Git SHA\uFF0C\u6BD4\u8F83\u56FA\u5B9A\u76EE\u6807\u7248\u672C\u540E\u66F4\u65B0\u539F\u4EFB\u52A1\u6216\u65B0\u5EFA\u4EFB\u52A1\uFF0C\u6BCF\u9879\u72EC\u7ACB\u5206\u652F TDD\u3001\u9A8C\u8BC1\u5E76\u672C\u5730\u5408\u5E76\uFF1B\u5168\u90E8\u5DEE\u5F02\u4E0E\u6574\u4F53\u9A8C\u6536\u901A\u8FC7\u540E\u624D\u63A8\u8FDB\u57FA\u7EBF\u3002\u8FD4\u4FEE\u6CBF\u7528\u539F\u4EFB\u52A1\u4E0E\u5206\u652F\u3002\u4EC5\u89C4\u5212\u4E0D\u5F00\u59CB\u5B9E\u73B0\uFF0C\u6307\u5B9A\u4EFB\u52A1\u5148\u6838\u9A8C\u4F9D\u8D56\u3002
|
|
1691
1735
|
|
|
1692
1736
|
\u4F18\u5148\u590D\u7528\u9002\u7528\u7684 hai-framework \u516C\u5F00 API \u4E0E\u5DF2\u6709\u4E1A\u52A1\u5B9E\u73B0\u3002\u5B8C\u6210\u65F6\u8BF4\u660E\u590D\u7528\u7684\u80FD\u529B\u3001\u76F8\u5173\u80FD\u529B\u672A\u91C7\u7528\u7684\u539F\u56E0\uFF0C\u4EE5\u53CA\u5B89\u5168\u8FB9\u754C\u548C\u7C7B\u578B\u3001\u914D\u7F6E\u3001\u6587\u6848\u3001\u8C03\u7528\u65B9\u7684\u540C\u6B65\u60C5\u51B5\u3002\u8D28\u91CF\u9A8C\u8BC1\u6309\u5F53\u524D package.json \u6267\u884C typecheck \u2192 lint \u2192 build \u2192 test\uFF1BUI\u3001\u8DEF\u7531\u4E0E\u7AEF\u5230\u7AEF\u6D41\u7A0B\u53E6\u8FD0\u884C test:e2e\uFF0C\u672A\u8FD0\u884C\u9879\u8BF4\u660E\u539F\u56E0\u3002\u5B89\u5168\u4E0E\u8D28\u91CF\u89C4\u5219\u65E2\u8981\u6309\u89C4\u8303\u5BA1\u67E5\uFF0C\u4E5F\u8981\u8FD0\u884C\u95E8\u7981\u9A8C\u8BC1\u3002
|
|
1693
1737
|
|
|
1694
|
-
\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\u666E\u901A\u5F00\u53D1\u5165\u53E3\u4E0E\u89C4\u8303\u663E\u5F0F\u5408\u5E76\u5230\u65E7\u6307\u5F15\u3002
|
|
1738
|
+
\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
|
|
1695
1739
|
|
|
1696
1740
|
`;
|
|
1697
1741
|
}
|
|
1742
|
+
function generateEnvironmentReadme() {
|
|
1743
|
+
return `## \u5206\u73AF\u5883\u914D\u7F6E
|
|
1744
|
+
|
|
1745
|
+
\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
|
|
1746
|
+
|
|
1747
|
+
| \u7528\u9014 | \u547D\u4EE4 |
|
|
1748
|
+
| --- | --- |
|
|
1749
|
+
| \u5F00\u53D1 / \u6D4B\u8BD5\u6A21\u5F0F / \u751F\u4EA7\u6A21\u5F0F\u9884\u89C8 | \`pnpm dev\` / \`dev:test\` / \`dev:prod\` |
|
|
1750
|
+
| \u7C7B\u578B\u68C0\u67E5\uFF08check \u4E3A\u522B\u540D\uFF09 | \`pnpm typecheck\` |
|
|
1751
|
+
| \u68C0\u67E5 / \u81EA\u52A8\u4FEE\u590D\u683C\u5F0F | \`pnpm lint\` / \`lint:fix\` |
|
|
1752
|
+
| \u6784\u5EFA / \u751F\u4EA7\u6A21\u5F0F\u6784\u5EFA | \`pnpm build\` / \`build:prod\` |
|
|
1753
|
+
| \u5355\u5143\u6D4B\u8BD5 / \u76D1\u542C / \u6D4F\u89C8\u5668\u9A8C\u6536 | \`pnpm test\` / \`test:watch\` / \`test:e2e\` |
|
|
1754
|
+
| Node \u670D\u52A1\u6309\u73AF\u5883\u542F\u52A8\uFF08\u6784\u5EFA\u540E\uFF09 | \`pnpm start\` / \`start:test\` / \`start:prod\` |
|
|
1755
|
+
|
|
1756
|
+
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`;
|
|
1757
|
+
}
|
|
1698
1758
|
function generateReadme(name, appTypeLabel, pm, appType) {
|
|
1699
1759
|
const install = getInstallCommand(pm);
|
|
1700
1760
|
const isStaticImage = appType === "mobile-app";
|
|
@@ -1718,6 +1778,10 @@ ${getRunCommand(pm, "podman:build:data")}`;
|
|
|
1718
1778
|
|
|
1719
1779
|
\u57FA\u4E8E hai Agent Framework \u6784\u5EFA\u7684${appTypeLabel}\u5E94\u7528\u3002
|
|
1720
1780
|
|
|
1781
|
+
## \u9875\u9762\u5E03\u5C40\u4E0E\u8DEF\u7531
|
|
1782
|
+
|
|
1783
|
+
\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"}
|
|
1784
|
+
|
|
1721
1785
|
## \u5F00\u53D1
|
|
1722
1786
|
|
|
1723
1787
|
\`\`\`bash
|
|
@@ -1725,6 +1789,8 @@ ${install}
|
|
|
1725
1789
|
${getRunCommand(pm, "dev")}
|
|
1726
1790
|
\`\`\`
|
|
1727
1791
|
|
|
1792
|
+
${generateEnvironmentReadme()}
|
|
1793
|
+
|
|
1728
1794
|
## \u6784\u5EFA
|
|
1729
1795
|
|
|
1730
1796
|
\`\`\`bash
|
|
@@ -1782,6 +1848,8 @@ ${pm} install
|
|
|
1782
1848
|
${pm} dev
|
|
1783
1849
|
\`\`\`
|
|
1784
1850
|
|
|
1851
|
+
${generateEnvironmentReadme()}
|
|
1852
|
+
|
|
1785
1853
|
## \u8D28\u91CF\u95E8\u7981
|
|
1786
1854
|
|
|
1787
1855
|
\`\`\`bash
|
|
@@ -1883,11 +1951,17 @@ curl --fail http://127.0.0.1:8080/runtime-config.js
|
|
|
1883
1951
|
|
|
1884
1952
|
\u5DF2\u542F\u7528\u524D\u7AEF\uFF1A${enabledFrontends || "Web / App / Desktop"}
|
|
1885
1953
|
|
|
1954
|
+
## \u9875\u9762\u5E03\u5C40\u4E0E\u8DEF\u7531
|
|
1955
|
+
|
|
1956
|
+
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
|
|
1957
|
+
|
|
1886
1958
|
## \u9ED8\u8BA4\u7BA1\u7406\u529F\u80FD
|
|
1887
1959
|
|
|
1888
|
-
\
|
|
1960
|
+
\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
|
|
1961
|
+
|
|
1962
|
+
\u9ED8\u8BA4\u542F\u7528 IAM\u3001reldb\u3001cache\u3001audit\u3001storage \u548C crypto\u3002\u521D\u59CB\u7BA1\u7406\u5458\u53EF\u4EE5\u7BA1\u7406\u7528\u6237\uFF08\u65B0\u589E\u3001\u7F16\u8F91\u3001\u7981\u7528\u3001\u5220\u9664\u3001\u89D2\u8272\u5206\u914D\u3001\u5934\u50CF\u4E0A\u4F20\uFF09\u3001\u67E5\u770B admin/user \u89D2\u8272\u53CA\u4EBA\u6570\u3001\u7EF4\u62A4\u89D2\u8272\u8BF4\u660E\u3001\u5206\u9875\u7B5B\u9009\u5BA1\u8BA1\u65E5\u5FD7\uFF1B\u521D\u59CB\u666E\u901A\u7528\u6237\u8FDB\u5165\u4E2A\u4EBA\u8D44\u6599\u3001\u5934\u50CF\u3001\u8BED\u8A00\u3001\u4E3B\u9898\u548C\u5BC6\u7801\u8BBE\u7F6E\u3002\u9ED8\u8BA4\u5BC6\u7801\u4FEE\u6539\u540E\u5168\u90E8\u4F1A\u8BDD\u5931\u6548\uFF0C\u9700\u8981\u91CD\u65B0\u767B\u5F55\uFF1B\u89D2\u8272\u6A21\u578B\u3001\u8BBF\u95EE\u8303\u56F4\u548C\u4F1A\u8BDD\u7B56\u7565\u7531\u4E1A\u52A1\u9700\u6C42\u51B3\u5B9A\u3002
|
|
1889
1963
|
|
|
1890
|
-
CLI \u5DF2\u5728\u6839\u76EE\u5F55\u751F\u6210 \`.env\`\uFF0C\u5176\u4E2D \`HAI_ADMIN_DEFAULT_PASSWORD\` \u662F\u6BCF\u4E2A\u9879\u76EE\u72EC\u7ACB\u751F\u6210\u7684\u968F\u673A\u5F3A\u5BC6\u7801\u3002\u76F4\u63A5\u8FD0\u884C \`dev\` / \`start\` \u5373\u53EF\uFF0C\u9996\u6B21\u767B\u5F55\u4F7F\u7528\u7528\u6237\u540D \`admin\` \u548C\u8BE5\u5BC6\u7801\uFF1B\u4E5F\u53EF\u901A\u8FC7\u540C\u540D\u73AF\u5883\u53D8\u91CF\u8986\u76D6\u3002\u5BC6\u7801\u987B\u6EE1\u8DB3 \`_iam.yml\` \u7B56\u7565\uFF0C\u542F\u52A8\u5931\u8D25\u4F1A\u663E\u793A\u5177\u4F53\u6821\u9A8C\u539F\u56E0\u53CA\u9519\u8BEF\u7801\u3002\`.env\` \u5DF2\u88AB Git \u548C\u5BB9\u5668\u6784\u5EFA\u5FFD\u7565\uFF0C\`.env.example\` \u4E0D\u542B\u771F\u5B9E\u5BC6\u7801\uFF1B\u90E8\u7F72\u65F6\u8BBE\u7F6E\u76EE\u6807\u73AF\u5883\u81EA\u5DF1\u7684\u5BC6\u7801\u3002\u5DF2\u6709\u7528\u6237\u65F6\u4E0D\u4F1A\u91CD\u590D\u521B\u5EFA\u7BA1\u7406\u5458\u6216\u91CD\u8BBE\u5BC6\u7801\u3002
|
|
1964
|
+
CLI \u5DF2\u5728\u6839\u76EE\u5F55\u751F\u6210 \`.env.local\`\uFF0C\u5176\u4E2D \`HAI_ADMIN_DEFAULT_PASSWORD\` \u662F\u6BCF\u4E2A\u9879\u76EE\u72EC\u7ACB\u751F\u6210\u7684\u968F\u673A\u5F3A\u5BC6\u7801\u3002\u76F4\u63A5\u8FD0\u884C \`dev\` / \`start\` \u5373\u53EF\uFF0C\u9996\u6B21\u767B\u5F55\u4F7F\u7528\u7528\u6237\u540D \`admin\` \u548C\u8BE5\u5BC6\u7801\uFF1B\u4E5F\u53EF\u901A\u8FC7\u540C\u540D\u73AF\u5883\u53D8\u91CF\u8986\u76D6\u3002\u5BC6\u7801\u987B\u6EE1\u8DB3 \`_iam.yml\` \u7B56\u7565\uFF0C\u542F\u52A8\u5931\u8D25\u4F1A\u663E\u793A\u5177\u4F53\u6821\u9A8C\u539F\u56E0\u53CA\u9519\u8BEF\u7801\u3002\`.env.local\` \u5DF2\u88AB Git \u548C\u5BB9\u5668\u6784\u5EFA\u5FFD\u7565\uFF0C\`.env.example\` \u4E0D\u542B\u771F\u5B9E\u5BC6\u7801\uFF1B\u90E8\u7F72\u65F6\u8BBE\u7F6E\u76EE\u6807\u73AF\u5883\u81EA\u5DF1\u7684\u5BC6\u7801\u3002\u5DF2\u6709\u7528\u6237\u65F6\u4E0D\u4F1A\u91CD\u590D\u521B\u5EFA\u7BA1\u7406\u5458\u6216\u91CD\u8BBE\u5BC6\u7801\u3002
|
|
1891
1965
|
|
|
1892
1966
|
\u672C\u5730\u5F00\u53D1\u9ED8\u8BA4 \`PUBLIC_API_BASE=http://127.0.0.1:3000\`\uFF0CVite \u4F7F\u7528\u540C\u6E90\u4EE3\u7406\uFF1B\u90E8\u7F72\u65F6\u6539\u4E3A\u5B9E\u9645\u516C\u7F51 API Origin\uFF0CAndroid \u6A21\u62DF\u5668\u8BBF\u95EE\u5BBF\u4E3B\u673A\u65F6\u4F7F\u7528 \`http://10.0.2.2:3000\`\u3002\u4E0D\u8981\u5C06\u5BC6\u94A5\u653E\u5165\u4EFB\u4F55 \`PUBLIC_\` \u6216 \`VITE_\` \u53D8\u91CF\u3002
|
|
1893
1967
|
|
|
@@ -1899,12 +1973,14 @@ CLI \u5DF2\u5728\u6839\u76EE\u5F55\u751F\u6210 \`.env\`\uFF0C\u5176\u4E2D \`HAI_
|
|
|
1899
1973
|
|
|
1900
1974
|
\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
|
|
1901
1975
|
|
|
1902
|
-
\u81EA\u52A8\u751F\u6210\u7684 \`.env\` \
|
|
1976
|
+
\u81EA\u52A8\u751F\u6210\u7684 \`.env\` \u5305\u542B\u5DF2\u542F\u7528\u6A21\u5757\u7684\u73AF\u5883\u53C2\u6570\uFF1BYAML \u663E\u5F0F\u5F15\u7528\u76F8\u540C\u53D8\u91CF\u5E76\u4FDD\u7559\u672C\u5730\u56DE\u9000\u3002\u7F16\u8F91\u73AF\u5883\u53C2\u6570\u4F18\u5148\u4FEE\u6539\u5BF9\u5E94 \`.env\`\uFF1B\u672A\u542F\u7528\u6A21\u5757\u4ECD\u4FDD\u7559\u6CE8\u91CA\u3002
|
|
1903
1977
|
|
|
1904
1978
|
\u6269\u5C55\u65F6\u5148\u5728\u670D\u52A1\u7AEF workspace \u5B89\u88C5\u9700\u8981\u7684\u5305\uFF0C\u5728 \`src/server/runtime.ts\` \u6309\u6A21\u5757\u516C\u5F00 Schema \u6821\u9A8C\u914D\u7F6E\u3001\u521D\u59CB\u5316\u5E76\u53CD\u5E8F\u5173\u95ED\uFF0C\u518D\u63A5\u5165 contract/procedure \u548C\u754C\u9762\u3002\u6574\u4EFD\u6CE8\u91CA\u6587\u4EF6\u5148\u53BB\u6389\u4E00\u5C42\u5916\u5C42\u6CE8\u91CA\uFF0C\u53EF\u9009\u5757\u7EE7\u7EED\u6309\u9700\u5F00\u542F\uFF1B\u914D\u7F6E\u6587\u4EF6\u5B58\u5728\u4E0D\u4EE3\u8868\u4E1A\u52A1\u529F\u80FD\u5DF2\u63A5\u5165\u3002\`crypto.init()\` \u65E0\u914D\u7F6E\u53C2\u6570\uFF0Cdatapipe \u4E3A\u7EAF\u51FD\u6570\uFF0C\u4E0D\u751F\u6210\u865A\u6784 YAML\u3002
|
|
1905
1979
|
|
|
1906
1980
|
\u73AF\u5883\u53D8\u91CF\u53EA\u8986\u76D6 YAML \u5DF2\u6709\u8DEF\u5F84\uFF0C\u6CE8\u91CA\u952E\u987B\u5148\u542F\u7528\uFF0C\u6216\u7528 \`HAI_DB\` / \`HAI_CACHE\` \u7B49\u6574\u4F53\u5BF9\u8C61\u8986\u76D6\u3002camelCase \u4E0D\u62C6\u8BCD\uFF0C\u4F8B\u5982 \`connectTimeout\` \u5BF9\u5E94 \`HAI_CACHE_CONNECTTIMEOUT\`\u3002\u4FEE\u6539\u914D\u7F6E\u540E\u91CD\u542F\u670D\u52A1\uFF1B\u8BED\u8A00\u5217\u8868\u5728\u5404\u524D\u7AEF/shared \u7684 \`project.inlang/settings.json\` \u4E2D\u540C\u6B65\u3002\`**/data/\` \u5FFD\u7565\u4EFB\u610F\u5C42\u7EA7\u7684\u6570\u636E\u76EE\u5F55\u53CA \`.gitkeep\`\uFF0C\u8FD0\u884C\u6570\u636E\u4E0D\u5165 Git\uFF0C\u8FD0\u884C\u65F6\u81EA\u52A8\u521B\u5EFA\u76EE\u5F55\u3002
|
|
1907
1981
|
|
|
1982
|
+
${generateEnvironmentReadme()}
|
|
1983
|
+
|
|
1908
1984
|
## \u5F00\u53D1\u3001\u7F16\u8BD1\u4E0E\u6253\u5305
|
|
1909
1985
|
|
|
1910
1986
|
\`\`\`bash
|
|
@@ -1942,7 +2018,7 @@ ${webBuildCommands}
|
|
|
1942
2018
|
|
|
1943
2019
|
\`\`\`bash
|
|
1944
2020
|
# Service\uFF1B\u4F7F\u7528 Podman \u65F6\u5C06 docker \u66FF\u6362\u4E3A podman
|
|
1945
|
-
docker run --rm -d --name ${name} -p 3000:3000 --env-file .env \\
|
|
2021
|
+
docker run --rm -d --name ${name} -p 3000:3000 --env-file .env --env-file .env.prod --env-file .env.local \\
|
|
1946
2022
|
--mount type=bind,source=/absolute/path/to/config,target=/app/config,readonly \\
|
|
1947
2023
|
--mount type=volume,source=${name}-data,target=/app/data \\
|
|
1948
2024
|
${name}:latest
|
|
@@ -1957,6 +2033,7 @@ curl --fail http://127.0.0.1:3000/ready
|
|
|
1957
2033
|
${webTestCommands}docker stop ${containerNames}
|
|
1958
2034
|
\`\`\`
|
|
1959
2035
|
|
|
2036
|
+
- \u4E0A\u4F8B\u6309\u516C\u5171\u57FA\u7840\u3001\u751F\u4EA7\u73AF\u5883\u3001\u672C\u673A\u79C1\u6709\u6587\u4EF6\u7684\u987A\u5E8F\u6CE8\u5165\u53D8\u91CF\uFF1B\u8FD0\u884C\u524D\u5728\u76EE\u6807\u673A\u5668\u521B\u5EFA \`.env.local\` \u5E76\u8BBE\u7F6E\u81EA\u5DF1\u7684\u7BA1\u7406\u5458\u5BC6\u7801\u3002\u90E8\u7F72\u5E73\u53F0\u4E5F\u53EF\u76F4\u63A5\u6CE8\u5165\u540C\u540D\u53D8\u91CF\u3002
|
|
1960
2037
|
- \u914D\u7F6E\u9ED8\u8BA4\u6765\u81EA \`packages/${name}-serv/config\`\uFF0C\u4E5F\u53EF\u6302\u8F7D\u5230 \`/app/config\`\uFF1BYAML \u4E2D\u7684 \`\${ENV_NAME:default}\` \u53EF\u7531 \`--env-file\` \u6216 \`-e\` \u6CE8\u5165\u3002
|
|
1961
2038
|
- \u5E26\u6570\u636E\u6784\u5EFA\u4F1A\u628A \`packages/${name}-serv/data\` \u56FA\u5316\u4E3A\u955C\u50CF\u521D\u59CB\u6570\u636E\uFF1B\u6301\u4E45\u5316\u5199\u5165\u4ECD\u5E94\u4F7F\u7528 volume\u3002
|
|
1962
2039
|
- Service \u7531 \`@h-ai/serv\` \u76F4\u63A5\u5BF9\u5916\u76D1\u542C\uFF0C\u4E0D\u989D\u5916\u542F\u52A8 Web \u8F6C\u53D1\u5C42\u3002
|
|
@@ -2118,7 +2195,7 @@ async function addModule(options) {
|
|
|
2118
2195
|
core.logger.info(`\u53EF\u7528\u6A21\u5757: ${Object.keys(MODULE_MAP).join(", ")}`);
|
|
2119
2196
|
return;
|
|
2120
2197
|
}
|
|
2121
|
-
const { generateProjectAiSupport, resolveSkillNamesForPackages } = await import('./cli-skill-templates-
|
|
2198
|
+
const { generateProjectAiSupport, resolveSkillNamesForPackages } = await import('./cli-skill-templates-A5VIR5J6.js');
|
|
2122
2199
|
if (moduleDef.packages.every((p) => project.haiPackages.includes(p))) {
|
|
2123
2200
|
const generatedFiles2 = await generateProjectAiSupport(
|
|
2124
2201
|
cwd,
|
|
@@ -2224,21 +2301,355 @@ async function addModule(options) {
|
|
|
2224
2301
|
throw error;
|
|
2225
2302
|
}
|
|
2226
2303
|
}
|
|
2304
|
+
function displayDeployValue(name, value) {
|
|
2305
|
+
if (/PASSWORD|SECRET|TOKEN|APIKEY|PRIVATEKEY|ACCESSKEY|AUTHORIZATION/i.test(name.replace(/[^a-z0-9]/gi, "")) || /(?:^|_)PASS$/i.test(name) || value.includes("PRIVATE KEY"))
|
|
2306
|
+
return cliM("cli_deploy_secret");
|
|
2307
|
+
try {
|
|
2308
|
+
const parsed2 = JSON.parse(value);
|
|
2309
|
+
if (parsed2 && typeof parsed2 === "object") {
|
|
2310
|
+
const sanitize = (object) => {
|
|
2311
|
+
if (Array.isArray(object))
|
|
2312
|
+
return object.map(sanitize);
|
|
2313
|
+
if (object && typeof object === "object")
|
|
2314
|
+
return Object.fromEntries(Object.entries(object).map(([key, item]) => [key, /PASSWORD|SECRET|TOKEN|APIKEY|PRIVATEKEY|ACCESSKEY|AUTHORIZATION/i.test(key.replace(/[^a-z0-9]/gi, "")) || /(?:^|_)PASS$/i.test(key) ? cliM("cli_deploy_secret") : typeof item === "string" ? displayDeployValue(key, item) : sanitize(item)]));
|
|
2315
|
+
return object;
|
|
2316
|
+
};
|
|
2317
|
+
return JSON.stringify(sanitize(parsed2));
|
|
2318
|
+
}
|
|
2319
|
+
} catch {
|
|
2320
|
+
}
|
|
2321
|
+
try {
|
|
2322
|
+
const url = new URL(value);
|
|
2323
|
+
if (url.username || url.password) {
|
|
2324
|
+
url.username = "***";
|
|
2325
|
+
url.password = "***";
|
|
2326
|
+
}
|
|
2327
|
+
for (const key of url.searchParams.keys()) {
|
|
2328
|
+
if (/password|secret|token|key|authorization/i.test(key))
|
|
2329
|
+
url.searchParams.set(key, "***");
|
|
2330
|
+
}
|
|
2331
|
+
return url.toString();
|
|
2332
|
+
} catch {
|
|
2333
|
+
return value;
|
|
2334
|
+
}
|
|
2335
|
+
}
|
|
2336
|
+
var CLOUD_FIELDS = {
|
|
2337
|
+
"neon": ["apiKey"],
|
|
2338
|
+
"upstash": ["email", "apiKey"],
|
|
2339
|
+
"cloudflare-r2": ["accountId", "apiToken", "accessKeyId", "secretAccessKey"],
|
|
2340
|
+
"resend": ["apiKey", "from"],
|
|
2341
|
+
"aliyun": ["accessKeyId", "accessKeySecret", "signName"]
|
|
2342
|
+
};
|
|
2343
|
+
function targetConfig(requirement, environment) {
|
|
2344
|
+
const result = {};
|
|
2345
|
+
const parent = environment.targetValues[requirement.envPrefix];
|
|
2346
|
+
if (parent) {
|
|
2347
|
+
try {
|
|
2348
|
+
Object.assign(result, JSON.parse(parent));
|
|
2349
|
+
} catch {
|
|
2350
|
+
return {};
|
|
2351
|
+
}
|
|
2352
|
+
}
|
|
2353
|
+
for (const [key, value] of Object.entries(environment.targetValues)) {
|
|
2354
|
+
if (key.startsWith(`${requirement.envPrefix}_`))
|
|
2355
|
+
result[key.slice(requirement.envPrefix.length + 1).toLowerCase()] = value;
|
|
2356
|
+
}
|
|
2357
|
+
return result;
|
|
2358
|
+
}
|
|
2359
|
+
function canKeepMiddleware(requirement, backend, environment) {
|
|
2360
|
+
const target = targetConfig(requirement, environment);
|
|
2361
|
+
if (requirement.service === "ldap" && backend.backend === "disabled")
|
|
2362
|
+
return environment.targetValues.HAI_IAM_LOGIN_LDAP === "false";
|
|
2363
|
+
if (requirement.service === "ldap") {
|
|
2364
|
+
if (!Object.keys(target).length)
|
|
2365
|
+
return false;
|
|
2366
|
+
} else if (target.type !== backend.backend) {
|
|
2367
|
+
return false;
|
|
2368
|
+
}
|
|
2369
|
+
return backend.fields.filter((field) => field.required && !field.secret).every((field) => {
|
|
2370
|
+
if ((target.url || requirement.config.url && Object.keys(target).length) && ["host", "port"].includes(field.name))
|
|
2371
|
+
return true;
|
|
2372
|
+
return target[field.name] !== void 0 && target[field.name] !== "" || target[field.name.toLowerCase()] !== void 0 && target[field.name.toLowerCase()] !== "";
|
|
2373
|
+
}) && backend.fields.filter((field) => field.required && field.secret).every((field) => Boolean(requirement.config[field.name] || environment.values[`${requirement.envPrefix}_${field.name.toUpperCase()}`]));
|
|
2374
|
+
}
|
|
2375
|
+
async function ask(question) {
|
|
2376
|
+
const answer = await prompts({ ...question, name: "value" });
|
|
2377
|
+
if (answer.value === void 0)
|
|
2378
|
+
throw new Error(cliM("cli_deploy_cancelled"));
|
|
2379
|
+
return answer.value;
|
|
2380
|
+
}
|
|
2381
|
+
function validateField(value, field) {
|
|
2382
|
+
if (value === "")
|
|
2383
|
+
return field.required ? cliM("cli_deploy_required") : true;
|
|
2384
|
+
if (field.kind === "port")
|
|
2385
|
+
return /^\d+$/.test(value) && Number(value) >= 1 && Number(value) <= 65535 || cliM("cli_deploy_invalid_value");
|
|
2386
|
+
if (field.kind === "number")
|
|
2387
|
+
return /^\d+$/.test(value) && Number(value) >= (field.minimum ?? 0) && Number(value) <= (field.maximum ?? Number.MAX_SAFE_INTEGER) || cliM("cli_deploy_invalid_value");
|
|
2388
|
+
if (field.kind === "boolean")
|
|
2389
|
+
return ["true", "false"].includes(value) || cliM("cli_deploy_invalid_value");
|
|
2390
|
+
if (field.kind === "url") {
|
|
2391
|
+
try {
|
|
2392
|
+
return Boolean(new URL(value).protocol);
|
|
2393
|
+
} catch {
|
|
2394
|
+
return cliM("cli_deploy_invalid_value");
|
|
2395
|
+
}
|
|
2396
|
+
}
|
|
2397
|
+
if (field.name === "ssl") {
|
|
2398
|
+
const parsed2 = parseDocument(value);
|
|
2399
|
+
const ssl = parsed2.errors.length ? void 0 : parsed2.toJS();
|
|
2400
|
+
return typeof ssl === "boolean" || ["require", "prefer", "allow", "disable"].includes(String(ssl)) || ssl !== null && typeof ssl === "object" && !Array.isArray(ssl) || cliM("cli_deploy_invalid_value");
|
|
2401
|
+
}
|
|
2402
|
+
if (field.name === "headers") {
|
|
2403
|
+
try {
|
|
2404
|
+
const headers = JSON.parse(value);
|
|
2405
|
+
return headers !== null && typeof headers === "object" && !Array.isArray(headers) && Object.values(headers).every((item) => typeof item === "string") || cliM("cli_deploy_invalid_value");
|
|
2406
|
+
} catch {
|
|
2407
|
+
return cliM("cli_deploy_invalid_value");
|
|
2408
|
+
}
|
|
2409
|
+
}
|
|
2410
|
+
if (field.name === "method")
|
|
2411
|
+
return ["POST", "PUT"].includes(value) || cliM("cli_deploy_invalid_value");
|
|
2412
|
+
return true;
|
|
2413
|
+
}
|
|
2414
|
+
function ensureConfigNode(appDir, requirement, backend) {
|
|
2415
|
+
if (requirement.service === "ldap" && backend === "disabled")
|
|
2416
|
+
return;
|
|
2417
|
+
const directory = join(appDir, "config");
|
|
2418
|
+
mkdirSync(directory, { recursive: true });
|
|
2419
|
+
const path7 = join(directory, `_${requirement.configName}.yml`);
|
|
2420
|
+
const document = parseDocument(existsSync(path7) ? readFileSync(path7, "utf8") : "{}\n");
|
|
2421
|
+
if (document.errors.length)
|
|
2422
|
+
throw new Error(cliM("cli_deploy_invalid_value"));
|
|
2423
|
+
if (requirement.configPath.length && !document.hasIn(requirement.configPath))
|
|
2424
|
+
document.setIn(requirement.configPath, requirement.configName === "reach" ? { name: requirement.config.name ?? requirement.service, type: backend } : {});
|
|
2425
|
+
if (requirement.service === "ldap") {
|
|
2426
|
+
if (!document.hasIn(["login"]))
|
|
2427
|
+
document.setIn(["login"], {});
|
|
2428
|
+
if (!document.hasIn(["login", "ldap"]))
|
|
2429
|
+
document.setIn(["login", "ldap"], `\${HAI_IAM_LOGIN_LDAP:false}`);
|
|
2430
|
+
}
|
|
2431
|
+
const content = document.toString();
|
|
2432
|
+
if (!existsSync(path7) || readFileSync(path7, "utf8") !== content)
|
|
2433
|
+
writeFileSync(path7, content);
|
|
2434
|
+
}
|
|
2435
|
+
function connectionEnv(requirement, backend, config) {
|
|
2436
|
+
if (requirement.service === "ldap")
|
|
2437
|
+
return backend === "disabled" ? { HAI_IAM_LOGIN_LDAP: "false" } : { HAI_IAM_LOGIN_LDAP: "true", [requirement.envPrefix]: JSON.stringify(config) };
|
|
2438
|
+
return { [requirement.envPrefix]: JSON.stringify({ ...config, type: backend }) };
|
|
2439
|
+
}
|
|
2440
|
+
async function configureDeployMiddleware(module, scan, environment, config, yes = false) {
|
|
2441
|
+
const modes = {};
|
|
2442
|
+
const services = {};
|
|
2443
|
+
const existingServices = config.services && typeof config.services === "object" ? config.services : {};
|
|
2444
|
+
const provider = config.provider;
|
|
2445
|
+
for (const requirement of scan.middlewares ?? []) {
|
|
2446
|
+
const choices = module.DEPLOY_MIDDLEWARE[requirement.service];
|
|
2447
|
+
const current = choices.find((item) => item.backend === requirement.backend);
|
|
2448
|
+
if (!current)
|
|
2449
|
+
throw new Error(cliM("cli_deploy_cloud_unsupported", { params: { backend: requirement.backend } }));
|
|
2450
|
+
core.logger.info(cliM("cli_deploy_middleware", { params: { service: requirement.service, backend: current.label, purpose: module.describeMiddlewareBackend(current), source: requirement.source } }));
|
|
2451
|
+
const canKeep = canKeepMiddleware(requirement, current, environment);
|
|
2452
|
+
const mode = yes ? canKeep ? "keep" : "default" : await ask({
|
|
2453
|
+
type: "select",
|
|
2454
|
+
name: "value",
|
|
2455
|
+
message: cliM("cli_deploy_action", { params: { service: requirement.service } }),
|
|
2456
|
+
choices: [
|
|
2457
|
+
{ title: cliM("cli_deploy_default"), value: "default" },
|
|
2458
|
+
{ title: cliM("cli_deploy_custom"), value: "custom" },
|
|
2459
|
+
...canKeep ? [{ title: cliM("cli_deploy_keep"), value: "keep" }] : []
|
|
2460
|
+
]
|
|
2461
|
+
});
|
|
2462
|
+
modes[requirement.envPrefix] = mode;
|
|
2463
|
+
const previous = modes[requirement.service];
|
|
2464
|
+
modes[requirement.service] = previous === "default" || mode === "default" ? "default" : previous === "custom" || mode === "custom" ? "custom" : "keep";
|
|
2465
|
+
if (mode === "keep") {
|
|
2466
|
+
if (!canKeep)
|
|
2467
|
+
throw new Error(cliM("cli_deploy_missing_keep", { params: { service: requirement.service } }));
|
|
2468
|
+
continue;
|
|
2469
|
+
}
|
|
2470
|
+
const backendName = yes ? current.backend : String(await ask({ type: "select", name: "value", message: cliM("cli_deploy_backend", { params: { service: requirement.service } }), initial: choices.indexOf(current), choices: choices.map((item) => ({ title: `${item.label} \u2014 ${module.describeMiddlewareBackend(item)}`, value: item.backend })) }));
|
|
2471
|
+
const backend = choices.find((item) => item.backend === backendName);
|
|
2472
|
+
if (!backend)
|
|
2473
|
+
throw new Error(cliM("cli_deploy_invalid_value"));
|
|
2474
|
+
requirement.backend = backendName;
|
|
2475
|
+
const updates = {};
|
|
2476
|
+
if (mode === "default" && backendName === "s3" && provider?.type === "docker-ssh") {
|
|
2477
|
+
const container = config.container && typeof config.container === "object" ? config.container : {};
|
|
2478
|
+
const currentEngine = container.s3Engine === "minio" ? "minio" : "rustfs";
|
|
2479
|
+
const engine = yes ? currentEngine : String(await ask({ type: "select", name: "value", message: cliM("cli_deploy_s3_engine"), initial: currentEngine === "rustfs" ? 0 : 1, choices: [{ title: "RustFS", value: "rustfs" }, { title: "MinIO", value: "minio" }] }));
|
|
2480
|
+
if (!["rustfs", "minio"].includes(engine))
|
|
2481
|
+
throw new Error(cliM("cli_deploy_invalid_value"));
|
|
2482
|
+
config.container = { ...container, s3Engine: engine };
|
|
2483
|
+
updates.HAI_DEPLOY_CONTAINER_S3ENGINE = engine;
|
|
2484
|
+
const deploymentFile = join(scan.appDir ?? ".", "config", "_deploy.yml");
|
|
2485
|
+
const deployment = parseDocument(readFileSync(deploymentFile, "utf8"));
|
|
2486
|
+
if (deployment.errors.length)
|
|
2487
|
+
throw new Error(cliM("cli_deploy_invalid_value"));
|
|
2488
|
+
if (!deployment.hasIn(["container", "s3Engine"])) {
|
|
2489
|
+
if (!deployment.hasIn(["container"]))
|
|
2490
|
+
deployment.setIn(["container"], {});
|
|
2491
|
+
deployment.setIn(["container", "s3Engine"], `\${HAI_DEPLOY_CONTAINER_S3ENGINE:rustfs}`);
|
|
2492
|
+
writeFileSync(deploymentFile, deployment.toString());
|
|
2493
|
+
}
|
|
2494
|
+
}
|
|
2495
|
+
if (mode === "default" && provider?.type === "vercel") {
|
|
2496
|
+
if (!backend.provisioner)
|
|
2497
|
+
throw new Error(cliM("cli_deploy_cloud_unsupported", { params: { backend: backend.label } }));
|
|
2498
|
+
const prior = existingServices[requirement.service];
|
|
2499
|
+
const priorConfig = prior && typeof prior === "object" ? prior : {};
|
|
2500
|
+
const cloud = { provisioner: backend.provisioner };
|
|
2501
|
+
for (const name of CLOUD_FIELDS[backend.provisioner]) {
|
|
2502
|
+
const key = `HAI_DEPLOY_SERVICES_${requirement.service.toUpperCase()}_${name.toUpperCase()}`;
|
|
2503
|
+
const initial = String(priorConfig[name] ?? environment.values[key] ?? "");
|
|
2504
|
+
const value = yes ? initial : String(await ask({ type: /key|token|secret/i.test(name) ? "password" : "text", name: "value", message: key, initial, validate: (input) => Boolean(input) || cliM("cli_deploy_required") }));
|
|
2505
|
+
if (!value)
|
|
2506
|
+
throw new Error(`${key}: ${cliM("cli_deploy_required")}`);
|
|
2507
|
+
cloud[name] = value;
|
|
2508
|
+
updates[key] = value;
|
|
2509
|
+
}
|
|
2510
|
+
services[requirement.service] = cloud;
|
|
2511
|
+
} else {
|
|
2512
|
+
if (mode === "default" && (requirement.service === "sms" || backendName === "api"))
|
|
2513
|
+
throw new Error(cliM("cli_deploy_cloud_unsupported", { params: { backend: backend.label } }));
|
|
2514
|
+
const connection = mode === "custom" && backendName === current.backend ? { ...requirement.config } : requirement.configName === "reach" ? { name: requirement.config.name ?? requirement.service } : {};
|
|
2515
|
+
for (const field of backend.fields) {
|
|
2516
|
+
const previous2 = backendName === current.backend ? requirement.config[field.name] : void 0;
|
|
2517
|
+
const initial = previous2 === void 0 ? field.defaultValue ?? "" : typeof previous2 === "object" ? JSON.stringify(previous2) : String(previous2);
|
|
2518
|
+
const variable = `${requirement.envPrefix}_${field.name.toUpperCase()}`;
|
|
2519
|
+
const info = moduleEnvironmentVariables(requirement.configName).find((item) => item.name === variable);
|
|
2520
|
+
const value = mode === "custom" && !yes ? String(await ask({ type: field.secret ? "password" : "text", name: "value", message: cliM("cli_deploy_field", { params: { name: variable, description: info?.description ?? field.name, constraint: info?.constraint ?? field.kind ?? "string" } }), initial, validate: (input) => validateField(String(input), field) })) : initial;
|
|
2521
|
+
if (value !== "") {
|
|
2522
|
+
connection[field.name] = field.kind === "number" || field.kind === "port" ? Number(value) : field.kind === "boolean" ? value === "true" : field.name === "headers" || field.name === "ssl" ? parseDocument(value).toJS() : value;
|
|
2523
|
+
} else {
|
|
2524
|
+
delete connection[field.name];
|
|
2525
|
+
}
|
|
2526
|
+
}
|
|
2527
|
+
Object.assign(updates, connectionEnv(requirement, backendName, connection));
|
|
2528
|
+
}
|
|
2529
|
+
ensureConfigNode(scan.appDir ?? ".", requirement, backendName);
|
|
2530
|
+
const saved = module.saveDeploymentEnvironment(environment, updates);
|
|
2531
|
+
if (!saved.success)
|
|
2532
|
+
throw new Error(saved.error.message);
|
|
2533
|
+
for (const [name, value] of Object.entries(updates)) {
|
|
2534
|
+
core.logger.info(cliM("cli_deploy_saved", { params: { name, value: displayDeployValue(name, value) } }));
|
|
2535
|
+
}
|
|
2536
|
+
}
|
|
2537
|
+
config.services = Object.keys(services).length ? services : void 0;
|
|
2538
|
+
return modes;
|
|
2539
|
+
}
|
|
2540
|
+
|
|
2541
|
+
// src/commands/cli-deploy.ts
|
|
2542
|
+
function configObject(value) {
|
|
2543
|
+
return value && typeof value === "object" && !Array.isArray(value) ? value : {};
|
|
2544
|
+
}
|
|
2545
|
+
async function configureDeployTarget(module, config, environment, nonInteractive) {
|
|
2546
|
+
const current = module.ProviderConfigSchema.safeParse(config.provider);
|
|
2547
|
+
if (current.success) {
|
|
2548
|
+
config.provider = current.data;
|
|
2549
|
+
core.logger.info(cliM("cli_deploy_target_reused", { params: { target: current.data.type === "docker-ssh" ? `${current.data.ssh.username}@${current.data.ssh.host}:${current.data.ssh.port}` : "Vercel" } }));
|
|
2550
|
+
return;
|
|
2551
|
+
}
|
|
2552
|
+
if (nonInteractive)
|
|
2553
|
+
throw new Error(cliM("cli_deploy_target_missing", { params: { fields: current.error.issues.map((issue) => `provider.${issue.path.join(".")}`).join(", ") } }));
|
|
2554
|
+
const original = configObject(config.provider);
|
|
2555
|
+
const platform = await prompts({
|
|
2556
|
+
type: "select",
|
|
2557
|
+
name: "value",
|
|
2558
|
+
message: cliM("cli_deploy_target_platform"),
|
|
2559
|
+
choices: [{ title: cliM("cli_deploy_target_ssh"), value: "docker-ssh" }, { title: "Vercel", value: "vercel" }],
|
|
2560
|
+
initial: original.type === "vercel" ? 1 : 0
|
|
2561
|
+
});
|
|
2562
|
+
if (!platform.value)
|
|
2563
|
+
throw new Error(cliM("cli_deploy_cancelled"));
|
|
2564
|
+
const input = async (name, initial, options = {}) => {
|
|
2565
|
+
const answer = await prompts({
|
|
2566
|
+
type: options.secret ? "password" : "text",
|
|
2567
|
+
name: "value",
|
|
2568
|
+
message: cliM("cli_deploy_target_field", { params: { name } }),
|
|
2569
|
+
initial: options.secret ? void 0 : initial === void 0 ? "" : String(initial),
|
|
2570
|
+
validate: (value) => options.port ? /^\d+$/.test(value) && Number(value) >= 1 && Number(value) <= 65535 || cliM("cli_deploy_invalid_value") : options.optional || !!value.trim() || cliM("cli_deploy_invalid_value")
|
|
2571
|
+
});
|
|
2572
|
+
if (answer.value === void 0)
|
|
2573
|
+
throw new Error(cliM("cli_deploy_cancelled"));
|
|
2574
|
+
return String(answer.value).trim();
|
|
2575
|
+
};
|
|
2576
|
+
let provider;
|
|
2577
|
+
if (platform.value === "docker-ssh") {
|
|
2578
|
+
core.logger.info(cliM("cli_deploy_target_ssh_hint"));
|
|
2579
|
+
const ssh = configObject(original.ssh);
|
|
2580
|
+
const remote = configObject(original.remote);
|
|
2581
|
+
const expose = configObject(original.expose);
|
|
2582
|
+
const host = await input("HAI_DEPLOY_PROVIDER_SSH_HOST", ssh.host);
|
|
2583
|
+
const port = Number(await input("HAI_DEPLOY_PROVIDER_SSH_PORT", ssh.port ?? 22, { port: true }));
|
|
2584
|
+
const username = await input("HAI_DEPLOY_PROVIDER_SSH_USERNAME", ssh.username);
|
|
2585
|
+
const identityFile = await input("HAI_DEPLOY_PROVIDER_SSH_IDENTITYFILE", ssh.identityFile, { optional: true });
|
|
2586
|
+
const knownHostsFile = await input("HAI_DEPLOY_PROVIDER_SSH_KNOWNHOSTSFILE", ssh.knownHostsFile, { optional: true });
|
|
2587
|
+
const baseDir = await input("HAI_DEPLOY_PROVIDER_REMOTE_BASEDIR", remote.baseDir || "/opt/hai/apps");
|
|
2588
|
+
const runtime = await prompts({ type: "select", name: "value", message: cliM("cli_deploy_target_runtime"), choices: ["auto", "podman", "docker"].map((value) => ({ title: value, value })), initial: ["auto", "podman", "docker"].indexOf(String(remote.runtime ?? "auto")) });
|
|
2589
|
+
if (!runtime.value)
|
|
2590
|
+
throw new Error(cliM("cli_deploy_cancelled"));
|
|
2591
|
+
const hostPort = Number(await input("HAI_DEPLOY_PROVIDER_EXPOSE_HOSTPORT", expose.hostPort || 3e3, { port: true }));
|
|
2592
|
+
const containerPort = Number(await input("HAI_DEPLOY_PROVIDER_EXPOSE_CONTAINERPORT", expose.containerPort || 3e3, { port: true }));
|
|
2593
|
+
provider = {
|
|
2594
|
+
type: "docker-ssh",
|
|
2595
|
+
ssh: { ...ssh, host, port, username, identityFile: identityFile || void 0, knownHostsFile: knownHostsFile || void 0 },
|
|
2596
|
+
remote: { ...remote, baseDir, runtime: runtime.value },
|
|
2597
|
+
expose: { type: "port", hostPort, containerPort }
|
|
2598
|
+
};
|
|
2599
|
+
} else {
|
|
2600
|
+
provider = { type: "vercel", token: await input("HAI_DEPLOY_PROVIDER_TOKEN", void 0, { secret: true }) };
|
|
2601
|
+
}
|
|
2602
|
+
const validated = module.ProviderConfigSchema.safeParse(provider);
|
|
2603
|
+
if (!validated.success)
|
|
2604
|
+
throw new Error(cliM("cli_deploy_invalid_value"));
|
|
2605
|
+
const saved = module.saveDeploymentEnvironment(environment, { HAI_DEPLOY_PROVIDER: JSON.stringify(validated.data) }, { private: true });
|
|
2606
|
+
if (!saved.success)
|
|
2607
|
+
throw new Error(saved.error.message);
|
|
2608
|
+
config.provider = validated.data;
|
|
2609
|
+
core.logger.info(cliM("cli_deploy_target_saved"));
|
|
2610
|
+
}
|
|
2227
2611
|
async function deployCommand(options) {
|
|
2228
2612
|
const spinner = ora();
|
|
2229
|
-
const cwd = options.cwd ??
|
|
2230
|
-
|
|
2613
|
+
const cwd = options.cwd ?? process3.cwd();
|
|
2614
|
+
let appDir = resolve(cwd, options.appDir ?? ".");
|
|
2231
2615
|
let closeDeploy = null;
|
|
2616
|
+
const originalEnv = { ...process3.env };
|
|
2232
2617
|
try {
|
|
2618
|
+
let environmentName = options.env;
|
|
2619
|
+
if (!environmentName) {
|
|
2620
|
+
if (!process3.stdin.isTTY)
|
|
2621
|
+
throw new Error(cliM("cli_deploy_environment_hint"));
|
|
2622
|
+
const answer = await prompts({ type: "text", name: "environment", message: cliM("cli_deploy_environment"), initial: "prod" });
|
|
2623
|
+
environmentName = answer.environment;
|
|
2624
|
+
if (!environmentName)
|
|
2625
|
+
throw new Error(cliM("cli_deploy_cancelled"));
|
|
2626
|
+
}
|
|
2627
|
+
if (!options.yes && !process3.stdin.isTTY)
|
|
2628
|
+
throw new Error(cliM("cli_deploy_environment_hint"));
|
|
2233
2629
|
let deployModule;
|
|
2234
2630
|
try {
|
|
2235
|
-
|
|
2631
|
+
const localRequire = createRequire(resolve(appDir, "package.json"));
|
|
2632
|
+
let localModule;
|
|
2633
|
+
try {
|
|
2634
|
+
localModule = localRequire.resolve("@h-ai/deploy");
|
|
2635
|
+
} catch {
|
|
2636
|
+
}
|
|
2637
|
+
deployModule = localModule ? await import(pathToFileURL(localModule).href) : await import('./src-6PPBKQ6Q.js');
|
|
2236
2638
|
} catch {
|
|
2237
2639
|
core.logger.error(chalk.red(cliM("cli_deploy_module_missing")));
|
|
2238
2640
|
core.logger.info(chalk.cyan(" pnpm add @h-ai/deploy"));
|
|
2239
2641
|
throw new Error(cliM("cli_deploy_module_missing"));
|
|
2240
2642
|
}
|
|
2241
2643
|
const { deploy } = deployModule;
|
|
2644
|
+
const scanResult = await deploy.scan(appDir, { environment: environmentName, envDir: options.envDir ? resolve(cwd, options.envDir) : void 0 });
|
|
2645
|
+
if (!scanResult.success)
|
|
2646
|
+
throw new Error(cliM("cli_deploy_scan_failed", { params: { message: scanResult.error.message } }));
|
|
2647
|
+
const scan = scanResult.data;
|
|
2648
|
+
appDir = scan.appDir ?? appDir;
|
|
2649
|
+
const environment = deployModule.loadDeploymentEnvironment({ environment: environmentName, envDir: scan.envDir ?? appDir });
|
|
2650
|
+
if (!environment.success)
|
|
2651
|
+
throw new Error(environment.error.message);
|
|
2652
|
+
Object.assign(process3.env, environment.data.values);
|
|
2242
2653
|
spinner.start(cliM("cli_deploy_load_credentials"));
|
|
2243
2654
|
const credResult = deploy.credentials.load();
|
|
2244
2655
|
if (!credResult.success) {
|
|
@@ -2252,19 +2663,44 @@ async function deployCommand(options) {
|
|
|
2252
2663
|
core.logger.info(chalk.cyan(cliM("cli_deploy_config_hint")));
|
|
2253
2664
|
throw new Error(cliM("cli_deploy_config_missing", { params: { path: configPath } }));
|
|
2254
2665
|
}
|
|
2255
|
-
const
|
|
2256
|
-
|
|
2257
|
-
|
|
2666
|
+
const loadedConfig = deployModule.loadDeploymentConfig("deploy", configPath, process3.env);
|
|
2667
|
+
if (!loadedConfig.success)
|
|
2668
|
+
throw new Error(loadedConfig.error.message);
|
|
2669
|
+
const deployConfig = loadedConfig.data;
|
|
2258
2670
|
spinner.succeed(cliM("cli_deploy_config_loaded"));
|
|
2259
|
-
|
|
2260
|
-
const scanResult = await deploy.scan(appDir);
|
|
2261
|
-
if (!scanResult.success) {
|
|
2262
|
-
throw new Error(cliM("cli_deploy_scan_failed", { params: { message: scanResult.error.message } }));
|
|
2263
|
-
}
|
|
2264
|
-
const scan = scanResult.data;
|
|
2671
|
+
await configureDeployTarget(deployModule, deployConfig, environment.data, options.yes);
|
|
2265
2672
|
spinner.succeed(cliM("cli_deploy_scanned", { params: { name: scan.appName, sveltekit: String(scan.isSvelteKit), services: scan.requiredServices.join(", ") || cliM("cli_deploy_none") } }));
|
|
2673
|
+
let skipMiddleware = options.skipMiddleware || options.skipProvision;
|
|
2674
|
+
if (!skipMiddleware && !options.yes && scan.requiredServices.length) {
|
|
2675
|
+
const action = await prompts({
|
|
2676
|
+
type: "select",
|
|
2677
|
+
name: "value",
|
|
2678
|
+
message: cliM("cli_deploy_middleware_plan"),
|
|
2679
|
+
choices: [{ title: cliM("cli_deploy_middleware_configure"), value: "configure" }, { title: cliM("cli_deploy_middleware_skip"), value: "skip" }]
|
|
2680
|
+
});
|
|
2681
|
+
if (!action.value)
|
|
2682
|
+
throw new Error(cliM("cli_deploy_cancelled"));
|
|
2683
|
+
skipMiddleware = action.value === "skip";
|
|
2684
|
+
}
|
|
2685
|
+
if (skipMiddleware)
|
|
2686
|
+
core.logger.info(cliM("cli_deploy_middleware_skipped"));
|
|
2687
|
+
const serviceModes = skipMiddleware ? Object.fromEntries(scan.requiredServices.map((service) => [service, "keep"])) : await configureDeployMiddleware(deployModule, scan, environment.data, deployConfig, options.yes);
|
|
2688
|
+
if (skipMiddleware)
|
|
2689
|
+
deployConfig.services = void 0;
|
|
2690
|
+
for (const key of Object.keys(process3.env)) {
|
|
2691
|
+
if (originalEnv[key] === void 0)
|
|
2692
|
+
delete process3.env[key];
|
|
2693
|
+
}
|
|
2694
|
+
Object.assign(process3.env, originalEnv);
|
|
2695
|
+
const refreshed = deployModule.loadDeploymentEnvironment({ environment: environment.data.environment, envDir: environment.data.envDir });
|
|
2696
|
+
if (!refreshed.success)
|
|
2697
|
+
throw new Error(refreshed.error.message);
|
|
2698
|
+
Object.assign(process3.env, refreshed.data.values);
|
|
2266
2699
|
spinner.start(cliM("cli_deploy_initializing"));
|
|
2267
|
-
const
|
|
2700
|
+
const validatedConfig = deployModule.DeployConfigSchema.safeParse(deployConfig);
|
|
2701
|
+
if (!validatedConfig.success)
|
|
2702
|
+
throw new Error(cliM("cli_deploy_init_failed", { params: { message: cliM("cli_deploy_invalid_value") } }));
|
|
2703
|
+
const initResult = await deploy.init(validatedConfig.data);
|
|
2268
2704
|
if (!initResult.success) {
|
|
2269
2705
|
throw new Error(cliM("cli_deploy_init_failed", { params: { message: initResult.error.message } }));
|
|
2270
2706
|
}
|
|
@@ -2273,8 +2709,11 @@ async function deployCommand(options) {
|
|
|
2273
2709
|
spinner.start(cliM("cli_deploy_deploying"));
|
|
2274
2710
|
const deployResult = await deploy.deployApp(appDir, {
|
|
2275
2711
|
projectName: options.projectName,
|
|
2276
|
-
skipProvision:
|
|
2277
|
-
skipBuild: options.skipBuild
|
|
2712
|
+
skipProvision: skipMiddleware,
|
|
2713
|
+
skipBuild: options.skipBuild,
|
|
2714
|
+
environment: environment.data.environment,
|
|
2715
|
+
envDir: environment.data.envDir,
|
|
2716
|
+
serviceModes
|
|
2278
2717
|
});
|
|
2279
2718
|
if (!deployResult.success) {
|
|
2280
2719
|
if (deployResult.error.ext?.recovery) {
|
|
@@ -2285,6 +2724,21 @@ async function deployCommand(options) {
|
|
|
2285
2724
|
throw new Error(cliM("cli_deploy_failed", { params: { message: deployResult.error.message } }));
|
|
2286
2725
|
}
|
|
2287
2726
|
spinner.succeed(chalk.green(cliM("cli_deploy_success")));
|
|
2727
|
+
for (const key of Object.keys(process3.env)) {
|
|
2728
|
+
if (originalEnv[key] === void 0)
|
|
2729
|
+
delete process3.env[key];
|
|
2730
|
+
}
|
|
2731
|
+
Object.assign(process3.env, originalEnv);
|
|
2732
|
+
const completedEnvironment = deployModule.loadDeploymentEnvironment({ environment: environment.data.environment, envDir: environment.data.envDir });
|
|
2733
|
+
if (!completedEnvironment.success)
|
|
2734
|
+
throw new Error(completedEnvironment.error.message);
|
|
2735
|
+
for (const requirement of scan.middlewares ?? []) {
|
|
2736
|
+
if ((serviceModes[requirement.envPrefix] ?? serviceModes[requirement.service]) === "keep")
|
|
2737
|
+
continue;
|
|
2738
|
+
const value = completedEnvironment.data.values[requirement.envPrefix];
|
|
2739
|
+
if (value !== void 0)
|
|
2740
|
+
core.logger.info(cliM("cli_deploy_saved", { params: { name: requirement.envPrefix, value: displayDeployValue(requirement.envPrefix, value) } }));
|
|
2741
|
+
}
|
|
2288
2742
|
core.logger.info("");
|
|
2289
2743
|
core.logger.info(chalk.cyan(cliM("cli_deploy_url", { params: { url: deployResult.data.url } })));
|
|
2290
2744
|
core.logger.info(chalk.gray(cliM("cli_deploy_id", { params: { id: deployResult.data.deploymentId } })));
|
|
@@ -2302,16 +2756,16 @@ async function deployCommand(options) {
|
|
|
2302
2756
|
await closeDeploy();
|
|
2303
2757
|
} catch (error) {
|
|
2304
2758
|
core.logger.error("Failed to close deploy module", { error });
|
|
2305
|
-
|
|
2759
|
+
process3.exitCode = 1;
|
|
2306
2760
|
}
|
|
2307
2761
|
}
|
|
2762
|
+
for (const key of Object.keys(process3.env)) {
|
|
2763
|
+
if (originalEnv[key] === void 0)
|
|
2764
|
+
delete process3.env[key];
|
|
2765
|
+
}
|
|
2766
|
+
Object.assign(process3.env, originalEnv);
|
|
2308
2767
|
}
|
|
2309
2768
|
}
|
|
2310
|
-
function interpolateEnvFallback(content) {
|
|
2311
|
-
return content.replace(/\$\{([^:}]+)(?::([^}]*))?\}/g, (_match, key, defaultValue) => {
|
|
2312
|
-
return process4.env[key] ?? defaultValue ?? "";
|
|
2313
|
-
});
|
|
2314
|
-
}
|
|
2315
2769
|
var GENERATORS = {
|
|
2316
2770
|
page: {
|
|
2317
2771
|
description: "SvelteKit \u9875\u9762",
|
|
@@ -2653,7 +3107,7 @@ async function initProject(options) {
|
|
|
2653
3107
|
const { project, existing, missing } = result;
|
|
2654
3108
|
spinner.succeed(`\u68C0\u6D4B\u5230\u9879\u76EE: ${chalk.cyan(project.name)}`);
|
|
2655
3109
|
if (project.isHaiProject) {
|
|
2656
|
-
const { generateProjectAiSupport, resolveSkillNamesForPackages } = await import('./cli-skill-templates-
|
|
3110
|
+
const { generateProjectAiSupport, resolveSkillNamesForPackages } = await import('./cli-skill-templates-A5VIR5J6.js');
|
|
2657
3111
|
const generatedFiles = await generateProjectAiSupport(cwd, resolveSkillNamesForPackages(project.haiPackages), {
|
|
2658
3112
|
overwriteSkills: false,
|
|
2659
3113
|
overwriteBridgeFiles: false
|
|
@@ -2757,7 +3211,7 @@ cli.command("create [name]", "\u521B\u5EFA\u65B0\u7684 hai \u9879\u76EE").option
|
|
|
2757
3211
|
cwd: options.cwd
|
|
2758
3212
|
});
|
|
2759
3213
|
} catch {
|
|
2760
|
-
|
|
3214
|
+
process3.exitCode = 1;
|
|
2761
3215
|
}
|
|
2762
3216
|
});
|
|
2763
3217
|
cli.command("add [module]", "\u5411\u73B0\u6709\u9879\u76EE\u6DFB\u52A0\u6A21\u5757").option("--no-install", "\u4E0D\u5B89\u88C5\u4F9D\u8D56").action(async (module, options) => {
|
|
@@ -2769,7 +3223,7 @@ cli.command("add [module]", "\u5411\u73B0\u6709\u9879\u76EE\u6DFB\u52A0\u6A21\u5
|
|
|
2769
3223
|
cwd: options.cwd
|
|
2770
3224
|
});
|
|
2771
3225
|
} catch {
|
|
2772
|
-
|
|
3226
|
+
process3.exit(1);
|
|
2773
3227
|
}
|
|
2774
3228
|
});
|
|
2775
3229
|
cli.command("init", "\u521D\u59CB\u5316/\u6821\u9A8C\u9879\u76EE\u914D\u7F6E").option("-f, --force", "\u5F3A\u5236\u91CD\u65B0\u751F\u6210\u914D\u7F6E").action(async (options) => {
|
|
@@ -2780,7 +3234,7 @@ cli.command("init", "\u521D\u59CB\u5316/\u6821\u9A8C\u9879\u76EE\u914D\u7F6E").o
|
|
|
2780
3234
|
cwd: options.cwd
|
|
2781
3235
|
});
|
|
2782
3236
|
} catch {
|
|
2783
|
-
|
|
3237
|
+
process3.exit(1);
|
|
2784
3238
|
}
|
|
2785
3239
|
});
|
|
2786
3240
|
cli.command("generate [type] [name]", "\u751F\u6210\u4EE3\u7801").alias("g").option("-o, --output <path>", "\u8F93\u51FA\u8DEF\u5F84").option("-f, --force", "\u8986\u76D6\u73B0\u6709\u6587\u4EF6").action(async (type, name, options) => {
|
|
@@ -2794,21 +3248,25 @@ cli.command("generate [type] [name]", "\u751F\u6210\u4EE3\u7801").alias("g").opt
|
|
|
2794
3248
|
cwd: options.cwd
|
|
2795
3249
|
});
|
|
2796
3250
|
} catch {
|
|
2797
|
-
|
|
3251
|
+
process3.exit(1);
|
|
2798
3252
|
}
|
|
2799
3253
|
});
|
|
2800
|
-
cli.command("deploy [appDir]", "\u90E8\u7F72\u5E94\u7528\u5230\u4E91\u5E73\u53F0").option("--project-name <name>", "\u9879\u76EE\u540D\u79F0\uFF08\u9ED8\u8BA4\
|
|
3254
|
+
cli.command("deploy [appDir]", "\u90E8\u7F72\u5E94\u7528\u5230\u4E91\u5E73\u53F0").option("--env <environment>", "\u90E8\u7F72\u73AF\u5883\uFF08test / prod \u7B49\uFF0C\u5FC5\u987B\u5B58\u5728\u5BF9\u5E94\u73AF\u5883\u6587\u4EF6\uFF09").option("--env-dir <path>", "\u73AF\u5883\u6587\u4EF6\u6240\u5728\u76EE\u5F55\uFF08\u9ED8\u8BA4\u81EA\u52A8\u67E5\u627E\uFF09").option("-y, --yes", "\u6309\u5F53\u524D\u540E\u7AEF\u9ED8\u8BA4\u914D\u7F6E\u90E8\u7F72\uFF0C\u76EE\u6807\u73AF\u5883\u5DF2\u6709\u5B8C\u6574\u914D\u7F6E\u65F6\u4FDD\u6301\u4E0D\u53D8").option("--project-name <name>", "\u9879\u76EE\u540D\u79F0\uFF08\u9ED8\u8BA4\u4F7F\u7528\u5E94\u7528\u76EE\u5F55\u540D\uFF09").option("--skip-provision", "\u914D\u7F6E\u5DF2\u5B8C\u5907\uFF0C\u8DF3\u8FC7\u4E2D\u95F4\u4EF6\u914D\u7F6E\u4E0E\u5F00\u901A\uFF0C\u76F4\u63A5\u90E8\u7F72").option("--skip-middleware", "\u914D\u7F6E\u5DF2\u5B8C\u5907\uFF0C\u8DF3\u8FC7\u5168\u90E8\u4E2D\u95F4\u4EF6\u5904\u7406\uFF0C\u76F4\u63A5\u90E8\u7F72").option("--skip-build", "\u8DF3\u8FC7\u6784\u5EFA").action(async (appDir, options) => {
|
|
2801
3255
|
try {
|
|
2802
3256
|
await deployCommand({
|
|
2803
3257
|
appDir,
|
|
3258
|
+
env: options.env,
|
|
3259
|
+
envDir: options.envDir,
|
|
3260
|
+
yes: options.yes,
|
|
2804
3261
|
projectName: options.projectName,
|
|
2805
3262
|
skipProvision: options.skipProvision,
|
|
3263
|
+
skipMiddleware: options.skipMiddleware,
|
|
2806
3264
|
skipBuild: options.skipBuild,
|
|
2807
3265
|
verbose: options.verbose,
|
|
2808
3266
|
cwd: options.cwd
|
|
2809
3267
|
});
|
|
2810
3268
|
} catch {
|
|
2811
|
-
|
|
3269
|
+
process3.exit(1);
|
|
2812
3270
|
}
|
|
2813
3271
|
});
|
|
2814
3272
|
cli.command("g:page <name>", "\u751F\u6210\u9875\u9762").action(async (name, options) => {
|