@h-ai/cli 0.1.0-alpha.47 → 0.1.0-alpha.49

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@h-ai/cli",
3
3
  "type": "module",
4
- "version": "0.1.0-alpha.47",
4
+ "version": "0.1.0-alpha.49",
5
5
  "description": "Hai Framework CLI for project scaffolding and code generation.",
6
6
  "license": "Apache-2.0",
7
7
  "keywords": [
@@ -34,7 +34,7 @@
34
34
  "ora": "^9.1.0",
35
35
  "prompts": "^2.4.2",
36
36
  "yaml": "^2.7.0",
37
- "@h-ai/core": "0.1.0-alpha.47"
37
+ "@h-ai/core": "0.1.0-alpha.49"
38
38
  },
39
39
  "devDependencies": {
40
40
  "@types/fs-extra": "^11.0.4",
@@ -2,6 +2,10 @@
2
2
  # {{projectName}} API Service - Serv / HTTP 入口配置
3
3
  # =============================================================================
4
4
 
5
+ server:
6
+ host: 127.0.0.1
7
+ port: 3000
8
+
5
9
  http:
6
10
  apiPrefix: /api/v1
7
11
  openapi:
@@ -1,3 +1,4 @@
1
+ import type { ServConfig } from '@h-ai/serv'
1
2
  import process from 'node:process'
2
3
  import { core } from '@h-ai/core'
3
4
  import { serv } from '@h-ai/serv'
@@ -9,7 +10,11 @@ const logger = core.logger.child({ app: '{{api.servicePackageName}}', scope: 'en
9
10
  async function main(): Promise<void> {
10
11
  await initApp()
11
12
 
13
+ // host/port 来自 config/_serv.yml 的 server(HOST/PORT 环境变量高于配置文件)。
14
+ const { server } = core.config.getOrThrow<ServConfig>('serv')
12
15
  serv.listen({{api.serviceAppFactoryName}}(), {
16
+ host: server.host,
17
+ port: server.port,
13
18
  onListening: info => logger.info('API service listening', { address: info.address, port: info.port }),
14
19
  onClose: closeApp,
15
20
  })
@@ -5,6 +5,10 @@
5
5
  # 业务接口路径直接写在对应 *-contract.ts;公共 HTTP 传输配置以本文件为准。
6
6
  # =============================================================================
7
7
 
8
+ server:
9
+ host: 127.0.0.1
10
+ port: 3000
11
+
8
12
  http:
9
13
  apiPrefix: /api/v1
10
14
  openapi:
@@ -1,18 +1,18 @@
1
- import process from 'node:process'
1
+ import type { ServConfigInput } from '@h-ai/serv'
2
2
  import { core } from '@h-ai/core'
3
- import { serv } from '@h-ai/serv'
3
+ import { serv, ServConfigSchema } from '@h-ai/serv'
4
4
  import { createServerApp } from './app-server.js'
5
5
 
6
6
  // 加载 config/ 下的 _core.yml / _serv.yml 等配置文件(约定优于配置)
7
7
  core.init({ configDir: './config', logging: { level: 'info' } })
8
8
 
9
+ // host/port 来自 config/_serv.yml 的 server(HOST/PORT 环境变量高于配置文件)。
10
+ const { server } = ServConfigSchema.parse(core.config.get<ServConfigInput>('serv') ?? {})
9
11
  const app = createServerApp()
10
- const port = process.env.PORT ? Number(process.env.PORT) : 3000
11
- const host = process.env.HOST ?? '127.0.0.1'
12
12
  const listenApp = app as Parameters<typeof serv.listen>[0]
13
13
 
14
14
  serv.listen(listenApp, {
15
- port,
16
- host,
17
- onListening: info => core.logger.info('{{fullstack.servPackageName}} listening', { host, port: info.port }),
15
+ host: server.host,
16
+ port: server.port,
17
+ onListening: info => core.logger.info('{{fullstack.servPackageName}} listening', { host: server.host, port: info.port }),
18
18
  })
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "{{projectName}}",
3
- "version": "0.1.0-alpha.47",
3
+ "version": "0.1.0-alpha.49",
4
4
  "private": true,
5
5
  "description": "{{#if_eq appType 'admin'}}Enterprise management system built with hai Agent Framework{{/if_eq}}{{#if_eq appType 'website'}}Enterprise website built with hai Agent Framework{{/if_eq}}{{#if_eq appType 'h5'}}Mobile H5 app built with hai Agent Framework{{/if_eq}}{{#if_eq appType 'api'}}REST API service built with hai Agent Framework{{/if_eq}}{{#if_eq appType 'mobile-app'}}Mobile app built with hai Agent Framework{{/if_eq}}",
6
6
  "keywords": ["hai", {{#unless isSvelteOnlyApp}}"sveltekit", {{/unless}}"svelte5", "{{appType}}"],
@@ -202,6 +202,7 @@ await ai.llm.ask('翻译这段话', { tempModel: { model: 'gpt-4o', apiKey: 'sk-
202
202
 
203
203
  ```ts
204
204
  import { createMcpServer, StreamableHTTPServerTransport } from '@h-ai/ai'
205
+ import { core } from '@h-ai/core'
205
206
 
206
207
  const server = createMcpServer({ name: 'my-server', version: '1.0.0' })
207
208
  server.registerTool('search', {
@@ -211,7 +212,7 @@ server.registerTool('search', {
211
212
  content: [{ type: 'text', text: `Results for ${query}` }],
212
213
  }))
213
214
 
214
- await server.connect(new StreamableHTTPServerTransport({ sessionIdGenerator: crypto.randomUUID }))
215
+ await server.connect(new StreamableHTTPServerTransport({ sessionIdGenerator: core.id.uuid }))
215
216
  ```
216
217
 
217
218
  ## 记忆、RAG 与知识库
@@ -233,7 +233,7 @@ export async function createArticle(input: CreateArticleInput): Promise<HaiResul
233
233
  logger.debug('Creating article', { title: input.title })
234
234
 
235
235
  const result = await reldb.crud.create('articles', {
236
- id: crypto.randomUUID(),
236
+ id: core.id.uuid(),
237
237
  ...input,
238
238
  created_at: new Date().toISOString(),
239
239
  })
@@ -316,6 +316,14 @@ core.id.isValidUUID('f47ac10b-...') // true/false
316
316
  core.id.isValidNanoId('abc123', 6) // true/false
317
317
  ```
318
318
 
319
+ #### UUID 生成规范(强制)
320
+
321
+ - 生成 UUID **一律用 `core.id.uuid()`**,禁止裸调 `crypto.randomUUID()`。
322
+ - 原因:`crypto.randomUUID` 仅在**安全上下文(HTTPS / localhost)**下可用,生产 http 环境会抛
323
+ `crypto.randomUUID is not a function`;`core.id.uuid()` 已做 `randomUUID → getRandomValues → Math.random` 三级兜底,Node.js 与浏览器通用。
324
+ - 需要不带横线的 compact 形式:`core.id.uuid().replaceAll('-', '')`。
325
+ - 代码评审与新建代码时以此为准,发现裸调 `crypto.randomUUID()` 一律改走 `core.id.uuid()`。
326
+
319
327
  ### 工具函数
320
328
 
321
329
  #### 类型检查 — `core.typeUtils`
@@ -131,6 +131,10 @@ transport 默认保护同源 `/api/*` endpoint 与 SvelteKit `__data.json` 页
131
131
  同时驱动 `hooks.server.ts` 与浏览器端 `kit.client.installBrowserTransport()`:
132
132
 
133
133
  ```yml
134
+ server:
135
+ host: 127.0.0.1
136
+ port: 3000
137
+
134
138
  transport:
135
139
  keyExchangePath: /api/_hai/key-exchange
136
140
  requireEncryption: true
@@ -150,7 +154,8 @@ import rawKitConfig from '../config/_kit.yml?raw'
150
154
  export const appKitConfig = resolveKitConfig(parse(rawKitConfig) ?? {})
151
155
  ```
152
156
 
153
- `_kit.yml` 只放公开的 transport 路径/开关,不放密钥;如自定义 `keyExchangePath`,浏览器端必须同步映射为 `keyExchangeUrl`。
157
+ `resolveKitConfig()` 同时解析 `server`(host/port)与 `transport`;host/port 优先级为 `HOST` / `PORT` 环境变量 > `_kit.yml` 的 `server` > 默认 `127.0.0.1:3000`(与 `@h-ai/serv` 同构)。
158
+ `_kit.yml` 只放公开的 server/transport 路径/开关,不放密钥;如自定义 `keyExchangePath`,浏览器端必须同步映射为 `keyExchangeUrl`。
154
159
 
155
160
  ## 核心 API
156
161
 
@@ -307,7 +307,7 @@ class UserRepository extends BaseReldbCrudRepository<User> {
307
307
  **自动能力**:
308
308
 
309
309
  - `createdAt` / `updatedAt` 字段自动填充时间戳
310
- - 主键生成(非 autoIncrement 主键默认使用 `crypto.randomUUID()`)
310
+ - 主键生成(非 autoIncrement 主键默认使用随机 UUID,底层带安全上下文守卫,不可用时回退;业务侧手动生成 UUID 一律用 `core.id.uuid()`)
311
311
  - BOOLEAN → 1/0(SQLite/MySQL)或 true/false(PostgreSQL)
312
312
  - TIMESTAMP → 毫秒时间戳(SQLite)或 Date(PG/MySQL)
313
313
  - JSON → 字符串序列化(SQLite/MySQL)或原生 JSONB(PG)
@@ -86,8 +86,11 @@ const app = serv.createApp({
86
86
  },
87
87
  })
88
88
 
89
- // Node.js 启动(自动读取 PORT / HOST 环境变量;onClose 自动监听 SIGINT/SIGTERM)
89
+ // Node.js 启动(host/port 来自 _serv.yml 的 server;HOST/PORT 环境变量高于配置文件;onClose 自动监听 SIGINT/SIGTERM)
90
+ const { server } = core.config.getOrThrow<ServConfig>('serv')
90
91
  serv.listen(app, {
92
+ host: server.host,
93
+ port: server.port,
91
94
  onListening: info => logger.info('API service listening', { port: info.port }),
92
95
  onClose: closeApp, // 业务模块关闭函数(ai/storage/iam/reldb 等反向释放)
93
96
  })
@@ -265,6 +268,10 @@ const app = serv.createApp({
265
268
 
266
269
  ```yaml
267
270
  # config/_serv.yml
271
+ server:
272
+ host: 127.0.0.1
273
+ port: 3000
274
+
268
275
  http:
269
276
  apiPrefix: /api/v1
270
277
  openapi: