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

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 (36) hide show
  1. package/README.md +14 -0
  2. package/dist/{chunk-U4PA632G.js → chunk-FVDXJYMO.js} +34 -67
  3. package/dist/chunk-FVDXJYMO.js.map +1 -0
  4. package/dist/cli-config-templates-WGN2I23J.js +5 -0
  5. package/dist/{cli-config-templates-GSXKLGU5.js.map → cli-config-templates-WGN2I23J.js.map} +1 -1
  6. package/dist/index.js +174 -105
  7. package/dist/index.js.map +1 -1
  8. package/dist/{src-QNEJJ3BV.js → src-6ZBWDBUB.js} +127 -87
  9. package/dist/{src-QNEJJ3BV.js.map → src-6ZBWDBUB.js.map} +1 -1
  10. package/package.json +13 -4
  11. package/templates/apps/api/apps/{{projectName}}-contract/package.json.hbs +1 -0
  12. package/templates/apps/api/apps/{{projectName}}-service/package.json.hbs +1 -0
  13. package/templates/apps/api/package.json.hbs +1 -0
  14. package/templates/apps/fullstack/apps/{{projectName}}-app/package.json.hbs +1 -0
  15. package/templates/apps/fullstack/apps/{{projectName}}-desktop/package.json.hbs +1 -0
  16. package/templates/apps/fullstack/apps/{{projectName}}-web/package.json.hbs +1 -0
  17. package/templates/apps/fullstack/package.json.hbs +1 -0
  18. package/templates/apps/fullstack/packages/{{projectName}}-contract/package.json.hbs +1 -0
  19. package/templates/apps/fullstack/packages/{{projectName}}-serv/package.json.hbs +1 -0
  20. package/templates/apps/fullstack/packages/{{projectName}}-shared/package.json.hbs +1 -0
  21. package/templates/base/eslint.config.js.hbs +1 -0
  22. package/templates/base/package.json.hbs +3 -1
  23. package/templates/skills/hai-ai/reference.md +5 -1
  24. package/templates/skills/hai-app-review/SKILL.md +6 -0
  25. package/templates/skills/hai-build/SKILL.md +8 -0
  26. package/templates/skills/hai-cache/SKILL.md +2 -0
  27. package/templates/skills/hai-capacitor/SKILL.md +2 -0
  28. package/templates/skills/hai-ci/SKILL.md +12 -0
  29. package/templates/skills/hai-core/SKILL.md +2 -0
  30. package/templates/skills/hai-crypto/SKILL.md +2 -0
  31. package/templates/skills/hai-deploy/SKILL.md +12 -0
  32. package/templates/skills/hai-iam/SKILL.md +6 -0
  33. package/templates/skills/hai-scheduler/SKILL.md +12 -0
  34. package/templates/skills/hai-ui/SKILL.md +4 -0
  35. package/dist/chunk-U4PA632G.js.map +0 -1
  36. package/dist/cli-config-templates-GSXKLGU5.js +0 -5
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { generateConfigFile } from './chunk-U4PA632G.js';
2
+ import { generateConfigFile } from './chunk-FVDXJYMO.js';
3
3
  import './chunk-WUYLYCW2.js';
4
4
  import './chunk-2H7UOFLK.js';
5
5
  import process3 from 'process';
@@ -1076,7 +1076,7 @@ async function createProject(options) {
1076
1076
  });
1077
1077
  await generateFromTemplates(projectPath, context);
1078
1078
  if (resolvedOptions.appType !== "fullstack") {
1079
- const { generateConfigFile: generateConfigFile2, generateEnvExample } = await import('./cli-config-templates-GSXKLGU5.js');
1079
+ const { generateConfigFile: generateConfigFile2, generateEnvExample } = await import('./cli-config-templates-WGN2I23J.js');
1080
1080
  const configs = resolvedOptions.moduleConfigs;
1081
1081
  const isApiWorkspace = resolvedOptions.appType === "api";
1082
1082
  const configDir = isApiWorkspace ? path2.join(projectPath, "apps", `${resolvedOptions.name}-service`, "config") : path2.join(projectPath, "config");
@@ -2118,6 +2118,69 @@ async function addModule(options) {
2118
2118
  throw error;
2119
2119
  }
2120
2120
  }
2121
+
2122
+ // messages/en-US.json
2123
+ var en_US_default = {
2124
+ cli_invalidGenerateName: "Use a name starting with a letter, with only letters, digits, hyphens or underscores",
2125
+ cli_generateOutsideWorkspace: "Generated files must remain inside the workspace, including symbolic link targets",
2126
+ cli_generateExists: "File already exists: {path}. Use --force to overwrite it",
2127
+ cli_deploy_module_missing: "Deploy module not found. Install @h-ai/deploy first:",
2128
+ cli_deploy_load_credentials: "Loading credentials...",
2129
+ cli_deploy_credentials_failed: "Failed to load credentials: {message}",
2130
+ cli_deploy_credentials_loaded: "Loaded {count} credentials",
2131
+ cli_deploy_load_config: "Loading deploy config...",
2132
+ cli_deploy_config_missing: "Deploy config not found: {path}",
2133
+ cli_deploy_config_hint: "Run: hai add deploy to generate config template",
2134
+ cli_deploy_config_loaded: "Deploy config loaded",
2135
+ cli_deploy_scanning: "Scanning application...",
2136
+ cli_deploy_scan_failed: "Scan failed: {message}",
2137
+ cli_deploy_scanned: "Scanned: {name} (SvelteKit: {sveltekit}, Services: {services})",
2138
+ cli_deploy_none: "none",
2139
+ cli_deploy_initializing: "Initializing deploy module...",
2140
+ cli_deploy_init_failed: "Init failed: {message}",
2141
+ cli_deploy_initialized: "Deploy module initialized",
2142
+ cli_deploy_deploying: "Deploying application...",
2143
+ cli_deploy_failed: "Deploy failed: {message}",
2144
+ cli_deploy_success: "Deployment successful!",
2145
+ cli_deploy_url: " URL: {url}",
2146
+ cli_deploy_id: " ID: {id}",
2147
+ cli_deploy_env: " Env: {names}",
2148
+ cli_deploy_command_failed: "Deploy command failed"
2149
+ };
2150
+
2151
+ // messages/zh-CN.json
2152
+ var zh_CN_default = {
2153
+ cli_invalidGenerateName: "\u540D\u79F0\u5FC5\u987B\u4EE5\u5B57\u6BCD\u5F00\u5934\uFF0C\u4E14\u4EC5\u5305\u542B\u5B57\u6BCD\u3001\u6570\u5B57\u3001\u77ED\u6A2A\u7EBF\u6216\u4E0B\u5212\u7EBF",
2154
+ cli_generateOutsideWorkspace: "\u751F\u6210\u6587\u4EF6\u5FC5\u987B\u4F4D\u4E8E\u5DE5\u4F5C\u76EE\u5F55\u5185\uFF0C\u7B26\u53F7\u94FE\u63A5\u76EE\u6807\u4E5F\u4E0D\u5F97\u8D8A\u754C",
2155
+ cli_generateExists: "\u6587\u4EF6\u5DF2\u5B58\u5728\uFF1A{path}\u3002\u4F7F\u7528 --force \u624D\u53EF\u8986\u76D6",
2156
+ cli_deploy_module_missing: "\u672A\u627E\u5230\u90E8\u7F72\u6A21\u5757\uFF0C\u8BF7\u5148\u5B89\u88C5 @h-ai/deploy\uFF1A",
2157
+ cli_deploy_load_credentials: "\u6B63\u5728\u52A0\u8F7D\u51ED\u8BC1\u2026",
2158
+ cli_deploy_credentials_failed: "\u52A0\u8F7D\u51ED\u8BC1\u5931\u8D25\uFF1A{message}",
2159
+ cli_deploy_credentials_loaded: "\u5DF2\u52A0\u8F7D {count} \u9879\u51ED\u8BC1",
2160
+ cli_deploy_load_config: "\u6B63\u5728\u52A0\u8F7D\u90E8\u7F72\u914D\u7F6E\u2026",
2161
+ cli_deploy_config_missing: "\u672A\u627E\u5230\u90E8\u7F72\u914D\u7F6E\uFF1A{path}",
2162
+ cli_deploy_config_hint: "\u8FD0\u884C hai add deploy \u751F\u6210\u90E8\u7F72\u914D\u7F6E\u6A21\u677F",
2163
+ cli_deploy_config_loaded: "\u90E8\u7F72\u914D\u7F6E\u5DF2\u52A0\u8F7D",
2164
+ cli_deploy_scanning: "\u6B63\u5728\u626B\u63CF\u5E94\u7528\u2026",
2165
+ cli_deploy_scan_failed: "\u626B\u63CF\u5931\u8D25\uFF1A{message}",
2166
+ cli_deploy_scanned: "\u5DF2\u626B\u63CF\uFF1A{name}\uFF08SvelteKit\uFF1A{sveltekit}\uFF0C\u670D\u52A1\uFF1A{services}\uFF09",
2167
+ cli_deploy_none: "\u65E0",
2168
+ cli_deploy_initializing: "\u6B63\u5728\u521D\u59CB\u5316\u90E8\u7F72\u6A21\u5757\u2026",
2169
+ cli_deploy_init_failed: "\u521D\u59CB\u5316\u5931\u8D25\uFF1A{message}",
2170
+ cli_deploy_initialized: "\u90E8\u7F72\u6A21\u5757\u5DF2\u521D\u59CB\u5316",
2171
+ cli_deploy_deploying: "\u6B63\u5728\u90E8\u7F72\u5E94\u7528\u2026",
2172
+ cli_deploy_failed: "\u90E8\u7F72\u5931\u8D25\uFF1A{message}",
2173
+ cli_deploy_success: "\u90E8\u7F72\u6210\u529F\uFF01",
2174
+ cli_deploy_url: " \u5730\u5740\uFF1A{url}",
2175
+ cli_deploy_id: " \u6807\u8BC6\uFF1A{id}",
2176
+ cli_deploy_env: " \u73AF\u5883\u53D8\u91CF\uFF1A{names}",
2177
+ cli_deploy_command_failed: "\u90E8\u7F72\u547D\u4EE4\u5931\u8D25"
2178
+ };
2179
+
2180
+ // src/cli-i18n.ts
2181
+ var cliM = core.i18n.createMessageGetter({ "en-US": en_US_default, "zh-CN": zh_CN_default });
2182
+
2183
+ // src/commands/cli-deploy.ts
2121
2184
  async function deployCommand(options) {
2122
2185
  const spinner = ora();
2123
2186
  const cwd = options.cwd ?? process3.cwd();
@@ -2126,67 +2189,68 @@ async function deployCommand(options) {
2126
2189
  try {
2127
2190
  let deployModule;
2128
2191
  try {
2129
- deployModule = await import('./src-QNEJJ3BV.js');
2192
+ deployModule = await import('./src-6ZBWDBUB.js');
2130
2193
  } catch {
2131
- core.logger.error(chalk.red("Deploy module not found. Install @h-ai/deploy first:"));
2194
+ core.logger.error(chalk.red(cliM("cli_deploy_module_missing")));
2132
2195
  core.logger.info(chalk.cyan(" pnpm add @h-ai/deploy"));
2133
- return;
2196
+ throw new Error(cliM("cli_deploy_module_missing"));
2134
2197
  }
2135
2198
  const { deploy } = deployModule;
2136
- spinner.start("Loading credentials...");
2199
+ spinner.start(cliM("cli_deploy_load_credentials"));
2137
2200
  const credResult = deploy.credentials.load();
2138
2201
  if (!credResult.success) {
2139
- spinner.fail(chalk.red(`Failed to load credentials: ${credResult.error.message}`));
2140
- return;
2202
+ throw new Error(cliM("cli_deploy_credentials_failed", { params: { message: credResult.error.message } }));
2141
2203
  }
2142
- spinner.succeed(`Loaded ${credResult.data.length} credentials`);
2143
- spinner.start("Loading deploy config...");
2204
+ spinner.succeed(cliM("cli_deploy_credentials_loaded", { params: { count: credResult.data.length } }));
2205
+ spinner.start(cliM("cli_deploy_load_config"));
2144
2206
  const configPath = resolve(appDir, "config", "_deploy.yml");
2145
2207
  if (!existsSync(configPath)) {
2146
- spinner.fail(chalk.red(`Deploy config not found: ${configPath}`));
2147
- core.logger.info(chalk.cyan(" Run: hai add deploy to generate config template"));
2148
- return;
2208
+ spinner.fail(chalk.red(cliM("cli_deploy_config_missing", { params: { path: configPath } })));
2209
+ core.logger.info(chalk.cyan(cliM("cli_deploy_config_hint")));
2210
+ throw new Error(cliM("cli_deploy_config_missing", { params: { path: configPath } }));
2149
2211
  }
2150
2212
  const configContent = readFileSync(configPath, "utf-8");
2151
2213
  const rawConfig = interpolateEnvFallback(configContent);
2152
2214
  const deployConfig = parse(rawConfig);
2153
- spinner.succeed("Deploy config loaded");
2154
- spinner.start("Scanning application...");
2215
+ spinner.succeed(cliM("cli_deploy_config_loaded"));
2216
+ spinner.start(cliM("cli_deploy_scanning"));
2155
2217
  const scanResult = await deploy.scan(appDir);
2156
2218
  if (!scanResult.success) {
2157
- spinner.fail(chalk.red(`Scan failed: ${scanResult.error.message}`));
2158
- return;
2219
+ throw new Error(cliM("cli_deploy_scan_failed", { params: { message: scanResult.error.message } }));
2159
2220
  }
2160
2221
  const scan = scanResult.data;
2161
- spinner.succeed(`Scanned: ${scan.appName} (SvelteKit: ${scan.isSvelteKit}, Services: ${scan.requiredServices.join(", ") || "none"})`);
2162
- spinner.start("Initializing deploy module...");
2222
+ spinner.succeed(cliM("cli_deploy_scanned", { params: { name: scan.appName, sveltekit: String(scan.isSvelteKit), services: scan.requiredServices.join(", ") || cliM("cli_deploy_none") } }));
2223
+ spinner.start(cliM("cli_deploy_initializing"));
2163
2224
  const initResult = await deploy.init(deployConfig);
2164
2225
  if (!initResult.success) {
2165
- spinner.fail(chalk.red(`Init failed: ${initResult.error.message}`));
2166
- return;
2226
+ throw new Error(cliM("cli_deploy_init_failed", { params: { message: initResult.error.message } }));
2167
2227
  }
2168
2228
  closeDeploy = () => deploy.close();
2169
- spinner.succeed("Deploy module initialized");
2170
- spinner.start("Deploying application...");
2229
+ spinner.succeed(cliM("cli_deploy_initialized"));
2230
+ spinner.start(cliM("cli_deploy_deploying"));
2171
2231
  const deployResult = await deploy.deployApp(appDir, {
2172
2232
  projectName: options.projectName,
2173
2233
  skipProvision: options.skipProvision,
2174
2234
  skipBuild: options.skipBuild
2175
2235
  });
2176
2236
  if (!deployResult.success) {
2177
- spinner.fail(chalk.red(`Deploy failed: ${deployResult.error.message}`));
2178
- return;
2237
+ if (deployResult.error.ext?.recovery) {
2238
+ core.logger.warn(deployResult.error.suggestion ?? cliM("cli_deploy_command_failed"), {
2239
+ recovery: deployResult.error.ext.recovery
2240
+ });
2241
+ }
2242
+ throw new Error(cliM("cli_deploy_failed", { params: { message: deployResult.error.message } }));
2179
2243
  }
2180
- spinner.succeed(chalk.green("Deployment successful!"));
2244
+ spinner.succeed(chalk.green(cliM("cli_deploy_success")));
2181
2245
  core.logger.info("");
2182
- core.logger.info(chalk.cyan(` URL: ${deployResult.data.url}`));
2183
- core.logger.info(chalk.gray(` ID: ${deployResult.data.deploymentId}`));
2246
+ core.logger.info(chalk.cyan(cliM("cli_deploy_url", { params: { url: deployResult.data.url } })));
2247
+ core.logger.info(chalk.gray(cliM("cli_deploy_id", { params: { id: deployResult.data.deploymentId } })));
2184
2248
  if (deployResult.data.envVarsSet.length > 0) {
2185
- core.logger.info(chalk.gray(` Env: ${deployResult.data.envVarsSet.join(", ")}`));
2249
+ core.logger.info(chalk.gray(cliM("cli_deploy_env", { params: { names: deployResult.data.envVarsSet.join(", ") } })));
2186
2250
  }
2187
2251
  core.logger.info("");
2188
2252
  } catch (error) {
2189
- spinner.fail(chalk.red("Deploy command failed"));
2253
+ spinner.fail(chalk.red(cliM("cli_deploy_command_failed")));
2190
2254
  core.logger.error(error instanceof Error ? error.message : String(error));
2191
2255
  throw error;
2192
2256
  } finally {
@@ -2195,6 +2259,7 @@ async function deployCommand(options) {
2195
2259
  await closeDeploy();
2196
2260
  } catch (error) {
2197
2261
  core.logger.error("Failed to close deploy module", { error });
2262
+ process3.exitCode = 1;
2198
2263
  }
2199
2264
  }
2200
2265
  }
@@ -2244,24 +2309,41 @@ async function generate(options) {
2244
2309
  if (!generator) {
2245
2310
  throw new Error(`\u672A\u77E5\u7684\u751F\u6210\u5668\u7C7B\u578B: ${resolvedOptions.type}`);
2246
2311
  }
2312
+ if (!/^[a-z][a-z0-9]*(?:[-_][a-z0-9]+)*$/i.test(resolvedOptions.name))
2313
+ throw new Error(cliM("cli_invalidGenerateName"));
2247
2314
  const outputDir = path2.resolve(cwd, resolvedOptions.output || generator.defaultPath);
2248
2315
  const resolvedCwd = path2.resolve(cwd);
2249
2316
  if (!outputDir.startsWith(resolvedCwd + path2.sep) && outputDir !== resolvedCwd) {
2250
- core.logger.error(chalk.red(`\u8F93\u51FA\u8DEF\u5F84 "${resolvedOptions.output}" \u8D85\u51FA\u5DE5\u4F5C\u76EE\u5F55\u8303\u56F4`));
2251
- return;
2317
+ throw new Error(cliM("cli_generateOutsideWorkspace"));
2252
2318
  }
2253
2319
  const context = createTemplateContext(resolvedOptions.name, {
2254
2320
  projectName: project?.name
2255
2321
  });
2256
2322
  spinner.start(`\u751F\u6210 ${generator.description}...`);
2257
- const files = await generator.generate(resolvedOptions.name, context, outputDir);
2323
+ const files = await generator.generate(context, outputDir);
2324
+ const realCwd = await fse.realpath(resolvedCwd);
2325
+ for (const file of files) {
2326
+ if (!resolvedOptions.force && await fse.pathExists(file.path))
2327
+ throw new Error(cliM("cli_generateExists", { params: { path: file.path } }));
2328
+ let ancestor = file.path;
2329
+ while (!await fse.pathExists(ancestor))
2330
+ ancestor = path2.dirname(ancestor);
2331
+ const realAncestor = await fse.realpath(ancestor);
2332
+ const relative = path2.relative(realCwd, realAncestor);
2333
+ if (relative === ".." || relative.startsWith(`..${path2.sep}`) || path2.isAbsolute(relative))
2334
+ throw new Error(cliM("cli_generateOutsideWorkspace"));
2335
+ }
2336
+ for (const file of files) {
2337
+ await fse.ensureDir(path2.dirname(file.path));
2338
+ await fse.writeFile(file.path, file.content, { encoding: "utf-8", flag: resolvedOptions.force ? "w" : "wx" });
2339
+ }
2258
2340
  spinner.succeed();
2259
2341
  core.logger.info("", {});
2260
2342
  core.logger.info(chalk.green("\u2714 \u751F\u6210\u5B8C\u6210\uFF01"));
2261
2343
  core.logger.info("", {});
2262
2344
  core.logger.info("\u521B\u5EFA\u7684\u6587\u4EF6:");
2263
2345
  files.forEach((file) => {
2264
- core.logger.info(chalk.cyan(` ${path2.relative(cwd, file)}`));
2346
+ core.logger.info(chalk.cyan(` ${path2.relative(cwd, file.path)}`));
2265
2347
  });
2266
2348
  core.logger.info("", {});
2267
2349
  } catch (error) {
@@ -2305,76 +2387,72 @@ async function resolveGenerateOptions(options) {
2305
2387
  cwd: options.cwd ?? "."
2306
2388
  };
2307
2389
  }
2308
- async function generatePage(_name, context, outputDir) {
2390
+ async function generatePage(context, outputDir) {
2309
2391
  const files = [];
2310
2392
  const pageDir = path2.join(outputDir, context.kebabCase);
2311
- const pageContent = `<script lang="ts">
2312
- /**
2313
- * ${context.pascalCase} \u9875\u9762
2314
- */
2315
- </script>
2316
-
2393
+ const pageContent = `<!-- ${context.pascalCase} \u9875\u9762 -->
2317
2394
  <svelte:head>
2318
2395
  <title>${context.pascalCase}</title>
2319
2396
  </svelte:head>
2320
2397
 
2321
- <div class="container mx-auto p-4">
2322
- <h1 class="text-2xl font-bold mb-4">${context.pascalCase}</h1>
2323
-
2398
+ <div class='container mx-auto p-4'>
2399
+ <h1 class='text-2xl font-bold mb-4'>${context.pascalCase}</h1>
2400
+
2324
2401
  <!-- \u9875\u9762\u5185\u5BB9 -->
2325
2402
  </div>
2326
2403
  `;
2327
2404
  const pagePath = path2.join(pageDir, "+page.svelte");
2328
- await writeFile(pagePath, pageContent);
2329
- files.push(pagePath);
2405
+ files.push({ path: pagePath, content: pageContent });
2330
2406
  const serverContent = `/**
2331
2407
  * ${context.pascalCase} \u9875\u9762\u670D\u52A1\u7AEF
2332
2408
  */
2333
- import type { PageServerLoad, Actions } from './$types'
2409
+ import type { Actions, PageServerLoad } from './$types'
2334
2410
 
2335
- export const load: PageServerLoad = async ({ locals }) => {
2411
+ export const load: PageServerLoad = async () => {
2336
2412
  return {
2337
2413
  // \u9875\u9762\u6570\u636E
2338
2414
  }
2339
2415
  }
2340
2416
 
2341
2417
  export const actions: Actions = {
2342
- default: async ({ request, locals }) => {
2418
+ default: async () => {
2343
2419
  // \u8868\u5355\u5904\u7406
2344
2420
  },
2345
2421
  }
2346
2422
  `;
2347
2423
  const serverPath = path2.join(pageDir, "+page.server.ts");
2348
- await writeFile(serverPath, serverContent);
2349
- files.push(serverPath);
2424
+ files.push({ path: serverPath, content: serverContent });
2350
2425
  return files;
2351
2426
  }
2352
- async function generateComponent(_name, context, outputDir) {
2427
+ async function generateComponent(context, outputDir) {
2353
2428
  const files = [];
2354
- const componentContent = `<script lang="ts">
2429
+ const componentContent = `<script lang='ts'>
2430
+ import type { Snippet } from 'svelte'
2431
+
2355
2432
  /**
2356
2433
  * ${context.pascalCase} \u7EC4\u4EF6
2357
2434
  */
2358
-
2435
+
2359
2436
  interface Props {
2360
2437
  /** \u81EA\u5B9A\u4E49 class */
2361
2438
  class?: string
2439
+ /** \u5B50\u5185\u5BB9\uFF08Svelte 5 snippet\uFF09 */
2440
+ children?: Snippet
2362
2441
  }
2363
-
2364
- let { class: className = '' }: Props = $props()
2442
+
2443
+ const { class: className = '', children }: Props = $props()
2365
2444
  </script>
2366
2445
 
2367
2446
  <div class={className}>
2368
2447
  <!-- \u7EC4\u4EF6\u5185\u5BB9 -->
2369
- <slot />
2448
+ {@render children?.()}
2370
2449
  </div>
2371
2450
  `;
2372
2451
  const componentPath = path2.join(outputDir, `${context.pascalCase}.svelte`);
2373
- await writeFile(componentPath, componentContent);
2374
- files.push(componentPath);
2452
+ files.push({ path: componentPath, content: componentContent });
2375
2453
  return files;
2376
2454
  }
2377
- async function generateApi(_name, context, outputDir) {
2455
+ async function generateApi(context, outputDir) {
2378
2456
  const files = [];
2379
2457
  const apiDir = path2.join(outputDir, context.kebabCase);
2380
2458
  const serverContent = `/**
@@ -2382,44 +2460,35 @@ async function generateApi(_name, context, outputDir) {
2382
2460
  */
2383
2461
  import type { RequestHandler } from './$types'
2384
2462
  import { kit } from '@h-ai/kit'
2463
+ import { z } from 'zod'
2464
+
2465
+ /** \u793A\u4F8B\u8F93\u5165\uFF1B\u6DFB\u52A0\u4E1A\u52A1\u903B\u8F91\u524D\u6269\u5C55\u5B57\u6BB5\u548C\u7EA6\u675F\u3002 */
2466
+ const InputSchema = z.object({
2467
+ /** \u4E1A\u52A1\u540D\u79F0\uFF08\u975E\u7A7A\uFF09 */
2468
+ name: z.string().min(1),
2469
+ })
2385
2470
 
2386
2471
  /**
2387
2472
  * GET ${context.kebabCase}
2388
2473
  */
2389
- export const GET: RequestHandler = async ({ locals }) => {
2390
- try {
2391
- const { requestId } = locals
2392
-
2393
- return kit.response.ok({ message: 'Hello from ${context.kebabCase}' }, requestId)
2394
- }
2395
- catch (error) {
2396
- return kit.response.internalError()
2397
- }
2398
- }
2474
+ export const GET: RequestHandler = kit.handler(async ({ locals }) => {
2475
+ return kit.response.ok({ items: [] }, locals.requestId)
2476
+ })
2399
2477
 
2400
2478
  /**
2401
2479
  * POST ${context.kebabCase}
2402
2480
  */
2403
- export const POST: RequestHandler = async ({ request, locals }) => {
2404
- try {
2405
- const { requestId } = locals
2406
- const body = await request.json()
2407
-
2408
- // \u5904\u7406\u8BF7\u6C42
2409
-
2410
- return kit.response.ok({ success: true }, requestId)
2411
- }
2412
- catch (error) {
2413
- return kit.response.internalError()
2414
- }
2415
- }
2481
+ export const POST: RequestHandler = kit.handler(async ({ request, locals }) => {
2482
+ const body = await kit.validate.body(request, InputSchema)
2483
+ // \u6821\u9A8C\u901A\u8FC7\u540E\u518D\u6267\u884C\u4E1A\u52A1\u64CD\u4F5C\uFF1B\u793A\u4F8B\u4EC5\u56DE\u4F20\u6570\u636E\uFF0C\u4E0D\u6267\u884C\u5199\u5165\u3002
2484
+ return kit.response.ok(body, locals.requestId)
2485
+ })
2416
2486
  `;
2417
2487
  const serverPath = path2.join(apiDir, "+server.ts");
2418
- await writeFile(serverPath, serverContent);
2419
- files.push(serverPath);
2488
+ files.push({ path: serverPath, content: serverContent });
2420
2489
  return files;
2421
2490
  }
2422
- async function generateModel(_name, context, outputDir) {
2491
+ async function generateModel(context, outputDir) {
2423
2492
  const files = [];
2424
2493
  const modelContent = `/**
2425
2494
  * ${context.pascalCase} \u6A21\u578B
@@ -2430,9 +2499,13 @@ import { z } from 'zod'
2430
2499
  * ${context.pascalCase} Schema
2431
2500
  */
2432
2501
  export const ${context.camelCase}Schema = z.object({
2502
+ /** \u8BB0\u5F55\u552F\u4E00\u6807\u8BC6 */
2433
2503
  id: z.string(),
2504
+ /** \u663E\u793A\u540D\u79F0 */
2434
2505
  name: z.string().min(1),
2506
+ /** \u521B\u5EFA\u65F6\u95F4 */
2435
2507
  createdAt: z.date(),
2508
+ /** \u6700\u540E\u4FEE\u6539\u65F6\u95F4 */
2436
2509
  updatedAt: z.date(),
2437
2510
  })
2438
2511
 
@@ -2458,39 +2531,35 @@ export type Create${context.pascalCase} = z.infer<typeof create${context.pascalC
2458
2531
  export type Update${context.pascalCase} = z.infer<typeof update${context.pascalCase}Schema>
2459
2532
  `;
2460
2533
  const modelPath = path2.join(outputDir, `${context.kebabCase}.ts`);
2461
- await writeFile(modelPath, modelContent);
2462
- files.push(modelPath);
2534
+ files.push({ path: modelPath, content: modelContent });
2463
2535
  return files;
2464
2536
  }
2465
- async function generateMigration(_name, context, outputDir) {
2537
+ async function generateMigration(context, outputDir) {
2466
2538
  const files = [];
2467
2539
  const timestamp = Date.now();
2468
2540
  const migrationContent = `/**
2469
2541
  * \u8FC1\u79FB: ${context.pascalCase}
2470
2542
  * \u65F6\u95F4: ${(/* @__PURE__ */ new Date()).toISOString()}
2471
2543
  */
2472
- import type { MigrationFn } from '@h-ai/reldb'
2544
+ import { reldb } from '@h-ai/reldb'
2473
2545
 
2474
- export const up: MigrationFn = async (db) => {
2475
- // \u5347\u7EA7\u903B\u8F91
2476
- await db.run(\`
2477
- CREATE TABLE IF NOT EXISTS ${context.snakeCase} (
2478
- id TEXT PRIMARY KEY,
2479
- name TEXT NOT NULL,
2480
- created_at INTEGER NOT NULL,
2481
- updated_at INTEGER NOT NULL
2482
- )
2483
- \`)
2546
+ /** \u5347\u7EA7\uFF1A\u8C03\u7528\u524D\u521D\u59CB\u5316 reldb\uFF0C\u8C03\u7528\u65B9\u5FC5\u987B\u68C0\u67E5\u8FD4\u56DE\u7684 HaiResult\u3002 */
2547
+ export function up() {
2548
+ return reldb.ddl.createTable('hai_app_${context.snakeCase}', {
2549
+ id: { type: 'TEXT', primaryKey: true },
2550
+ name: { type: 'TEXT', notNull: true },
2551
+ created_at: { type: 'TIMESTAMP', notNull: true },
2552
+ updated_at: { type: 'TIMESTAMP', notNull: true },
2553
+ })
2484
2554
  }
2485
2555
 
2486
- export const down: MigrationFn = async (db) => {
2487
- // \u56DE\u6EDA\u903B\u8F91
2488
- await db.run('DROP TABLE IF EXISTS ${context.snakeCase}')
2556
+ /** \u56DE\u6EDA\u4F1A\u5220\u9664\u6574\u5F20\u8868\uFF1B\u7531\u5E94\u7528\u786E\u8BA4\u6570\u636E\u53EF\u5220\u9664\u540E\u663E\u5F0F\u8C03\u7528\u3002 */
2557
+ export function down() {
2558
+ return reldb.ddl.dropTable('hai_app_${context.snakeCase}')
2489
2559
  }
2490
2560
  `;
2491
2561
  const migrationPath = path2.join(outputDir, `${timestamp}_${context.snakeCase}.ts`);
2492
- await writeFile(migrationPath, migrationContent);
2493
- files.push(migrationPath);
2562
+ files.push({ path: migrationPath, content: migrationContent });
2494
2563
  return files;
2495
2564
  }
2496
2565
  var MODULE_CONFIG_MAP = {