@h-ai/cli 0.1.0-alpha5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (109) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +167 -0
  3. package/dist/chunk-2H7UOFLK.js +10 -0
  4. package/dist/chunk-2H7UOFLK.js.map +1 -0
  5. package/dist/chunk-LANIDJC7.js +659 -0
  6. package/dist/chunk-LANIDJC7.js.map +1 -0
  7. package/dist/chunk-YV7WPUI6.js +206 -0
  8. package/dist/chunk-YV7WPUI6.js.map +1 -0
  9. package/dist/config-templates-S5RI2MC6.js +5 -0
  10. package/dist/config-templates-S5RI2MC6.js.map +1 -0
  11. package/dist/index.d.ts +2 -0
  12. package/dist/index.js +1645 -0
  13. package/dist/index.js.map +1 -0
  14. package/dist/skill-templates-N5DU5FNX.js +5 -0
  15. package/dist/skill-templates-N5DU5FNX.js.map +1 -0
  16. package/dist/src-XJ6YXNAF.js +15030 -0
  17. package/dist/src-XJ6YXNAF.js.map +1 -0
  18. package/package.json +59 -0
  19. package/templates/README.md +126 -0
  20. package/templates/apps/admin/messages/en-US.json +33 -0
  21. package/templates/apps/admin/messages/zh-CN.json +33 -0
  22. package/templates/apps/admin/src/routes/+layout.svelte.hbs +30 -0
  23. package/templates/apps/admin/src/routes/+page.svelte.hbs +35 -0
  24. package/templates/apps/admin/src/routes/admin/+layout.svelte.hbs +44 -0
  25. package/templates/apps/admin/src/routes/admin/+page.svelte.hbs +45 -0
  26. package/templates/apps/admin/src/routes/admin/settings/+page.svelte.hbs +37 -0
  27. package/templates/apps/admin/src/routes/api/health/+server.ts +12 -0
  28. package/templates/apps/android-app/.env.hbs +3 -0
  29. package/templates/apps/android-app/capacitor.config.ts.hbs +12 -0
  30. package/templates/apps/android-app/messages/en-US.json +24 -0
  31. package/templates/apps/android-app/messages/zh-CN.json +24 -0
  32. package/templates/apps/android-app/src/lib/api.ts.hbs +30 -0
  33. package/templates/apps/android-app/src/lib/capacitor.ts +21 -0
  34. package/templates/apps/android-app/src/routes/+layout.svelte.hbs +42 -0
  35. package/templates/apps/android-app/src/routes/+layout.ts +7 -0
  36. package/templates/apps/android-app/src/routes/+page.svelte.hbs +27 -0
  37. package/templates/apps/api/src/routes/+layout.svelte +14 -0
  38. package/templates/apps/api/src/routes/+page.svelte +11 -0
  39. package/templates/apps/api/src/routes/api/v1/health/+server.ts +14 -0
  40. package/templates/apps/api/src/routes/api/v1/items/+server.ts +37 -0
  41. package/templates/apps/api/src/routes/api/v1/items/[id]/+server.ts +30 -0
  42. package/templates/apps/h5/messages/en-US.json +37 -0
  43. package/templates/apps/h5/messages/zh-CN.json +37 -0
  44. package/templates/apps/h5/src/routes/+layout.svelte.hbs +57 -0
  45. package/templates/apps/h5/src/routes/+page.svelte.hbs +27 -0
  46. package/templates/apps/h5/src/routes/discover/+page.svelte.hbs +27 -0
  47. package/templates/apps/h5/src/routes/profile/+page.svelte.hbs +27 -0
  48. package/templates/apps/website/messages/en-US.json +32 -0
  49. package/templates/apps/website/messages/zh-CN.json +32 -0
  50. package/templates/apps/website/src/routes/+layout.svelte.hbs +66 -0
  51. package/templates/apps/website/src/routes/+page.svelte.hbs +33 -0
  52. package/templates/apps/website/src/routes/about/+page.svelte.hbs +27 -0
  53. package/templates/apps/website/src/routes/api/health/+server.ts +12 -0
  54. package/templates/apps/website/src/routes/contact/+page.svelte.hbs +59 -0
  55. package/templates/apps/website/src/routes/services/+page.svelte.hbs +63 -0
  56. package/templates/base/e2e/helpers.ts +49 -0
  57. package/templates/base/package.json.hbs +67 -0
  58. package/templates/base/playwright.config.ts +32 -0
  59. package/templates/base/src/app.css +2 -0
  60. package/templates/base/src/app.d.ts.hbs +18 -0
  61. package/templates/base/src/app.html.hbs +16 -0
  62. package/templates/base/src/hooks.server.ts.hbs +123 -0
  63. package/templates/base/src/lib/server/init.ts.hbs +190 -0
  64. package/templates/base/svelte.config.js.hbs +30 -0
  65. package/templates/base/tsconfig.json +8 -0
  66. package/templates/base/vite.config.ts.hbs +27 -0
  67. package/templates/features/ai/routes/api/chat/+server.ts +26 -0
  68. package/templates/features/datapipe/routes/api/datapipe/pipeline/+server.ts +41 -0
  69. package/templates/features/iam/routes-admin/(auth)/+layout.svelte.hbs +21 -0
  70. package/templates/features/iam/routes-admin/(auth)/auth/login/+page.svelte.hbs +98 -0
  71. package/templates/features/iam/routes-admin/(auth)/auth/register/+page.svelte.hbs +124 -0
  72. package/templates/features/iam/routes-h5/auth/login/+page.svelte.hbs +98 -0
  73. package/templates/features/iam/routes-h5/auth/register/+page.svelte.hbs +114 -0
  74. package/templates/features/iam/routes-shared/api/auth/login/+server.ts +32 -0
  75. package/templates/features/iam/routes-shared/api/auth/logout/+server.ts +15 -0
  76. package/templates/features/iam/routes-shared/api/auth/me/+server.ts +12 -0
  77. package/templates/features/iam/routes-shared/api/auth/register/+server.ts +26 -0
  78. package/templates/features/payment/routes/api/v1/payment/create/+server.ts +30 -0
  79. package/templates/features/payment/routes/api/v1/payment/notify/[provider]/+server.ts +42 -0
  80. package/templates/features/payment/routes/api/v1/payment/query/[orderNo]/+server.ts +25 -0
  81. package/templates/features/storage/routes/api/upload/+server.ts +35 -0
  82. package/templates/features/vecdb/routes/api/vecdb/search/+server.ts +36 -0
  83. package/templates/i18n/project.inlang/settings.json +11 -0
  84. package/templates/skills/AGENTS.md +26 -0
  85. package/templates/skills/CLAUDE.md +12 -0
  86. package/templates/skills/copilot-instructions.md +61 -0
  87. package/templates/skills/hai-ai/SKILL.md +659 -0
  88. package/templates/skills/hai-api-client/SKILL.md +346 -0
  89. package/templates/skills/hai-app-create/SKILL.md +371 -0
  90. package/templates/skills/hai-app-review/SKILL.md +156 -0
  91. package/templates/skills/hai-app-tests/SKILL.md +468 -0
  92. package/templates/skills/hai-audit/SKILL.md +141 -0
  93. package/templates/skills/hai-build/SKILL.md +319 -0
  94. package/templates/skills/hai-cache/SKILL.md +184 -0
  95. package/templates/skills/hai-capacitor/SKILL.md +389 -0
  96. package/templates/skills/hai-core/SKILL.md +544 -0
  97. package/templates/skills/hai-crypto/SKILL.md +183 -0
  98. package/templates/skills/hai-datapipe/SKILL.md +214 -0
  99. package/templates/skills/hai-deploy/SKILL.md +210 -0
  100. package/templates/skills/hai-iam/SKILL.md +355 -0
  101. package/templates/skills/hai-kit/SKILL.md +319 -0
  102. package/templates/skills/hai-payment/SKILL.md +289 -0
  103. package/templates/skills/hai-reach/SKILL.md +325 -0
  104. package/templates/skills/hai-reldb/SKILL.md +459 -0
  105. package/templates/skills/hai-scheduler/SKILL.md +242 -0
  106. package/templates/skills/hai-storage/SKILL.md +230 -0
  107. package/templates/skills/hai-ui/SKILL.md +275 -0
  108. package/templates/skills/hai-vecdb/SKILL.md +273 -0
  109. package/templates/skills/opencode.json +11 -0
