@h-ai/cli 0.1.0-alpha.67 → 0.1.0-alpha.68
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 +40 -4
- package/README.ja.md +40 -4
- package/README.md +40 -4
- package/dist/{src-2V6VP4I5.js → chunk-FI56GONY.js} +32 -3103
- package/dist/chunk-FI56GONY.js.map +1 -0
- package/dist/chunk-MBPB37FR.js +1348 -0
- package/dist/chunk-MBPB37FR.js.map +1 -0
- package/dist/{chunk-7DSURTJ4.js → chunk-ZFNBUF77.js} +327 -199
- package/dist/chunk-ZFNBUF77.js.map +1 -0
- package/dist/cli-config-templates-W2LLPMB6.js +5 -0
- package/dist/{cli-config-templates-6AHNUGBN.js.map → cli-config-templates-W2LLPMB6.js.map} +1 -1
- package/dist/{cli-skill-templates-FC3QRKDO.js → cli-skill-templates-US5CCAJ4.js} +3 -3
- package/dist/{cli-skill-templates-FC3QRKDO.js.map → cli-skill-templates-US5CCAJ4.js.map} +1 -1
- package/dist/index.js +920 -336
- package/dist/index.js.map +1 -1
- package/dist/metafile-esm.json +1 -0
- package/dist/src-DSJJXM2F.js +3849 -0
- package/dist/src-DSJJXM2F.js.map +1 -0
- package/package.json +9 -8
- package/templates/apps/admin/messages/ja-JP.json +38 -0
- package/templates/apps/admin/src/routes/+layout.svelte.hbs +16 -6
- package/templates/apps/admin/src/routes/admin/+layout.svelte.hbs +3 -3
- package/templates/apps/api/apps/{{projectName}}-contract/src/index.ts.hbs +2 -0
- package/templates/apps/api/apps/{{projectName}}-contract/src/{{projectName}}-contract.ts.hbs +4 -1
- package/templates/apps/api/apps/{{projectName}}-service/README.md.hbs +5 -1
- package/templates/apps/api/apps/{{projectName}}-service/config/_core.yml.hbs +0 -10
- package/templates/apps/api/apps/{{projectName}}-service/config/_serv.yml.hbs +2 -1
- package/templates/apps/api/apps/{{projectName}}-service/src/app.ts.hbs +24 -11
- package/templates/apps/api/apps/{{projectName}}-service/src/index.ts.hbs +8 -1
- package/templates/apps/api/apps/{{projectName}}-service/src/lib/server/init.ts.hbs +68 -11
- package/templates/apps/api/apps/{{projectName}}-service/src/server/procedures/index.ts.hbs +10 -2
- package/templates/apps/api/e2e/api-service.spec.ts.hbs +2 -2
- package/templates/apps/api/package.json.hbs +1 -1
- package/templates/apps/api/playwright.config.ts.hbs +15 -7
- package/templates/apps/fullstack/apps/{{projectName}}-app/README.md.hbs +1 -1
- package/templates/apps/fullstack/apps/{{projectName}}-app/messages/ja-JP.json.hbs +7 -0
- package/templates/apps/fullstack/apps/{{projectName}}-app/src/App.svelte.hbs +7 -1
- package/templates/apps/fullstack/apps/{{projectName}}-app/vite.config.ts.hbs +1 -1
- package/templates/apps/fullstack/apps/{{projectName}}-desktop/README.md.hbs +1 -1
- package/templates/apps/fullstack/apps/{{projectName}}-desktop/messages/ja-JP.json.hbs +7 -0
- package/templates/apps/fullstack/apps/{{projectName}}-desktop/vite.config.ts.hbs +1 -1
- package/templates/apps/fullstack/apps/{{projectName}}-miniapp/messages/ja-JP.json.hbs +12 -0
- package/templates/apps/fullstack/apps/{{projectName}}-web/README.md.hbs +1 -1
- package/templates/apps/fullstack/apps/{{projectName}}-web/messages/ja-JP.json.hbs +7 -0
- package/templates/apps/fullstack/apps/{{projectName}}-web/vite.config.ts.hbs +1 -1
- package/templates/apps/fullstack/e2e/fullstack.spec.ts.hbs +19 -8
- package/templates/apps/fullstack/package.json.hbs +1 -1
- package/templates/apps/fullstack/packages/{{projectName}}-contract/src/admin-schemas.ts.hbs +1 -1
- package/templates/apps/fullstack/packages/{{projectName}}-contract/src/index.ts.hbs +2 -0
- package/templates/apps/fullstack/packages/{{projectName}}-contract/src/{{projectName}}-contract.ts.hbs +3 -0
- package/templates/apps/fullstack/packages/{{projectName}}-serv/README.md.hbs +5 -2
- package/templates/apps/fullstack/packages/{{projectName}}-serv/config/_core.yml.hbs +0 -6
- package/templates/apps/fullstack/packages/{{projectName}}-serv/config/_reach.yml.hbs +8 -0
- package/templates/apps/fullstack/packages/{{projectName}}-serv/config/_serv.yml.hbs +2 -1
- package/templates/apps/fullstack/packages/{{projectName}}-serv/package.json.hbs +6 -0
- package/templates/apps/fullstack/packages/{{projectName}}-serv/src/app-server.ts.hbs +9 -5
- package/templates/apps/fullstack/packages/{{projectName}}-serv/src/server/admin-service.ts.hbs +3 -2
- package/templates/apps/fullstack/packages/{{projectName}}-serv/src/server/messages.ts.hbs +16 -1
- package/templates/apps/fullstack/packages/{{projectName}}-serv/src/server/procedures/admin-procedures.ts.hbs +7 -3
- package/templates/apps/fullstack/packages/{{projectName}}-serv/src/server/procedures/index.ts.hbs +3 -0
- package/templates/apps/fullstack/packages/{{projectName}}-serv/src/server/runtime.ts.hbs +58 -7
- package/templates/apps/fullstack/packages/{{projectName}}-serv/tests/app-server.test.ts.hbs +13 -3
- package/templates/apps/fullstack/packages/{{projectName}}-shared/README.md.hbs +9 -4
- package/templates/apps/fullstack/packages/{{projectName}}-shared/messages/ja-JP.json.hbs +105 -0
- package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/components/AdminWorkspace.svelte.hbs +3 -3
- package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/components/AppShell.svelte.hbs +5 -5
- package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/pages/audit.svelte.hbs +7 -7
- package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/pages/settings.svelte.hbs +1 -1
- package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/pages/users.svelte.hbs +8 -8
- package/templates/apps/fullstack/packages/{{projectName}}-shared/tests/api-client.test.ts.hbs +11 -12
- package/templates/apps/fullstack/playwright.config.ts.hbs +15 -6
- package/templates/apps/h5/messages/ja-JP.json +40 -0
- package/templates/apps/h5/src/routes/+layout.svelte.hbs +14 -3
- package/templates/apps/mobile-app/messages/ja-JP.json +36 -0
- package/templates/apps/mobile-app/src/App.svelte.hbs +13 -3
- package/templates/apps/mobile-app/src/lib/api.ts.hbs +173 -14
- package/templates/apps/website/messages/ja-JP.json +32 -0
- package/templates/apps/website/src/routes/+layout.svelte.hbs +10 -2
- package/templates/base/e2e/app.spec.ts.hbs +13 -8
- package/templates/base/package.json.hbs +4 -1
- package/templates/base/playwright.config.ts +15 -4
- package/templates/base/src/hooks.server.ts.hbs +15 -6
- package/templates/base/src/lib/server/init.ts.hbs +139 -30
- package/templates/base/vite.config.ts.hbs +2 -2
- package/templates/features/iam/routes-shared/api/auth/logout/+server.ts +11 -6
- package/templates/features/payment/routes/api/v1/payment/create/+server.ts +18 -25
- package/templates/features/payment/routes/api/v1/payment/notify/[provider]/+server.ts +4 -0
- package/templates/i18n/project.inlang/settings.json +1 -1
- package/templates/shared/e2e-env.ts +98 -0
- package/templates/shared/run-e2e.mjs +51 -12
- package/templates/skills/AGENTS.md +4 -0
- package/templates/skills/bridges/admin/AGENTS.md +5 -1
- package/templates/skills/bridges/api/AGENTS.md +2 -0
- package/templates/skills/bridges/fullstack/AGENTS.md.hbs +4 -0
- package/templates/skills/bridges/h5/AGENTS.md +5 -1
- package/templates/skills/bridges/mobile-app/AGENTS.md +5 -1
- package/templates/skills/bridges/website/AGENTS.md +5 -1
- package/templates/skills/hai-ai/SKILL.md +8 -2
- package/templates/skills/hai-ai/guide.md +134 -60
- package/templates/skills/hai-ai/reference.md +51 -11
- package/templates/skills/hai-api-client/SKILL.md +2 -0
- package/templates/skills/hai-api-client/reference.md +17 -5
- package/templates/skills/hai-api-contract/SKILL.md +1 -0
- package/templates/skills/hai-api-contract/reference.md +4 -2
- package/templates/skills/hai-app-create/SKILL.md +4 -0
- package/templates/skills/hai-app-create/development.md +20 -4
- package/templates/skills/hai-app-create/reference.md +2 -1
- package/templates/skills/hai-app-review/SKILL.md +6 -2
- package/templates/skills/hai-app-review/differential.md +34 -0
- package/templates/skills/hai-app-tests/SKILL.md +3 -1
- package/templates/skills/hai-app-tests/reference.md +4 -4
- package/templates/skills/hai-app-tests/validation.md +10 -3
- package/templates/skills/hai-audit/SKILL.md +10 -8
- package/templates/skills/hai-audit/reference.md +3 -0
- package/templates/skills/hai-build/reference.md +6 -4
- package/templates/skills/hai-cache/SKILL.md +9 -3
- package/templates/skills/hai-cache/reference.md +53 -11
- package/templates/skills/hai-capacitor/SKILL.md +3 -1
- package/templates/skills/hai-capacitor/reference.md +9 -5
- package/templates/skills/hai-core/SKILL.md +4 -0
- package/templates/skills/hai-core/reference.md +25 -5
- package/templates/skills/hai-crypto/SKILL.md +10 -2
- package/templates/skills/hai-crypto/reference.md +64 -10
- package/templates/skills/hai-datapipe/SKILL.md +2 -0
- package/templates/skills/hai-datapipe/reference.md +3 -1
- package/templates/skills/hai-deploy/SKILL.md +9 -4
- package/templates/skills/hai-deploy/middleware.md +51 -37
- package/templates/skills/hai-iam/SKILL.md +24 -8
- package/templates/skills/hai-iam/reference.md +181 -154
- package/templates/skills/hai-kit/SKILL.md +23 -7
- package/templates/skills/hai-kit/reference.md +42 -24
- package/templates/skills/hai-payment/SKILL.md +13 -11
- package/templates/skills/hai-payment/reference.md +80 -41
- package/templates/skills/hai-pr-review/SKILL.md +21 -88
- package/templates/skills/hai-reach/SKILL.md +5 -2
- package/templates/skills/hai-reach/reference.md +83 -22
- package/templates/skills/hai-reldb/SKILL.md +5 -2
- package/templates/skills/hai-reldb/reference.md +68 -25
- package/templates/skills/hai-scheduler/SKILL.md +8 -5
- package/templates/skills/hai-scheduler/reference.md +38 -24
- package/templates/skills/hai-serv/SKILL.md +23 -8
- package/templates/skills/hai-serv/reference.md +123 -87
- package/templates/skills/hai-spec-implement/SKILL.md +4 -0
- package/templates/skills/hai-spec-implement/evaluation.md +18 -0
- package/templates/skills/hai-spec-implement/handoff.md +47 -0
- package/templates/skills/hai-spec-implement/templates.md +5 -0
- package/templates/skills/hai-spec-implement/workflow.md +2 -0
- package/templates/skills/hai-spec-review/SKILL.md +4 -0
- package/templates/skills/hai-storage/SKILL.md +8 -2
- package/templates/skills/hai-storage/reference.md +64 -25
- package/templates/skills/hai-ui/SKILL.md +16 -2
- package/templates/skills/hai-ui/guide.md +3 -3
- package/templates/skills/hai-ui/reference.md +17 -6
- package/templates/skills/hai-ui-design/SKILL.md +31 -0
- package/templates/skills/hai-ui-design/patterns.md +26 -0
- package/templates/skills/hai-ui-review/SKILL.md +30 -0
- package/templates/skills/hai-ui-review/checklist.md +22 -0
- package/templates/skills/hai-vecdb/SKILL.md +12 -8
- package/templates/skills/hai-vecdb/reference.md +33 -3
- package/dist/chunk-7DSURTJ4.js.map +0 -1
- package/dist/chunk-AOQ4N657.js +0 -640
- package/dist/chunk-AOQ4N657.js.map +0 -1
- package/dist/cli-config-templates-6AHNUGBN.js +0 -5
- package/dist/src-2V6VP4I5.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
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, generateProductionEnvironment, moduleEnvironmentVariables, parameterizeConfig } from './chunk-ZFNBUF77.js';
|
|
3
|
+
import { cliM } from './chunk-MBPB37FR.js';
|
|
4
|
+
import { external_exports } from './chunk-FI56GONY.js';
|
|
4
5
|
import './chunk-2H7UOFLK.js';
|
|
5
6
|
import process4 from 'process';
|
|
6
7
|
import { cac } from 'cac';
|
|
@@ -10,15 +11,17 @@ import { fileURLToPath, pathToFileURL } from 'url';
|
|
|
10
11
|
import { wrapAnsi } from 'fast-wrap-ansi';
|
|
11
12
|
import fse from 'fs-extra';
|
|
12
13
|
import Handlebars2 from 'handlebars';
|
|
14
|
+
import { parseEnv } from 'util';
|
|
13
15
|
import ora from 'ora';
|
|
14
16
|
import prompts from 'prompts';
|
|
15
17
|
import { Buffer } from 'buffer';
|
|
16
18
|
import { execSync, execFileSync, spawn } from 'child_process';
|
|
17
|
-
import { randomBytes, createHash } from 'crypto';
|
|
18
|
-
import { stringify, parseDocument, isScalar, isMap } from 'yaml';
|
|
19
|
+
import { randomBytes, createECDH, createHash, randomUUID } from 'crypto';
|
|
20
|
+
import { stringify, parse, parseDocument, isScalar, isMap } from 'yaml';
|
|
19
21
|
import { readFileSync, existsSync, mkdirSync, mkdtempSync, createReadStream, writeFileSync, renameSync, rmSync, readdirSync, copyFileSync } from 'fs';
|
|
20
22
|
import { createRequire } from 'module';
|
|
21
23
|
import { core } from '@h-ai/core';
|
|
24
|
+
import { glob } from 'fs/promises';
|
|
22
25
|
|
|
23
26
|
var CAMEL_CASE_SEPARATOR_REGEX = /[-_\s]+(.)?/g;
|
|
24
27
|
var LEADING_CHAR_REGEX = /^./;
|
|
@@ -429,6 +432,8 @@ function buildAppHaiCatalogPackages(options) {
|
|
|
429
432
|
const packageNames = ["@h-ai/cli"];
|
|
430
433
|
if (options.isSvelteOnlyApp) {
|
|
431
434
|
packageNames.push("@h-ai/api-client", "@h-ai/api-contract", "@h-ai/capacitor", "@h-ai/ui");
|
|
435
|
+
if (options.featureMap.crypto)
|
|
436
|
+
packageNames.push("@h-ai/crypto");
|
|
432
437
|
return pickCatalogPackages(packageNames, HAI_CATALOG_PACKAGES);
|
|
433
438
|
}
|
|
434
439
|
packageNames.push("@h-ai/core", "@h-ai/kit");
|
|
@@ -470,7 +475,7 @@ function buildAppHaiCatalogPackages(options) {
|
|
|
470
475
|
packageNames.push("@h-ai/capacitor");
|
|
471
476
|
return pickCatalogPackages(packageNames, HAI_CATALOG_PACKAGES);
|
|
472
477
|
}
|
|
473
|
-
function buildFullstackCatalogPackages(frontends, deploy = false) {
|
|
478
|
+
function buildFullstackCatalogPackages(frontends, deploy = false, features = {}) {
|
|
474
479
|
const packageNames = [
|
|
475
480
|
...FULLSTACK_ROOT_CATALOG_PACKAGE_NAMES,
|
|
476
481
|
"@h-ai/cli",
|
|
@@ -486,6 +491,10 @@ function buildFullstackCatalogPackages(frontends, deploy = false) {
|
|
|
486
491
|
packageNames.push(...FULLSTACK_DESKTOP_CATALOG_PACKAGE_NAMES);
|
|
487
492
|
if (deploy)
|
|
488
493
|
packageNames.push("@h-ai/deploy");
|
|
494
|
+
if (features.ai)
|
|
495
|
+
packageNames.push("@h-ai/ai");
|
|
496
|
+
if (features.vecdb)
|
|
497
|
+
packageNames.push("@h-ai/vecdb");
|
|
489
498
|
return pickCatalogPackages(packageNames, FULLSTACK_CATALOG_PACKAGES);
|
|
490
499
|
}
|
|
491
500
|
var FEATURE_ROUTE_DIRS = {
|
|
@@ -591,8 +600,12 @@ function buildTemplateContext(options) {
|
|
|
591
600
|
featureMap.cache = true;
|
|
592
601
|
featureMap.crypto = true;
|
|
593
602
|
}
|
|
594
|
-
if (featureMap.audit || featureMap.scheduler || featureMap.payment) {
|
|
603
|
+
if (featureMap.audit || featureMap.scheduler || featureMap.payment || featureMap.crypto) {
|
|
604
|
+
featureMap.db = true;
|
|
605
|
+
}
|
|
606
|
+
if (featureMap.ai) {
|
|
595
607
|
featureMap.db = true;
|
|
608
|
+
featureMap.vecdb = true;
|
|
596
609
|
}
|
|
597
610
|
const isApi = options.appType === "api";
|
|
598
611
|
const isFullstack = options.appType === "fullstack";
|
|
@@ -603,7 +616,7 @@ function buildTemplateContext(options) {
|
|
|
603
616
|
const selectedFrontends = options.frontends ?? DEFAULT_FULLSTACK_FRONTENDS;
|
|
604
617
|
let haiCatalogPackages;
|
|
605
618
|
if (isFullstack) {
|
|
606
|
-
haiCatalogPackages = buildFullstackCatalogPackages(selectedFrontends, featureMap.deploy);
|
|
619
|
+
haiCatalogPackages = buildFullstackCatalogPackages(selectedFrontends, featureMap.deploy, featureMap);
|
|
607
620
|
} else if (isApi) {
|
|
608
621
|
haiCatalogPackages = buildApiWorkspaceCatalogPackages(featureMap);
|
|
609
622
|
} else {
|
|
@@ -695,9 +708,8 @@ function buildFullstackTemplateContext(projectName, frontendsInput, moduleConfig
|
|
|
695
708
|
function resolveI18nContext(moduleConfigs) {
|
|
696
709
|
const coreLocale = moduleConfigs?.core?.defaultLocale ?? "zh-CN";
|
|
697
710
|
const baseLocale = moduleConfigs?.fullstack?.i18n?.baseLocale ?? coreLocale;
|
|
698
|
-
const fallbackLocale = baseLocale === "en-US" ? "zh-CN" : "en-US";
|
|
699
711
|
const requested = moduleConfigs?.fullstack?.i18n?.locales;
|
|
700
|
-
const locales = requested && requested.length > 0 ? [...new Set(requested)] : [baseLocale,
|
|
712
|
+
const locales = requested && requested.length > 0 ? [...new Set(requested)] : [.../* @__PURE__ */ new Set([baseLocale, "zh-CN", "en-US", "ja-JP"])];
|
|
701
713
|
if (!locales.includes(baseLocale)) {
|
|
702
714
|
locales.unshift(baseLocale);
|
|
703
715
|
}
|
|
@@ -779,6 +791,7 @@ async function generateFromTemplates(projectPath, context) {
|
|
|
779
791
|
}
|
|
780
792
|
await renderDynamicFiles(root, projectPath, context);
|
|
781
793
|
await fse.copy(path2.join(root, "shared", "run-e2e.mjs"), path2.join(projectPath, "scripts", "run-e2e.mjs"));
|
|
794
|
+
await fse.copy(path2.join(root, "shared", "e2e-env.ts"), path2.join(projectPath, "scripts", "e2e-env.ts"));
|
|
782
795
|
await fse.ensureDir(path2.join(projectPath, "images", "dependencies"));
|
|
783
796
|
await fse.writeFile(path2.join(projectPath, "images", "dependencies", ".gitkeep"), "");
|
|
784
797
|
await fse.copy(path2.join(root, "shared", ".gitattributes"), path2.join(projectPath, ".gitattributes"));
|
|
@@ -794,6 +807,9 @@ async function generateFromTemplates(projectPath, context) {
|
|
|
794
807
|
const i18nDir = path2.join(root, "i18n");
|
|
795
808
|
if (await fse.pathExists(i18nDir)) {
|
|
796
809
|
await fse.copy(i18nDir, projectPath, { overwrite: true });
|
|
810
|
+
const settingsPath = path2.join(projectPath, "project.inlang", "settings.json");
|
|
811
|
+
const settings = await fse.readJson(settingsPath);
|
|
812
|
+
await fse.writeJson(settingsPath, { ...settings, baseLocale: context.defaultLocale }, { spaces: 2 });
|
|
797
813
|
}
|
|
798
814
|
}
|
|
799
815
|
if (includeBaseTemplate) {
|
|
@@ -921,136 +937,242 @@ var RE_HTTPS_URL = /^https:\/\/.+/;
|
|
|
921
937
|
var RE_GIT_SSH_URL = /^git@.+:.+/;
|
|
922
938
|
var APP_TYPES = {
|
|
923
939
|
"admin": {
|
|
924
|
-
name
|
|
925
|
-
|
|
940
|
+
get name() {
|
|
941
|
+
return cliM("cli_appAdminName");
|
|
942
|
+
},
|
|
943
|
+
get description() {
|
|
944
|
+
return cliM("cli_appAdminDescription");
|
|
945
|
+
},
|
|
926
946
|
defaultFeatures: ["iam", "db", "cache", "crypto"]
|
|
927
947
|
},
|
|
928
948
|
"website": {
|
|
929
|
-
name
|
|
930
|
-
|
|
949
|
+
get name() {
|
|
950
|
+
return cliM("cli_appWebsiteName");
|
|
951
|
+
},
|
|
952
|
+
get description() {
|
|
953
|
+
return cliM("cli_appWebsiteDescription");
|
|
954
|
+
},
|
|
931
955
|
defaultFeatures: ["db", "cache"]
|
|
932
956
|
},
|
|
933
957
|
"h5": {
|
|
934
|
-
name
|
|
935
|
-
|
|
958
|
+
get name() {
|
|
959
|
+
return cliM("cli_appH5Name");
|
|
960
|
+
},
|
|
961
|
+
get description() {
|
|
962
|
+
return cliM("cli_appH5Description");
|
|
963
|
+
},
|
|
936
964
|
defaultFeatures: ["db", "cache"]
|
|
937
965
|
},
|
|
938
966
|
"api": {
|
|
939
|
-
name
|
|
940
|
-
|
|
967
|
+
get name() {
|
|
968
|
+
return cliM("cli_appApiName");
|
|
969
|
+
},
|
|
970
|
+
get description() {
|
|
971
|
+
return cliM("cli_appApiDescription");
|
|
972
|
+
},
|
|
941
973
|
defaultFeatures: ["db", "cache"]
|
|
942
974
|
},
|
|
943
975
|
"mobile-app": {
|
|
944
|
-
name
|
|
945
|
-
|
|
976
|
+
get name() {
|
|
977
|
+
return cliM("cli_appMobileName");
|
|
978
|
+
},
|
|
979
|
+
get description() {
|
|
980
|
+
return cliM("cli_appMobileDescription");
|
|
981
|
+
},
|
|
946
982
|
defaultFeatures: ["api-client", "capacitor"]
|
|
947
983
|
},
|
|
948
984
|
"fullstack": {
|
|
949
|
-
name
|
|
950
|
-
|
|
985
|
+
get name() {
|
|
986
|
+
return cliM("cli_appFullstackName");
|
|
987
|
+
},
|
|
988
|
+
get description() {
|
|
989
|
+
return cliM("cli_appFullstackDescription");
|
|
990
|
+
},
|
|
951
991
|
defaultFeatures: ["iam", "db", "cache", "crypto", "storage", "audit"]
|
|
952
992
|
}
|
|
953
993
|
};
|
|
954
994
|
var FRONTEND_TARGETS = {
|
|
955
|
-
web: { name: "Web", description
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
995
|
+
web: { name: "Web", get description() {
|
|
996
|
+
return cliM("cli_frontendWebDescription");
|
|
997
|
+
}, implemented: true },
|
|
998
|
+
app: { name: "App", get description() {
|
|
999
|
+
return cliM("cli_frontendAppDescription");
|
|
1000
|
+
}, implemented: true },
|
|
1001
|
+
miniapp: {
|
|
1002
|
+
get name() {
|
|
1003
|
+
return cliM("cli_frontendMiniappName");
|
|
1004
|
+
},
|
|
1005
|
+
get description() {
|
|
1006
|
+
return cliM("cli_frontendMiniappDescription");
|
|
1007
|
+
},
|
|
1008
|
+
implemented: false
|
|
1009
|
+
},
|
|
1010
|
+
desktop: {
|
|
1011
|
+
get name() {
|
|
1012
|
+
return cliM("cli_frontendDesktopName");
|
|
1013
|
+
},
|
|
1014
|
+
get description() {
|
|
1015
|
+
return cliM("cli_frontendDesktopDescription");
|
|
1016
|
+
},
|
|
1017
|
+
implemented: true
|
|
1018
|
+
}
|
|
959
1019
|
};
|
|
960
1020
|
var DEFAULT_FULLSTACK_FRONTENDS2 = ["web", "app", "desktop"];
|
|
961
1021
|
var FEATURES = {
|
|
962
1022
|
"iam": {
|
|
963
1023
|
id: "iam",
|
|
964
|
-
name
|
|
965
|
-
|
|
1024
|
+
get name() {
|
|
1025
|
+
return cliM("cli_featureIamName");
|
|
1026
|
+
},
|
|
1027
|
+
get description() {
|
|
1028
|
+
return cliM("cli_featureIamDescription");
|
|
1029
|
+
},
|
|
966
1030
|
packages: ["@h-ai/iam"],
|
|
967
1031
|
dependencies: ["crypto", "db", "cache"]
|
|
968
1032
|
},
|
|
969
1033
|
"db": {
|
|
970
1034
|
id: "db",
|
|
971
|
-
name
|
|
972
|
-
|
|
1035
|
+
get name() {
|
|
1036
|
+
return cliM("cli_featureDbName");
|
|
1037
|
+
},
|
|
1038
|
+
get description() {
|
|
1039
|
+
return cliM("cli_featureDbDescription");
|
|
1040
|
+
},
|
|
973
1041
|
packages: ["@h-ai/reldb"]
|
|
974
1042
|
},
|
|
975
1043
|
"cache": {
|
|
976
1044
|
id: "cache",
|
|
977
|
-
name
|
|
978
|
-
|
|
1045
|
+
get name() {
|
|
1046
|
+
return cliM("cli_featureCacheName");
|
|
1047
|
+
},
|
|
1048
|
+
get description() {
|
|
1049
|
+
return cliM("cli_featureCacheDescription");
|
|
1050
|
+
},
|
|
979
1051
|
packages: ["@h-ai/cache"]
|
|
980
1052
|
},
|
|
981
1053
|
"ai": {
|
|
982
1054
|
id: "ai",
|
|
983
|
-
name
|
|
984
|
-
|
|
985
|
-
|
|
1055
|
+
get name() {
|
|
1056
|
+
return cliM("cli_featureAiName");
|
|
1057
|
+
},
|
|
1058
|
+
get description() {
|
|
1059
|
+
return cliM("cli_featureAiDescription");
|
|
1060
|
+
},
|
|
1061
|
+
packages: ["@h-ai/ai"],
|
|
1062
|
+
dependencies: ["db", "vecdb"]
|
|
986
1063
|
},
|
|
987
1064
|
"storage": {
|
|
988
1065
|
id: "storage",
|
|
989
|
-
name
|
|
990
|
-
|
|
1066
|
+
get name() {
|
|
1067
|
+
return cliM("cli_featureStorageName");
|
|
1068
|
+
},
|
|
1069
|
+
get description() {
|
|
1070
|
+
return cliM("cli_featureStorageDescription");
|
|
1071
|
+
},
|
|
991
1072
|
packages: ["@h-ai/storage"]
|
|
992
1073
|
},
|
|
993
1074
|
"crypto": {
|
|
994
1075
|
id: "crypto",
|
|
995
|
-
name
|
|
996
|
-
|
|
997
|
-
|
|
1076
|
+
get name() {
|
|
1077
|
+
return cliM("cli_featureCryptoName");
|
|
1078
|
+
},
|
|
1079
|
+
get description() {
|
|
1080
|
+
return cliM("cli_featureCryptoDescription");
|
|
1081
|
+
},
|
|
1082
|
+
packages: ["@h-ai/crypto"],
|
|
1083
|
+
dependencies: ["db"]
|
|
998
1084
|
},
|
|
999
1085
|
"audit": {
|
|
1000
1086
|
id: "audit",
|
|
1001
|
-
name
|
|
1002
|
-
|
|
1087
|
+
get name() {
|
|
1088
|
+
return cliM("cli_featureAuditName");
|
|
1089
|
+
},
|
|
1090
|
+
get description() {
|
|
1091
|
+
return cliM("cli_featureAuditDescription");
|
|
1092
|
+
},
|
|
1003
1093
|
packages: ["@h-ai/audit"],
|
|
1004
1094
|
dependencies: ["db"]
|
|
1005
1095
|
},
|
|
1006
1096
|
"reach": {
|
|
1007
1097
|
id: "reach",
|
|
1008
|
-
name
|
|
1009
|
-
|
|
1098
|
+
get name() {
|
|
1099
|
+
return cliM("cli_featureReachName");
|
|
1100
|
+
},
|
|
1101
|
+
get description() {
|
|
1102
|
+
return cliM("cli_featureReachDescription");
|
|
1103
|
+
},
|
|
1010
1104
|
packages: ["@h-ai/reach"]
|
|
1011
1105
|
},
|
|
1012
1106
|
"payment": {
|
|
1013
1107
|
id: "payment",
|
|
1014
|
-
name
|
|
1015
|
-
|
|
1108
|
+
get name() {
|
|
1109
|
+
return cliM("cli_featurePaymentName");
|
|
1110
|
+
},
|
|
1111
|
+
get description() {
|
|
1112
|
+
return cliM("cli_featurePaymentDescription");
|
|
1113
|
+
},
|
|
1016
1114
|
packages: ["@h-ai/payment"],
|
|
1017
1115
|
dependencies: ["db"]
|
|
1018
1116
|
},
|
|
1019
1117
|
"vecdb": {
|
|
1020
1118
|
id: "vecdb",
|
|
1021
|
-
name
|
|
1022
|
-
|
|
1119
|
+
get name() {
|
|
1120
|
+
return cliM("cli_featureVecdbName");
|
|
1121
|
+
},
|
|
1122
|
+
get description() {
|
|
1123
|
+
return cliM("cli_featureVecdbDescription");
|
|
1124
|
+
},
|
|
1023
1125
|
packages: ["@h-ai/vecdb"]
|
|
1024
1126
|
},
|
|
1025
1127
|
"datapipe": {
|
|
1026
1128
|
id: "datapipe",
|
|
1027
|
-
name
|
|
1028
|
-
|
|
1129
|
+
get name() {
|
|
1130
|
+
return cliM("cli_featureDatapipeName");
|
|
1131
|
+
},
|
|
1132
|
+
get description() {
|
|
1133
|
+
return cliM("cli_featureDatapipeDescription");
|
|
1134
|
+
},
|
|
1029
1135
|
packages: ["@h-ai/datapipe"]
|
|
1030
1136
|
},
|
|
1031
1137
|
"scheduler": {
|
|
1032
1138
|
id: "scheduler",
|
|
1033
|
-
name
|
|
1034
|
-
|
|
1139
|
+
get name() {
|
|
1140
|
+
return cliM("cli_featureSchedulerName");
|
|
1141
|
+
},
|
|
1142
|
+
get description() {
|
|
1143
|
+
return cliM("cli_featureSchedulerDescription");
|
|
1144
|
+
},
|
|
1035
1145
|
packages: ["@h-ai/scheduler"],
|
|
1036
1146
|
dependencies: ["db"]
|
|
1037
1147
|
},
|
|
1038
1148
|
"deploy": {
|
|
1039
1149
|
id: "deploy",
|
|
1040
|
-
name
|
|
1041
|
-
|
|
1150
|
+
get name() {
|
|
1151
|
+
return cliM("cli_featureDeployName");
|
|
1152
|
+
},
|
|
1153
|
+
get description() {
|
|
1154
|
+
return cliM("cli_featureDeployDescription");
|
|
1155
|
+
},
|
|
1042
1156
|
packages: ["@h-ai/deploy"]
|
|
1043
1157
|
},
|
|
1044
1158
|
"api-client": {
|
|
1045
1159
|
id: "api-client",
|
|
1046
|
-
name
|
|
1047
|
-
|
|
1160
|
+
get name() {
|
|
1161
|
+
return cliM("cli_featureApiClientName");
|
|
1162
|
+
},
|
|
1163
|
+
get description() {
|
|
1164
|
+
return cliM("cli_featureApiClientDescription");
|
|
1165
|
+
},
|
|
1048
1166
|
packages: ["@h-ai/api-client"]
|
|
1049
1167
|
},
|
|
1050
1168
|
"capacitor": {
|
|
1051
1169
|
id: "capacitor",
|
|
1052
|
-
name
|
|
1053
|
-
|
|
1170
|
+
get name() {
|
|
1171
|
+
return cliM("cli_featureCapacitorName");
|
|
1172
|
+
},
|
|
1173
|
+
get description() {
|
|
1174
|
+
return cliM("cli_featureCapacitorDescription");
|
|
1175
|
+
},
|
|
1054
1176
|
packages: ["@h-ai/capacitor"]
|
|
1055
1177
|
}
|
|
1056
1178
|
};
|
|
@@ -1074,17 +1196,23 @@ var PROJECT_NAME_REGEX = /^[a-z0-9-]+$/;
|
|
|
1074
1196
|
var PROJECT_TEMPLATES = {
|
|
1075
1197
|
minimal: {
|
|
1076
1198
|
name: "minimal",
|
|
1077
|
-
description
|
|
1199
|
+
get description() {
|
|
1200
|
+
return cliM("cli_templateMinimalDescription");
|
|
1201
|
+
},
|
|
1078
1202
|
features: []
|
|
1079
1203
|
},
|
|
1080
1204
|
default: {
|
|
1081
1205
|
name: "default",
|
|
1082
|
-
description
|
|
1206
|
+
get description() {
|
|
1207
|
+
return cliM("cli_templateDefaultDescription");
|
|
1208
|
+
},
|
|
1083
1209
|
features: ["iam", "db", "cache", "crypto"]
|
|
1084
1210
|
},
|
|
1085
1211
|
full: {
|
|
1086
1212
|
name: "full",
|
|
1087
|
-
description
|
|
1213
|
+
get description() {
|
|
1214
|
+
return cliM("cli_templateFullDescription");
|
|
1215
|
+
},
|
|
1088
1216
|
features: [
|
|
1089
1217
|
"iam",
|
|
1090
1218
|
"db",
|
|
@@ -1105,7 +1233,9 @@ var PROJECT_TEMPLATES = {
|
|
|
1105
1233
|
},
|
|
1106
1234
|
custom: {
|
|
1107
1235
|
name: "custom",
|
|
1108
|
-
description
|
|
1236
|
+
get description() {
|
|
1237
|
+
return cliM("cli_templateCustomDescription");
|
|
1238
|
+
},
|
|
1109
1239
|
features: []
|
|
1110
1240
|
}
|
|
1111
1241
|
};
|
|
@@ -1113,28 +1243,28 @@ async function createProject(options) {
|
|
|
1113
1243
|
const spinner = ora();
|
|
1114
1244
|
try {
|
|
1115
1245
|
const resolvedOptions = await resolveOptions(options);
|
|
1116
|
-
if (!PROJECT_NAME_REGEX.test(resolvedOptions.name))
|
|
1117
|
-
|
|
1118
|
-
return;
|
|
1119
|
-
}
|
|
1246
|
+
if (!PROJECT_NAME_REGEX.test(resolvedOptions.name))
|
|
1247
|
+
throw new Error(cliM("cli_createInvalidName", { params: { name: resolvedOptions.name } }));
|
|
1120
1248
|
const projectPath = path2.resolve(resolvedOptions.cwd ?? ".", resolvedOptions.name);
|
|
1121
1249
|
if (await fileExists(projectPath)) {
|
|
1122
1250
|
const { overwrite } = await prompts({
|
|
1123
1251
|
type: "confirm",
|
|
1252
|
+
active: cliM("cli_yes"),
|
|
1253
|
+
inactive: cliM("cli_no"),
|
|
1124
1254
|
name: "overwrite",
|
|
1125
|
-
message:
|
|
1255
|
+
message: cliM("cli_createOverwrite", { params: { name: resolvedOptions.name } }),
|
|
1126
1256
|
initial: false
|
|
1127
1257
|
});
|
|
1128
1258
|
if (!overwrite) {
|
|
1129
|
-
writeCliMessage(chalk.yellow("
|
|
1259
|
+
writeCliMessage(chalk.yellow(cliM("cli_cancelled")));
|
|
1130
1260
|
return;
|
|
1131
1261
|
}
|
|
1132
1262
|
await fse.remove(projectPath);
|
|
1133
1263
|
}
|
|
1134
|
-
spinner.start("
|
|
1264
|
+
spinner.start(cliM("cli_createDirectory"));
|
|
1135
1265
|
await fse.ensureDir(projectPath);
|
|
1136
1266
|
spinner.succeed();
|
|
1137
|
-
spinner.start("
|
|
1267
|
+
spinner.start(cliM("cli_createFiles"));
|
|
1138
1268
|
const context = buildTemplateContext({
|
|
1139
1269
|
name: resolvedOptions.name,
|
|
1140
1270
|
appType: resolvedOptions.appType,
|
|
@@ -1146,7 +1276,7 @@ async function createProject(options) {
|
|
|
1146
1276
|
});
|
|
1147
1277
|
await generateFromTemplates(projectPath, context);
|
|
1148
1278
|
{
|
|
1149
|
-
const { generateConfigFile: generateConfigFile2, generateEnvExample } = await import('./cli-config-templates-
|
|
1279
|
+
const { generateConfigFile: generateConfigFile2, generateEnvExample, generateProductionEnvironment: generateProductionEnvironment2 } = await import('./cli-config-templates-W2LLPMB6.js');
|
|
1150
1280
|
const configs = resolvedOptions.moduleConfigs;
|
|
1151
1281
|
const isApiWorkspace = resolvedOptions.appType === "api";
|
|
1152
1282
|
const isFullstack = resolvedOptions.appType === "fullstack";
|
|
@@ -1172,7 +1302,7 @@ async function createProject(options) {
|
|
|
1172
1302
|
if (/^_.*\.yml$/.test(file))
|
|
1173
1303
|
configContents[file.slice(1, -4)] = await fse.readFile(path2.join(configDir, file), "utf8");
|
|
1174
1304
|
}
|
|
1175
|
-
const envFeatures = resolvedOptions.features;
|
|
1305
|
+
const envFeatures = isFullstack ? [.../* @__PURE__ */ new Set([...resolvedOptions.features, "reach"])] : resolvedOptions.features;
|
|
1176
1306
|
const envContents = generateEnvExample(envFeatures, configs, isFullstack, configContents);
|
|
1177
1307
|
let envExample = isApiWorkspace ? envContents : isFullstack ? `${envContents}
|
|
1178
1308
|
# First-start administrator password; must satisfy the IAM password policy.
|
|
@@ -1205,29 +1335,36 @@ ${runtimeExample.split("\n").map((line) => line && !line.startsWith("#") ? `# ${
|
|
|
1205
1335
|
${sharedVariables.join("\n")}
|
|
1206
1336
|
`);
|
|
1207
1337
|
for (const [mode, environment] of [["dev", "development"], ["test", "test"], ["prod", "production"]]) {
|
|
1208
|
-
|
|
1338
|
+
const values = mode === "prod" ? generateProductionEnvironment2(envFeatures, configs) : `# \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
|
|
1209
1339
|
HAI_CORE_ENV=${environment}
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
`);
|
|
1340
|
+
HAI_CORE_LOGGING_LEVEL=${mode === "test" ? "warn" : "debug"}
|
|
1341
|
+
`;
|
|
1342
|
+
await writeFile(path2.join(envDirectory, `.env.${mode}`), values);
|
|
1213
1343
|
}
|
|
1344
|
+
const privateEntries = [];
|
|
1214
1345
|
if (isFullstack) {
|
|
1215
1346
|
const password = `Aa1!${randomBytes(24).toString("base64url")}`;
|
|
1216
|
-
|
|
1217
|
-
HAI_ADMIN_DEFAULT_PASSWORD=${password}
|
|
1218
|
-
`, { mode: 384 });
|
|
1219
|
-
} else {
|
|
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");
|
|
1347
|
+
privateEntries.push(`HAI_ADMIN_DEFAULT_PASSWORD=${password}`);
|
|
1221
1348
|
}
|
|
1349
|
+
if (isFullstack || isApiWorkspace && resolvedOptions.features.includes("crypto")) {
|
|
1350
|
+
const key = createECDH("SM2");
|
|
1351
|
+
key.generateKeys();
|
|
1352
|
+
const id = `bootstrap-${randomBytes(6).toString("hex")}`;
|
|
1353
|
+
const ring = { activeKeyId: id, keys: { [id]: { publicKey: key.getPublicKey("hex"), privateKey: key.getPrivateKey("hex").padStart(64, "0") } } };
|
|
1354
|
+
privateEntries.push(`HAI_CRYPTO_TRANSPORT_SERVERKEYRING='${JSON.stringify(ring)}'`);
|
|
1355
|
+
}
|
|
1356
|
+
await fse.writeFile(path2.join(envDirectory, ".env.local"), `# \u672C\u673A\u79C1\u6709\u914D\u7F6E\uFF0C\u4E0D\u63D0\u4EA4 Git\uFF1B\u90E8\u7F72\u51ED\u636E\u7531\u76EE\u6807\u73AF\u5883\u5355\u72EC\u63D0\u4F9B\u3002
|
|
1357
|
+
${privateEntries.join("\n")}
|
|
1358
|
+
`, { mode: 384 });
|
|
1222
1359
|
}
|
|
1223
|
-
const { generateSkillFiles } = await import('./cli-skill-templates-
|
|
1360
|
+
const { generateSkillFiles } = await import('./cli-skill-templates-US5CCAJ4.js');
|
|
1224
1361
|
await generateSkillFiles(
|
|
1225
1362
|
projectPath,
|
|
1226
1363
|
resolvedOptions.features,
|
|
1227
1364
|
resolvedOptions.appType,
|
|
1228
1365
|
resolvedOptions.name
|
|
1229
1366
|
);
|
|
1230
|
-
const appTypeLabel =
|
|
1367
|
+
const appTypeLabel = cliM(`cli_app${{ "admin": "Admin", "website": "Website", "h5": "H5", "api": "Api", "mobile-app": "Mobile", "fullstack": "Fullstack" }[resolvedOptions.appType]}Name`, { locale: "zh-CN" });
|
|
1231
1368
|
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);
|
|
1232
1369
|
await writeFile(
|
|
1233
1370
|
path2.join(projectPath, "README.md"),
|
|
@@ -1235,20 +1372,20 @@ HAI_ADMIN_DEFAULT_PASSWORD=${password}
|
|
|
1235
1372
|
);
|
|
1236
1373
|
spinner.succeed();
|
|
1237
1374
|
if (resolvedOptions.git) {
|
|
1238
|
-
spinner.start("
|
|
1375
|
+
spinner.start(cliM("cli_createGit"));
|
|
1239
1376
|
execSync("git init", { cwd: projectPath, stdio: "ignore" });
|
|
1240
1377
|
if (resolvedOptions.gitRemote) {
|
|
1241
1378
|
const gitRemote = resolvedOptions.gitRemote;
|
|
1242
1379
|
if (!RE_HTTPS_URL.test(gitRemote) && !RE_GIT_SSH_URL.test(gitRemote)) {
|
|
1243
1380
|
spinner.fail();
|
|
1244
|
-
throw new Error(
|
|
1381
|
+
throw new Error(cliM("cli_createRemoteFailure", { params: { remote: gitRemote } }));
|
|
1245
1382
|
}
|
|
1246
1383
|
execFileSync("git", ["remote", "add", "origin", gitRemote], { cwd: projectPath, stdio: "ignore" });
|
|
1247
1384
|
}
|
|
1248
1385
|
spinner.succeed();
|
|
1249
1386
|
}
|
|
1250
1387
|
if (resolvedOptions.install) {
|
|
1251
|
-
spinner.start(
|
|
1388
|
+
spinner.start(cliM("cli_createInstall", { params: { manager: resolvedOptions.packageManager } }));
|
|
1252
1389
|
installDependencies(projectPath, resolvedOptions.packageManager);
|
|
1253
1390
|
spinner.succeed();
|
|
1254
1391
|
}
|
|
@@ -1263,10 +1400,10 @@ async function resolveOptions(options) {
|
|
|
1263
1400
|
const useDefaults = options.yes ?? false;
|
|
1264
1401
|
writeCliMessage("");
|
|
1265
1402
|
writeCliMessage(chalk.bold.cyan(" \u{1F680} hai Agent Framework"));
|
|
1266
|
-
writeCliMessage(chalk.gray("
|
|
1403
|
+
writeCliMessage(chalk.gray(cliM("cli_createTagline")));
|
|
1267
1404
|
writeCliMessage("");
|
|
1268
1405
|
const onCancel = () => {
|
|
1269
|
-
writeCliMessage(chalk.red("
|
|
1406
|
+
writeCliMessage(chalk.red(cliM("cli_cancelledLine")));
|
|
1270
1407
|
process4.exit(1);
|
|
1271
1408
|
};
|
|
1272
1409
|
let projectName = options.name;
|
|
@@ -1276,13 +1413,13 @@ async function resolveOptions(options) {
|
|
|
1276
1413
|
const { name } = await prompts({
|
|
1277
1414
|
type: "text",
|
|
1278
1415
|
name: "name",
|
|
1279
|
-
message: "
|
|
1416
|
+
message: cliM("cli_createNamePrompt"),
|
|
1280
1417
|
initial: "my-app",
|
|
1281
1418
|
validate: (value) => {
|
|
1282
1419
|
if (!value.trim())
|
|
1283
|
-
return "
|
|
1420
|
+
return cliM("cli_createNameRequired");
|
|
1284
1421
|
if (!PROJECT_NAME_REGEX.test(value))
|
|
1285
|
-
return "
|
|
1422
|
+
return cliM("cli_createNamePattern");
|
|
1286
1423
|
return true;
|
|
1287
1424
|
}
|
|
1288
1425
|
}, { onCancel });
|
|
@@ -1295,8 +1432,9 @@ async function resolveOptions(options) {
|
|
|
1295
1432
|
const { appType } = await prompts({
|
|
1296
1433
|
type: "select",
|
|
1297
1434
|
name: "appType",
|
|
1298
|
-
message: "
|
|
1299
|
-
hint:
|
|
1435
|
+
message: cliM("cli_createTypePrompt"),
|
|
1436
|
+
hint: `
|
|
1437
|
+
${formatPromptDescription(cliM("cli_selectHint"))}`,
|
|
1300
1438
|
choices: Object.entries(APP_TYPES).map(([key, t]) => ({
|
|
1301
1439
|
title: t.name,
|
|
1302
1440
|
description: formatPromptDescription(t.description),
|
|
@@ -1318,8 +1456,9 @@ async function resolveOptions(options) {
|
|
|
1318
1456
|
const { template } = await prompts({
|
|
1319
1457
|
type: "select",
|
|
1320
1458
|
name: "template",
|
|
1321
|
-
message: "
|
|
1322
|
-
hint:
|
|
1459
|
+
message: cliM("cli_createTemplatePrompt"),
|
|
1460
|
+
hint: `
|
|
1461
|
+
${formatPromptDescription(cliM("cli_selectHint"))}`,
|
|
1323
1462
|
choices: Object.values(PROJECT_TEMPLATES).map((t) => ({
|
|
1324
1463
|
title: t.name,
|
|
1325
1464
|
description: formatPromptDescription(t.description),
|
|
@@ -1333,7 +1472,9 @@ async function resolveOptions(options) {
|
|
|
1333
1472
|
let selectedFeatures = [];
|
|
1334
1473
|
const appTypeDefaults = APP_TYPES[selectedAppType].defaultFeatures;
|
|
1335
1474
|
if (selectedAppType === "fullstack") {
|
|
1336
|
-
selectedFeatures = resolveFeatureDependencies(appTypeDefaults);
|
|
1475
|
+
selectedFeatures = resolveFeatureDependencies([...appTypeDefaults, ...options.features ?? []]);
|
|
1476
|
+
} else if (options.features) {
|
|
1477
|
+
selectedFeatures = resolveFeatureDependencies(options.features);
|
|
1337
1478
|
} else if (selectedTemplate === "custom" && !options.features && useDefaults) {
|
|
1338
1479
|
selectedFeatures = resolveFeatureDependencies(appTypeDefaults);
|
|
1339
1480
|
} else if (selectedTemplate === "custom" && !options.features) {
|
|
@@ -1348,13 +1489,12 @@ async function resolveOptions(options) {
|
|
|
1348
1489
|
name: "features",
|
|
1349
1490
|
message: cliM("cli_featuresPrompt"),
|
|
1350
1491
|
choices: featureChoices,
|
|
1351
|
-
hint:
|
|
1492
|
+
hint: `
|
|
1493
|
+
${formatPromptDescription(cliM("cli_multiselectHint"))}`,
|
|
1352
1494
|
instructions: false
|
|
1353
1495
|
}, { onCancel });
|
|
1354
1496
|
selectedFeatures = features || [];
|
|
1355
1497
|
selectedFeatures = resolveFeatureDependencies(selectedFeatures);
|
|
1356
|
-
} else if (selectedTemplate === "custom" && options.features) {
|
|
1357
|
-
selectedFeatures = resolveFeatureDependencies(options.features);
|
|
1358
1498
|
} else {
|
|
1359
1499
|
const templateFeatures = PROJECT_TEMPLATES[selectedTemplate]?.features || [];
|
|
1360
1500
|
const merged = /* @__PURE__ */ new Set([...templateFeatures, ...appTypeDefaults]);
|
|
@@ -1370,8 +1510,10 @@ async function resolveOptions(options) {
|
|
|
1370
1510
|
} else if (addExamples === void 0) {
|
|
1371
1511
|
const { examples } = await prompts({
|
|
1372
1512
|
type: "confirm",
|
|
1513
|
+
active: cliM("cli_yes"),
|
|
1514
|
+
inactive: cliM("cli_no"),
|
|
1373
1515
|
name: "examples",
|
|
1374
|
-
message: "
|
|
1516
|
+
message: cliM("cli_createExamplesPrompt"),
|
|
1375
1517
|
initial: true
|
|
1376
1518
|
}, { onCancel });
|
|
1377
1519
|
addExamples = examples;
|
|
@@ -1384,10 +1526,12 @@ async function resolveOptions(options) {
|
|
|
1384
1526
|
if (!packageManager) {
|
|
1385
1527
|
const { pm } = await prompts({
|
|
1386
1528
|
type: "select",
|
|
1529
|
+
hint: `
|
|
1530
|
+
${formatPromptDescription(cliM("cli_selectHint"))}`,
|
|
1387
1531
|
name: "pm",
|
|
1388
|
-
message: "
|
|
1532
|
+
message: cliM("cli_createManagerPrompt"),
|
|
1389
1533
|
choices: [
|
|
1390
|
-
{ title: "
|
|
1534
|
+
{ title: cliM("cli_createPnpmRecommended"), value: "pnpm" },
|
|
1391
1535
|
{ title: "npm", value: "npm" },
|
|
1392
1536
|
{ title: "yarn", value: "yarn" }
|
|
1393
1537
|
],
|
|
@@ -1401,8 +1545,10 @@ async function resolveOptions(options) {
|
|
|
1401
1545
|
} else if (install === void 0) {
|
|
1402
1546
|
const { doInstall } = await prompts({
|
|
1403
1547
|
type: "confirm",
|
|
1548
|
+
active: cliM("cli_yes"),
|
|
1549
|
+
inactive: cliM("cli_no"),
|
|
1404
1550
|
name: "doInstall",
|
|
1405
|
-
message: "
|
|
1551
|
+
message: cliM("cli_createInstallPrompt"),
|
|
1406
1552
|
initial: true
|
|
1407
1553
|
}, { onCancel });
|
|
1408
1554
|
install = doInstall;
|
|
@@ -1413,8 +1559,10 @@ async function resolveOptions(options) {
|
|
|
1413
1559
|
} else if (git === void 0) {
|
|
1414
1560
|
const { initGit } = await prompts({
|
|
1415
1561
|
type: "confirm",
|
|
1562
|
+
active: cliM("cli_yes"),
|
|
1563
|
+
inactive: cliM("cli_no"),
|
|
1416
1564
|
name: "initGit",
|
|
1417
|
-
message: "
|
|
1565
|
+
message: cliM("cli_createGitPrompt"),
|
|
1418
1566
|
initial: true
|
|
1419
1567
|
}, { onCancel });
|
|
1420
1568
|
git = initGit;
|
|
@@ -1426,14 +1574,14 @@ async function resolveOptions(options) {
|
|
|
1426
1574
|
const { remote } = await prompts({
|
|
1427
1575
|
type: "text",
|
|
1428
1576
|
name: "remote",
|
|
1429
|
-
message: "
|
|
1577
|
+
message: cliM("cli_createRemotePrompt"),
|
|
1430
1578
|
initial: "",
|
|
1431
1579
|
validate: (value) => {
|
|
1432
1580
|
if (!value.trim())
|
|
1433
1581
|
return true;
|
|
1434
1582
|
if (RE_HTTPS_URL.test(value) || RE_GIT_SSH_URL.test(value))
|
|
1435
1583
|
return true;
|
|
1436
|
-
return "
|
|
1584
|
+
return cliM("cli_createRemoteInvalid");
|
|
1437
1585
|
}
|
|
1438
1586
|
}, { onCancel });
|
|
1439
1587
|
gitRemote = remote?.trim() || "";
|
|
@@ -1494,7 +1642,9 @@ async function resolveFrontendTargets(frontends, useDefaults, onCancel) {
|
|
|
1494
1642
|
value: id,
|
|
1495
1643
|
selected: DEFAULT_FULLSTACK_FRONTENDS2.includes(id)
|
|
1496
1644
|
})),
|
|
1497
|
-
|
|
1645
|
+
// 独立行显示帮助,避免宽字符让 prompts 按字符数清除时遗留折行。
|
|
1646
|
+
hint: `
|
|
1647
|
+
${formatPromptDescription(cliM("cli_multiselectHint"))}`,
|
|
1498
1648
|
instructions: false
|
|
1499
1649
|
}, { onCancel });
|
|
1500
1650
|
return normalizeFrontendTargets(selectedFrontends ?? DEFAULT_FULLSTACK_FRONTENDS2);
|
|
@@ -1526,12 +1676,12 @@ function resolveFeatureDependencies(features) {
|
|
|
1526
1676
|
async function promptModuleConfigs(features, projectName) {
|
|
1527
1677
|
const configs = {};
|
|
1528
1678
|
const onCancel = () => {
|
|
1529
|
-
writeCliMessage(chalk.red("
|
|
1679
|
+
writeCliMessage(chalk.red(cliM("cli_cancelledLine")));
|
|
1530
1680
|
process4.exit(1);
|
|
1531
1681
|
};
|
|
1532
1682
|
writeCliMessage("");
|
|
1533
|
-
writeCliMessage(chalk.bold("
|
|
1534
|
-
writeCliMessage(chalk.gray("
|
|
1683
|
+
writeCliMessage(chalk.bold(cliM("cli_createModuleConfig")));
|
|
1684
|
+
writeCliMessage(chalk.gray(cliM("cli_createModuleConfigHint")));
|
|
1535
1685
|
writeCliMessage("");
|
|
1536
1686
|
configs.core = await promptCoreConfig(projectName, onCancel);
|
|
1537
1687
|
if (features.includes("db")) {
|
|
@@ -1552,27 +1702,32 @@ async function promptModuleConfigs(features, projectName) {
|
|
|
1552
1702
|
return configs;
|
|
1553
1703
|
}
|
|
1554
1704
|
async function promptCoreConfig(projectName, onCancel) {
|
|
1555
|
-
writeCliMessage(chalk.cyan("
|
|
1705
|
+
writeCliMessage(chalk.cyan(cliM("cli_createCoreConfig")));
|
|
1556
1706
|
const { defaultLocale } = await prompts({
|
|
1557
1707
|
type: "select",
|
|
1708
|
+
hint: `
|
|
1709
|
+
${formatPromptDescription(cliM("cli_selectHint"))}`,
|
|
1558
1710
|
name: "defaultLocale",
|
|
1559
|
-
message: "
|
|
1711
|
+
message: cliM("cli_createLocalePrompt"),
|
|
1560
1712
|
choices: [
|
|
1561
|
-
{ title: "
|
|
1562
|
-
{ title: "
|
|
1713
|
+
{ title: cliM("cli_createChineseLocale"), value: "zh-CN" },
|
|
1714
|
+
{ title: cliM("cli_createEnglishLocale"), value: "en-US" },
|
|
1715
|
+
{ title: cliM("cli_createJapaneseLocale"), value: "ja-JP" }
|
|
1563
1716
|
],
|
|
1564
1717
|
initial: 0
|
|
1565
1718
|
}, { onCancel });
|
|
1566
1719
|
return { name: projectName, defaultLocale };
|
|
1567
1720
|
}
|
|
1568
1721
|
async function promptDbConfig(projectName, onCancel) {
|
|
1569
|
-
writeCliMessage(chalk.cyan("
|
|
1722
|
+
writeCliMessage(chalk.cyan(cliM("cli_createDbConfig")));
|
|
1570
1723
|
const { type } = await prompts({
|
|
1571
1724
|
type: "select",
|
|
1725
|
+
hint: `
|
|
1726
|
+
${formatPromptDescription(cliM("cli_selectHint"))}`,
|
|
1572
1727
|
name: "type",
|
|
1573
|
-
message: "
|
|
1728
|
+
message: cliM("cli_createDbTypePrompt"),
|
|
1574
1729
|
choices: [
|
|
1575
|
-
{ title: "
|
|
1730
|
+
{ title: cliM("cli_createSqliteOption"), value: "sqlite" },
|
|
1576
1731
|
{ title: "PostgreSQL", value: "postgresql" },
|
|
1577
1732
|
{ title: "MySQL", value: "mysql" }
|
|
1578
1733
|
],
|
|
@@ -1582,26 +1737,28 @@ async function promptDbConfig(projectName, onCancel) {
|
|
|
1582
1737
|
const { database } = await prompts({
|
|
1583
1738
|
type: "text",
|
|
1584
1739
|
name: "database",
|
|
1585
|
-
message: "
|
|
1740
|
+
message: cliM("cli_createSqlitePathPrompt"),
|
|
1586
1741
|
initial: "./data/app.db"
|
|
1587
1742
|
}, { onCancel });
|
|
1588
1743
|
return { type, database };
|
|
1589
1744
|
}
|
|
1590
1745
|
const answers = await prompts([
|
|
1591
|
-
{ type: "text", name: "host", message:
|
|
1592
|
-
{ type: "number", name: "port", message: "
|
|
1593
|
-
{ type: "text", name: "database", message: "
|
|
1746
|
+
{ type: "text", name: "host", message: cliM("cli_createDbHostPrompt", { params: { backend: type === "postgresql" ? "PostgreSQL" : "MySQL" } }), initial: "localhost" },
|
|
1747
|
+
{ type: "number", name: "port", message: cliM("cli_createPortPrompt"), initial: type === "postgresql" ? 5432 : 3306 },
|
|
1748
|
+
{ type: "text", name: "database", message: cliM("cli_createDatabasePrompt"), initial: projectName }
|
|
1594
1749
|
], { onCancel });
|
|
1595
1750
|
return { type, host: answers.host, port: answers.port, database: answers.database };
|
|
1596
1751
|
}
|
|
1597
1752
|
async function promptCacheConfig(onCancel) {
|
|
1598
|
-
writeCliMessage(chalk.cyan("
|
|
1753
|
+
writeCliMessage(chalk.cyan(cliM("cli_createCacheConfig")));
|
|
1599
1754
|
const { type } = await prompts({
|
|
1600
1755
|
type: "select",
|
|
1756
|
+
hint: `
|
|
1757
|
+
${formatPromptDescription(cliM("cli_selectHint"))}`,
|
|
1601
1758
|
name: "type",
|
|
1602
|
-
message: "
|
|
1759
|
+
message: cliM("cli_createCacheTypePrompt"),
|
|
1603
1760
|
choices: [
|
|
1604
|
-
{ title: "
|
|
1761
|
+
{ title: cliM("cli_createMemoryOption"), value: "memory" },
|
|
1605
1762
|
{ title: "Redis", value: "redis" }
|
|
1606
1763
|
],
|
|
1607
1764
|
initial: 0
|
|
@@ -1609,27 +1766,29 @@ async function promptCacheConfig(onCancel) {
|
|
|
1609
1766
|
if (type === "memory")
|
|
1610
1767
|
return { type };
|
|
1611
1768
|
const answers = await prompts([
|
|
1612
|
-
{ type: "text", name: "host", message: "
|
|
1613
|
-
{ type: "number", name: "port", message: "
|
|
1769
|
+
{ type: "text", name: "host", message: cliM("cli_createRedisHostPrompt"), initial: "localhost" },
|
|
1770
|
+
{ type: "number", name: "port", message: cliM("cli_createRedisPortPrompt"), initial: 6379 }
|
|
1614
1771
|
], { onCancel });
|
|
1615
1772
|
return { type, host: answers.host, port: answers.port };
|
|
1616
1773
|
}
|
|
1617
1774
|
async function promptIamConfig(onCancel) {
|
|
1618
|
-
writeCliMessage(chalk.cyan("
|
|
1775
|
+
writeCliMessage(chalk.cyan(cliM("cli_createIamConfig")));
|
|
1619
1776
|
const answers = await prompts([
|
|
1620
|
-
{ type: "confirm", name: "loginPassword", message: "
|
|
1621
|
-
{ type: "confirm", name: "loginOtp", message: "
|
|
1777
|
+
{ type: "confirm", active: cliM("cli_yes"), inactive: cliM("cli_no"), name: "loginPassword", message: cliM("cli_createPasswordPrompt"), initial: true },
|
|
1778
|
+
{ type: "confirm", active: cliM("cli_yes"), inactive: cliM("cli_no"), name: "loginOtp", message: cliM("cli_createOtpPrompt"), initial: false }
|
|
1622
1779
|
], { onCancel });
|
|
1623
1780
|
return { loginPassword: answers.loginPassword, loginOtp: answers.loginOtp };
|
|
1624
1781
|
}
|
|
1625
1782
|
async function promptStorageConfig(onCancel) {
|
|
1626
|
-
writeCliMessage(chalk.cyan("
|
|
1783
|
+
writeCliMessage(chalk.cyan(cliM("cli_createStorageConfig")));
|
|
1627
1784
|
const { type } = await prompts({
|
|
1628
1785
|
type: "select",
|
|
1786
|
+
hint: `
|
|
1787
|
+
${formatPromptDescription(cliM("cli_selectHint"))}`,
|
|
1629
1788
|
name: "type",
|
|
1630
|
-
message: "
|
|
1789
|
+
message: cliM("cli_createStorageTypePrompt"),
|
|
1631
1790
|
choices: [
|
|
1632
|
-
{ title: "
|
|
1791
|
+
{ title: cliM("cli_createLocalStorageOption"), value: "local" },
|
|
1633
1792
|
{ title: "S3 / MinIO / OSS", value: "s3" }
|
|
1634
1793
|
],
|
|
1635
1794
|
initial: 0
|
|
@@ -1638,7 +1797,7 @@ async function promptStorageConfig(onCancel) {
|
|
|
1638
1797
|
const { localPath } = await prompts({
|
|
1639
1798
|
type: "text",
|
|
1640
1799
|
name: "localPath",
|
|
1641
|
-
message: "
|
|
1800
|
+
message: cliM("cli_createStoragePathPrompt"),
|
|
1642
1801
|
initial: "./data/uploads"
|
|
1643
1802
|
}, { onCancel });
|
|
1644
1803
|
return { type, localPath };
|
|
@@ -1646,15 +1805,17 @@ async function promptStorageConfig(onCancel) {
|
|
|
1646
1805
|
return { type };
|
|
1647
1806
|
}
|
|
1648
1807
|
async function promptAiConfig(onCancel) {
|
|
1649
|
-
writeCliMessage(chalk.cyan("
|
|
1808
|
+
writeCliMessage(chalk.cyan(cliM("cli_createAiConfig")));
|
|
1650
1809
|
const { defaultProvider } = await prompts({
|
|
1651
1810
|
type: "select",
|
|
1811
|
+
hint: `
|
|
1812
|
+
${formatPromptDescription(cliM("cli_selectHint"))}`,
|
|
1652
1813
|
name: "defaultProvider",
|
|
1653
|
-
message: "
|
|
1814
|
+
message: cliM("cli_createProviderPrompt"),
|
|
1654
1815
|
choices: [
|
|
1655
1816
|
{ title: "OpenAI", value: "openai" },
|
|
1656
1817
|
{ title: "Anthropic (Claude)", value: "anthropic" },
|
|
1657
|
-
{ title: "
|
|
1818
|
+
{ title: cliM("cli_createCompatibleAiOption"), value: "custom" }
|
|
1658
1819
|
],
|
|
1659
1820
|
initial: 0
|
|
1660
1821
|
}, { onCancel });
|
|
@@ -1662,7 +1823,7 @@ async function promptAiConfig(onCancel) {
|
|
|
1662
1823
|
const { model } = await prompts({
|
|
1663
1824
|
type: "text",
|
|
1664
1825
|
name: "model",
|
|
1665
|
-
message: "
|
|
1826
|
+
message: cliM("cli_createModelPrompt"),
|
|
1666
1827
|
initial: defaultModel
|
|
1667
1828
|
}, { onCancel });
|
|
1668
1829
|
return { defaultProvider, model };
|
|
@@ -1758,6 +1919,8 @@ function generateEnvironmentReadme() {
|
|
|
1758
1919
|
|
|
1759
1920
|
\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
|
|
1760
1921
|
|
|
1922
|
+
\u751F\u4EA7 .env.prod \u6309\u5DF2\u542F\u7528\u6A21\u5757\u9009\u62E9\u5171\u4EAB PostgreSQL\uFF08\u660E\u786E\u9009\u62E9 MySQL \u65F6\u4FDD\u7559\uFF09\u3001Redis\u3001S3\u3001Qdrant\uFF0C\u9ED8\u8BA4\u4F7F\u7528 Compose \u5185\u90E8\u670D\u52A1\u540D db/cache/storage/vecdb\u3002\u5BC6\u7801\u3001S3 \u51ED\u636E\u3001\u6A21\u578B Key \u4E0E\u53D7\u4FDD\u62A4 Qdrant \u7684 Key \u53EA\u7531\u79C1\u6709\u73AF\u5883\u6CE8\u5165\uFF1B\u5BB9\u5668\u5916\u8FD0\u884C\u8986\u76D6\u5185\u90E8\u8FDE\u63A5\u5730\u5740\u3002\u751F\u4EA7 Reach \u5FC5\u987B\u6CE8\u5165\u771F\u5B9E HAI_REACH_PROVIDERS JSON\uFF0C\u516C\u5171\u7A7A\u6570\u7EC4\u4F1A\u62D2\u7EDD\u542F\u52A8\u3002AI \u81EA\u52A8\u8865\u5168 db+vecdb \u4F9D\u8D56\uFF0C\u5E76\u5728\u4E8C\u8005\u5C31\u7EEA\u540E\u521D\u59CB\u5316\uFF0C\u5931\u8D25\u9006\u5E8F\u6E05\u7406\u3002
|
|
1923
|
+
|
|
1761
1924
|
| \u7528\u9014 | \u547D\u4EE4 |
|
|
1762
1925
|
| --- | --- |
|
|
1763
1926
|
| \u5F00\u53D1 / \u6D4B\u8BD5\u6A21\u5F0F / \u751F\u4EA7\u6A21\u5F0F\u9884\u89C8 | \`pnpm dev\` / \`dev:test\` / \`dev:prod\` |
|
|
@@ -1792,7 +1955,8 @@ ${pm} exec hai deploy ${appDir} --env prod --services web
|
|
|
1792
1955
|
\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
1956
|
\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
1957
|
\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
|
|
1958
|
+
\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\u3001util-linux flock \u548C\u652F\u6301 flock \u7684\u6587\u4EF6\u7CFB\u7EDF\uFF0C\u542F\u52A8\u9636\u6BB5\u4E0D\u8054\u7F51\u62C9\u53D6\u3002
|
|
1959
|
+
\u56FA\u5B9A .deploy.lock \u5185\u6838\u9501\u8986\u76D6\u90E8\u7F72\u53CA\u6062\u590D\uFF1B\u7981\u6B62\u5220\u9664\u6216\u66FF\u6362\uFF0C\u6240\u6709\u6301\u6709\u8FDB\u7A0B\u9000\u51FA\u6216\u91CD\u542F\u540E\u81EA\u52A8\u91CA\u653E\u3002\u5F3A\u6740\u540E\u987B\u6838\u5BF9\u7ED3\u679C\u6587\u4EF6\u3001current \u548C\u5BB9\u5668\u72B6\u6001\uFF0C\u518D\u7528\u65B0\u5305\u6062\u590D\u3002
|
|
1796
1960
|
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
1961
|
\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
1962
|
\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
|
|
@@ -1817,7 +1981,7 @@ function generateReadme(name, appTypeLabel, pm, appType) {
|
|
|
1817
1981
|
|
|
1818
1982
|
### \u9875\u9762\u5E03\u5C40\u4E0E\u8DEF\u7531
|
|
1819
1983
|
|
|
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"}
|
|
1984
|
+
\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"}${appType === "mobile-app" ? "\n\n\u9009\u62E9 IAM \u6216 crypto \u7684\u79FB\u52A8\u5BA2\u6237\u7AEF\u9ED8\u8BA4\u542F\u7528\u52A0\u5BC6 transport\uFF0C\u5148\u521D\u59CB\u5316 crypto\uFF0C\u518D\u521D\u59CB\u5316 typed API client\uFF1B\u7ED3\u675F\u65F6\u6309\u76F8\u53CD\u987A\u5E8F\u5173\u95ED\u3002\u4EC5\u660E\u786E\u8FDE\u63A5\u5339\u914D\u7684\u660E\u6587 API \u65F6\u8BBE\u7F6E PUBLIC_API_TRANSPORT=off\uFF1BPUBLIC_API_KEY_EXCHANGE_PATH \u4EC5\u8986\u76D6\u516C\u5F00\u534F\u5546\u5B50\u8DEF\u5F84\u3002\u63E1\u624B\u5931\u8D25\u4E0D\u4F1A\u9000\u56DE\u660E\u6587\uFF0C\u670D\u52A1\u7AEF\u79C1\u94A5\u4E0D\u8FDB\u5165\u5BA2\u6237\u7AEF\u3002\u672A\u9009\u62E9 crypto \u7684\u5BA2\u6237\u7AEF\u4FDD\u7559\u660E\u6587 API \u6A21\u5F0F\u3002" : ""}
|
|
1821
1985
|
|
|
1822
1986
|
### \u5F00\u53D1
|
|
1823
1987
|
|
|
@@ -1961,9 +2125,11 @@ CLI \u5DF2\u5728\u6839\u76EE\u5F55\u751F\u6210 \`.env.local\`\uFF0C\u5176\u4E2D
|
|
|
1961
2125
|
|
|
1962
2126
|
\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
|
|
1963
2127
|
|
|
2128
|
+
\u670D\u52A1\u7AEF\u7248\u672C\u5316\u4F20\u8F93\u5BC6\u94A5\u73AF\u5728\u521B\u5EFA\u65F6\u4E00\u6B21\u6027\u5199\u5165\u88AB\u5FFD\u7565\u7684 \`.env.local\`\uFF0C\u53D8\u91CF\u4E3A \`HAI_CRYPTO_TRANSPORT_SERVERKEYRING\`\uFF1B\u8FD0\u884C\u65F6\u4E0D\u751F\u6210\u5BC6\u94A5\u3002\u90E8\u7F72\u7531 Secret Manager \u5411\u6240\u6709\u8282\u70B9\u5206\u53D1\u540C\u4E00\u53D7\u7BA1 ring\uFF0C\u914D\u5408\u5171\u4EAB registry\u3002\u79C1\u94A5\u4E0D\u8FDB\u5165\u516C\u5F00\u914D\u7F6E\u3001PUBLIC_/VITE_ \u6216\u65E5\u5FD7\u3002
|
|
2129
|
+
|
|
1964
2130
|
\u6240\u6709\u4E1A\u52A1 JSON \u8BF7\u6C42\u548C\u54CD\u5E94\u9ED8\u8BA4\u901A\u8FC7 \`@h-ai/crypto\` transport \u53CC\u5411\u52A0\u5BC6\uFF08\u542B\u767B\u5F55\u3001\u5237\u65B0\u3001\u5934\u50CF\u4E0A\u4F20\uFF09\uFF1B\u524D\u7AEF\u7EDF\u4E00\u4F7F\u7528\u5171\u4EAB typed API client\u3002Web \u8BBF\u95EE\u4EE4\u724C\u4EC5\u5B58\u5185\u5B58\u3001\u5237\u65B0\u4EE4\u724C\u7531 HttpOnly Cookie \u4FDD\u5B58\uFF1B\u53D7\u4FE1\u539F\u751F\u7AEF\u4F7F\u7528\u5E73\u53F0\u5B89\u5168\u5B58\u50A8\u6216\u5185\u5B58\u3002\u5934\u50CF\u4E3A\u968F\u673A\u8DEF\u5F84\u53EA\u8BFB\u56FE\u7247\u8D44\u6E90\uFF0C\u652F\u6301 PNG/JPEG/WebP\uFF0C\u6700\u5927 512 KiB\u3002\u5065\u5EB7\u68C0\u67E5\u548C\u53EA\u8BFB\u5934\u50CF\u56FE\u7247\u53EF\u76F4\u63A5\u8BBF\u95EE\u3002
|
|
1965
2131
|
|
|
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\
|
|
2132
|
+
\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\u5F00\u53D1\u9ED8\u8BA4 SQLite\u3001\u672C\u5730\u5B58\u50A8\u4E0E\u5185\u5B58\u7F13\u5B58\uFF1B\u751F\u4EA7\u542F\u52A8\u62D2\u7EDD\u8FD9\u4E9B\u672C\u5730 Provider\uFF0C\u5FC5\u987B\u5C06\u6240\u6709\u8282\u70B9\u8FDE\u63A5\u5230\u540C\u4E00\u5171\u4EAB\u6570\u636E\u5E93\u3001Redis \u4E0E S3\u3002\u4F20\u8F93\u5FC5\u987B\u5728\u6240\u6709\u8282\u70B9\u6CE8\u5165\u76F8\u540C\u53D7\u7BA1\u7248\u672C\u5316\u5BC6\u94A5\u73AF\u4E0E\u5171\u4EAB registry\uFF0C\u65E0\u9700\u4F1A\u8BDD\u7C98\u6027\uFF1B\u91CD\u542F\u8BFB\u53D6\u539F ring\uFF0C\u8F6E\u6362\u5148\u5206\u53D1\u65B0\u65E7\u7248\u672C\u518D\u5207 activeKeyId\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
|
|
1967
2133
|
|
|
1968
2134
|
### \u914D\u7F6E\u6269\u5C55
|
|
1969
2135
|
|
|
@@ -2031,10 +2197,10 @@ Apache-2.0
|
|
|
2031
2197
|
}
|
|
2032
2198
|
function printCompletionMessage(options) {
|
|
2033
2199
|
writeCliMessage("");
|
|
2034
|
-
writeCliMessage(chalk.green("
|
|
2200
|
+
writeCliMessage(chalk.green(cliM("cli_createSuccess")));
|
|
2035
2201
|
writeCliMessage("");
|
|
2036
2202
|
if (options.features.length > 0) {
|
|
2037
|
-
writeCliMessage(chalk.bold("
|
|
2203
|
+
writeCliMessage(chalk.bold(cliM("cli_createFeatures")));
|
|
2038
2204
|
for (const featureId of options.features) {
|
|
2039
2205
|
const feature = FEATURES[featureId];
|
|
2040
2206
|
if (feature) {
|
|
@@ -2043,7 +2209,7 @@ function printCompletionMessage(options) {
|
|
|
2043
2209
|
}
|
|
2044
2210
|
writeCliMessage("");
|
|
2045
2211
|
}
|
|
2046
|
-
writeCliMessage("
|
|
2212
|
+
writeCliMessage(cliM("cli_nextSteps"));
|
|
2047
2213
|
writeCliMessage(chalk.cyan(` cd ${options.name}`));
|
|
2048
2214
|
if (options.appType === "fullstack")
|
|
2049
2215
|
writeCliMessage(chalk.cyan(cliM("cli_adminPasswordSaved")));
|
|
@@ -2084,25 +2250,61 @@ async function detectProject(cwd) {
|
|
|
2084
2250
|
// src/commands/cli-add.ts
|
|
2085
2251
|
var MODULE_MAP = {
|
|
2086
2252
|
// iam 依赖 crypto(password hash)/ db(user 存储)/ cache(session 缓存)
|
|
2087
|
-
"iam": { packages: ["@h-ai/iam"], deps: ["crypto", "db", "cache"], configKey: "iam", description
|
|
2253
|
+
"iam": { packages: ["@h-ai/iam"], deps: ["crypto", "db", "cache"], configKey: "iam", get description() {
|
|
2254
|
+
return cliM("cli_featureIamName");
|
|
2255
|
+
} },
|
|
2088
2256
|
// 'db' 和 'reldb' 都映射到 @h-ai/reldb,兼容用户习惯(hai add db 和 hai add reldb 均可)
|
|
2089
|
-
"db": { packages: ["@h-ai/reldb"], configKey: "db", description
|
|
2090
|
-
|
|
2091
|
-
|
|
2092
|
-
"
|
|
2093
|
-
|
|
2094
|
-
|
|
2095
|
-
"
|
|
2096
|
-
|
|
2097
|
-
|
|
2098
|
-
"
|
|
2099
|
-
|
|
2100
|
-
|
|
2101
|
-
"
|
|
2102
|
-
|
|
2103
|
-
|
|
2104
|
-
"
|
|
2105
|
-
|
|
2257
|
+
"db": { packages: ["@h-ai/reldb"], configKey: "db", get description() {
|
|
2258
|
+
return cliM("cli_featureDbName");
|
|
2259
|
+
} },
|
|
2260
|
+
"reldb": { packages: ["@h-ai/reldb"], configKey: "db", get description() {
|
|
2261
|
+
return cliM("cli_featureDbName");
|
|
2262
|
+
} },
|
|
2263
|
+
"cache": { packages: ["@h-ai/cache"], configKey: "cache", get description() {
|
|
2264
|
+
return cliM("cli_featureCacheName");
|
|
2265
|
+
} },
|
|
2266
|
+
"ai": { packages: ["@h-ai/ai"], deps: ["db", "vecdb"], configKey: "ai", get description() {
|
|
2267
|
+
return cliM("cli_featureAiName");
|
|
2268
|
+
} },
|
|
2269
|
+
"storage": { packages: ["@h-ai/storage"], configKey: "storage", get description() {
|
|
2270
|
+
return cliM("cli_featureStorageName");
|
|
2271
|
+
} },
|
|
2272
|
+
"crypto": { packages: ["@h-ai/crypto"], deps: ["db"], get description() {
|
|
2273
|
+
return cliM("cli_featureCryptoName");
|
|
2274
|
+
} },
|
|
2275
|
+
"kit": { packages: ["@h-ai/kit"], get description() {
|
|
2276
|
+
return cliM("cli_featureKitName");
|
|
2277
|
+
} },
|
|
2278
|
+
"ui": { packages: ["@h-ai/ui"], get description() {
|
|
2279
|
+
return cliM("cli_featureUiName");
|
|
2280
|
+
} },
|
|
2281
|
+
"deploy": { packages: ["@h-ai/deploy"], configKey: "deploy", get description() {
|
|
2282
|
+
return cliM("cli_featureDeployName");
|
|
2283
|
+
} },
|
|
2284
|
+
"vecdb": { packages: ["@h-ai/vecdb"], configKey: "vecdb", get description() {
|
|
2285
|
+
return cliM("cli_featureVecdbName");
|
|
2286
|
+
} },
|
|
2287
|
+
"datapipe": { packages: ["@h-ai/datapipe"], get description() {
|
|
2288
|
+
return cliM("cli_featureDatapipeName");
|
|
2289
|
+
} },
|
|
2290
|
+
"reach": { packages: ["@h-ai/reach"], configKey: "reach", get description() {
|
|
2291
|
+
return cliM("cli_featureReachName");
|
|
2292
|
+
} },
|
|
2293
|
+
"scheduler": { packages: ["@h-ai/scheduler"], deps: ["db"], configKey: "scheduler", get description() {
|
|
2294
|
+
return cliM("cli_featureSchedulerName");
|
|
2295
|
+
} },
|
|
2296
|
+
"audit": { packages: ["@h-ai/audit"], deps: ["db"], configKey: "audit", get description() {
|
|
2297
|
+
return cliM("cli_featureAuditName");
|
|
2298
|
+
} },
|
|
2299
|
+
"payment": { packages: ["@h-ai/payment"], configKey: "payment", get description() {
|
|
2300
|
+
return cliM("cli_featurePaymentName");
|
|
2301
|
+
} },
|
|
2302
|
+
"capacitor": { packages: ["@h-ai/capacitor"], get description() {
|
|
2303
|
+
return cliM("cli_featureCapacitorName");
|
|
2304
|
+
} },
|
|
2305
|
+
"api-client": { packages: ["@h-ai/api-client"], get description() {
|
|
2306
|
+
return cliM("cli_featureApiClientName");
|
|
2307
|
+
} }
|
|
2106
2308
|
};
|
|
2107
2309
|
async function addModule(options) {
|
|
2108
2310
|
const spinner = ora();
|
|
@@ -2110,7 +2312,7 @@ async function addModule(options) {
|
|
|
2110
2312
|
try {
|
|
2111
2313
|
const project = await detectProject(cwd);
|
|
2112
2314
|
if (!project) {
|
|
2113
|
-
writeCliError(chalk.red("
|
|
2315
|
+
writeCliError(chalk.red(cliM("cli_projectNotFound")));
|
|
2114
2316
|
return;
|
|
2115
2317
|
}
|
|
2116
2318
|
let moduleName = options.module;
|
|
@@ -2121,13 +2323,14 @@ async function addModule(options) {
|
|
|
2121
2323
|
value: key
|
|
2122
2324
|
}));
|
|
2123
2325
|
if (choices.length === 0) {
|
|
2124
|
-
writeCliMessage(chalk.green("
|
|
2326
|
+
writeCliMessage(chalk.green(cliM("cli_addAllInstalled")));
|
|
2125
2327
|
return;
|
|
2126
2328
|
}
|
|
2127
2329
|
const { selected } = await prompts({
|
|
2128
2330
|
type: "select",
|
|
2331
|
+
hint: cliM("cli_selectHint"),
|
|
2129
2332
|
name: "selected",
|
|
2130
|
-
message: "
|
|
2333
|
+
message: cliM("cli_addModulePrompt"),
|
|
2131
2334
|
choices
|
|
2132
2335
|
});
|
|
2133
2336
|
if (!selected)
|
|
@@ -2136,11 +2339,11 @@ async function addModule(options) {
|
|
|
2136
2339
|
}
|
|
2137
2340
|
const moduleDef = MODULE_MAP[moduleName];
|
|
2138
2341
|
if (!moduleDef) {
|
|
2139
|
-
writeCliError(chalk.red(
|
|
2140
|
-
writeCliMessage(
|
|
2342
|
+
writeCliError(chalk.red(cliM("cli_addUnknownModule", { params: { module: moduleName } })));
|
|
2343
|
+
writeCliMessage(cliM("cli_addAvailableModules", { params: { modules: Object.keys(MODULE_MAP).join(", ") } }));
|
|
2141
2344
|
return;
|
|
2142
2345
|
}
|
|
2143
|
-
const { generateProjectAiSupport, resolveSkillNamesForPackages } = await import('./cli-skill-templates-
|
|
2346
|
+
const { generateProjectAiSupport, resolveSkillNamesForPackages } = await import('./cli-skill-templates-US5CCAJ4.js');
|
|
2144
2347
|
if (moduleDef.packages.every((p) => project.haiPackages.includes(p))) {
|
|
2145
2348
|
const generatedFiles2 = await generateProjectAiSupport(
|
|
2146
2349
|
cwd,
|
|
@@ -2151,12 +2354,12 @@ async function addModule(options) {
|
|
|
2151
2354
|
}
|
|
2152
2355
|
);
|
|
2153
2356
|
if (generatedFiles2.length > 0) {
|
|
2154
|
-
writeCliMessage(chalk.yellow(
|
|
2357
|
+
writeCliMessage(chalk.yellow(cliM("cli_addSupportCompleted", { params: { module: moduleName } })));
|
|
2155
2358
|
for (const generatedFile of generatedFiles2) {
|
|
2156
|
-
writeCliMessage(chalk.gray(
|
|
2359
|
+
writeCliMessage(chalk.gray(cliM("cli_addSupportFile", { params: { file: generatedFile } })));
|
|
2157
2360
|
}
|
|
2158
2361
|
} else {
|
|
2159
|
-
writeCliMessage(chalk.yellow(
|
|
2362
|
+
writeCliMessage(chalk.yellow(cliM("cli_addAlreadyInstalled", { params: { module: moduleName } })));
|
|
2160
2363
|
}
|
|
2161
2364
|
return;
|
|
2162
2365
|
}
|
|
@@ -2171,7 +2374,7 @@ async function addModule(options) {
|
|
|
2171
2374
|
}
|
|
2172
2375
|
}
|
|
2173
2376
|
}
|
|
2174
|
-
spinner.start("
|
|
2377
|
+
spinner.start(cliM("cli_addUpdateManifest"));
|
|
2175
2378
|
const pkgPath = path2.join(cwd, "package.json");
|
|
2176
2379
|
const pkg = await fse.readJson(pkgPath);
|
|
2177
2380
|
if (!pkg.dependencies)
|
|
@@ -2192,12 +2395,12 @@ async function addModule(options) {
|
|
|
2192
2395
|
const configDir = path2.join(cwd, "config");
|
|
2193
2396
|
const configPath = path2.join(configDir, `_${moduleDef.configKey}.yml`);
|
|
2194
2397
|
if (!await fileExists(configPath)) {
|
|
2195
|
-
spinner.start(
|
|
2398
|
+
spinner.start(cliM("cli_addConfig", { params: { config: moduleDef.configKey } }));
|
|
2196
2399
|
const content = generateConfigFile(moduleDef.configKey);
|
|
2197
2400
|
await writeFile(configPath, content);
|
|
2198
2401
|
spinner.succeed();
|
|
2199
2402
|
} else {
|
|
2200
|
-
writeCliMessage(chalk.gray(
|
|
2403
|
+
writeCliMessage(chalk.gray(cliM("cli_addConfigExists", { params: { config: moduleDef.configKey } })));
|
|
2201
2404
|
}
|
|
2202
2405
|
}
|
|
2203
2406
|
if (moduleDef.deps) {
|
|
@@ -2208,7 +2411,7 @@ async function addModule(options) {
|
|
|
2208
2411
|
if (!await fileExists(depConfigPath)) {
|
|
2209
2412
|
const content = generateConfigFile(depDef.configKey);
|
|
2210
2413
|
await writeFile(depConfigPath, content);
|
|
2211
|
-
writeCliMessage(chalk.gray(
|
|
2414
|
+
writeCliMessage(chalk.gray(cliM("cli_addDependencyConfig", { params: { config: depDef.configKey } })));
|
|
2212
2415
|
}
|
|
2213
2416
|
}
|
|
2214
2417
|
}
|
|
@@ -2224,28 +2427,36 @@ async function addModule(options) {
|
|
|
2224
2427
|
);
|
|
2225
2428
|
if (generatedFiles.length > 0) {
|
|
2226
2429
|
for (const generatedFile of generatedFiles) {
|
|
2227
|
-
writeCliMessage(chalk.gray(
|
|
2430
|
+
writeCliMessage(chalk.gray(cliM("cli_addSupportFile", { params: { file: generatedFile } })));
|
|
2228
2431
|
}
|
|
2229
2432
|
}
|
|
2433
|
+
const productionPath = path2.join(cwd, ".env.prod");
|
|
2434
|
+
const previousProduction = await fileExists(productionPath) ? await fse.readFile(productionPath, "utf8") : "";
|
|
2435
|
+
const configured = new Set([...previousProduction.matchAll(/^([A-Z][A-Z0-9_]*)=/gm)].map((match) => match[1]));
|
|
2436
|
+
const previousValues = parseEnv(previousProduction);
|
|
2437
|
+
const production = generateProductionEnvironment([moduleName, ...moduleDef.deps ?? []], previousValues.HAI_DB_TYPE === "mysql" ? { db: { type: "mysql", database: previousValues.HAI_DB_DATABASE } } : void 0).split("\n").filter((line) => !/^[A-Z][A-Z0-9_]*=/.test(line) || !configured.has(line.split("=")[0]));
|
|
2438
|
+
await writeFile(productionPath, `${previousProduction.trimEnd()}
|
|
2439
|
+
|
|
2440
|
+
${production.join("\n")}`);
|
|
2230
2441
|
const doInstall = options.install !== false;
|
|
2231
2442
|
if (doInstall) {
|
|
2232
|
-
spinner.start("
|
|
2443
|
+
spinner.start(cliM("cli_addInstall"));
|
|
2233
2444
|
const { execSync: execSync2 } = await import('child_process');
|
|
2234
2445
|
const pm = await detectPackageManager(cwd);
|
|
2235
2446
|
execSync2(`${pm} install`, { cwd, stdio: "ignore" });
|
|
2236
2447
|
spinner.succeed();
|
|
2237
2448
|
}
|
|
2238
2449
|
writeCliMessage("");
|
|
2239
|
-
writeCliMessage(chalk.green(
|
|
2450
|
+
writeCliMessage(chalk.green(cliM("cli_addSuccess", { params: { module: moduleName } })));
|
|
2240
2451
|
writeCliMessage("");
|
|
2241
|
-
writeCliMessage("
|
|
2452
|
+
writeCliMessage(cliM("cli_addPackages"));
|
|
2242
2453
|
for (const pkg2 of allPackages) {
|
|
2243
2454
|
writeCliMessage(chalk.cyan(` ${pkg2}`));
|
|
2244
2455
|
}
|
|
2245
2456
|
writeCliMessage("");
|
|
2246
2457
|
} catch (error) {
|
|
2247
2458
|
spinner.fail();
|
|
2248
|
-
writeCliError(chalk.red("
|
|
2459
|
+
writeCliError(chalk.red(cliM("cli_addFailed")), { error });
|
|
2249
2460
|
throw error;
|
|
2250
2461
|
}
|
|
2251
2462
|
}
|
|
@@ -2446,38 +2657,133 @@ function prepareDependencyInputs(cwd) {
|
|
|
2446
2657
|
function basenameSafe(directory) {
|
|
2447
2658
|
return directory.slice(dirname(directory).length + 1).toLowerCase().replace(/[^a-z0-9-]/g, "-").replace(/^-+/, "") || "app";
|
|
2448
2659
|
}
|
|
2449
|
-
|
|
2450
|
-
|
|
2451
|
-
|
|
2452
|
-
|
|
2453
|
-
|
|
2454
|
-
|
|
2455
|
-
|
|
2456
|
-
|
|
2457
|
-
|
|
2458
|
-
|
|
2459
|
-
|
|
2460
|
-
|
|
2461
|
-
|
|
2462
|
-
|
|
2463
|
-
|
|
2464
|
-
|
|
2465
|
-
|
|
2466
|
-
|
|
2467
|
-
|
|
2468
|
-
|
|
2469
|
-
|
|
2470
|
-
|
|
2471
|
-
|
|
2472
|
-
|
|
2473
|
-
|
|
2474
|
-
|
|
2475
|
-
|
|
2476
|
-
|
|
2477
|
-
|
|
2478
|
-
|
|
2479
|
-
|
|
2480
|
-
|
|
2660
|
+
|
|
2661
|
+
// ../reldb/dist/chunk-SDS2ZT3V.js
|
|
2662
|
+
external_exports.enum(["sqlite", "postgresql", "mysql"]);
|
|
2663
|
+
var ReldbOperationLogConfigSchema = external_exports.object({
|
|
2664
|
+
read: external_exports.boolean().default(false),
|
|
2665
|
+
write: external_exports.boolean().default(false),
|
|
2666
|
+
/** 安全数量摘要的最大字符数;0 表示省略摘要,永不记录 SQL 原文或参数值。 */
|
|
2667
|
+
maxLength: external_exports.number().int().min(0).default(1e3),
|
|
2668
|
+
level: external_exports.enum(["info", "debug", "trace"]).default("debug")
|
|
2669
|
+
});
|
|
2670
|
+
var OperationLogConfigFieldSchema = {
|
|
2671
|
+
operationLog: ReldbOperationLogConfigSchema.optional()
|
|
2672
|
+
};
|
|
2673
|
+
var PoolConfigSchema = external_exports.object({
|
|
2674
|
+
/** PostgreSQL 已建立连接的最小保留数(默认 1,不主动预建) */
|
|
2675
|
+
min: external_exports.number().int().min(0).default(1),
|
|
2676
|
+
/** 最大连接数(默认 10) */
|
|
2677
|
+
max: external_exports.number().int().min(1).default(10),
|
|
2678
|
+
/** PostgreSQL 空闲连接超时,毫秒(默认 30000;0 禁用) */
|
|
2679
|
+
idleTimeout: external_exports.number().int().min(0).default(3e4),
|
|
2680
|
+
/** 获取连接超时,毫秒(默认10000);PostgreSQL >=0,0禁用;MySQL排队+建连总期限为1–2147483647 */
|
|
2681
|
+
acquireTimeout: external_exports.number().int().min(0).default(1e4)
|
|
2682
|
+
});
|
|
2683
|
+
var MysqlPoolConfigSchema = external_exports.object({
|
|
2684
|
+
/** 最大连接数(默认 10) */
|
|
2685
|
+
max: external_exports.number().int().min(1).default(10),
|
|
2686
|
+
/** 获取连接超时,毫秒(默认10000);PostgreSQL >=0,0禁用;MySQL排队+建连总期限为1–2147483647 */
|
|
2687
|
+
acquireTimeout: external_exports.number().int().min(1).max(2147483647).default(1e4),
|
|
2688
|
+
/** MySQL 等待队列最大请求数(默认 1000;正整数,不支持无限队列) */
|
|
2689
|
+
queueLimit: external_exports.number().int().min(1).default(1e3)
|
|
2690
|
+
}).strict();
|
|
2691
|
+
var SslConfigSchema = external_exports.union([
|
|
2692
|
+
external_exports.boolean(),
|
|
2693
|
+
external_exports.enum(["require", "prefer", "allow", "disable"]),
|
|
2694
|
+
external_exports.record(external_exports.string(), external_exports.unknown())
|
|
2695
|
+
]);
|
|
2696
|
+
var MysqlSslConfigSchema = external_exports.union([
|
|
2697
|
+
external_exports.literal(false),
|
|
2698
|
+
external_exports.object({
|
|
2699
|
+
/** MySQL 是否校验证书链(默认 true);false 明确放弃服务端认证 */
|
|
2700
|
+
rejectUnauthorized: external_exports.boolean().default(true),
|
|
2701
|
+
/** MySQL 是否验证 DNS 主机身份(默认 true);仅证书链验证开启时有效 */
|
|
2702
|
+
verifyIdentity: external_exports.boolean().default(true),
|
|
2703
|
+
/** MySQL 信任的 CA,PEM 内容;省略时使用 Node 信任库 */
|
|
2704
|
+
ca: external_exports.string().min(1).optional(),
|
|
2705
|
+
/** MySQL 客户端证书,PEM 内容;必须同时提供 key */
|
|
2706
|
+
cert: external_exports.string().min(1).optional(),
|
|
2707
|
+
/** MySQL 客户端私钥,PEM 内容;必须同时提供 cert */
|
|
2708
|
+
key: external_exports.string().min(1).optional()
|
|
2709
|
+
}).strict().refine((ssl) => Boolean(ssl.cert) === Boolean(ssl.key), { path: ["key"] })
|
|
2710
|
+
]);
|
|
2711
|
+
var SqliteOptionsSchema = external_exports.object({
|
|
2712
|
+
/** 是否启用 WAL 模式(默认 true),可提高并发读写性能 */
|
|
2713
|
+
walMode: external_exports.boolean().default(true),
|
|
2714
|
+
/** 是否只读模式(默认 false) */
|
|
2715
|
+
readonly: external_exports.boolean().default(false),
|
|
2716
|
+
/**
|
|
2717
|
+
* 执行方式(默认 sync)。
|
|
2718
|
+
*
|
|
2719
|
+
* - `sync`:主线程直接执行 better-sqlite3(同步阻塞主线程事件循环)。
|
|
2720
|
+
* - `worker`:在专用 worker 线程持有唯一连接执行,把阻塞移出主线程。
|
|
2721
|
+
* 注意 WAL 只改善数据库锁并发,无法避免 better-sqlite3 同步调用占用调用线程。
|
|
2722
|
+
*/
|
|
2723
|
+
executor: external_exports.enum(["sync", "worker"]).default("sync"),
|
|
2724
|
+
/** worker 执行方式下等待连接打开的超时(毫秒,默认 10000) */
|
|
2725
|
+
workerConnectTimeoutMs: external_exports.number().int().positive().default(1e4)
|
|
2726
|
+
});
|
|
2727
|
+
var MysqlOptionsSchema = external_exports.object({
|
|
2728
|
+
/** 字符集(默认 utf8mb4),推荐使用 utf8mb4 支持完整 Unicode */
|
|
2729
|
+
charset: external_exports.string().default("utf8mb4")
|
|
2730
|
+
}).strict();
|
|
2731
|
+
var NetworkConfigSchema = external_exports.object({
|
|
2732
|
+
/**
|
|
2733
|
+
* 连接字符串(可选)
|
|
2734
|
+
* 如果提供 url,将优先使用,忽略 host/port 等字段
|
|
2735
|
+
*/
|
|
2736
|
+
url: external_exports.string().optional(),
|
|
2737
|
+
/** 数据库主机地址(默认 localhost) */
|
|
2738
|
+
host: external_exports.string().default("localhost"),
|
|
2739
|
+
/**
|
|
2740
|
+
* 数据库端口
|
|
2741
|
+
* - PostgreSQL 默认 5432
|
|
2742
|
+
* - MySQL 默认 3306
|
|
2743
|
+
*/
|
|
2744
|
+
port: external_exports.number().int().min(1).max(65535).optional(),
|
|
2745
|
+
/** 数据库名称 */
|
|
2746
|
+
database: external_exports.string(),
|
|
2747
|
+
/** 数据库用户名 */
|
|
2748
|
+
user: external_exports.string().optional(),
|
|
2749
|
+
/** 数据库密码 */
|
|
2750
|
+
password: external_exports.string().optional(),
|
|
2751
|
+
/** SSL/TLS 连接配置 */
|
|
2752
|
+
ssl: SslConfigSchema.optional(),
|
|
2753
|
+
/** 连接池配置 */
|
|
2754
|
+
pool: PoolConfigSchema.optional()
|
|
2755
|
+
});
|
|
2756
|
+
var SqliteConfigSchema = external_exports.object({
|
|
2757
|
+
type: external_exports.literal("sqlite"),
|
|
2758
|
+
/** 数据库文件路径(如 './data.db')或 ':memory:'(内存数据库) */
|
|
2759
|
+
database: external_exports.string(),
|
|
2760
|
+
/** SQLite 特定选项 */
|
|
2761
|
+
sqlite: SqliteOptionsSchema.optional(),
|
|
2762
|
+
...OperationLogConfigFieldSchema
|
|
2763
|
+
});
|
|
2764
|
+
var PostgresqlConfigSchema = NetworkConfigSchema.extend({
|
|
2765
|
+
type: external_exports.literal("postgresql"),
|
|
2766
|
+
...OperationLogConfigFieldSchema
|
|
2767
|
+
});
|
|
2768
|
+
var MysqlConfigSchema = NetworkConfigSchema.extend({
|
|
2769
|
+
type: external_exports.literal("mysql"),
|
|
2770
|
+
/** 网络数据库URL;MySQL空串表示使用分字段,非空仅mysql://且拒绝query/fragment,优先于host/port/database/user/password */
|
|
2771
|
+
url: external_exports.union([external_exports.literal("").transform(() => void 0), external_exports.url({ protocol: /^mysql$/ }).refine((value) => !value.includes("?") && !value.includes("#"))]).optional(),
|
|
2772
|
+
/** 数据库:SQLite文件路径或:memory:;PostgreSQL必填名称;MySQL无URL时必填,URL模式从URL读取 */
|
|
2773
|
+
database: external_exports.string().min(1).optional(),
|
|
2774
|
+
/** TLS:PostgreSQL使用其模式;MySQL仅false或严格对象,空对象默认校验证书链和DNS身份 */
|
|
2775
|
+
ssl: MysqlSslConfigSchema.optional(),
|
|
2776
|
+
/** 连接池配置:PostgreSQL支持min/idleTimeout;MySQL仅max/acquireTimeout/queueLimit,拒绝其他字段 */
|
|
2777
|
+
pool: MysqlPoolConfigSchema.optional(),
|
|
2778
|
+
/** MySQL 特定选项 */
|
|
2779
|
+
mysql: MysqlOptionsSchema.optional(),
|
|
2780
|
+
...OperationLogConfigFieldSchema
|
|
2781
|
+
}).refine((config) => Boolean(config.url) || Boolean(config.database), { path: ["database"] });
|
|
2782
|
+
var ReldbConfigSchema = external_exports.discriminatedUnion("type", [
|
|
2783
|
+
SqliteConfigSchema,
|
|
2784
|
+
PostgresqlConfigSchema,
|
|
2785
|
+
MysqlConfigSchema
|
|
2786
|
+
]);
|
|
2481
2787
|
var CLOUD_FIELDS = {
|
|
2482
2788
|
"neon": ["apiKey"],
|
|
2483
2789
|
"upstash": ["email", "apiKey"],
|
|
@@ -2523,7 +2829,7 @@ async function ask(question) {
|
|
|
2523
2829
|
throw new Error(cliM("cli_deploy_cancelled"));
|
|
2524
2830
|
return answer.value;
|
|
2525
2831
|
}
|
|
2526
|
-
function validateField(value, field) {
|
|
2832
|
+
function validateField(value, field, backend) {
|
|
2527
2833
|
if (value === "")
|
|
2528
2834
|
return field.required ? cliM("cli_deploy_required") : true;
|
|
2529
2835
|
if (field.kind === "port")
|
|
@@ -2541,8 +2847,8 @@ function validateField(value, field) {
|
|
|
2541
2847
|
}
|
|
2542
2848
|
if (field.name === "ssl") {
|
|
2543
2849
|
const parsed2 = parseDocument(value);
|
|
2544
|
-
const
|
|
2545
|
-
return
|
|
2850
|
+
const schema = backend === "mysql" ? MysqlSslConfigSchema : SslConfigSchema;
|
|
2851
|
+
return parsed2.errors.length === 0 && schema.safeParse(parsed2.toJS()).success || cliM("cli_deploy_invalid_value");
|
|
2546
2852
|
}
|
|
2547
2853
|
if (field.name === "headers") {
|
|
2548
2854
|
try {
|
|
@@ -2561,8 +2867,8 @@ function ensureConfigNode(appDir, requirement, backend) {
|
|
|
2561
2867
|
return;
|
|
2562
2868
|
const directory = join(appDir, "config");
|
|
2563
2869
|
mkdirSync(directory, { recursive: true });
|
|
2564
|
-
const
|
|
2565
|
-
const document = parseDocument(existsSync(
|
|
2870
|
+
const path9 = join(directory, `_${requirement.configName}.yml`);
|
|
2871
|
+
const document = parseDocument(existsSync(path9) ? readFileSync(path9, "utf8") : "{}\n");
|
|
2566
2872
|
if (document.errors.length)
|
|
2567
2873
|
throw new Error(cliM("cli_deploy_invalid_value"));
|
|
2568
2874
|
if (requirement.configPath.length && !document.hasIn(requirement.configPath))
|
|
@@ -2574,8 +2880,8 @@ function ensureConfigNode(appDir, requirement, backend) {
|
|
|
2574
2880
|
document.setIn(["login", "ldap"], `\${HAI_IAM_LOGIN_LDAP:false}`);
|
|
2575
2881
|
}
|
|
2576
2882
|
const content = document.toString();
|
|
2577
|
-
if (!existsSync(
|
|
2578
|
-
writeFileSync(
|
|
2883
|
+
if (!existsSync(path9) || readFileSync(path9, "utf8") !== content)
|
|
2884
|
+
writeFileSync(path9, content);
|
|
2579
2885
|
}
|
|
2580
2886
|
function connectionEnv(requirement, backend, config) {
|
|
2581
2887
|
if (requirement.service === "ldap")
|
|
@@ -2658,7 +2964,7 @@ async function configureDeployMiddleware(module, scan, environment, config, yes
|
|
|
2658
2964
|
}
|
|
2659
2965
|
services[requirement.service] = cloud;
|
|
2660
2966
|
} else {
|
|
2661
|
-
if (mode === "default" &&
|
|
2967
|
+
if (mode === "default" && ["aliyun-sms", "api"].includes(backendName))
|
|
2662
2968
|
throw new Error(cliM("cli_deploy_cloud_unsupported", { params: { backend: backend.label } }));
|
|
2663
2969
|
const connection = mode === "custom" && backendName === current.backend ? { ...requirement.config } : requirement.configName === "reach" ? { name: requirement.config.name ?? requirement.service } : {};
|
|
2664
2970
|
for (const field of backend.fields) {
|
|
@@ -2666,22 +2972,27 @@ async function configureDeployMiddleware(module, scan, environment, config, yes
|
|
|
2666
2972
|
const initial = previous2 === void 0 ? field.defaultValue ?? "" : typeof previous2 === "object" ? JSON.stringify(previous2) : String(previous2);
|
|
2667
2973
|
const variable = `${requirement.envPrefix}_${field.name.toUpperCase()}`;
|
|
2668
2974
|
const info = moduleEnvironmentVariables(requirement.configName).find((item) => item.name === variable);
|
|
2669
|
-
const value = mode === "custom" && !yes ? String(await ask({ type: field.secret ? "password" : "text", name: "value", message: cliM("cli_deploy_field", { params: {
|
|
2975
|
+
const value = mode === "custom" && !yes ? String(await ask({ type: field.secret ? "password" : "text", name: "value", message: cliM("cli_deploy_field", { params: { description: info?.description ?? field.name, constraint: info?.constraint ?? field.kind ?? "string" } }), initial, validate: (input) => validateField(String(input), field, backendName) })) : initial;
|
|
2976
|
+
if (field.name === "ssl" && validateField(value, field, backendName) !== true)
|
|
2977
|
+
throw new Error(cliM("cli_deploy_invalid_value"));
|
|
2670
2978
|
if (value !== "") {
|
|
2671
2979
|
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;
|
|
2672
2980
|
} else {
|
|
2673
2981
|
delete connection[field.name];
|
|
2674
2982
|
}
|
|
2675
2983
|
}
|
|
2984
|
+
if (requirement.service === "db") {
|
|
2985
|
+
const validated = ReldbConfigSchema.safeParse({ ...connection, type: backendName });
|
|
2986
|
+
if (!validated.success)
|
|
2987
|
+
throw new Error(cliM("cli_deploy_config_invalid", { params: { path: join(scan.appDir ?? ".", "config", "_db.yml"), fields: validated.error.issues.map((issue) => issue.path.join(".")).join(", ") } }));
|
|
2988
|
+
}
|
|
2676
2989
|
Object.assign(updates, connectionEnv(requirement, backendName, connection));
|
|
2677
2990
|
}
|
|
2678
2991
|
ensureConfigNode(scan.appDir ?? ".", requirement, backendName);
|
|
2679
2992
|
const saved = module.saveDeploymentEnvironment(environment, updates);
|
|
2680
2993
|
if (!saved.success)
|
|
2681
2994
|
throw new Error(saved.error.message);
|
|
2682
|
-
|
|
2683
|
-
writeCliMessage(cliM("cli_deploy_saved", { params: { name, value: displayDeployValue(name, value) } }));
|
|
2684
|
-
}
|
|
2995
|
+
writeCliMessage(cliM("cli_deploy_saved", { params: { service: requirement.service } }));
|
|
2685
2996
|
}
|
|
2686
2997
|
config.services = Object.keys(services).length ? services : void 0;
|
|
2687
2998
|
return modes;
|
|
@@ -2697,14 +3008,19 @@ async function configureDeployTarget(module, config, environment, nonInteractive
|
|
|
2697
3008
|
if (existing.type && existing.type !== "docker-ssh")
|
|
2698
3009
|
throw new Error(cliM("cli_deploy_bundle_provider"));
|
|
2699
3010
|
config.provider = { type: "docker-ssh", remote: { runtime: "auto" }, expose: { hostPort: 3e3 }, ...existing };
|
|
3011
|
+
const candidate = module.ProviderConfigSchema.safeParse(config.provider);
|
|
3012
|
+
if (!candidate.success && candidate.error.issues.some((issue) => issue.path[0] === "ssh")) {
|
|
3013
|
+
const provider2 = configObject(config.provider);
|
|
3014
|
+
delete provider2.ssh;
|
|
3015
|
+
}
|
|
2700
3016
|
}
|
|
2701
3017
|
const current = module.ProviderConfigSchema.safeParse(config.provider);
|
|
2702
3018
|
if (current.success && (bundleOnly || current.data.type !== "docker-ssh" || current.data.ssh)) {
|
|
2703
3019
|
config.provider = current.data;
|
|
2704
|
-
writeCliMessage(cliM("cli_deploy_target_reused", { params: { target: current.data.type === "docker-ssh"
|
|
3020
|
+
writeCliMessage(bundleOnly ? cliM("cli_deploy_bundle_target") : 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}` : "Vercel" } }));
|
|
2705
3021
|
return;
|
|
2706
3022
|
}
|
|
2707
|
-
if (nonInteractive)
|
|
3023
|
+
if (nonInteractive || bundleOnly)
|
|
2708
3024
|
throw new Error(cliM("cli_deploy_target_missing", { params: { fields: current.success ? "provider.ssh" : current.error.issues.map((issue) => `provider.${issue.path.join(".")}`).join(", ") } }));
|
|
2709
3025
|
const original = configObject(config.provider);
|
|
2710
3026
|
const platform = await prompts({
|
|
@@ -2810,7 +3126,7 @@ async function deployCommand(options) {
|
|
|
2810
3126
|
localModule = localRequire.resolve("@h-ai/deploy");
|
|
2811
3127
|
} catch {
|
|
2812
3128
|
}
|
|
2813
|
-
deployModule = localModule ? await import(pathToFileURL(localModule).href) : await import('./src-
|
|
3129
|
+
deployModule = localModule ? await import(pathToFileURL(localModule).href) : await import('./src-DSJJXM2F.js');
|
|
2814
3130
|
} catch {
|
|
2815
3131
|
writeCliError(chalk.red(cliM("cli_deploy_module_missing")));
|
|
2816
3132
|
writeCliMessage(chalk.cyan(" pnpm add -D @h-ai/deploy"));
|
|
@@ -2948,38 +3264,29 @@ async function deployCommand(options) {
|
|
|
2948
3264
|
serviceModes
|
|
2949
3265
|
});
|
|
2950
3266
|
if (!deployResult.success) {
|
|
3267
|
+
const execution = deployModule.getDeployCommandState(deployResult.error);
|
|
3268
|
+
if (execution) {
|
|
3269
|
+
const localKey = execution.localTermination === "not-started" ? "cli_deploy_local_not_started" : execution.localTermination === "confirmed" ? "cli_deploy_local_confirmed" : "cli_deploy_local_unknown";
|
|
3270
|
+
writeCliMessage(cliM(localKey));
|
|
3271
|
+
if (execution.remoteExecution)
|
|
3272
|
+
writeCliMessage(cliM(execution.remoteExecution === "finished" ? "cli_deploy_remote_finished" : "cli_deploy_remote_unknown"));
|
|
3273
|
+
if (execution.deploymentId && execution.resultFile)
|
|
3274
|
+
writeCliMessage(cliM("cli_deploy_result_check", { params: { id: execution.deploymentId, path: execution.resultFile } }));
|
|
3275
|
+
}
|
|
2951
3276
|
if (deployResult.error.ext?.recovery) {
|
|
2952
3277
|
writeCliMessage(deployResult.error.suggestion ?? cliM("cli_deploy_command_failed"));
|
|
2953
3278
|
}
|
|
2954
3279
|
throw new Error(cliM("cli_deploy_failed", { params: { message: deployResult.error.message } }));
|
|
2955
3280
|
}
|
|
2956
3281
|
spinner.succeed(chalk.green(cliM(deployResult.data.status === "packaged" ? "cli_deploy_bundle_ready" : "cli_deploy_success")));
|
|
2957
|
-
for (const key of Object.keys(process4.env)) {
|
|
2958
|
-
if (originalEnv[key] === void 0)
|
|
2959
|
-
delete process4.env[key];
|
|
2960
|
-
}
|
|
2961
|
-
Object.assign(process4.env, originalEnv);
|
|
2962
|
-
const completedEnvironment = deployModule.loadDeploymentEnvironment({ environment: environment.data.environment, envDir: environment.data.envDir });
|
|
2963
|
-
if (!completedEnvironment.success)
|
|
2964
|
-
throw new Error(completedEnvironment.error.message);
|
|
2965
|
-
for (const requirement of scan.middlewares ?? []) {
|
|
2966
|
-
if ((serviceModes[requirement.envPrefix] ?? serviceModes[requirement.service]) === "keep")
|
|
2967
|
-
continue;
|
|
2968
|
-
const value = completedEnvironment.data.values[requirement.envPrefix];
|
|
2969
|
-
if (value !== void 0)
|
|
2970
|
-
writeCliMessage(cliM("cli_deploy_saved", { params: { name: requirement.envPrefix, value: displayDeployValue(requirement.envPrefix, value) } }));
|
|
2971
|
-
}
|
|
2972
3282
|
writeCliMessage("");
|
|
2973
|
-
if (deployResult.data.bundlePath)
|
|
3283
|
+
if (deployResult.data.bundlePath && (deployResult.data.status !== "packaged" || !deployResult.data.instructions))
|
|
2974
3284
|
writeCliMessage(cliM("cli_deploy_bundle_path", { params: { path: deployResult.data.bundlePath } }));
|
|
2975
3285
|
if (deployResult.data.status === "packaged")
|
|
2976
3286
|
writeCliMessage(deployResult.data.instructions ?? "");
|
|
2977
3287
|
else
|
|
2978
3288
|
writeCliMessage(chalk.cyan(cliM("cli_deploy_url", { params: { url: deployResult.data.url } })));
|
|
2979
3289
|
writeCliMessage(chalk.gray(cliM("cli_deploy_id", { params: { id: deployResult.data.deploymentId } })));
|
|
2980
|
-
if (deployResult.data.envVarsSet.length > 0) {
|
|
2981
|
-
writeCliMessage(chalk.gray(cliM("cli_deploy_env", { params: { names: deployResult.data.envVarsSet.join(", ") } })));
|
|
2982
|
-
}
|
|
2983
3290
|
writeCliMessage("");
|
|
2984
3291
|
} catch (error) {
|
|
2985
3292
|
spinner.fail(chalk.red(cliM("cli_deploy_command_failed")));
|
|
@@ -3003,29 +3310,213 @@ async function deployCommand(options) {
|
|
|
3003
3310
|
restoreProjectLogger?.();
|
|
3004
3311
|
}
|
|
3005
3312
|
}
|
|
3313
|
+
function appendAt(source, marker, entry) {
|
|
3314
|
+
if (source.split(marker).length !== 2)
|
|
3315
|
+
throw new Error(cliM("cli_generateUnsupportedAggregator"));
|
|
3316
|
+
if (source.split(/\r?\n/).some((line) => line.trim() === entry.trim()))
|
|
3317
|
+
return source;
|
|
3318
|
+
const moduleName = /^import .+ from ['"](.+)['"]$/.exec(entry)?.[1];
|
|
3319
|
+
if (moduleName) {
|
|
3320
|
+
for (const match of source.matchAll(/^import (?!type\b)[^\r\n]+ from ['"](\.[^'"]+)['"]\r?$/gm)) {
|
|
3321
|
+
if (match[1].localeCompare(moduleName) > 0)
|
|
3322
|
+
return `${source.slice(0, match.index)}${entry}
|
|
3323
|
+
${source.slice(match.index)}`;
|
|
3324
|
+
}
|
|
3325
|
+
}
|
|
3326
|
+
return source.replace(marker, `${entry}
|
|
3327
|
+
${marker}`);
|
|
3328
|
+
}
|
|
3329
|
+
async function generateServApi(context, target) {
|
|
3330
|
+
const contracts = target.packages.filter((pkg) => pkg.dependencies["@h-ai/api-contract"] && pkg.dependencies.zod && pkg.name in target.target.dependencies);
|
|
3331
|
+
if (contracts.length !== 1)
|
|
3332
|
+
throw new Error(cliM("cli_generateUnsupportedAggregator"));
|
|
3333
|
+
const contract = contracts[0];
|
|
3334
|
+
const contractSources = await fse.readdir(path2.join(contract.directory, "src"));
|
|
3335
|
+
const aggregators = [];
|
|
3336
|
+
for (const name of contractSources.filter((name2) => name2.endsWith(".ts"))) {
|
|
3337
|
+
const file = path2.join(contract.directory, "src", name);
|
|
3338
|
+
const content = await fse.readFile(file, "utf8");
|
|
3339
|
+
if (content.includes("// @hai-generator:contracts:entries"))
|
|
3340
|
+
aggregators.push({ path: file, content });
|
|
3341
|
+
}
|
|
3342
|
+
if (aggregators.length !== 1)
|
|
3343
|
+
throw new Error(cliM("cli_generateUnsupportedAggregator"));
|
|
3344
|
+
const aggregator = aggregators[0];
|
|
3345
|
+
const contractIndex = path2.join(contract.directory, "src/index.ts");
|
|
3346
|
+
const procedureIndex = path2.join(target.target.directory, "src/server/procedures/index.ts");
|
|
3347
|
+
const indexSource = await fse.readFile(contractIndex, "utf8");
|
|
3348
|
+
const procedureSource = await fse.readFile(procedureIndex, "utf8");
|
|
3349
|
+
const contractName = `${context.camelCase}Contract`;
|
|
3350
|
+
const factoryName = `create${context.pascalCase}Procedures`;
|
|
3351
|
+
const domainMarker = `// @hai-generator:domain ${context.camelCase}`;
|
|
3352
|
+
const contractFile = path2.join(contract.directory, "src", `${context.kebabCase}-contract.ts`);
|
|
3353
|
+
const procedureFile = path2.join(target.target.directory, "src/server/procedures", `${context.kebabCase}-procedures.ts`);
|
|
3354
|
+
const importLine = `import { ${contractName} } from './${context.kebabCase}-contract.js'`;
|
|
3355
|
+
const entryLine = ` ${context.camelCase}: ${contractName},`;
|
|
3356
|
+
const factoryImport = `import { ${factoryName} } from './${context.kebabCase}-procedures.js'`;
|
|
3357
|
+
const domainProperty = new RegExp(`^\\s*(?:${context.camelCase}|['"]${context.camelCase}['"]):`, "m");
|
|
3358
|
+
if (domainProperty.test(aggregator.content) || domainProperty.test(procedureSource)) {
|
|
3359
|
+
for (const file of [contractFile, procedureFile]) {
|
|
3360
|
+
if (!await fse.pathExists(file) || !(await fse.readFile(file, "utf8")).includes(domainMarker))
|
|
3361
|
+
throw new Error(cliM("cli_generateDomainExists", { params: { name: context.camelCase } }));
|
|
3362
|
+
}
|
|
3363
|
+
}
|
|
3364
|
+
const contractContent = `${domainMarker}
|
|
3365
|
+
import { apiContract } from '@h-ai/api-contract'
|
|
3366
|
+
import { z } from 'zod'
|
|
3367
|
+
|
|
3368
|
+
/**
|
|
3369
|
+
* ${context.pascalCase} \u793A\u4F8B\u5951\u7EA6\uFF1AGET \u5217\u8868\u4E0E POST \u6821\u9A8C\u56DE\u663E\uFF0C\u4E0D\u6267\u884C\u6301\u4E45\u5199\u5165\u3002
|
|
3370
|
+
* @example client.${context.camelCase}.create({ name: 'Example' })
|
|
3371
|
+
*/
|
|
3372
|
+
export const ${contractName} = {
|
|
3373
|
+
list: apiContract
|
|
3374
|
+
.route({ method: 'GET', path: '/${context.kebabCase}', operationId: '${context.camelCase}.list', tags: ['${context.kebabCase}'] })
|
|
3375
|
+
.output(apiContract.haiResultSchema(z.object({ items: z.array(z.object({ name: z.string() })) }))),
|
|
3376
|
+
create: apiContract
|
|
3377
|
+
.route({ method: 'POST', path: '/${context.kebabCase}', operationId: '${context.camelCase}.create', tags: ['${context.kebabCase}'] })
|
|
3378
|
+
.input(z.object({ name: z.string().min(1) }))
|
|
3379
|
+
.output(apiContract.haiResultSchema(z.object({ name: z.string() }))),
|
|
3380
|
+
}
|
|
3381
|
+
`;
|
|
3382
|
+
const procedureContent = `${domainMarker}
|
|
3383
|
+
import type { ServContext } from '@h-ai/serv'
|
|
3384
|
+
import { ok } from '@h-ai/core'
|
|
3385
|
+
import { serv } from '@h-ai/serv'
|
|
3386
|
+
import { ${contractName} } from '${contract.name}'
|
|
3387
|
+
|
|
3388
|
+
/**
|
|
3389
|
+
* \u521B\u5EFA ${context.pascalCase} \u793A\u4F8B\u8FC7\u7A0B\uFF1B\u65B0\u589E\u6301\u4E45\u5199\u5165\u524D\u63A5\u5165\u6388\u6743\u4E0E\u4E1A\u52A1\u670D\u52A1\u3002
|
|
3390
|
+
* @returns \u4E0E\u5171\u4EAB\u5951\u7EA6\u4E00\u81F4\u7684\u516C\u5F00\u8FC7\u7A0B\uFF1B\u793A\u4F8B\u4E0D\u8BBF\u95EE\u7528\u6237\u6570\u636E\u3002
|
|
3391
|
+
* @example const procedures = { ${context.camelCase}: ${factoryName}() }
|
|
3392
|
+
*/
|
|
3393
|
+
export function ${factoryName}() {
|
|
3394
|
+
return serv
|
|
3395
|
+
.implement(${contractName})
|
|
3396
|
+
.context<ServContext>()
|
|
3397
|
+
.route('list', () => ok({ items: [] }))
|
|
3398
|
+
.route('create', ({ input }) => ok({ name: input.name }))
|
|
3399
|
+
.build()
|
|
3400
|
+
}
|
|
3401
|
+
`;
|
|
3402
|
+
return [
|
|
3403
|
+
{ path: contractFile, content: contractContent },
|
|
3404
|
+
{ path: procedureFile, content: procedureContent },
|
|
3405
|
+
{ path: aggregator.path, previousContent: aggregator.content, content: appendAt(appendAt(aggregator.content, "// @hai-generator:contracts:imports", importLine), " // @hai-generator:contracts:entries", entryLine) },
|
|
3406
|
+
{ path: contractIndex, previousContent: indexSource, content: appendAt(indexSource, "// @hai-generator:contracts:exports", `export * from './${context.kebabCase}-contract.js'`) },
|
|
3407
|
+
{ path: procedureIndex, previousContent: procedureSource, content: appendAt(appendAt(procedureSource, "// @hai-generator:procedures:imports", factoryImport), " // @hai-generator:procedures:entries", ` ${context.camelCase}: ${factoryName}(),`) }
|
|
3408
|
+
];
|
|
3409
|
+
}
|
|
3410
|
+
function isInside(root, file) {
|
|
3411
|
+
const relative2 = path2.relative(root, file);
|
|
3412
|
+
return relative2 !== ".." && !relative2.startsWith(`..${path2.sep}`) && !path2.isAbsolute(relative2);
|
|
3413
|
+
}
|
|
3414
|
+
async function readPackage(directory) {
|
|
3415
|
+
if (!await fse.pathExists(path2.join(directory, "package.json")))
|
|
3416
|
+
return void 0;
|
|
3417
|
+
const value = await fse.readJson(path2.join(directory, "package.json"));
|
|
3418
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
3419
|
+
throw new Error(cliM("cli_generateUnsupportedProject"));
|
|
3420
|
+
const dependencies = {};
|
|
3421
|
+
for (const key of ["dependencies", "devDependencies"]) {
|
|
3422
|
+
const entries = key in value ? Reflect.get(value, key) : void 0;
|
|
3423
|
+
if (entries && typeof entries === "object" && !Array.isArray(entries)) {
|
|
3424
|
+
for (const [name2, version] of Object.entries(entries)) {
|
|
3425
|
+
if (typeof version === "string")
|
|
3426
|
+
dependencies[name2] = version;
|
|
3427
|
+
}
|
|
3428
|
+
}
|
|
3429
|
+
}
|
|
3430
|
+
const name = "name" in value && typeof value.name === "string" ? value.name : path2.basename(directory);
|
|
3431
|
+
if (!/^(?:@[\w.-]+\/)?[\w.-]+$/.test(name))
|
|
3432
|
+
throw new Error(cliM("cli_generateUnsupportedProject"));
|
|
3433
|
+
return { directory, name, dependencies };
|
|
3434
|
+
}
|
|
3435
|
+
function generateArchitecture(pkg) {
|
|
3436
|
+
if (pkg.dependencies["@sveltejs/kit"])
|
|
3437
|
+
return "sveltekit";
|
|
3438
|
+
if (pkg.dependencies["@h-ai/serv"])
|
|
3439
|
+
return "serv";
|
|
3440
|
+
return pkg.dependencies.svelte ? "svelte" : "other";
|
|
3441
|
+
}
|
|
3442
|
+
async function resolveGenerateTarget(options) {
|
|
3443
|
+
const cwd = await fse.realpath(path2.resolve(options.cwd ?? "."));
|
|
3444
|
+
let root = cwd;
|
|
3445
|
+
for (let current = cwd; ; current = path2.dirname(current)) {
|
|
3446
|
+
if (await fse.pathExists(path2.join(current, "pnpm-workspace.yaml"))) {
|
|
3447
|
+
root = current;
|
|
3448
|
+
break;
|
|
3449
|
+
}
|
|
3450
|
+
if (path2.dirname(current) === current)
|
|
3451
|
+
break;
|
|
3452
|
+
}
|
|
3453
|
+
const packages = [];
|
|
3454
|
+
const addPackage = async (directory) => {
|
|
3455
|
+
if (!isInside(root, directory))
|
|
3456
|
+
throw new Error(cliM("cli_generateOutsideWorkspace"));
|
|
3457
|
+
const real = await fse.realpath(directory);
|
|
3458
|
+
if (!isInside(root, real))
|
|
3459
|
+
throw new Error(cliM("cli_generateOutsideWorkspace"));
|
|
3460
|
+
const pkg = await readPackage(real);
|
|
3461
|
+
if (pkg && !packages.some((value) => value.directory === real))
|
|
3462
|
+
packages.push(pkg);
|
|
3463
|
+
};
|
|
3464
|
+
await addPackage(root);
|
|
3465
|
+
if (await fse.pathExists(path2.join(root, "pnpm-workspace.yaml"))) {
|
|
3466
|
+
const workspace = parse(await fse.readFile(path2.join(root, "pnpm-workspace.yaml"), "utf8"));
|
|
3467
|
+
const patterns = workspace && typeof workspace === "object" && "packages" in workspace && Array.isArray(workspace.packages) ? workspace.packages : [];
|
|
3468
|
+
for (const pattern of patterns) {
|
|
3469
|
+
if (typeof pattern !== "string" || pattern.startsWith("!") || pattern.split(/[\\/]/).includes("..") || path2.isAbsolute(pattern))
|
|
3470
|
+
throw new Error(cliM("cli_generateUnsupportedProject"));
|
|
3471
|
+
for await (const manifest of glob(`${pattern}/package.json`, { cwd: root, exclude: ["**/node_modules/**"] }))
|
|
3472
|
+
await addPackage(path2.dirname(path2.resolve(root, manifest)));
|
|
3473
|
+
}
|
|
3474
|
+
}
|
|
3475
|
+
if (cwd !== root)
|
|
3476
|
+
await addPackage(cwd);
|
|
3477
|
+
const selected = options.workspace ? packages.filter((pkg) => pkg.name === options.workspace || pkg.directory === path2.resolve(root, options.workspace)) : packages.filter((pkg) => pkg.directory === cwd);
|
|
3478
|
+
const applicable = (pkg) => {
|
|
3479
|
+
const architecture = generateArchitecture(pkg);
|
|
3480
|
+
return options.type === "api" ? architecture === "serv" || architecture === "sveltekit" && Boolean(pkg.dependencies["@h-ai/kit"]) && Boolean(pkg.dependencies.zod) : options.type === "page" ? architecture === "sveltekit" : options.type === "component" ? ["sveltekit", "svelte"].includes(architecture) : Object.keys(pkg.dependencies).some((name) => name.startsWith("@h-ai/"));
|
|
3481
|
+
};
|
|
3482
|
+
const candidates = options.workspace || selected.length === 1 && (cwd !== root || applicable(selected[0])) ? selected.filter(applicable) : packages.filter(applicable);
|
|
3483
|
+
if (candidates.length !== 1)
|
|
3484
|
+
throw new Error(cliM(candidates.length > 1 ? "cli_generateAmbiguousWorkspace" : "cli_generateUnsupportedProject"));
|
|
3485
|
+
const target = candidates[0];
|
|
3486
|
+
if (generateArchitecture(target) === "sveltekit" && (options.type === "page" || options.type === "api")) {
|
|
3487
|
+
for (const name of ["svelte.config.js", "svelte.config.mjs", "svelte.config.cjs", "svelte.config.ts"]) {
|
|
3488
|
+
const configFile = path2.join(target.directory, name);
|
|
3489
|
+
if (await fse.pathExists(configFile) && /\bfiles\s*[:.[(]/.test(await fse.readFile(configFile, "utf8")))
|
|
3490
|
+
throw new Error(cliM("cli_generateCustomRoutes"));
|
|
3491
|
+
}
|
|
3492
|
+
}
|
|
3493
|
+
return { root, target, packages };
|
|
3494
|
+
}
|
|
3495
|
+
|
|
3496
|
+
// src/commands/cli-generate.ts
|
|
3006
3497
|
var GENERATORS = {
|
|
3007
3498
|
page: {
|
|
3008
|
-
description:
|
|
3499
|
+
description: () => cliM("cli_generatePageDescription"),
|
|
3009
3500
|
defaultPath: "src/routes",
|
|
3010
3501
|
generate: generatePage
|
|
3011
3502
|
},
|
|
3012
3503
|
component: {
|
|
3013
|
-
description:
|
|
3504
|
+
description: () => cliM("cli_generateComponentDescription"),
|
|
3014
3505
|
defaultPath: "src/lib/components",
|
|
3015
3506
|
generate: generateComponent
|
|
3016
3507
|
},
|
|
3017
3508
|
api: {
|
|
3018
|
-
description:
|
|
3509
|
+
description: () => cliM("cli_generateApiDescription"),
|
|
3019
3510
|
defaultPath: "src/routes/api",
|
|
3020
3511
|
generate: generateApi
|
|
3021
3512
|
},
|
|
3022
3513
|
model: {
|
|
3023
|
-
description: "
|
|
3514
|
+
description: () => cliM("cli_generateModelDescription"),
|
|
3024
3515
|
defaultPath: "src/lib/models",
|
|
3025
3516
|
generate: generateModel
|
|
3026
3517
|
},
|
|
3027
3518
|
migration: {
|
|
3028
|
-
description: "
|
|
3519
|
+
description: () => cliM("cli_generateMigrationDescription"),
|
|
3029
3520
|
defaultPath: "migrations",
|
|
3030
3521
|
generate: generateMigration
|
|
3031
3522
|
}
|
|
@@ -3034,55 +3525,43 @@ async function generate(options) {
|
|
|
3034
3525
|
const spinner = ora();
|
|
3035
3526
|
const cwd = options.cwd ?? ".";
|
|
3036
3527
|
try {
|
|
3037
|
-
const project = await detectProject(cwd);
|
|
3038
|
-
if (!project?.isHaiProject) {
|
|
3039
|
-
writeCliMessage(chalk.yellow("\u8B66\u544A: \u5F53\u524D\u76EE\u5F55\u4E0D\u662F hai \u9879\u76EE"));
|
|
3040
|
-
}
|
|
3041
3528
|
const resolvedOptions = await resolveGenerateOptions(options);
|
|
3042
3529
|
const generator = GENERATORS[resolvedOptions.type];
|
|
3043
|
-
if (!generator) {
|
|
3044
|
-
throw new Error(`\u672A\u77E5\u7684\u751F\u6210\u5668\u7C7B\u578B: ${resolvedOptions.type}`);
|
|
3045
|
-
}
|
|
3046
3530
|
if (!/^[a-z][a-z0-9]*(?:[-_][a-z0-9]+)*$/i.test(resolvedOptions.name))
|
|
3047
3531
|
throw new Error(cliM("cli_invalidGenerateName"));
|
|
3048
|
-
const
|
|
3049
|
-
const
|
|
3050
|
-
|
|
3051
|
-
|
|
3532
|
+
const target = await resolveGenerateTarget(resolvedOptions);
|
|
3533
|
+
const architecture = generateArchitecture(target.target);
|
|
3534
|
+
const outputDir = path2.resolve(target.target.directory, resolvedOptions.output || generator.defaultPath);
|
|
3535
|
+
if (["page", "api"].includes(resolvedOptions.type) && architecture === "sveltekit") {
|
|
3536
|
+
const routes = path2.join(target.target.directory, "src/routes");
|
|
3537
|
+
if (!outputDir.startsWith(routes + path2.sep) && outputDir !== routes)
|
|
3538
|
+
throw new Error(cliM("cli_generateInvalidRouteOutput"));
|
|
3052
3539
|
}
|
|
3540
|
+
if (resolvedOptions.type === "api" && architecture === "serv" && resolvedOptions.output)
|
|
3541
|
+
throw new Error(cliM("cli_generateInvalidRouteOutput"));
|
|
3542
|
+
const relativeOutput = path2.relative(target.root, outputDir);
|
|
3543
|
+
if (relativeOutput === ".." || relativeOutput.startsWith(`..${path2.sep}`) || path2.isAbsolute(relativeOutput))
|
|
3544
|
+
throw new Error(cliM("cli_generateOutsideWorkspace"));
|
|
3053
3545
|
const context = createTemplateContext(resolvedOptions.name, {
|
|
3054
|
-
projectName:
|
|
3546
|
+
projectName: target.target.name
|
|
3055
3547
|
});
|
|
3056
|
-
spinner.start(
|
|
3057
|
-
const files = await generator.generate(context, outputDir);
|
|
3058
|
-
|
|
3059
|
-
|
|
3060
|
-
if (!resolvedOptions.force && await fse.pathExists(file.path))
|
|
3061
|
-
throw new Error(cliM("cli_generateExists", { params: { path: file.path } }));
|
|
3062
|
-
let ancestor = file.path;
|
|
3063
|
-
while (!await fse.pathExists(ancestor))
|
|
3064
|
-
ancestor = path2.dirname(ancestor);
|
|
3065
|
-
const realAncestor = await fse.realpath(ancestor);
|
|
3066
|
-
const relative2 = path2.relative(realCwd, realAncestor);
|
|
3067
|
-
if (relative2 === ".." || relative2.startsWith(`..${path2.sep}`) || path2.isAbsolute(relative2))
|
|
3068
|
-
throw new Error(cliM("cli_generateOutsideWorkspace"));
|
|
3069
|
-
}
|
|
3070
|
-
for (const file of files) {
|
|
3071
|
-
await fse.ensureDir(path2.dirname(file.path));
|
|
3072
|
-
await fse.writeFile(file.path, file.content, { encoding: "utf-8", flag: resolvedOptions.force ? "w" : "wx" });
|
|
3073
|
-
}
|
|
3074
|
-
spinner.succeed();
|
|
3548
|
+
spinner.start(cliM("cli_generateProgress", { params: { description: generator.description() } }));
|
|
3549
|
+
const files = resolvedOptions.type === "api" && architecture === "serv" ? await generateServApi(context, target) : await generator.generate(context, outputDir);
|
|
3550
|
+
await writeGeneratedFiles(files, target.root, resolvedOptions.force);
|
|
3551
|
+
spinner.succeed(cliM("cli_generateComplete"));
|
|
3075
3552
|
writeCliMessage("");
|
|
3076
|
-
writeCliMessage(
|
|
3077
|
-
writeCliMessage("");
|
|
3078
|
-
writeCliMessage("\u521B\u5EFA\u7684\u6587\u4EF6:");
|
|
3553
|
+
writeCliMessage(cliM("cli_generateFiles"));
|
|
3079
3554
|
files.forEach((file) => {
|
|
3080
3555
|
writeCliMessage(chalk.cyan(` ${path2.relative(cwd, file.path)}`));
|
|
3081
3556
|
});
|
|
3082
3557
|
writeCliMessage("");
|
|
3083
3558
|
} catch (error) {
|
|
3084
|
-
spinner.fail();
|
|
3085
|
-
writeCliError(
|
|
3559
|
+
spinner.fail(cliM("cli_generateFailed"));
|
|
3560
|
+
writeCliError(cliM("cli_generateFailureDetail"), { error });
|
|
3561
|
+
if (error instanceof AggregateError) {
|
|
3562
|
+
for (const cause of error.errors)
|
|
3563
|
+
writeCliError(cliM("cli_generateFailureDetail"), { error: cause });
|
|
3564
|
+
}
|
|
3086
3565
|
throw error;
|
|
3087
3566
|
}
|
|
3088
3567
|
}
|
|
@@ -3092,9 +3571,9 @@ async function resolveGenerateOptions(options) {
|
|
|
3092
3571
|
questions.push({
|
|
3093
3572
|
type: "select",
|
|
3094
3573
|
name: "type",
|
|
3095
|
-
message: "
|
|
3574
|
+
message: cliM("cli_generateTypePrompt"),
|
|
3096
3575
|
choices: Object.entries(GENERATORS).map(([key, value]) => ({
|
|
3097
|
-
title: `${key} - ${value.description}`,
|
|
3576
|
+
title: `${key} - ${value.description()}`,
|
|
3098
3577
|
value: key
|
|
3099
3578
|
}))
|
|
3100
3579
|
});
|
|
@@ -3103,24 +3582,102 @@ async function resolveGenerateOptions(options) {
|
|
|
3103
3582
|
questions.push({
|
|
3104
3583
|
type: "text",
|
|
3105
3584
|
name: "name",
|
|
3106
|
-
message: "
|
|
3585
|
+
message: cliM("cli_generateNamePrompt"),
|
|
3107
3586
|
validate: (value) => {
|
|
3108
3587
|
if (!value.trim())
|
|
3109
|
-
return "
|
|
3588
|
+
return cliM("cli_generateNameRequired");
|
|
3110
3589
|
return true;
|
|
3111
3590
|
}
|
|
3112
3591
|
});
|
|
3113
3592
|
}
|
|
3114
|
-
const answers = questions.length > 0 ? await prompts(questions
|
|
3593
|
+
const answers = questions.length > 0 ? await prompts(questions, { onCancel: () => {
|
|
3594
|
+
throw new Error(cliM("cli_generateCancelled"));
|
|
3595
|
+
} }) : {};
|
|
3596
|
+
const type = options.type || ("type" in answers ? answers.type : void 0);
|
|
3597
|
+
const name = options.name || ("name" in answers ? answers.name : void 0);
|
|
3598
|
+
if (!isGeneratorType(type))
|
|
3599
|
+
throw new Error(cliM("cli_generateUnknownType", { params: { type: String(type ?? "") } }));
|
|
3600
|
+
if (typeof name !== "string" || !name.trim())
|
|
3601
|
+
throw new Error(cliM("cli_generateNameRequired"));
|
|
3115
3602
|
return {
|
|
3116
|
-
type
|
|
3117
|
-
name
|
|
3603
|
+
type,
|
|
3604
|
+
name,
|
|
3118
3605
|
output: options.output ?? "",
|
|
3119
3606
|
force: options.force ?? false,
|
|
3120
3607
|
verbose: options.verbose ?? false,
|
|
3121
|
-
cwd: options.cwd ?? "."
|
|
3608
|
+
cwd: options.cwd ?? ".",
|
|
3609
|
+
workspace: options.workspace ?? ""
|
|
3122
3610
|
};
|
|
3123
3611
|
}
|
|
3612
|
+
function isGeneratorType(value) {
|
|
3613
|
+
return typeof value === "string" && Object.hasOwn(GENERATORS, value);
|
|
3614
|
+
}
|
|
3615
|
+
async function writeGeneratedFiles(files, root, force) {
|
|
3616
|
+
if (new Set(files.map((file) => path2.resolve(file.path))).size !== files.length)
|
|
3617
|
+
throw new Error(cliM("cli_generateUnsupportedAggregator"));
|
|
3618
|
+
const previous = /* @__PURE__ */ new Map();
|
|
3619
|
+
const stages = /* @__PURE__ */ new Map();
|
|
3620
|
+
const written = [];
|
|
3621
|
+
const failures = [];
|
|
3622
|
+
for (const file of files) {
|
|
3623
|
+
const relativeFile = path2.relative(root, file.path);
|
|
3624
|
+
if (relativeFile === ".." || relativeFile.startsWith(`..${path2.sep}`) || path2.isAbsolute(relativeFile))
|
|
3625
|
+
throw new Error(cliM("cli_generateOutsideWorkspace"));
|
|
3626
|
+
let ancestor = file.path;
|
|
3627
|
+
while (!await fse.pathExists(ancestor))
|
|
3628
|
+
ancestor = path2.dirname(ancestor);
|
|
3629
|
+
const relative2 = path2.relative(root, await fse.realpath(ancestor));
|
|
3630
|
+
if (relative2 === ".." || relative2.startsWith(`..${path2.sep}`) || path2.isAbsolute(relative2))
|
|
3631
|
+
throw new Error(cliM("cli_generateOutsideWorkspace"));
|
|
3632
|
+
const content = await fse.pathExists(file.path) ? await fse.readFile(file.path, "utf8") : void 0;
|
|
3633
|
+
if (file.previousContent !== void 0 ? content !== file.previousContent : content !== void 0 && !force)
|
|
3634
|
+
throw new Error(cliM("cli_generateExists", { params: { path: file.path } }));
|
|
3635
|
+
previous.set(file.path, content);
|
|
3636
|
+
}
|
|
3637
|
+
try {
|
|
3638
|
+
for (const file of files) {
|
|
3639
|
+
await fse.ensureDir(path2.dirname(file.path));
|
|
3640
|
+
const stage = path2.join(path2.dirname(file.path), `.${path2.basename(file.path)}.${randomUUID()}.tmp`);
|
|
3641
|
+
stages.set(file.path, stage);
|
|
3642
|
+
await fse.writeFile(stage, file.content, { encoding: "utf8", flag: "wx" });
|
|
3643
|
+
}
|
|
3644
|
+
for (const file of files) {
|
|
3645
|
+
const current = await fse.pathExists(file.path) ? await fse.readFile(file.path, "utf8") : void 0;
|
|
3646
|
+
if (current !== previous.get(file.path))
|
|
3647
|
+
throw new Error(cliM("cli_generateExists", { params: { path: file.path } }));
|
|
3648
|
+
await fse.rename(stages.get(file.path), file.path);
|
|
3649
|
+
written.push(file);
|
|
3650
|
+
}
|
|
3651
|
+
} catch (error) {
|
|
3652
|
+
failures.push(error);
|
|
3653
|
+
for (const file of written.reverse()) {
|
|
3654
|
+
try {
|
|
3655
|
+
if (await fse.readFile(file.path, "utf8") !== file.content)
|
|
3656
|
+
throw new Error(cliM("cli_generateExists", { params: { path: file.path } }));
|
|
3657
|
+
const original = previous.get(file.path);
|
|
3658
|
+
if (original === void 0)
|
|
3659
|
+
await fse.unlink(file.path);
|
|
3660
|
+
else
|
|
3661
|
+
await fse.writeFile(file.path, original, "utf8");
|
|
3662
|
+
} catch (rollbackError) {
|
|
3663
|
+
failures.push(rollbackError);
|
|
3664
|
+
}
|
|
3665
|
+
}
|
|
3666
|
+
} finally {
|
|
3667
|
+
for (const stage of stages.values()) {
|
|
3668
|
+
try {
|
|
3669
|
+
await fse.unlink(stage);
|
|
3670
|
+
} catch (error) {
|
|
3671
|
+
if (!error || typeof error !== "object" || !("code" in error) || error.code !== "ENOENT")
|
|
3672
|
+
failures.push(error);
|
|
3673
|
+
}
|
|
3674
|
+
}
|
|
3675
|
+
}
|
|
3676
|
+
if (failures.length > 1)
|
|
3677
|
+
throw new AggregateError(failures, cliM("cli_generateRollbackFailed"));
|
|
3678
|
+
if (failures.length === 1)
|
|
3679
|
+
throw failures[0];
|
|
3680
|
+
}
|
|
3124
3681
|
async function generatePage(context, outputDir) {
|
|
3125
3682
|
const files = [];
|
|
3126
3683
|
const pageDir = path2.join(outputDir, context.kebabCase);
|
|
@@ -3297,19 +3854,45 @@ export function down() {
|
|
|
3297
3854
|
return files;
|
|
3298
3855
|
}
|
|
3299
3856
|
var MODULE_CONFIG_MAP = {
|
|
3300
|
-
"@h-ai/core": { configKey: "core", label
|
|
3301
|
-
|
|
3302
|
-
|
|
3303
|
-
"@h-ai/
|
|
3304
|
-
|
|
3305
|
-
|
|
3306
|
-
"@h-ai/
|
|
3307
|
-
|
|
3308
|
-
|
|
3309
|
-
"@h-ai/
|
|
3310
|
-
|
|
3311
|
-
|
|
3312
|
-
"@h-ai/
|
|
3857
|
+
"@h-ai/core": { configKey: "core", get label() {
|
|
3858
|
+
return cliM("cli_coreLabel");
|
|
3859
|
+
} },
|
|
3860
|
+
"@h-ai/serv": { configKey: "serv", get label() {
|
|
3861
|
+
return cliM("cli_serviceLabel");
|
|
3862
|
+
} },
|
|
3863
|
+
"@h-ai/reldb": { configKey: "db", get label() {
|
|
3864
|
+
return cliM("cli_featureDbName");
|
|
3865
|
+
} },
|
|
3866
|
+
"@h-ai/cache": { configKey: "cache", get label() {
|
|
3867
|
+
return cliM("cli_featureCacheName");
|
|
3868
|
+
} },
|
|
3869
|
+
"@h-ai/iam": { configKey: "iam", get label() {
|
|
3870
|
+
return cliM("cli_featureIamName");
|
|
3871
|
+
} },
|
|
3872
|
+
"@h-ai/storage": { configKey: "storage", get label() {
|
|
3873
|
+
return cliM("cli_featureStorageName");
|
|
3874
|
+
} },
|
|
3875
|
+
"@h-ai/ai": { configKey: "ai", get label() {
|
|
3876
|
+
return cliM("cli_featureAiName");
|
|
3877
|
+
} },
|
|
3878
|
+
"@h-ai/vecdb": { configKey: "vecdb", get label() {
|
|
3879
|
+
return cliM("cli_featureVecdbName");
|
|
3880
|
+
} },
|
|
3881
|
+
"@h-ai/reach": { configKey: "reach", get label() {
|
|
3882
|
+
return cliM("cli_featureReachName");
|
|
3883
|
+
} },
|
|
3884
|
+
"@h-ai/scheduler": { configKey: "scheduler", get label() {
|
|
3885
|
+
return cliM("cli_featureSchedulerName");
|
|
3886
|
+
} },
|
|
3887
|
+
"@h-ai/audit": { configKey: "audit", get label() {
|
|
3888
|
+
return cliM("cli_featureAuditName");
|
|
3889
|
+
} },
|
|
3890
|
+
"@h-ai/payment": { configKey: "payment", get label() {
|
|
3891
|
+
return cliM("cli_featurePaymentName");
|
|
3892
|
+
} },
|
|
3893
|
+
"@h-ai/deploy": { configKey: "deploy", get label() {
|
|
3894
|
+
return cliM("cli_featureDeployName");
|
|
3895
|
+
} }
|
|
3313
3896
|
};
|
|
3314
3897
|
async function validateProject(cwd) {
|
|
3315
3898
|
const project = await detectProject(cwd);
|
|
@@ -3335,16 +3918,16 @@ async function initProject(options) {
|
|
|
3335
3918
|
const spinner = ora();
|
|
3336
3919
|
const cwd = options.cwd ?? ".";
|
|
3337
3920
|
try {
|
|
3338
|
-
spinner.start("
|
|
3921
|
+
spinner.start(cliM("cli_initDetecting"));
|
|
3339
3922
|
const result = await validateProject(cwd);
|
|
3340
3923
|
if (!result) {
|
|
3341
|
-
spinner.fail(chalk.red("
|
|
3924
|
+
spinner.fail(chalk.red(cliM("cli_projectNotFound")));
|
|
3342
3925
|
return;
|
|
3343
3926
|
}
|
|
3344
3927
|
const { project, existing, missing } = result;
|
|
3345
|
-
spinner.succeed(
|
|
3928
|
+
spinner.succeed(cliM("cli_initDetected", { params: { name: chalk.cyan(project.name) } }));
|
|
3346
3929
|
if (project.isHaiProject) {
|
|
3347
|
-
const { generateProjectAiSupport, resolveSkillNamesForPackages } = await import('./cli-skill-templates-
|
|
3930
|
+
const { generateProjectAiSupport, resolveSkillNamesForPackages } = await import('./cli-skill-templates-US5CCAJ4.js');
|
|
3348
3931
|
const generatedFiles = await generateProjectAiSupport(cwd, resolveSkillNamesForPackages(project.haiPackages), {
|
|
3349
3932
|
overwriteSkills: false,
|
|
3350
3933
|
overwriteBridgeFiles: false
|
|
@@ -3354,30 +3937,30 @@ async function initProject(options) {
|
|
|
3354
3937
|
}
|
|
3355
3938
|
}
|
|
3356
3939
|
writeCliMessage("");
|
|
3357
|
-
writeCliMessage(chalk.bold("
|
|
3358
|
-
writeCliMessage(
|
|
3359
|
-
writeCliMessage(
|
|
3360
|
-
writeCliMessage(
|
|
3940
|
+
writeCliMessage(chalk.bold(cliM("cli_initStatus")));
|
|
3941
|
+
writeCliMessage(cliM("cli_initName", { params: { name: chalk.cyan(project.name) } }));
|
|
3942
|
+
writeCliMessage(cliM("cli_initVersion", { params: { version: project.version } }));
|
|
3943
|
+
writeCliMessage(cliM("cli_initModules", { params: { modules: project.haiPackages.length > 0 ? project.haiPackages.map((p) => chalk.cyan(p)).join(", ") : chalk.gray(cliM("cli_none")) } }));
|
|
3361
3944
|
writeCliMessage("");
|
|
3362
3945
|
if (existing.length > 0) {
|
|
3363
|
-
writeCliMessage(chalk.bold("
|
|
3946
|
+
writeCliMessage(chalk.bold(cliM("cli_initExisting")));
|
|
3364
3947
|
for (const key of existing) {
|
|
3365
3948
|
writeCliMessage(chalk.green(` \u2714 config/_${key}.yml`));
|
|
3366
3949
|
}
|
|
3367
3950
|
}
|
|
3368
3951
|
if (missing.length === 0 && !options.force) {
|
|
3369
3952
|
writeCliMessage("");
|
|
3370
|
-
writeCliMessage(chalk.green("
|
|
3953
|
+
writeCliMessage(chalk.green(cliM("cli_initReady")));
|
|
3371
3954
|
return;
|
|
3372
3955
|
}
|
|
3373
3956
|
if (missing.length > 0) {
|
|
3374
3957
|
writeCliMessage("");
|
|
3375
|
-
writeCliMessage(chalk.bold("
|
|
3958
|
+
writeCliMessage(chalk.bold(cliM("cli_initMissing")));
|
|
3376
3959
|
for (const item of missing) {
|
|
3377
3960
|
writeCliMessage(chalk.yellow(` \u2718 config/_${item.configKey}.yml (${item.label})`));
|
|
3378
3961
|
}
|
|
3379
3962
|
writeCliMessage("");
|
|
3380
|
-
spinner.start("
|
|
3963
|
+
spinner.start(cliM("cli_initGenerating"));
|
|
3381
3964
|
let generated = 0;
|
|
3382
3965
|
for (const item of missing) {
|
|
3383
3966
|
const configPath = path2.join(cwd, "config", `_${item.configKey}.yml`);
|
|
@@ -3385,11 +3968,11 @@ async function initProject(options) {
|
|
|
3385
3968
|
await writeFile(configPath, content);
|
|
3386
3969
|
generated++;
|
|
3387
3970
|
}
|
|
3388
|
-
spinner.succeed(
|
|
3971
|
+
spinner.succeed(cliM("cli_initGenerated", { params: { count: generated } }));
|
|
3389
3972
|
}
|
|
3390
3973
|
if (options.force && existing.length > 0) {
|
|
3391
3974
|
writeCliMessage("");
|
|
3392
|
-
spinner.start("
|
|
3975
|
+
spinner.start(cliM("cli_initRegenerating"));
|
|
3393
3976
|
let regenerated = 0;
|
|
3394
3977
|
for (const pkg of project.haiPackages) {
|
|
3395
3978
|
const mapping = MODULE_CONFIG_MAP[pkg];
|
|
@@ -3400,25 +3983,25 @@ async function initProject(options) {
|
|
|
3400
3983
|
await writeFile(configPath, content);
|
|
3401
3984
|
regenerated++;
|
|
3402
3985
|
}
|
|
3403
|
-
spinner.succeed(
|
|
3986
|
+
spinner.succeed(cliM("cli_initRegenerated", { params: { count: regenerated } }));
|
|
3404
3987
|
}
|
|
3405
3988
|
const coreConfigPath = path2.join(cwd, "config", "_core.yml");
|
|
3406
3989
|
if (!await fileExists(coreConfigPath)) {
|
|
3407
|
-
spinner.start("
|
|
3990
|
+
spinner.start(cliM("cli_initCore"));
|
|
3408
3991
|
const content = generateConfigFile("core");
|
|
3409
3992
|
await writeFile(coreConfigPath, content);
|
|
3410
3993
|
spinner.succeed();
|
|
3411
3994
|
}
|
|
3412
3995
|
writeCliMessage("");
|
|
3413
|
-
writeCliMessage(chalk.green("
|
|
3996
|
+
writeCliMessage(chalk.green(cliM("cli_initSuccess")));
|
|
3414
3997
|
writeCliMessage("");
|
|
3415
|
-
writeCliMessage(chalk.gray("
|
|
3416
|
-
writeCliMessage(chalk.gray("
|
|
3417
|
-
writeCliMessage(chalk.gray("
|
|
3998
|
+
writeCliMessage(chalk.gray(cliM("cli_nextSteps")));
|
|
3999
|
+
writeCliMessage(chalk.gray(cliM("cli_initEditConfig")));
|
|
4000
|
+
writeCliMessage(chalk.gray(cliM("cli_initStartDev")));
|
|
3418
4001
|
writeCliMessage("");
|
|
3419
4002
|
} catch (error) {
|
|
3420
4003
|
spinner.fail();
|
|
3421
|
-
writeCliError(chalk.red("
|
|
4004
|
+
writeCliError(chalk.red(cliM("cli_initFailed")), { error });
|
|
3422
4005
|
throw error;
|
|
3423
4006
|
}
|
|
3424
4007
|
}
|
|
@@ -3475,12 +4058,13 @@ cli.command("init", cliM("cli_help_init")).option("-f, --force", cliM("cli_help_
|
|
|
3475
4058
|
process4.exit(1);
|
|
3476
4059
|
}
|
|
3477
4060
|
});
|
|
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) => {
|
|
4061
|
+
cli.command("generate [type] [name]", cliM("cli_help_generate")).option("-w, --workspace <name>", cliM("cli_help_workspace")).alias("g").option("-o, --output <path>", cliM("cli_help_output")).option("-f, --force", cliM("cli_help_force")).action(async (type, name, options) => {
|
|
3479
4062
|
try {
|
|
3480
4063
|
await generate({
|
|
3481
4064
|
type,
|
|
3482
4065
|
name: name ?? "",
|
|
3483
4066
|
output: options.output,
|
|
4067
|
+
...options.workspace ? { workspace: String(options.workspace) } : {},
|
|
3484
4068
|
force: options.force,
|
|
3485
4069
|
verbose: options.verbose,
|
|
3486
4070
|
cwd: options.cwd
|
|
@@ -3522,17 +4106,17 @@ cli.command("deploy [appDir]", cliM("cli_help_deploy")).option("--env <environme
|
|
|
3522
4106
|
process4.exitCode = 1;
|
|
3523
4107
|
}
|
|
3524
4108
|
});
|
|
3525
|
-
cli.command("g:page <name>", cliM("cli_help_generatePage")).action(async (name, options) => {
|
|
3526
|
-
await generate({ type: "page", name, cwd: options.cwd });
|
|
4109
|
+
cli.command("g:page <name>", cliM("cli_help_generatePage")).option("-w, --workspace <name>", cliM("cli_help_workspace")).action(async (name, options) => {
|
|
4110
|
+
await generate({ type: "page", name, cwd: options.cwd, ...options.workspace ? { workspace: String(options.workspace) } : {} });
|
|
3527
4111
|
});
|
|
3528
|
-
cli.command("g:component <name>", cliM("cli_help_generateComponent")).action(async (name, options) => {
|
|
3529
|
-
await generate({ type: "component", name, cwd: options.cwd });
|
|
4112
|
+
cli.command("g:component <name>", cliM("cli_help_generateComponent")).option("-w, --workspace <name>", cliM("cli_help_workspace")).action(async (name, options) => {
|
|
4113
|
+
await generate({ type: "component", name, cwd: options.cwd, ...options.workspace ? { workspace: String(options.workspace) } : {} });
|
|
3530
4114
|
});
|
|
3531
|
-
cli.command("g:api <name>", cliM("cli_help_generateApi")).action(async (name, options) => {
|
|
3532
|
-
await generate({ type: "api", name, cwd: options.cwd });
|
|
4115
|
+
cli.command("g:api <name>", cliM("cli_help_generateApi")).option("-w, --workspace <name>", cliM("cli_help_workspace")).action(async (name, options) => {
|
|
4116
|
+
await generate({ type: "api", name, cwd: options.cwd, ...options.workspace ? { workspace: String(options.workspace) } : {} });
|
|
3533
4117
|
});
|
|
3534
|
-
cli.command("g:model <name>", cliM("cli_help_generateModel")).action(async (name, options) => {
|
|
3535
|
-
await generate({ type: "model", name, cwd: options.cwd });
|
|
4118
|
+
cli.command("g:model <name>", cliM("cli_help_generateModel")).option("-w, --workspace <name>", cliM("cli_help_workspace")).action(async (name, options) => {
|
|
4119
|
+
await generate({ type: "model", name, cwd: options.cwd, ...options.workspace ? { workspace: String(options.workspace) } : {} });
|
|
3536
4120
|
});
|
|
3537
4121
|
cli.version(VERSION, "-V, --version");
|
|
3538
4122
|
cli.help();
|