@h-ai/cli 0.1.0-alpha.48 → 0.1.0-alpha.50

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.48",
4
+ "version": "0.1.0-alpha.50",
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.48"
37
+ "@h-ai/core": "0.1.0-alpha.50"
38
38
  },
39
39
  "devDependencies": {
40
40
  "@types/fs-extra": "^11.0.4",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "{{projectName}}",
3
- "version": "0.1.0-alpha.48",
3
+ "version": "0.1.0-alpha.50",
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`
@@ -226,7 +226,7 @@ const consumed = await iam.ticket.consume(ticket, { purpose: 'ai-audio' }) //
226
226
  | 方法 | 签名 | 说明 |
227
227
  | -------------- | ----------------------------------------------------------------------- | -------------------------------------- |
228
228
  | `createApiKey` | `(userId, options: CreateApiKeyOptions) => Promise<HaiResult<CreateApiKeyResult>>` | 创建 API Key(明文密钥仅返回一次) |
229
- | `listApiKeys` | `(userId) => Promise<HaiResult<ApiKey[]>>` | 列出用户的所有 API Key |
229
+ | `listApiKeys` | `(userId, options?) => Promise<HaiResult<PaginatedResult<ApiKey>>>` | 分页列出用户的 API Key |
230
230
  | `getApiKey` | `(keyId) => Promise<HaiResult<ApiKey \| null>>` | 获取 API Key 详情 |
231
231
  | `revokeApiKey` | `(keyId) => Promise<HaiResult<void>>` | 吊销/删除 API Key |
232
232
  | `verifyApiKey` | `(rawKey) => Promise<HaiResult<ApiKey>>` | 验证 API Key 并返回实体(含用户 ID) |
@@ -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)
@@ -474,6 +474,7 @@ const p = usePlatform()
474
474
  | `card` | 卡片列表项渲染 snippet;仍复用分页和独立滚动容器 |
475
475
  | `detailColumns` | 点击后打开详情抽屉的列 key,例如 `['title']` |
476
476
  | `sortableColumns` | 可执行服务端排序的列;为空时使用全部列表列 |
477
+ | `loading` / `loaded` / `error` / `onRetry` / `keepPreviousData` | 受控加载态:仅 `loaded && total===0` 显示空状态;首次显示查询中;失败显示错误+重试;`keepPreviousData`(默认 true)失败保留旧数据 |
477
478
 
478
479
  注意:`CrudPage` 会占满父级可用高度;表格无论是否有数据都会撑满剩余空间,仅数据区滚动,表头和分页栏固定。
479
480
 
@@ -501,6 +502,12 @@ form={{ variant: 'drawer', drawerWidth: '40rem' }}
501
502
 
502
503
  抽屉默认可拖动左侧边缘调整宽度,并按 `crud.resource.name` 记忆;传 `drawerResizable: false` 可关闭。
503
504
 
505
+ ### 纯客户端控制器与惰性页签
506
+
507
+ - 无 SvelteKit SSR 时用 `defineCrud` + `createCrudController` 托管 `data` / `nav`:latest-wins(seq + AbortSignal)、失败保留旧数据、并发去重、查询串回填地址栏。`<CrudPage data={c.data} nav={c.nav} loading={c.loading} loaded={c.loaded} error={c.error} onRetry={c.reload} />`。
508
+ - `Tabs` 传 `lazy` 后未激活页签不渲染(`children` 提供 `isActivated(key)`),`onactivate(key)` 首次激活触发一次;配合 `createCrudController({ autoLoad: false })` 在页签首次可见时才 `load()`。
509
+ - Toast 已内置 `notifySuccess` / `notifyError(err, fallback?)` / `notifyInfo` / `notifyWarning`,业务应用无需自建 toast 工具文件。
510
+
504
511
  ---
505
512
 
506
513
  ## Design Token 系统