package/dist/index.js ADDED
@@ -0,0 +1,1645 @@
1
+ #!/usr/bin/env node
2
+ import { generateConfigFile } from './chunk-LANIDJC7.js';
3
+ import './chunk-YV7WPUI6.js';
4
+ import './chunk-2H7UOFLK.js';
5
+ import process3 from 'process';
6
+ import { core } from '@h-ai/core';
7
+ import { cac } from 'cac';
8
+ import chalk from 'chalk';
9
+ import path2, { resolve } from 'path';
10
+ import fse from 'fs-extra';
11
+ import ora from 'ora';
12
+ import prompts from 'prompts';
13
+ import Handlebars from 'handlebars';
14
+ import { execSync } from 'child_process';
15
+ import { fileURLToPath } from 'url';
16
+ import { existsSync, readFileSync } from 'fs';
17
+ import { parse } from 'yaml';
18
+
19
+ function toCamelCase(str) {
20
+ return str.replace(/[-_\s]+(.)?/g, (_, c) => c ? c.toUpperCase() : "").replace(/^./, (c) => c.toLowerCase());
21
+ }
22
+ function toPascalCase(str) {
23
+ const camel = toCamelCase(str);
24
+ return camel.charAt(0).toUpperCase() + camel.slice(1);
25
+ }
26
+ function toKebabCase(str) {
27
+ return str.replace(/([a-z])([A-Z])/g, "$1-$2").replace(/[\s_]+/g, "-").toLowerCase();
28
+ }
29
+ function toSnakeCase(str) {
30
+ return str.replace(/([a-z])([A-Z])/g, "$1_$2").replace(/[\s-]+/g, "_").toLowerCase();
31
+ }
32
+ function createTemplateContext(name, extra = {}) {
33
+ return {
34
+ camelCase: toCamelCase(name),
35
+ pascalCase: toPascalCase(name),
36
+ kebabCase: toKebabCase(name),
37
+ snakeCase: toSnakeCase(name),
38
+ ...extra
39
+ };
40
+ }
41
+ async function writeFile(filePath, content) {
42
+ await fse.ensureDir(path2.dirname(filePath));
43
+ await fse.writeFile(filePath, content, "utf-8");
44
+ }
45
+ async function fileExists(filePath) {
46
+ try {
47
+ await fse.access(filePath);
48
+ return true;
49
+ } catch {
50
+ return false;
51
+ }
52
+ }
53
+ async function detectPackageManager(cwd) {
54
+ if (await fileExists(path2.join(cwd, "pnpm-lock.yaml"))) {
55
+ return "pnpm";
56
+ }
57
+ if (await fileExists(path2.join(cwd, "yarn.lock"))) {
58
+ return "yarn";
59
+ }
60
+ return "npm";
61
+ }
62
+ function registerHelpers() {
63
+ Handlebars.registerHelper("if_eq", function(a, b, options) {
64
+ return a === b ? options.fn(this) : options.inverse(this);
65
+ });
66
+ Handlebars.registerHelper("date", () => {
67
+ return (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
68
+ });
69
+ Handlebars.registerHelper("year", () => {
70
+ return (/* @__PURE__ */ new Date()).getFullYear();
71
+ });
72
+ Handlebars.registerHelper("upper", (str) => {
73
+ return str.toUpperCase();
74
+ });
75
+ Handlebars.registerHelper("lower", (str) => {
76
+ return str.toLowerCase();
77
+ });
78
+ }
79
+ registerHelpers();
80
+ var FEATURE_ROUTE_DIRS = {
81
+ iam: ["routes-shared"],
82
+ storage: ["routes"],
83
+ ai: ["routes"],
84
+ payment: ["routes"],
85
+ vecdb: ["routes"],
86
+ datapipe: ["routes"],
87
+ reach: ["routes"],
88
+ scheduler: ["routes"],
89
+ audit: ["routes"]
90
+ };
91
+ var FEATURE_APP_ROUTE_DIRS = {
92
+ iam: ["admin", "h5"]
93
+ };
94
+ function getTemplatesRoot() {
95
+ const currentDir = path2.dirname(fileURLToPath(import.meta.url));
96
+ const distPath = path2.resolve(currentDir, "..", "templates");
97
+ if (fse.pathExistsSync(distPath)) {
98
+ return distPath;
99
+ }
100
+ return path2.resolve(currentDir, "..", "..", "templates");
101
+ }
102
+ async function copyStaticDir(src, dest, excludePaths) {
103
+ if (!await fse.pathExists(src)) {
104
+ return;
105
+ }
106
+ await fse.copy(src, dest, {
107
+ overwrite: true,
108
+ filter: (filePath) => {
109
+ if (filePath.endsWith(".hbs")) {
110
+ return false;
111
+ }
112
+ if (excludePaths && excludePaths.length > 0) {
113
+ const rel = path2.relative(src, filePath);
114
+ return !excludePaths.some((p) => rel === p || rel.startsWith(`${p}${path2.sep}`));
115
+ }
116
+ return true;
117
+ }
118
+ });
119
+ }
120
+ async function findHbsFiles(dir) {
121
+ if (!await fse.pathExists(dir)) {
122
+ return [];
123
+ }
124
+ const results = [];
125
+ async function walk(current) {
126
+ const entries = await fse.readdir(current, { withFileTypes: true });
127
+ for (const entry of entries) {
128
+ const fullPath = path2.join(current, entry.name);
129
+ if (entry.isDirectory()) {
130
+ await walk(fullPath);
131
+ } else if (entry.name.endsWith(".hbs")) {
132
+ results.push(path2.relative(dir, fullPath));
133
+ }
134
+ }
135
+ }
136
+ await walk(dir);
137
+ return results;
138
+ }
139
+ function buildTemplateContext(options) {
140
+ const featureMap = {};
141
+ for (const f of options.features) {
142
+ featureMap[f] = true;
143
+ }
144
+ if (featureMap.iam) {
145
+ featureMap.db = true;
146
+ featureMap.cache = true;
147
+ featureMap.crypto = true;
148
+ }
149
+ if (featureMap.audit || featureMap.scheduler || featureMap.payment) {
150
+ featureMap.db = true;
151
+ }
152
+ const isApi = options.appType === "api";
153
+ const isCapacitorApp = options.appType === "android-app";
154
+ return {
155
+ projectName: options.name,
156
+ appType: options.appType,
157
+ features: featureMap,
158
+ hasUi: !isApi,
159
+ hasI18n: !isApi,
160
+ isCapacitorApp,
161
+ defaultLocale: options.moduleConfigs?.core?.defaultLocale ?? "zh-CN",
162
+ packageManager: options.packageManager
163
+ };
164
+ }
165
+ async function generateFromTemplates(projectPath, context) {
166
+ const root = getTemplatesRoot();
167
+ await copyStaticDir(path2.join(root, "base"), projectPath);
168
+ const appDir = path2.join(root, "apps", context.appType);
169
+ if (await fse.pathExists(appDir)) {
170
+ const appExcludes = context.hasI18n ? [] : ["messages"];
171
+ await copyStaticDir(appDir, projectPath, appExcludes);
172
+ }
173
+ for (const featureId of Object.keys(context.features)) {
174
+ if (!context.features[featureId]) {
175
+ continue;
176
+ }
177
+ if (!/^[a-z0-9-]+$/.test(featureId)) {
178
+ continue;
179
+ }
180
+ const featureDir = path2.join(root, "features", featureId);
181
+ if (!await fse.pathExists(featureDir)) {
182
+ continue;
183
+ }
184
+ const sharedDirs = FEATURE_ROUTE_DIRS[featureId] || [];
185
+ for (const dirName of sharedDirs) {
186
+ const routesSrc = path2.join(featureDir, dirName);
187
+ if (await fse.pathExists(routesSrc)) {
188
+ await copyStaticDir(routesSrc, path2.join(projectPath, "src", "routes"));
189
+ }
190
+ }
191
+ const appSpecificTypes = FEATURE_APP_ROUTE_DIRS[featureId] || [];
192
+ if (appSpecificTypes.includes(context.appType)) {
193
+ const appRouteSrc = path2.join(featureDir, `routes-${context.appType}`);
194
+ if (await fse.pathExists(appRouteSrc)) {
195
+ await copyStaticDir(appRouteSrc, path2.join(projectPath, "src", "routes"));
196
+ }
197
+ }
198
+ }
199
+ await renderDynamicFiles(root, projectPath, context);
200
+ if (context.hasI18n) {
201
+ const i18nDir = path2.join(root, "i18n");
202
+ if (await fse.pathExists(i18nDir)) {
203
+ await fse.copy(i18nDir, projectPath, { overwrite: true });
204
+ }
205
+ }
206
+ await fse.ensureDir(path2.join(projectPath, "static"));
207
+ }
208
+ async function renderDynamicFiles(templatesRoot, projectPath, context) {
209
+ const rootDirs = [
210
+ path2.join(templatesRoot, "base"),
211
+ path2.join(templatesRoot, "apps", context.appType)
212
+ ];
213
+ for (const dir of rootDirs) {
214
+ await renderHbsInDir(dir, projectPath, context);
215
+ }
216
+ const routesDest = path2.join(projectPath, "src", "routes");
217
+ for (const featureId of Object.keys(context.features)) {
218
+ if (!context.features[featureId]) {
219
+ continue;
220
+ }
221
+ const featureDir = path2.join(templatesRoot, "features", featureId);
222
+ if (!await fse.pathExists(featureDir)) {
223
+ continue;
224
+ }
225
+ const sharedDirs = FEATURE_ROUTE_DIRS[featureId] || [];
226
+ for (const dirName of sharedDirs) {
227
+ await renderHbsInDir(path2.join(featureDir, dirName), routesDest, context);
228
+ }
229
+ const appSpecificTypes = FEATURE_APP_ROUTE_DIRS[featureId] || [];
230
+ if (appSpecificTypes.includes(context.appType)) {
231
+ await renderHbsInDir(
232
+ path2.join(featureDir, `routes-${context.appType}`),
233
+ routesDest,
234
+ context
235
+ );
236
+ }
237
+ }
238
+ }
239
+ async function renderHbsInDir(dir, destRoot, context) {
240
+ const hbsFiles = await findHbsFiles(dir);
241
+ for (const relPath of hbsFiles) {
242
+ const srcPath = path2.join(dir, relPath);
243
+ const outRelPath = relPath.replace(/\.hbs$/, "");
244
+ const destPath = path2.join(destRoot, outRelPath);
245
+ const template = await fse.readFile(srcPath, "utf-8");
246
+ const compiled = Handlebars.compile(template, { noEscape: true });
247
+ const rendered = compiled(context);
248
+ await fse.ensureDir(path2.dirname(destPath));
249
+ await fse.writeFile(destPath, rendered, "utf-8");
250
+ }
251
+ }
252
+
253
+ // src/commands/create.ts
254
+ var APP_TYPES = {
255
+ "admin": {
256
+ name: "\u7BA1\u7406\u540E\u53F0",
257
+ description: "\u4F01\u4E1A\u7EA7\u7BA1\u7406\u7CFB\u7EDF\uFF0C\u542B IAM\u3001\u6570\u636E\u5E93\u3001\u5B8C\u6574 UI",
258
+ defaultFeatures: ["iam", "db", "cache", "crypto"]
259
+ },
260
+ "website": {
261
+ name: "\u4F01\u4E1A\u5B98\u7F51",
262
+ description: "SSR/SSG \u4F01\u4E1A\u5B98\u7F51\uFF0CSEO \u53CB\u597D\u3001\u54CD\u5E94\u5F0F\u5E03\u5C40",
263
+ defaultFeatures: ["db", "cache"]
264
+ },
265
+ "h5": {
266
+ name: "H5 \u5E94\u7528",
267
+ description: "\u79FB\u52A8\u7AEF H5 \u5E94\u7528\uFF0C\u89E6\u6478\u4F18\u5316\u3001PWA \u652F\u6301",
268
+ defaultFeatures: ["db", "cache"]
269
+ },
270
+ "api": {
271
+ name: "API \u670D\u52A1",
272
+ description: "\u7EAF API \u540E\u7AEF\u670D\u52A1\uFF0CRESTful \u8DEF\u7531\u3001\u65E0 UI",
273
+ defaultFeatures: ["db", "cache"]
274
+ },
275
+ "android-app": {
276
+ name: "Android \u5E94\u7528",
277
+ description: "Android \u539F\u751F\u5E94\u7528\uFF08Capacitor + SvelteKit SPA\uFF09\uFF0C\u53EF\u6253\u5305\u4E3A APK",
278
+ defaultFeatures: ["iam", "db", "cache", "crypto", "api-client", "capacitor"]
279
+ }
280
+ };
281
+ var FEATURES = {
282
+ "iam": {
283
+ id: "iam",
284
+ name: "\u8EAB\u4EFD\u4E0E\u8BBF\u95EE\u7BA1\u7406",
285
+ description: "Session/JWT \u4F1A\u8BDD\u7BA1\u7406\u3001RBAC \u6743\u9650\u63A7\u5236",
286
+ packages: ["@h-ai/iam"],
287
+ dependencies: ["crypto", "db", "cache"]
288
+ },
289
+ "db": {
290
+ id: "db",
291
+ name: "\u6570\u636E\u5E93",
292
+ description: "\u591A\u6570\u636E\u5E93\u652F\u6301 (SQLite/PostgreSQL/MySQL)",
293
+ packages: ["@h-ai/reldb"]
294
+ },
295
+ "cache": {
296
+ id: "cache",
297
+ name: "\u7F13\u5B58",
298
+ description: "Redis / \u5185\u5B58\u7F13\u5B58",
299
+ packages: ["@h-ai/cache"]
300
+ },
301
+ "ai": {
302
+ id: "ai",
303
+ name: "AI \u96C6\u6210",
304
+ description: "LLM \u9002\u914D\u5668\u3001MCP \u534F\u8BAE\u3001\u6280\u80FD\u7CFB\u7EDF\u3001\u6D41\u5F0F\u54CD\u5E94",
305
+ packages: ["@h-ai/ai"]
306
+ },
307
+ "storage": {
308
+ id: "storage",
309
+ name: "\u6587\u4EF6\u5B58\u50A8",
310
+ description: "\u672C\u5730\u5B58\u50A8\u3001S3/OSS/COS \u4E91\u5B58\u50A8",
311
+ packages: ["@h-ai/storage"]
312
+ },
313
+ "crypto": {
314
+ id: "crypto",
315
+ name: "\u52A0\u5BC6\u6A21\u5757",
316
+ description: "\u56FD\u5BC6 SM2/SM3/SM4\u3001Argon2 \u5BC6\u7801\u54C8\u5E0C",
317
+ packages: ["@h-ai/crypto"]
318
+ },
319
+ "audit": {
320
+ id: "audit",
321
+ name: "\u5BA1\u8BA1\u65E5\u5FD7",
322
+ description: "\u64CD\u4F5C\u5BA1\u8BA1\u3001\u5B89\u5168\u8FFD\u8E2A",
323
+ packages: ["@h-ai/audit"],
324
+ dependencies: ["db"]
325
+ },
326
+ "reach": {
327
+ id: "reach",
328
+ name: "\u89E6\u8FBE\u670D\u52A1",
329
+ description: "\u90AE\u4EF6\u3001\u77ED\u4FE1\u3001\u5FAE\u4FE1\u7B49\u6D88\u606F\u89E6\u8FBE",
330
+ packages: ["@h-ai/reach"]
331
+ },
332
+ "payment": {
333
+ id: "payment",
334
+ name: "\u7EDF\u4E00\u652F\u4ED8",
335
+ description: "\u5FAE\u4FE1\u652F\u4ED8\u3001\u652F\u4ED8\u5B9D\u3001Stripe \u7EDF\u4E00\u63A5\u5165",
336
+ packages: ["@h-ai/payment"],
337
+ dependencies: ["db"]
338
+ },
339
+ "vecdb": {
340
+ id: "vecdb",
341
+ name: "\u5411\u91CF\u6570\u636E\u5E93",
342
+ description: "\u5411\u91CF\u68C0\u7D22\u4E0E\u76F8\u4F3C\u5EA6\u641C\u7D22\uFF08LanceDB / pgvector / Qdrant\uFF09",
343
+ packages: ["@h-ai/vecdb"]
344
+ },
345
+ "datapipe": {
346
+ id: "datapipe",
347
+ name: "\u6570\u636E\u5904\u7406\u7BA1\u9053",
348
+ description: "\u6587\u672C\u6E05\u6D17\u3001\u5206\u5757\u4E0E\u53EF\u7EC4\u5408\u5904\u7406\u6D41\u6C34\u7EBF",
349
+ packages: ["@h-ai/datapipe"]
350
+ },
351
+ "scheduler": {
352
+ id: "scheduler",
353
+ name: "\u4EFB\u52A1\u8C03\u5EA6",
354
+ description: "Cron \u4EFB\u52A1\u6CE8\u518C\u3001\u6267\u884C\u4E0E\u65E5\u5FD7\u8FFD\u8E2A",
355
+ packages: ["@h-ai/scheduler"],
356
+ dependencies: ["db"]
357
+ },
358
+ "deploy": {
359
+ id: "deploy",
360
+ name: "\u81EA\u52A8\u5316\u90E8\u7F72",
361
+ description: "\u7EDF\u4E00\u90E8\u7F72\u7F16\u6392\u4E0E\u591A\u5E73\u53F0\u53D1\u5E03\u652F\u6301",
362
+ packages: ["@h-ai/deploy"]
363
+ },
364
+ "api-client": {
365
+ id: "api-client",
366
+ name: "HTTP \u5BA2\u6237\u7AEF",
367
+ description: "\u7EDF\u4E00 API \u8C03\u7528\u3001\u9519\u8BEF\u5904\u7406\u4E0E\u6D41\u5F0F\u8BF7\u6C42\u652F\u6301",
368
+ packages: ["@h-ai/api-client"]
369
+ },
370
+ "capacitor": {
371
+ id: "capacitor",
372
+ name: "\u79FB\u52A8\u7AEF\u80FD\u529B",
373
+ description: "Capacitor \u539F\u751F\u6865\u63A5\u80FD\u529B\uFF08\u504F\u79FB\u52A8\u5E94\u7528\uFF09",
374
+ packages: ["@h-ai/capacitor"]
375
+ }
376
+ };
377
+ var SELECTABLE_FEATURES = [
378
+ "iam",
379
+ "db",
380
+ "cache",
381
+ "ai",
382
+ "storage",
383
+ "crypto",
384
+ "audit",
385
+ "reach",
386
+ "payment",
387
+ "vecdb",
388
+ "datapipe",
389
+ "scheduler",
390
+ "deploy",
391
+ "api-client",
392
+ "capacitor"
393
+ ];
394
+ var PROJECT_TEMPLATES = {
395
+ minimal: {
396
+ name: "minimal",
397
+ description: "\u6700\u5C0F\u6A21\u677F \u2014 \u4EC5\u6838\u5FC3\u529F\u80FD",
398
+ features: []
399
+ },
400
+ default: {
401
+ name: "default",
402
+ description: "\u6807\u51C6\u6A21\u677F \u2014 IAM + \u6570\u636E\u5E93 + \u7F13\u5B58",
403
+ features: ["iam", "db", "cache", "crypto"]
404
+ },
405
+ full: {
406
+ name: "full",
407
+ description: "\u5B8C\u6574\u6A21\u677F \u2014 \u6240\u6709\u529F\u80FD",
408
+ features: [
409
+ "iam",
410
+ "db",
411
+ "cache",
412
+ "crypto",
413
+ "ai",
414
+ "storage",
415
+ "audit",
416
+ "reach",
417
+ "payment",
418
+ "vecdb",
419
+ "datapipe",
420
+ "scheduler",
421
+ "deploy",
422
+ "api-client",
423
+ "capacitor"
424
+ ]
425
+ },
426
+ custom: {
427
+ name: "custom",
428
+ description: "\u81EA\u5B9A\u4E49 \u2014 \u9009\u62E9\u9700\u8981\u7684\u529F\u80FD",
429
+ features: []
430
+ }
431
+ };
432
+ async function createProject(options) {
433
+ const spinner = ora();
434
+ try {
435
+ const resolvedOptions = await resolveOptions(options);
436
+ if (!/^[a-z0-9-]+$/.test(resolvedOptions.name)) {
437
+ core.logger.error(chalk.red(`\u9879\u76EE\u540D\u79F0\u4E0D\u5408\u6CD5: "${resolvedOptions.name}"\uFF0C\u53EA\u80FD\u5305\u542B\u5C0F\u5199\u5B57\u6BCD\u3001\u6570\u5B57\u548C\u8FDE\u5B57\u7B26`));
438
+ return;
439
+ }
440
+ const projectPath = path2.resolve(resolvedOptions.cwd ?? ".", resolvedOptions.name);
441
+ if (await fileExists(projectPath)) {
442
+ const { overwrite } = await prompts({
443
+ type: "confirm",
444
+ name: "overwrite",
445
+ message: `\u76EE\u5F55 ${resolvedOptions.name} \u5DF2\u5B58\u5728\uFF0C\u662F\u5426\u8986\u76D6\uFF1F`,
446
+ initial: false
447
+ });
448
+ if (!overwrite) {
449
+ core.logger.info(chalk.yellow("\u5DF2\u53D6\u6D88"));
450
+ return;
451
+ }
452
+ await fse.remove(projectPath);
453
+ }
454
+ spinner.start("\u521B\u5EFA\u9879\u76EE\u76EE\u5F55...");
455
+ await fse.ensureDir(projectPath);
456
+ spinner.succeed();
457
+ spinner.start("\u751F\u6210\u9879\u76EE\u6587\u4EF6...");
458
+ const context = buildTemplateContext({
459
+ name: resolvedOptions.name,
460
+ appType: resolvedOptions.appType,
461
+ features: resolvedOptions.features,
462
+ moduleConfigs: resolvedOptions.moduleConfigs,
463
+ packageManager: resolvedOptions.packageManager
464
+ });
465
+ await generateFromTemplates(projectPath, context);
466
+ const { generateConfigFile: generateConfigFile2 } = await import('./config-templates-S5RI2MC6.js');
467
+ const configs = resolvedOptions.moduleConfigs;
468
+ for (const featureId of resolvedOptions.features) {
469
+ const configKey = getFeatureConfigKey(featureId);
470
+ if (configKey) {
471
+ const content = generateConfigFile2(configKey, configs);
472
+ await writeFile(path2.join(projectPath, "config", `_${configKey}.yml`), content);
473
+ }
474
+ }
475
+ await writeFile(
476
+ path2.join(projectPath, "config", "_core.yml"),
477
+ generateConfigFile2("core", configs)
478
+ );
479
+ const { generateEnvExample } = await import('./config-templates-S5RI2MC6.js');
480
+ await writeFile(
481
+ path2.join(projectPath, ".env.example"),
482
+ generateEnvExample(resolvedOptions.features, configs)
483
+ );
484
+ const { generateSkillFiles } = await import('./skill-templates-N5DU5FNX.js');
485
+ await generateSkillFiles(projectPath, resolvedOptions.features, resolvedOptions.appType);
486
+ const appTypeLabel = APP_TYPES[resolvedOptions.appType].name;
487
+ await writeFile(
488
+ path2.join(projectPath, "README.md"),
489
+ generateReadme(resolvedOptions.name, appTypeLabel, resolvedOptions.packageManager)
490
+ );
491
+ spinner.succeed();
492
+ if (resolvedOptions.git) {
493
+ spinner.start("\u521D\u59CB\u5316 Git \u4ED3\u5E93...");
494
+ execSync("git init", { cwd: projectPath, stdio: "ignore" });
495
+ spinner.succeed();
496
+ }
497
+ if (resolvedOptions.install) {
498
+ spinner.start(`\u5B89\u88C5\u4F9D\u8D56 (${resolvedOptions.packageManager})...`);
499
+ const installCmd = getInstallCommand(resolvedOptions.packageManager);
500
+ execSync(installCmd, { cwd: projectPath, stdio: "ignore" });
501
+ spinner.succeed();
502
+ }
503
+ printCompletionMessage(resolvedOptions);
504
+ } catch (error) {
505
+ spinner.fail();
506
+ core.logger.error(chalk.red("\u521B\u5EFA\u9879\u76EE\u5931\u8D25:"), { error });
507
+ throw error;
508
+ }
509
+ }
510
+ async function resolveOptions(options) {
511
+ core.logger.info("", {});
512
+ core.logger.info(chalk.bold.cyan(" \u{1F680} hai Agent Framework"));
513
+ core.logger.info(chalk.gray(" AI-Native \xB7 Configuration-Driven \xB7 Security-First"));
514
+ core.logger.info("", {});
515
+ const onCancel = () => {
516
+ core.logger.info(chalk.red("\n\u5DF2\u53D6\u6D88"));
517
+ process3.exit(1);
518
+ };
519
+ let projectName = options.name;
520
+ if (!projectName) {
521
+ const { name } = await prompts({
522
+ type: "text",
523
+ name: "name",
524
+ message: "\u9879\u76EE\u540D\u79F0:",
525
+ initial: "my-app",
526
+ validate: (value) => {
527
+ if (!value.trim())
528
+ return "\u9879\u76EE\u540D\u79F0\u4E0D\u80FD\u4E3A\u7A7A";
529
+ if (!/^[a-z0-9-]+$/.test(value))
530
+ return "\u9879\u76EE\u540D\u79F0\u53EA\u80FD\u5305\u542B\u5C0F\u5199\u5B57\u6BCD\u3001\u6570\u5B57\u548C\u8FDE\u5B57\u7B26";
531
+ return true;
532
+ }
533
+ }, { onCancel });
534
+ projectName = name;
535
+ }
536
+ let resolvedAppType = options.appType;
537
+ if (!resolvedAppType) {
538
+ const { appType } = await prompts({
539
+ type: "select",
540
+ name: "appType",
541
+ message: "\u5E94\u7528\u7C7B\u578B:",
542
+ choices: Object.entries(APP_TYPES).map(([key, t]) => ({
543
+ title: `${chalk.bold(t.name.padEnd(10))} ${chalk.gray(t.description)}`,
544
+ value: key
545
+ })),
546
+ initial: 0
547
+ }, { onCancel });
548
+ resolvedAppType = appType;
549
+ }
550
+ const selectedAppType = resolvedAppType || "admin";
551
+ let selectedTemplate = options.template;
552
+ if (!selectedTemplate) {
553
+ const { template } = await prompts({
554
+ type: "select",
555
+ name: "template",
556
+ message: "\u9009\u62E9\u6A21\u677F:",
557
+ choices: Object.values(PROJECT_TEMPLATES).map((t) => ({
558
+ title: `${chalk.bold(t.name.padEnd(10))} ${chalk.gray(t.description)}`,
559
+ value: t.name
560
+ })),
561
+ initial: 1
562
+ }, { onCancel });
563
+ selectedTemplate = template;
564
+ }
565
+ let selectedFeatures = [];
566
+ const appTypeDefaults = APP_TYPES[selectedAppType].defaultFeatures;
567
+ if (selectedTemplate === "custom" && !options.features) {
568
+ const featureChoices = SELECTABLE_FEATURES.map((id) => ({
569
+ title: `${chalk.bold(FEATURES[id].name.padEnd(10))} ${chalk.gray(FEATURES[id].description)}`,
570
+ value: id,
571
+ selected: appTypeDefaults.includes(id)
572
+ }));
573
+ const { features } = await prompts({
574
+ type: "multiselect",
575
+ name: "features",
576
+ message: "\u9009\u62E9\u529F\u80FD (\u7A7A\u683C\u9009\u62E9\uFF0C\u56DE\u8F66\u786E\u8BA4):",
577
+ choices: featureChoices,
578
+ hint: "- \u7A7A\u683C\u9009\u62E9\uFF0C\u56DE\u8F66\u786E\u8BA4",
579
+ instructions: false
580
+ }, { onCancel });
581
+ selectedFeatures = features || [];
582
+ selectedFeatures = resolveFeatureDependencies(selectedFeatures);
583
+ } else if (selectedTemplate === "custom" && options.features) {
584
+ selectedFeatures = resolveFeatureDependencies(options.features);
585
+ } else {
586
+ const templateFeatures = PROJECT_TEMPLATES[selectedTemplate]?.features || [];
587
+ const merged = /* @__PURE__ */ new Set([...templateFeatures, ...appTypeDefaults]);
588
+ selectedFeatures = resolveFeatureDependencies(Array.from(merged));
589
+ }
590
+ const moduleConfigs = options.moduleConfigs ?? await promptModuleConfigs(selectedFeatures, projectName);
591
+ let addExamples = options.examples;
592
+ if (addExamples === void 0) {
593
+ const { examples } = await prompts({
594
+ type: "confirm",
595
+ name: "examples",
596
+ message: "\u662F\u5426\u6DFB\u52A0\u793A\u4F8B\u4EE3\u7801?",
597
+ initial: true
598
+ }, { onCancel });
599
+ addExamples = examples;
600
+ }
601
+ const detected = await detectPackageManager(options.cwd ?? ".");
602
+ let packageManager = options.packageManager;
603
+ if (!packageManager) {
604
+ const { pm } = await prompts({
605
+ type: "select",
606
+ name: "pm",
607
+ message: "\u5305\u7BA1\u7406\u5668:",
608
+ choices: [
609
+ { title: "pnpm (\u63A8\u8350)", value: "pnpm" },
610
+ { title: "npm", value: "npm" },
611
+ { title: "yarn", value: "yarn" }
612
+ ],
613
+ initial: detected === "pnpm" ? 0 : detected === "npm" ? 1 : 2
614
+ }, { onCancel });
615
+ packageManager = pm;
616
+ }
617
+ let install = options.install;
618
+ if (install === void 0) {
619
+ const { doInstall } = await prompts({
620
+ type: "confirm",
621
+ name: "doInstall",
622
+ message: "\u662F\u5426\u5B89\u88C5\u4F9D\u8D56?",
623
+ initial: true
624
+ }, { onCancel });
625
+ install = doInstall;
626
+ }
627
+ let git = options.git;
628
+ if (git === void 0) {
629
+ const { initGit } = await prompts({
630
+ type: "confirm",
631
+ name: "initGit",
632
+ message: "\u662F\u5426\u521D\u59CB\u5316 Git?",
633
+ initial: true
634
+ }, { onCancel });
635
+ git = initGit;
636
+ }
637
+ return {
638
+ name: projectName,
639
+ appType: selectedAppType,
640
+ template: selectedTemplate ?? "default",
641
+ features: selectedFeatures,
642
+ moduleConfigs,
643
+ examples: addExamples ?? true,
644
+ install: install ?? true,
645
+ packageManager: packageManager || "pnpm",
646
+ git: git ?? true,
647
+ verbose: options.verbose ?? false,
648
+ cwd: options.cwd ?? "."
649
+ };
650
+ }
651
+ function resolveFeatureDependencies(features) {
652
+ const result = new Set(features);
653
+ for (const featureId of features) {
654
+ const feature = FEATURES[featureId];
655
+ if (feature?.dependencies) {
656
+ for (const dep of feature.dependencies) {
657
+ result.add(dep);
658
+ }
659
+ }
660
+ }
661
+ return Array.from(result);
662
+ }
663
+ async function promptModuleConfigs(features, projectName) {
664
+ const configs = {};
665
+ const onCancel = () => {
666
+ core.logger.info(chalk.red("\n\u5DF2\u53D6\u6D88"));
667
+ process3.exit(1);
668
+ };
669
+ core.logger.info("");
670
+ core.logger.info(chalk.bold(" \u2699 \u6A21\u5757\u914D\u7F6E"));
671
+ core.logger.info(chalk.gray(" \u8BBE\u7F6E\u5173\u952E\u914D\u7F6E\u9879\uFF08\u56DE\u8F66\u4F7F\u7528\u9ED8\u8BA4\u503C\uFF09"));
672
+ core.logger.info("");
673
+ configs.core = await promptCoreConfig(projectName, onCancel);
674
+ if (features.includes("db")) {
675
+ configs.db = await promptDbConfig(projectName, onCancel);
676
+ }
677
+ if (features.includes("cache")) {
678
+ configs.cache = await promptCacheConfig(onCancel);
679
+ }
680
+ if (features.includes("iam")) {
681
+ configs.iam = await promptIamConfig(onCancel);
682
+ }
683
+ if (features.includes("storage")) {
684
+ configs.storage = await promptStorageConfig(onCancel);
685
+ }
686
+ if (features.includes("ai")) {
687
+ configs.ai = await promptAiConfig(onCancel);
688
+ }
689
+ return configs;
690
+ }
691
+ async function promptCoreConfig(projectName, onCancel) {
692
+ core.logger.info(chalk.cyan(" [core] \u5E94\u7528\u57FA\u7840\u914D\u7F6E"));
693
+ const { defaultLocale } = await prompts({
694
+ type: "select",
695
+ name: "defaultLocale",
696
+ message: "\u9ED8\u8BA4\u8BED\u8A00:",
697
+ choices: [
698
+ { title: "\u4E2D\u6587 (zh-CN)", value: "zh-CN" },
699
+ { title: "English (en-US)", value: "en-US" }
700
+ ],
701
+ initial: 0
702
+ }, { onCancel });
703
+ return { name: projectName, defaultLocale };
704
+ }
705
+ async function promptDbConfig(projectName, onCancel) {
706
+ core.logger.info(chalk.cyan(" [db] \u6570\u636E\u5E93\u914D\u7F6E"));
707
+ const { type } = await prompts({
708
+ type: "select",
709
+ name: "type",
710
+ message: "\u6570\u636E\u5E93\u7C7B\u578B:",
711
+ choices: [
712
+ { title: "SQLite (\u96F6\u914D\u7F6E\uFF0C\u9002\u5408\u5F00\u53D1)", value: "sqlite" },
713
+ { title: "PostgreSQL", value: "postgresql" },
714
+ { title: "MySQL", value: "mysql" }
715
+ ],
716
+ initial: 0
717
+ }, { onCancel });
718
+ if (type === "sqlite") {
719
+ const { database } = await prompts({
720
+ type: "text",
721
+ name: "database",
722
+ message: "SQLite \u6570\u636E\u5E93\u8DEF\u5F84:",
723
+ initial: "./data/app.db"
724
+ }, { onCancel });
725
+ return { type, database };
726
+ }
727
+ const answers = await prompts([
728
+ { type: "text", name: "host", message: `${type === "postgresql" ? "PostgreSQL" : "MySQL"} \u4E3B\u673A:`, initial: "localhost" },
729
+ { type: "number", name: "port", message: "\u7AEF\u53E3:", initial: type === "postgresql" ? 5432 : 3306 },
730
+ { type: "text", name: "database", message: "\u6570\u636E\u5E93\u540D\u79F0:", initial: projectName }
731
+ ], { onCancel });
732
+ return { type, host: answers.host, port: answers.port, database: answers.database };
733
+ }
734
+ async function promptCacheConfig(onCancel) {
735
+ core.logger.info(chalk.cyan(" [cache] \u7F13\u5B58\u914D\u7F6E"));
736
+ const { type } = await prompts({
737
+ type: "select",
738
+ name: "type",
739
+ message: "\u7F13\u5B58\u7C7B\u578B:",
740
+ choices: [
741
+ { title: "\u5185\u5B58\u7F13\u5B58 (\u96F6\u914D\u7F6E\uFF0C\u9002\u5408\u5F00\u53D1)", value: "memory" },
742
+ { title: "Redis", value: "redis" }
743
+ ],
744
+ initial: 0
745
+ }, { onCancel });
746
+ if (type === "memory")
747
+ return { type };
748
+ const answers = await prompts([
749
+ { type: "text", name: "host", message: "Redis \u4E3B\u673A:", initial: "localhost" },
750
+ { type: "number", name: "port", message: "Redis \u7AEF\u53E3:", initial: 6379 }
751
+ ], { onCancel });
752
+ return { type, host: answers.host, port: answers.port };
753
+ }
754
+ async function promptIamConfig(onCancel) {
755
+ core.logger.info(chalk.cyan(" [iam] \u8BA4\u8BC1\u6388\u6743\u914D\u7F6E"));
756
+ const answers = await prompts([
757
+ { type: "confirm", name: "loginPassword", message: "\u542F\u7528\u5BC6\u7801\u767B\u5F55?", initial: true },
758
+ { type: "confirm", name: "loginOtp", message: "\u542F\u7528 OTP \u9A8C\u8BC1\u7801\u767B\u5F55?", initial: false }
759
+ ], { onCancel });
760
+ return { loginPassword: answers.loginPassword, loginOtp: answers.loginOtp };
761
+ }
762
+ async function promptStorageConfig(onCancel) {
763
+ core.logger.info(chalk.cyan(" [storage] \u6587\u4EF6\u5B58\u50A8\u914D\u7F6E"));
764
+ const { type } = await prompts({
765
+ type: "select",
766
+ name: "type",
767
+ message: "\u5B58\u50A8\u7C7B\u578B:",
768
+ choices: [
769
+ { title: "\u672C\u5730\u5B58\u50A8 (\u96F6\u914D\u7F6E\uFF0C\u9002\u5408\u5F00\u53D1)", value: "local" },
770
+ { title: "S3 / MinIO / OSS", value: "s3" }
771
+ ],
772
+ initial: 0
773
+ }, { onCancel });
774
+ if (type === "local") {
775
+ const { localPath } = await prompts({
776
+ type: "text",
777
+ name: "localPath",
778
+ message: "\u5B58\u50A8\u8DEF\u5F84:",
779
+ initial: "./data/uploads"
780
+ }, { onCancel });
781
+ return { type, localPath };
782
+ }
783
+ return { type };
784
+ }
785
+ async function promptAiConfig(onCancel) {
786
+ core.logger.info(chalk.cyan(" [ai] AI \u96C6\u6210\u914D\u7F6E"));
787
+ const { defaultProvider } = await prompts({
788
+ type: "select",
789
+ name: "defaultProvider",
790
+ message: "AI Provider:",
791
+ choices: [
792
+ { title: "OpenAI", value: "openai" },
793
+ { title: "Anthropic (Claude)", value: "anthropic" },
794
+ { title: "\u5176\u4ED6 (OpenAI \u517C\u5BB9)", value: "custom" }
795
+ ],
796
+ initial: 0
797
+ }, { onCancel });
798
+ const defaultModel = defaultProvider === "openai" ? "gpt-4o-mini" : defaultProvider === "anthropic" ? "claude-3-sonnet" : "";
799
+ const { model } = await prompts({
800
+ type: "text",
801
+ name: "model",
802
+ message: "\u9ED8\u8BA4\u6A21\u578B:",
803
+ initial: defaultModel
804
+ }, { onCancel });
805
+ return { defaultProvider, model };
806
+ }
807
+ function getFeatureConfigKey(featureId) {
808
+ const map = {
809
+ db: "db",
810
+ cache: "cache",
811
+ iam: "iam",
812
+ storage: "storage",
813
+ ai: "ai",
814
+ deploy: "deploy",
815
+ vecdb: "vecdb",
816
+ reach: "reach",
817
+ scheduler: "scheduler",
818
+ audit: "audit",
819
+ payment: "payment"
820
+ };
821
+ return map[featureId] ?? null;
822
+ }
823
+ function getInstallCommand(pm) {
824
+ switch (pm) {
825
+ case "pnpm":
826
+ return "pnpm install";
827
+ case "yarn":
828
+ return "yarn";
829
+ default:
830
+ return "npm install";
831
+ }
832
+ }
833
+ function generateReadme(name, appTypeLabel, pm) {
834
+ return `# ${name}
835
+
836
+ \u57FA\u4E8E hai Agent Framework \u6784\u5EFA\u7684${appTypeLabel}\u5E94\u7528\u3002
837
+
838
+ ## \u5F00\u53D1
839
+
840
+ \`\`\`bash
841
+ ${pm} install
842
+ ${pm} dev
843
+ \`\`\`
844
+
845
+ ## \u6784\u5EFA
846
+
847
+ \`\`\`bash
848
+ ${pm} build
849
+ ${pm} preview
850
+ \`\`\`
851
+
852
+ ## \u6587\u6863
853
+
854
+ - [hai Agent Framework](https://github.com/hai-series/hai-framework)
855
+ - [SvelteKit](https://kit.svelte.dev/)
856
+ - [Svelte 5](https://svelte.dev/)
857
+ `;
858
+ }
859
+ function printCompletionMessage(options) {
860
+ core.logger.info("", {});
861
+ core.logger.info(chalk.green("\u2714 \u9879\u76EE\u521B\u5EFA\u6210\u529F\uFF01"));
862
+ core.logger.info("", {});
863
+ if (options.features.length > 0) {
864
+ core.logger.info(chalk.bold("\u5DF2\u542F\u7528\u529F\u80FD:"));
865
+ for (const featureId of options.features) {
866
+ const feature = FEATURES[featureId];
867
+ if (feature) {
868
+ core.logger.info(chalk.gray(` \u2022 ${feature.name} (${featureId})`));
869
+ }
870
+ }
871
+ core.logger.info("", {});
872
+ }
873
+ core.logger.info("\u4E0B\u4E00\u6B65\uFF1A");
874
+ core.logger.info(chalk.cyan(` cd ${options.name}`));
875
+ if (!options.install) {
876
+ core.logger.info(chalk.cyan(` ${options.packageManager} install`));
877
+ }
878
+ core.logger.info(chalk.cyan(` ${options.packageManager} dev`));
879
+ core.logger.info("", {});
880
+ }
881
+ async function detectProject(cwd) {
882
+ const pkgPath = path2.join(cwd, "package.json");
883
+ if (!await fileExists(pkgPath)) {
884
+ return null;
885
+ }
886
+ try {
887
+ const pkg = await fse.readJson(pkgPath);
888
+ const deps = { ...pkg.dependencies, ...pkg.devDependencies };
889
+ const haiPackages = Object.keys(deps).filter((name) => name.startsWith("@h-ai/"));
890
+ return {
891
+ name: pkg.name,
892
+ version: pkg.version,
893
+ description: pkg.description,
894
+ isHaiProject: haiPackages.length > 0,
895
+ haiPackages
896
+ };
897
+ } catch {
898
+ return null;
899
+ }
900
+ }
901
+
902
+ // src/commands/add.ts
903
+ var MODULE_MAP = {
904
+ // iam 依赖 crypto(password hash)/ db(user 存储)/ cache(session 缓存)
905
+ "iam": { packages: ["@h-ai/iam"], deps: ["crypto", "db", "cache"], configKey: "iam", description: "\u8EAB\u4EFD\u4E0E\u8BBF\u95EE\u7BA1\u7406" },
906
+ // 'db' 和 'reldb' 都映射到 @h-ai/reldb,兼容用户习惯(hai add db 和 hai add reldb 均可)
907
+ "db": { packages: ["@h-ai/reldb"], configKey: "db", description: "\u6570\u636E\u5E93" },
908
+ "reldb": { packages: ["@h-ai/reldb"], configKey: "db", description: "\u6570\u636E\u5E93" },
909
+ "cache": { packages: ["@h-ai/cache"], configKey: "cache", description: "\u7F13\u5B58" },
910
+ "ai": { packages: ["@h-ai/ai"], configKey: "ai", description: "AI \u96C6\u6210" },
911
+ "storage": { packages: ["@h-ai/storage"], configKey: "storage", description: "\u6587\u4EF6\u5B58\u50A8" },
912
+ "crypto": { packages: ["@h-ai/crypto"], description: "\u52A0\u5BC6\u6A21\u5757" },
913
+ "kit": { packages: ["@h-ai/kit"], description: "SvelteKit \u96C6\u6210\u5DE5\u5177" },
914
+ "ui": { packages: ["@h-ai/ui"], description: "UI \u7EC4\u4EF6\u5E93" },
915
+ "deploy": { packages: ["@h-ai/deploy"], configKey: "deploy", description: "\u81EA\u52A8\u5316\u90E8\u7F72" },
916
+ "vecdb": { packages: ["@h-ai/vecdb"], configKey: "vecdb", description: "\u5411\u91CF\u6570\u636E\u5E93" },
917
+ "datapipe": { packages: ["@h-ai/datapipe"], description: "\u6570\u636E\u5904\u7406\u7BA1\u9053" },
918
+ "reach": { packages: ["@h-ai/reach"], configKey: "reach", description: "\u6D88\u606F\u63A8\u9001" },
919
+ "scheduler": { packages: ["@h-ai/scheduler"], deps: ["db"], configKey: "scheduler", description: "\u4EFB\u52A1\u8C03\u5EA6" },
920
+ "audit": { packages: ["@h-ai/audit"], deps: ["db"], configKey: "audit", description: "\u5BA1\u8BA1\u65E5\u5FD7" },
921
+ "payment": { packages: ["@h-ai/payment"], configKey: "payment", description: "\u652F\u4ED8" },
922
+ "capacitor": { packages: ["@h-ai/capacitor"], description: "\u79FB\u52A8\u7AEF\u80FD\u529B" },
923
+ "api-client": { packages: ["@h-ai/api-client"], description: "HTTP \u5BA2\u6237\u7AEF" }
924
+ };
925
+ async function addModule(options) {
926
+ const spinner = ora();
927
+ const cwd = options.cwd ?? ".";
928
+ try {
929
+ const project = await detectProject(cwd);
930
+ if (!project) {
931
+ core.logger.error(chalk.red("\u672A\u68C0\u6D4B\u5230\u9879\u76EE\uFF08\u627E\u4E0D\u5230 package.json\uFF09"));
932
+ return;
933
+ }
934
+ let moduleName = options.module;
935
+ if (!moduleName) {
936
+ const installed = new Set(project.haiPackages);
937
+ const choices = Object.entries(MODULE_MAP).filter(([, def]) => !def.packages.every((p) => installed.has(p))).map(([key, def]) => ({
938
+ title: `${chalk.bold(key.padEnd(10))} ${chalk.gray(def.description)}`,
939
+ value: key
940
+ }));
941
+ if (choices.length === 0) {
942
+ core.logger.info(chalk.green("\u6240\u6709\u6A21\u5757\u5DF2\u5B89\u88C5\uFF01"));
943
+ return;
944
+ }
945
+ const { selected } = await prompts({
946
+ type: "select",
947
+ name: "selected",
948
+ message: "\u9009\u62E9\u8981\u6DFB\u52A0\u7684\u6A21\u5757:",
949
+ choices
950
+ });
951
+ if (!selected)
952
+ return;
953
+ moduleName = selected;
954
+ }
955
+ const moduleDef = MODULE_MAP[moduleName];
956
+ if (!moduleDef) {
957
+ core.logger.error(chalk.red(`\u672A\u77E5\u6A21\u5757: ${moduleName}`));
958
+ core.logger.info(`\u53EF\u7528\u6A21\u5757: ${Object.keys(MODULE_MAP).join(", ")}`);
959
+ return;
960
+ }
961
+ const { generateProjectAiSupport, resolveSkillNamesForPackages } = await import('./skill-templates-N5DU5FNX.js');
962
+ if (moduleDef.packages.every((p) => project.haiPackages.includes(p))) {
963
+ const generatedFiles2 = await generateProjectAiSupport(
964
+ cwd,
965
+ resolveSkillNamesForPackages(project.haiPackages),
966
+ {
967
+ overwriteSkills: false,
968
+ overwriteBridgeFiles: false
969
+ }
970
+ );
971
+ if (generatedFiles2.length > 0) {
972
+ core.logger.info(chalk.yellow(`\u6A21\u5757 ${moduleName} \u5DF2\u5B89\u88C5\uFF0C\u5DF2\u8865\u5168 AI \u652F\u6301\u6587\u4EF6`));
973
+ for (const generatedFile of generatedFiles2) {
974
+ core.logger.info(chalk.gray(` + ${generatedFile} (AI support)`));
975
+ }
976
+ } else {
977
+ core.logger.info(chalk.yellow(`\u6A21\u5757 ${moduleName} \u5DF2\u5B89\u88C5`));
978
+ }
979
+ return;
980
+ }
981
+ const allPackages = new Set(moduleDef.packages);
982
+ if (moduleDef.deps) {
983
+ for (const dep of moduleDef.deps) {
984
+ const depDef = MODULE_MAP[dep];
985
+ if (depDef) {
986
+ for (const pkg2 of depDef.packages) {
987
+ allPackages.add(pkg2);
988
+ }
989
+ }
990
+ }
991
+ }
992
+ spinner.start("\u66F4\u65B0 package.json...");
993
+ const pkgPath = path2.join(cwd, "package.json");
994
+ const pkg = await fse.readJson(pkgPath);
995
+ if (!pkg.dependencies)
996
+ pkg.dependencies = {};
997
+ for (const pkgName of allPackages) {
998
+ if (!pkg.dependencies[pkgName]) {
999
+ pkg.dependencies[pkgName] = "workspace:*";
1000
+ }
1001
+ }
1002
+ await fse.writeJson(pkgPath, pkg, { spaces: 2 });
1003
+ spinner.succeed();
1004
+ if (moduleDef.configKey) {
1005
+ const configDir = path2.join(cwd, "config");
1006
+ const configPath = path2.join(configDir, `_${moduleDef.configKey}.yml`);
1007
+ if (!await fileExists(configPath)) {
1008
+ spinner.start(`\u751F\u6210\u914D\u7F6E\u6587\u4EF6 config/_${moduleDef.configKey}.yml...`);
1009
+ const content = generateConfigFile(moduleDef.configKey);
1010
+ await writeFile(configPath, content);
1011
+ spinner.succeed();
1012
+ } else {
1013
+ core.logger.info(chalk.gray(`\u914D\u7F6E\u6587\u4EF6\u5DF2\u5B58\u5728: config/_${moduleDef.configKey}.yml`));
1014
+ }
1015
+ }
1016
+ if (moduleDef.deps) {
1017
+ for (const dep of moduleDef.deps) {
1018
+ const depDef = MODULE_MAP[dep];
1019
+ if (depDef?.configKey) {
1020
+ const depConfigPath = path2.join(cwd, "config", `_${depDef.configKey}.yml`);
1021
+ if (!await fileExists(depConfigPath)) {
1022
+ const content = generateConfigFile(depDef.configKey);
1023
+ await writeFile(depConfigPath, content);
1024
+ core.logger.info(chalk.gray(` + config/_${depDef.configKey}.yml (\u4F9D\u8D56)`));
1025
+ }
1026
+ }
1027
+ }
1028
+ }
1029
+ const skillPackages = [...project.haiPackages, ...allPackages];
1030
+ const generatedFiles = await generateProjectAiSupport(
1031
+ cwd,
1032
+ resolveSkillNamesForPackages(skillPackages),
1033
+ {
1034
+ overwriteSkills: false,
1035
+ overwriteBridgeFiles: false
1036
+ }
1037
+ );
1038
+ if (generatedFiles.length > 0) {
1039
+ for (const generatedFile of generatedFiles) {
1040
+ core.logger.info(chalk.gray(` + ${generatedFile} (AI support)`));
1041
+ }
1042
+ }
1043
+ const doInstall = options.install !== false;
1044
+ if (doInstall) {
1045
+ spinner.start("\u5B89\u88C5\u4F9D\u8D56...");
1046
+ const { execSync: execSync2 } = await import('child_process');
1047
+ const pm = await detectPackageManager(cwd);
1048
+ execSync2(`${pm} install`, { cwd, stdio: "ignore" });
1049
+ spinner.succeed();
1050
+ }
1051
+ core.logger.info("");
1052
+ core.logger.info(chalk.green(`\u2714 \u6A21\u5757 ${moduleName} \u6DFB\u52A0\u6210\u529F\uFF01`));
1053
+ core.logger.info("");
1054
+ core.logger.info("\u5DF2\u6DFB\u52A0\u7684\u5305:");
1055
+ for (const pkg2 of allPackages) {
1056
+ core.logger.info(chalk.cyan(` ${pkg2}`));
1057
+ }
1058
+ core.logger.info("");
1059
+ } catch (error) {
1060
+ spinner.fail();
1061
+ core.logger.error(chalk.red("\u6DFB\u52A0\u6A21\u5757\u5931\u8D25:"), { error });
1062
+ throw error;
1063
+ }
1064
+ }
1065
+ async function deployCommand(options) {
1066
+ const spinner = ora();
1067
+ const cwd = options.cwd ?? process3.cwd();
1068
+ const appDir = resolve(cwd, options.appDir ?? ".");
1069
+ try {
1070
+ let deployModule;
1071
+ try {
1072
+ deployModule = await import('./src-XJ6YXNAF.js');
1073
+ } catch {
1074
+ core.logger.error(chalk.red("Deploy module not found. Install @h-ai/deploy first:"));
1075
+ core.logger.info(chalk.cyan(" pnpm add @h-ai/deploy"));
1076
+ return;
1077
+ }
1078
+ const { deploy, loadCredentials, scanApp } = deployModule;
1079
+ spinner.start("Loading credentials...");
1080
+ const credResult = loadCredentials();
1081
+ if (!credResult.success) {
1082
+ spinner.fail(chalk.red(`Failed to load credentials: ${credResult.error.message}`));
1083
+ return;
1084
+ }
1085
+ spinner.succeed(`Loaded ${credResult.data.length} credentials`);
1086
+ spinner.start("Loading deploy config...");
1087
+ const configPath = resolve(appDir, "config", "_deploy.yml");
1088
+ if (!existsSync(configPath)) {
1089
+ spinner.fail(chalk.red(`Deploy config not found: ${configPath}`));
1090
+ core.logger.info(chalk.cyan(" Run: hai add deploy to generate config template"));
1091
+ return;
1092
+ }
1093
+ const configContent = readFileSync(configPath, "utf-8");
1094
+ const rawConfig = interpolateEnvFallback(configContent);
1095
+ const deployConfig = parse(rawConfig);
1096
+ spinner.succeed("Deploy config loaded");
1097
+ spinner.start("Scanning application...");
1098
+ const scanResult = await scanApp(appDir);
1099
+ if (!scanResult.success) {
1100
+ spinner.fail(chalk.red(`Scan failed: ${scanResult.error.message}`));
1101
+ return;
1102
+ }
1103
+ const scan = scanResult.data;
1104
+ spinner.succeed(`Scanned: ${scan.appName} (SvelteKit: ${scan.isSvelteKit}, Services: ${scan.requiredServices.join(", ") || "none"})`);
1105
+ spinner.start("Initializing deploy module...");
1106
+ const initResult = await deploy.init(deployConfig);
1107
+ if (!initResult.success) {
1108
+ spinner.fail(chalk.red(`Init failed: ${initResult.error.message}`));
1109
+ return;
1110
+ }
1111
+ spinner.succeed("Deploy module initialized");
1112
+ spinner.start("Deploying application...");
1113
+ const deployResult = await deploy.deployApp(appDir, {
1114
+ projectName: options.projectName,
1115
+ skipProvision: options.skipProvision,
1116
+ skipBuild: options.skipBuild
1117
+ });
1118
+ if (!deployResult.success) {
1119
+ spinner.fail(chalk.red(`Deploy failed: ${deployResult.error.message}`));
1120
+ await deploy.close();
1121
+ return;
1122
+ }
1123
+ spinner.succeed(chalk.green("Deployment successful!"));
1124
+ core.logger.info("");
1125
+ core.logger.info(chalk.cyan(` URL: ${deployResult.data.url}`));
1126
+ core.logger.info(chalk.gray(` ID: ${deployResult.data.deploymentId}`));
1127
+ if (deployResult.data.envVarsSet.length > 0) {
1128
+ core.logger.info(chalk.gray(` Env: ${deployResult.data.envVarsSet.join(", ")}`));
1129
+ }
1130
+ core.logger.info("");
1131
+ await deploy.close();
1132
+ } catch (error) {
1133
+ spinner.fail(chalk.red("Deploy command failed"));
1134
+ core.logger.error(error instanceof Error ? error.message : String(error));
1135
+ throw error;
1136
+ }
1137
+ }
1138
+ function interpolateEnvFallback(content) {
1139
+ return content.replace(/\$\{([^:}]+)(?::([^}]*))?\}/g, (_match, key, defaultValue) => {
1140
+ return process3.env[key] ?? defaultValue ?? "";
1141
+ });
1142
+ }
1143
+ var GENERATORS = {
1144
+ page: {
1145
+ description: "SvelteKit \u9875\u9762",
1146
+ defaultPath: "src/routes",
1147
+ generate: generatePage
1148
+ },
1149
+ component: {
1150
+ description: "Svelte \u7EC4\u4EF6",
1151
+ defaultPath: "src/lib/components",
1152
+ generate: generateComponent
1153
+ },
1154
+ api: {
1155
+ description: "API \u7AEF\u70B9",
1156
+ defaultPath: "src/routes/api",
1157
+ generate: generateApi
1158
+ },
1159
+ model: {
1160
+ description: "\u6570\u636E\u6A21\u578B",
1161
+ defaultPath: "src/lib/models",
1162
+ generate: generateModel
1163
+ },
1164
+ migration: {
1165
+ description: "\u6570\u636E\u5E93\u8FC1\u79FB",
1166
+ defaultPath: "migrations",
1167
+ generate: generateMigration
1168
+ }
1169
+ };
1170
+ async function generate(options) {
1171
+ const spinner = ora();
1172
+ const cwd = options.cwd ?? ".";
1173
+ try {
1174
+ const project = await detectProject(cwd);
1175
+ if (!project?.isHaiProject) {
1176
+ core.logger.warn(chalk.yellow("\u8B66\u544A: \u5F53\u524D\u76EE\u5F55\u4E0D\u662F hai \u9879\u76EE"));
1177
+ }
1178
+ const resolvedOptions = await resolveGenerateOptions(options);
1179
+ const generator = GENERATORS[resolvedOptions.type];
1180
+ if (!generator) {
1181
+ throw new Error(`\u672A\u77E5\u7684\u751F\u6210\u5668\u7C7B\u578B: ${resolvedOptions.type}`);
1182
+ }
1183
+ const outputDir = path2.resolve(cwd, resolvedOptions.output || generator.defaultPath);
1184
+ const resolvedCwd = path2.resolve(cwd);
1185
+ if (!outputDir.startsWith(resolvedCwd + path2.sep) && outputDir !== resolvedCwd) {
1186
+ core.logger.error(chalk.red(`\u8F93\u51FA\u8DEF\u5F84 "${resolvedOptions.output}" \u8D85\u51FA\u5DE5\u4F5C\u76EE\u5F55\u8303\u56F4`));
1187
+ return;
1188
+ }
1189
+ const context = createTemplateContext(resolvedOptions.name, {
1190
+ projectName: project?.name
1191
+ });
1192
+ spinner.start(`\u751F\u6210 ${generator.description}...`);
1193
+ const files = await generator.generate(resolvedOptions.name, context, outputDir);
1194
+ spinner.succeed();
1195
+ core.logger.info("", {});
1196
+ core.logger.info(chalk.green("\u2714 \u751F\u6210\u5B8C\u6210\uFF01"));
1197
+ core.logger.info("", {});
1198
+ core.logger.info("\u521B\u5EFA\u7684\u6587\u4EF6:");
1199
+ files.forEach((file) => {
1200
+ core.logger.info(chalk.cyan(` ${path2.relative(cwd, file)}`));
1201
+ });
1202
+ core.logger.info("", {});
1203
+ } catch (error) {
1204
+ spinner.fail();
1205
+ core.logger.error(chalk.red("\u751F\u6210\u5931\u8D25:"), { error });
1206
+ throw error;
1207
+ }
1208
+ }
1209
+ async function resolveGenerateOptions(options) {
1210
+ const questions = [];
1211
+ if (!options.type) {
1212
+ questions.push({
1213
+ type: "select",
1214
+ name: "type",
1215
+ message: "\u9009\u62E9\u751F\u6210\u7C7B\u578B:",
1216
+ choices: Object.entries(GENERATORS).map(([key, value]) => ({
1217
+ title: `${key} - ${value.description}`,
1218
+ value: key
1219
+ }))
1220
+ });
1221
+ }
1222
+ if (!options.name) {
1223
+ questions.push({
1224
+ type: "text",
1225
+ name: "name",
1226
+ message: "\u540D\u79F0:",
1227
+ validate: (value) => {
1228
+ if (!value.trim())
1229
+ return "\u540D\u79F0\u4E0D\u80FD\u4E3A\u7A7A";
1230
+ return true;
1231
+ }
1232
+ });
1233
+ }
1234
+ const answers = questions.length > 0 ? await prompts(questions) : {};
1235
+ return {
1236
+ type: options.type || answers.type,
1237
+ name: options.name || answers.name,
1238
+ output: options.output ?? "",
1239
+ force: options.force ?? false,
1240
+ verbose: options.verbose ?? false,
1241
+ cwd: options.cwd ?? "."
1242
+ };
1243
+ }
1244
+ async function generatePage(_name, context, outputDir) {
1245
+ const files = [];
1246
+ const pageDir = path2.join(outputDir, context.kebabCase);
1247
+ const pageContent = `<script lang="ts">
1248
+ /**
1249
+ * ${context.pascalCase} \u9875\u9762
1250
+ */
1251
+ </script>
1252
+
1253
+ <svelte:head>
1254
+ <title>${context.pascalCase}</title>
1255
+ </svelte:head>
1256
+
1257
+ <div class="container mx-auto p-4">
1258
+ <h1 class="text-2xl font-bold mb-4">${context.pascalCase}</h1>
1259
+
1260
+ <!-- \u9875\u9762\u5185\u5BB9 -->
1261
+ </div>
1262
+ `;
1263
+ const pagePath = path2.join(pageDir, "+page.svelte");
1264
+ await writeFile(pagePath, pageContent);
1265
+ files.push(pagePath);
1266
+ const serverContent = `/**
1267
+ * ${context.pascalCase} \u9875\u9762\u670D\u52A1\u7AEF
1268
+ */
1269
+ import type { PageServerLoad, Actions } from './$types'
1270
+
1271
+ export const load: PageServerLoad = async ({ locals }) => {
1272
+ return {
1273
+ // \u9875\u9762\u6570\u636E
1274
+ }
1275
+ }
1276
+
1277
+ export const actions: Actions = {
1278
+ default: async ({ request, locals }) => {
1279
+ // \u8868\u5355\u5904\u7406
1280
+ },
1281
+ }
1282
+ `;
1283
+ const serverPath = path2.join(pageDir, "+page.server.ts");
1284
+ await writeFile(serverPath, serverContent);
1285
+ files.push(serverPath);
1286
+ return files;
1287
+ }
1288
+ async function generateComponent(_name, context, outputDir) {
1289
+ const files = [];
1290
+ const componentContent = `<script lang="ts">
1291
+ /**
1292
+ * ${context.pascalCase} \u7EC4\u4EF6
1293
+ */
1294
+
1295
+ interface Props {
1296
+ /** \u81EA\u5B9A\u4E49 class */
1297
+ class?: string
1298
+ }
1299
+
1300
+ let { class: className = '' }: Props = $props()
1301
+ </script>
1302
+
1303
+ <div class={className}>
1304
+ <!-- \u7EC4\u4EF6\u5185\u5BB9 -->
1305
+ <slot />
1306
+ </div>
1307
+ `;
1308
+ const componentPath = path2.join(outputDir, `${context.pascalCase}.svelte`);
1309
+ await writeFile(componentPath, componentContent);
1310
+ files.push(componentPath);
1311
+ return files;
1312
+ }
1313
+ async function generateApi(_name, context, outputDir) {
1314
+ const files = [];
1315
+ const apiDir = path2.join(outputDir, context.kebabCase);
1316
+ const serverContent = `/**
1317
+ * ${context.pascalCase} API
1318
+ */
1319
+ import type { RequestHandler } from './$types'
1320
+ import { kit } from '@h-ai/kit'
1321
+
1322
+ /**
1323
+ * GET ${context.kebabCase}
1324
+ */
1325
+ export const GET: RequestHandler = async ({ locals }) => {
1326
+ try {
1327
+ const { requestId } = locals
1328
+
1329
+ return kit.response.ok({ message: 'Hello from ${context.kebabCase}' }, requestId)
1330
+ }
1331
+ catch (error) {
1332
+ return kit.response.internalError()
1333
+ }
1334
+ }
1335
+
1336
+ /**
1337
+ * POST ${context.kebabCase}
1338
+ */
1339
+ export const POST: RequestHandler = async ({ request, locals }) => {
1340
+ try {
1341
+ const { requestId } = locals
1342
+ const body = await request.json()
1343
+
1344
+ // \u5904\u7406\u8BF7\u6C42
1345
+
1346
+ return kit.response.ok({ success: true }, requestId)
1347
+ }
1348
+ catch (error) {
1349
+ return kit.response.internalError()
1350
+ }
1351
+ }
1352
+ `;
1353
+ const serverPath = path2.join(apiDir, "+server.ts");
1354
+ await writeFile(serverPath, serverContent);
1355
+ files.push(serverPath);
1356
+ return files;
1357
+ }
1358
+ async function generateModel(_name, context, outputDir) {
1359
+ const files = [];
1360
+ const modelContent = `/**
1361
+ * ${context.pascalCase} \u6A21\u578B
1362
+ */
1363
+ import { z } from 'zod'
1364
+
1365
+ /**
1366
+ * ${context.pascalCase} Schema
1367
+ */
1368
+ export const ${context.camelCase}Schema = z.object({
1369
+ id: z.string(),
1370
+ name: z.string().min(1),
1371
+ createdAt: z.date(),
1372
+ updatedAt: z.date(),
1373
+ })
1374
+
1375
+ /**
1376
+ * \u521B\u5EFA ${context.pascalCase} Schema
1377
+ */
1378
+ export const create${context.pascalCase}Schema = ${context.camelCase}Schema.omit({
1379
+ id: true,
1380
+ createdAt: true,
1381
+ updatedAt: true,
1382
+ })
1383
+
1384
+ /**
1385
+ * \u66F4\u65B0 ${context.pascalCase} Schema
1386
+ */
1387
+ export const update${context.pascalCase}Schema = create${context.pascalCase}Schema.partial()
1388
+
1389
+ /**
1390
+ * ${context.pascalCase} \u7C7B\u578B
1391
+ */
1392
+ export type ${context.pascalCase} = z.infer<typeof ${context.camelCase}Schema>
1393
+ export type Create${context.pascalCase} = z.infer<typeof create${context.pascalCase}Schema>
1394
+ export type Update${context.pascalCase} = z.infer<typeof update${context.pascalCase}Schema>
1395
+ `;
1396
+ const modelPath = path2.join(outputDir, `${context.kebabCase}.ts`);
1397
+ await writeFile(modelPath, modelContent);
1398
+ files.push(modelPath);
1399
+ return files;
1400
+ }
1401
+ async function generateMigration(_name, context, outputDir) {
1402
+ const files = [];
1403
+ const timestamp = Date.now();
1404
+ const migrationContent = `/**
1405
+ * \u8FC1\u79FB: ${context.pascalCase}
1406
+ * \u65F6\u95F4: ${(/* @__PURE__ */ new Date()).toISOString()}
1407
+ */
1408
+ import type { MigrationFn } from '@h-ai/reldb'
1409
+
1410
+ export const up: MigrationFn = async (db) => {
1411
+ // \u5347\u7EA7\u903B\u8F91
1412
+ await db.run(\`
1413
+ CREATE TABLE IF NOT EXISTS ${context.snakeCase} (
1414
+ id TEXT PRIMARY KEY,
1415
+ name TEXT NOT NULL,
1416
+ created_at INTEGER NOT NULL,
1417
+ updated_at INTEGER NOT NULL
1418
+ )
1419
+ \`)
1420
+ }
1421
+
1422
+ export const down: MigrationFn = async (db) => {
1423
+ // \u56DE\u6EDA\u903B\u8F91
1424
+ await db.run('DROP TABLE IF EXISTS ${context.snakeCase}')
1425
+ }
1426
+ `;
1427
+ const migrationPath = path2.join(outputDir, `${timestamp}_${context.snakeCase}.ts`);
1428
+ await writeFile(migrationPath, migrationContent);
1429
+ files.push(migrationPath);
1430
+ return files;
1431
+ }
1432
+ var MODULE_CONFIG_MAP = {
1433
+ "@h-ai/core": { configKey: "core", label: "Core" },
1434
+ "@h-ai/reldb": { configKey: "db", label: "Database" },
1435
+ "@h-ai/cache": { configKey: "cache", label: "Cache" },
1436
+ "@h-ai/iam": { configKey: "iam", label: "IAM" },
1437
+ "@h-ai/storage": { configKey: "storage", label: "Storage" },
1438
+ "@h-ai/ai": { configKey: "ai", label: "AI" },
1439
+ "@h-ai/vecdb": { configKey: "vecdb", label: "VecDB" },
1440
+ "@h-ai/reach": { configKey: "reach", label: "Reach" },
1441
+ "@h-ai/scheduler": { configKey: "scheduler", label: "Scheduler" },
1442
+ "@h-ai/audit": { configKey: "audit", label: "Audit" },
1443
+ "@h-ai/payment": { configKey: "payment", label: "Payment" },
1444
+ "@h-ai/deploy": { configKey: "deploy", label: "Deploy" }
1445
+ };
1446
+ async function validateProject(cwd) {
1447
+ const project = await detectProject(cwd);
1448
+ if (!project) {
1449
+ return null;
1450
+ }
1451
+ const existing = [];
1452
+ const missing = [];
1453
+ for (const pkg of project.haiPackages) {
1454
+ const mapping = MODULE_CONFIG_MAP[pkg];
1455
+ if (!mapping)
1456
+ continue;
1457
+ const configPath = path2.join(cwd, "config", `_${mapping.configKey}.yml`);
1458
+ if (await fileExists(configPath)) {
1459
+ existing.push(mapping.configKey);
1460
+ } else {
1461
+ missing.push({ pkg, configKey: mapping.configKey, label: mapping.label });
1462
+ }
1463
+ }
1464
+ return { existing, missing, project };
1465
+ }
1466
+ async function initProject(options) {
1467
+ const spinner = ora();
1468
+ const cwd = options.cwd ?? ".";
1469
+ try {
1470
+ spinner.start("\u68C0\u6D4B\u9879\u76EE...");
1471
+ const result = await validateProject(cwd);
1472
+ if (!result) {
1473
+ spinner.fail(chalk.red("\u672A\u68C0\u6D4B\u5230\u9879\u76EE\uFF08\u627E\u4E0D\u5230 package.json\uFF09"));
1474
+ return;
1475
+ }
1476
+ const { project, existing, missing } = result;
1477
+ spinner.succeed(`\u68C0\u6D4B\u5230\u9879\u76EE: ${chalk.cyan(project.name)}`);
1478
+ core.logger.info("");
1479
+ core.logger.info(chalk.bold("\u9879\u76EE\u72B6\u6001:"));
1480
+ core.logger.info(` \u540D\u79F0: ${chalk.cyan(project.name)}`);
1481
+ core.logger.info(` \u7248\u672C: ${project.version}`);
1482
+ core.logger.info(` \u5DF2\u5B89\u88C5\u6A21\u5757: ${project.haiPackages.length > 0 ? project.haiPackages.map((p) => chalk.cyan(p)).join(", ") : chalk.gray("\u65E0")}`);
1483
+ core.logger.info("");
1484
+ if (existing.length > 0) {
1485
+ core.logger.info(chalk.bold("\u5DF2\u6709\u914D\u7F6E:"));
1486
+ for (const key of existing) {
1487
+ core.logger.info(chalk.green(` \u2714 config/_${key}.yml`));
1488
+ }
1489
+ }
1490
+ if (missing.length === 0 && !options.force) {
1491
+ core.logger.info("");
1492
+ core.logger.info(chalk.green("\u2714 \u6240\u6709\u914D\u7F6E\u6587\u4EF6\u5DF2\u5C31\u7EEA\uFF01"));
1493
+ return;
1494
+ }
1495
+ if (missing.length > 0) {
1496
+ core.logger.info("");
1497
+ core.logger.info(chalk.bold("\u7F3A\u5931\u914D\u7F6E:"));
1498
+ for (const item of missing) {
1499
+ core.logger.info(chalk.yellow(` \u2718 config/_${item.configKey}.yml (${item.label})`));
1500
+ }
1501
+ core.logger.info("");
1502
+ spinner.start("\u751F\u6210\u7F3A\u5931\u914D\u7F6E\u6587\u4EF6...");
1503
+ let generated = 0;
1504
+ for (const item of missing) {
1505
+ const configPath = path2.join(cwd, "config", `_${item.configKey}.yml`);
1506
+ const content = generateConfigFile(item.configKey);
1507
+ await writeFile(configPath, content);
1508
+ generated++;
1509
+ }
1510
+ spinner.succeed(`\u5DF2\u751F\u6210 ${generated} \u4E2A\u914D\u7F6E\u6587\u4EF6`);
1511
+ }
1512
+ if (options.force && existing.length > 0) {
1513
+ core.logger.info("");
1514
+ spinner.start("\u5F3A\u5236\u91CD\u65B0\u751F\u6210\u6240\u6709\u914D\u7F6E\u6587\u4EF6...");
1515
+ let regenerated = 0;
1516
+ for (const pkg of project.haiPackages) {
1517
+ const mapping = MODULE_CONFIG_MAP[pkg];
1518
+ if (!mapping)
1519
+ continue;
1520
+ const configPath = path2.join(cwd, "config", `_${mapping.configKey}.yml`);
1521
+ const content = generateConfigFile(mapping.configKey);
1522
+ await writeFile(configPath, content);
1523
+ regenerated++;
1524
+ }
1525
+ spinner.succeed(`\u5DF2\u91CD\u65B0\u751F\u6210 ${regenerated} \u4E2A\u914D\u7F6E\u6587\u4EF6`);
1526
+ }
1527
+ const coreConfigPath = path2.join(cwd, "config", "_core.yml");
1528
+ if (!await fileExists(coreConfigPath)) {
1529
+ spinner.start("\u751F\u6210\u6838\u5FC3\u914D\u7F6E config/_core.yml...");
1530
+ const content = generateConfigFile("core");
1531
+ await writeFile(coreConfigPath, content);
1532
+ spinner.succeed();
1533
+ }
1534
+ core.logger.info("");
1535
+ core.logger.info(chalk.green("\u2714 \u9879\u76EE\u914D\u7F6E\u521D\u59CB\u5316\u5B8C\u6210\uFF01"));
1536
+ core.logger.info("");
1537
+ core.logger.info(chalk.gray("\u4E0B\u4E00\u6B65:"));
1538
+ core.logger.info(chalk.gray(" 1. \u7F16\u8F91 config/ \u4E2D\u7684\u914D\u7F6E\u6587\u4EF6"));
1539
+ core.logger.info(chalk.gray(" 2. \u8FD0\u884C pnpm dev \u542F\u52A8\u5F00\u53D1\u670D\u52A1"));
1540
+ core.logger.info("");
1541
+ } catch (error) {
1542
+ spinner.fail();
1543
+ core.logger.error(chalk.red("\u521D\u59CB\u5316\u5931\u8D25:"), { error });
1544
+ throw error;
1545
+ }
1546
+ }
1547
+
1548
+ // src/index.ts
1549
+ var VERSION = "0.0.1";
1550
+ var cli = cac("hai");
1551
+ cli.option("-v, --verbose", "\u663E\u793A\u8BE6\u7EC6\u8F93\u51FA");
1552
+ cli.option("-C, --cwd <path>", "\u5DE5\u4F5C\u76EE\u5F55");
1553
+ cli.command("create [name]", "\u521B\u5EFA\u65B0\u7684 hai \u9879\u76EE").option("--type <type>", "\u5E94\u7528\u7C7B\u578B (admin, website, h5, api)").option("-t, --template <template>", "\u9879\u76EE\u6A21\u677F (minimal, default, full, custom)").option("-f, --features <features>", "\u529F\u80FD\u5217\u8868 (\u9017\u53F7\u5206\u9694: iam,db,cache,ai,storage,crypto)").option("--examples", "\u6DFB\u52A0\u793A\u4F8B\u4EE3\u7801").option("--no-examples", "\u4E0D\u6DFB\u52A0\u793A\u4F8B\u4EE3\u7801").option("--no-install", "\u4E0D\u5B89\u88C5\u4F9D\u8D56").option("-p, --package-manager <pm>", "\u5305\u7BA1\u7406\u5668 (pnpm, npm, yarn)").option("--no-git", "\u4E0D\u521D\u59CB\u5316 Git").action(async (name, options) => {
1554
+ try {
1555
+ const features = typeof options.features === "string" ? options.features.split(",").map((f) => f.trim()) : void 0;
1556
+ await createProject({
1557
+ name: name ?? "",
1558
+ appType: options.type,
1559
+ template: options.template,
1560
+ features,
1561
+ examples: options.examples,
1562
+ install: options.install,
1563
+ packageManager: options.packageManager,
1564
+ git: options.git,
1565
+ verbose: options.verbose,
1566
+ cwd: options.cwd
1567
+ });
1568
+ } catch {
1569
+ process3.exit(1);
1570
+ }
1571
+ });
1572
+ cli.command("add [module]", "\u5411\u73B0\u6709\u9879\u76EE\u6DFB\u52A0\u6A21\u5757").option("--no-install", "\u4E0D\u5B89\u88C5\u4F9D\u8D56").action(async (module, options) => {
1573
+ try {
1574
+ await addModule({
1575
+ module,
1576
+ install: options.install,
1577
+ verbose: options.verbose,
1578
+ cwd: options.cwd
1579
+ });
1580
+ } catch {
1581
+ process3.exit(1);
1582
+ }
1583
+ });
1584
+ cli.command("init", "\u521D\u59CB\u5316/\u6821\u9A8C\u9879\u76EE\u914D\u7F6E").option("-f, --force", "\u5F3A\u5236\u91CD\u65B0\u751F\u6210\u914D\u7F6E").action(async (options) => {
1585
+ try {
1586
+ await initProject({
1587
+ force: options.force,
1588
+ verbose: options.verbose,
1589
+ cwd: options.cwd
1590
+ });
1591
+ } catch {
1592
+ process3.exit(1);
1593
+ }
1594
+ });
1595
+ cli.command("generate [type] [name]", "\u751F\u6210\u4EE3\u7801").alias("g").option("-o, --output <path>", "\u8F93\u51FA\u8DEF\u5F84").option("-f, --force", "\u8986\u76D6\u73B0\u6709\u6587\u4EF6").action(async (type, name, options) => {
1596
+ try {
1597
+ await generate({
1598
+ type,
1599
+ name: name ?? "",
1600
+ output: options.output,
1601
+ force: options.force,
1602
+ verbose: options.verbose,
1603
+ cwd: options.cwd
1604
+ });
1605
+ } catch {
1606
+ process3.exit(1);
1607
+ }
1608
+ });
1609
+ cli.command("deploy [appDir]", "\u90E8\u7F72\u5E94\u7528\u5230\u4E91\u5E73\u53F0").option("--project-name <name>", "\u9879\u76EE\u540D\u79F0\uFF08\u9ED8\u8BA4\u4ECE package.json \u8BFB\u53D6\uFF09").option("--skip-provision", "\u8DF3\u8FC7\u57FA\u7840\u8BBE\u65BD\u5F00\u901A").option("--skip-build", "\u8DF3\u8FC7\u6784\u5EFA").action(async (appDir, options) => {
1610
+ try {
1611
+ await deployCommand({
1612
+ appDir,
1613
+ projectName: options.projectName,
1614
+ skipProvision: options.skipProvision,
1615
+ skipBuild: options.skipBuild,
1616
+ verbose: options.verbose,
1617
+ cwd: options.cwd
1618
+ });
1619
+ } catch {
1620
+ process3.exit(1);
1621
+ }
1622
+ });
1623
+ cli.command("g:page <name>", "\u751F\u6210\u9875\u9762").action(async (name, options) => {
1624
+ await generate({ type: "page", name, cwd: options.cwd });
1625
+ });
1626
+ cli.command("g:component <name>", "\u751F\u6210\u7EC4\u4EF6").action(async (name, options) => {
1627
+ await generate({ type: "component", name, cwd: options.cwd });
1628
+ });
1629
+ cli.command("g:api <name>", "\u751F\u6210 API").action(async (name, options) => {
1630
+ await generate({ type: "api", name, cwd: options.cwd });
1631
+ });
1632
+ cli.command("g:model <name>", "\u751F\u6210\u6570\u636E\u6A21\u578B").action(async (name, options) => {
1633
+ await generate({ type: "model", name, cwd: options.cwd });
1634
+ });
1635
+ cli.version(VERSION);
1636
+ cli.help();
1637
+ var parsed = cli.parse();
1638
+ if (!cli.matchedCommand && !parsed.options.help && !parsed.options.version) {
1639
+ core.logger.info("", {});
1640
+ core.logger.info(chalk.cyan("hai Agent Framework CLI"));
1641
+ core.logger.info("", {});
1642
+ cli.outputHelp();
1643
+ }
1644
+ //# sourceMappingURL=index.js.map
1645
+ //# sourceMappingURL=index.js.map