@h-ai/cli 0.1.0-alpha.44 → 0.1.0-alpha.45

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.44",
4
+ "version": "0.1.0-alpha.45",
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.44"
37
+ "@h-ai/core": "0.1.0-alpha.45"
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.44",
3
+ "version": "0.1.0-alpha.45",
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}}"],
@@ -39,6 +39,7 @@ description: 使用 @h-ai/serv 将 oRPC contract 挂载为最小 HTTP App 抽象
39
39
  - 通过 `serv.createRuntimeSecurityPolicy(...)` 对生产 CORS Origin、原生 WebView Origin、Secure Cookie 与 API 文档暴露执行 fail-closed 策略
40
40
  - 启用传输加密:`serv.createApp({ transport: { crypto } })`
41
41
  - 高级场景通过 `createApp({ middlewares })` 自定义 HTTP middleware,或通过共享类型自定义 procedure wrapper
42
+ - 公开只读文件通过 `serv.storageAssets(...)` 统一处理安全 key、MIME 白名单、GET/HEAD、ETag 与缓存头
42
43
  - 为 Audio WebSocket 配置一次性 ticket 校验、IAM/模型/配额授权与并发释放钩子
43
44
 
44
45
  ---
@@ -299,7 +300,7 @@ export function createServerApp() {
299
300
  http?, // ServHttpConfigInput — HTTP 端点配置(见下方配置节)
300
301
  middlewares?, // readonly ServMiddlewareMount[] — 自定义 HTTP middleware(日志/CORS/限流/租户头校验)
301
302
  iam?, // ServIam — 顶层 IAM 句柄,同时驱动 access token 校验与 refresh cookie【推荐】
302
- refreshCookie?, // RefreshCookieConfig — httpOnly refresh cookie 刷新路径(见下方 cookie 节)
303
+ refreshCookie?, // RefreshCookieConfig — 浏览器 cookie / 受信原生 body 的 refresh token 通道
303
304
  transport?, // { crypto, keyExchangePath?, excludePaths?, maxClients? } — 统一传输加密
304
305
  verifyToken?, // (token) => Promise<HaiResult<ServSession>> — 逃脱口:不使用 iam 时提供自定义校验
305
306
  createContext?, // CreateServContext — 高级:完全接管上下文构造(设置后 serv 不再自动填充 session)
@@ -453,9 +454,13 @@ const app = serv.createApp({
453
454
  },
454
455
  {
455
456
  path: '/assets/*',
456
- middleware: c => c.body(imageBytes, 200, {
457
- 'Content-Type': 'image/png',
458
- 'Cache-Control': 'public, max-age=31536000, immutable',
457
+ middleware: serv.storageAssets({
458
+ storage,
459
+ pathPrefix: '/assets/',
460
+ keyPattern: /^public\/[\w-]+\.png$/,
461
+ allowedContentTypes: ['image/png'],
462
+ cacheControl: 'public, max-age=31536000, immutable',
463
+ crossOriginResourcePolicy: 'cross-origin',
459
464
  }),
460
465
  },
461
466
  ],
@@ -465,7 +470,8 @@ const app = serv.createApp({
465
470
  执行顺序固定为:`securityHeaders` → `middlewares` → `transport`(若启用)→ health/refresh-cookie/OpenAPI/RPC/docs/oRPC routes。
466
471
 
467
472
  - `middlewares` 先于 `transport` 执行,因此 CORS preflight 或二进制端点可以直接短路;短路响应仍带内置安全头,但不会经过 transport。
468
- - 文件端点的允许方法、路径校验、MIME、缓存和访问授权由应用显式决定;框架不内置头像等业务概念。
473
+ - 公开只读文件优先使用 `serv.storageAssets(...)`;应用仍须显式提供 key/MIME 白名单、缓存与跨域策略,框架不内置头像等业务概念。
474
+ - 私有资源、Range 下载或动态授权继续使用自定义 middleware/procedure。
469
475
  - 若需要读取解密后的业务 body,请改用 context / procedure 层扩展。
470
476
  - 浏览器若需读取自定义响应头(例如 transport 的 `X-Encrypted`),记得通过 `serv.cors({ exposedHeaders: [...] })` 显式暴露。
471
477
 
@@ -695,6 +701,7 @@ procedure 包装器内置以下错误:
695
701
  `serv.parseRequestContext()` / `extractBearerToken()` 解析、`buildAuthContextFactory()` 校验的就是它。
696
702
  - `refreshToken`:长期凭证,启用 `refreshCookie` 后只保存在 httpOnly cookie;
697
703
  它不会走 `Authorization`,也不会被 `extractBearerToken()` 读取,只会在 `/auth/refresh` 被服务端取出。
704
+ 受信原生客户端可通过 `nativeTokenTransport` 改用 JSON body,但必须同时校验客户端标识与 Origin。
698
705
 
699
706
  ### 适用场景
700
707
 
@@ -709,6 +716,9 @@ procedure 包装器内置以下错误:
709
716
  4. serv 读取 cookie → 调用 `iam.session.refresh` → 返回新 access token + 更新 cookie(响应体不暴露 refresh token)
710
717
  5. POST `/auth/logout` → serv 清除 cookie(`Max-Age=0`)
711
718
 
719
+ 原生客户端匹配 `nativeTokenTransport.isRequest` 后,登录/注册/刷新响应保留轮换后的
720
+ `refreshToken`,刷新请求从 `{ refreshToken }` body 读取,且不读写浏览器 Cookie。
721
+
712
722
  若同时启用 `transport: { crypto }`,`/auth/refresh` 也必须走加密链路;cookie-only 刷新请求允许空 body,服务端不要把 `Request.body === null` 误判为需要解密的请求体。
713
723
 
714
724
  ### 配置
@@ -724,6 +734,12 @@ const app = serv.createApp({
724
734
  // maxAge?: number 默认 30 * 24 * 3600(30 天)
725
735
  // secure?: boolean 默认 NODE_ENV=production 时开启
726
736
  // onRefresh?: (token) => ... 可选:覆盖默认的 iam.session.refresh
737
+ nativeTokenTransport: {
738
+ // 必须 fail-closed:同时校验平台标识与允许的原生 Origin。
739
+ isRequest: request =>
740
+ request.headers.get('x-client-platform') === 'capacitor'
741
+ && security.isNativeOrigin(request.headers.get('origin') ?? ''),
742
+ },
727
743
  },
728
744
  // ✅ 顶层 iam 同时为 access token 校验提供默认实现,无需再传 verifyToken
729
745
  })