@h-ai/cli 1.0.0-beta.21 → 1.0.0-beta.23

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 (60) hide show
  1. package/README.en.md +13 -12
  2. package/README.ja.md +13 -12
  3. package/README.md +13 -12
  4. package/dist/{chunk-2ZL5ORQ2.js → chunk-P2VZ3VV6.js} +2 -14
  5. package/dist/chunk-P2VZ3VV6.js.map +1 -0
  6. package/dist/{chunk-L53P43B2.js → chunk-V7WGSBX3.js} +41 -21
  7. package/dist/chunk-V7WGSBX3.js.map +1 -0
  8. package/dist/cli-config-templates-VLXJTHCQ.js +5 -0
  9. package/dist/{cli-config-templates-LWMD2I6V.js.map → cli-config-templates-VLXJTHCQ.js.map} +1 -1
  10. package/dist/{cli-skill-templates-D6S2OQ6X.js → cli-skill-templates-JKF6E3PI.js} +3 -3
  11. package/dist/{cli-skill-templates-D6S2OQ6X.js.map → cli-skill-templates-JKF6E3PI.js.map} +1 -1
  12. package/dist/index.js +161 -244
  13. package/dist/index.js.map +1 -1
  14. package/dist/metafile-esm.json +1 -1
  15. package/package.json +14 -14
  16. package/templates/apps/fullstack/docs/security-configuration.md.hbs +3 -3
  17. package/templates/apps/fullstack/images/web/nginx.conf.hbs +3 -1
  18. package/templates/apps/fullstack/packages/{{projectName}}-serv/README.md.hbs +1 -1
  19. package/templates/apps/fullstack/packages/{{projectName}}-shared/tests/api-client.test.ts.hbs +4 -1
  20. package/templates/base/images/web/nginx.conf.hbs +3 -1
  21. package/templates/base/package.json.hbs +1 -1
  22. package/templates/shared/download-error/http.conf +19 -0
  23. package/templates/shared/download-error/server.conf +52 -0
  24. package/templates/skills/hai-ai/acp.md +1 -1
  25. package/templates/skills/hai-ai/guide.md +1 -1
  26. package/templates/skills/hai-ai/reference.md +1 -1
  27. package/templates/skills/hai-api-client/SKILL.md +2 -2
  28. package/templates/skills/hai-app-create/SKILL.md +2 -0
  29. package/templates/skills/hai-app-create/development.md +3 -3
  30. package/templates/skills/hai-app-create/logging.md +32 -0
  31. package/templates/skills/hai-app-create/project.md +1 -0
  32. package/templates/skills/hai-app-create/reference.md +2 -4
  33. package/templates/skills/hai-app-review/SKILL.md +1 -1
  34. package/templates/skills/hai-audit/SKILL.md +1 -1
  35. package/templates/skills/hai-audit/reference.md +1 -1
  36. package/templates/skills/hai-build/SKILL.md +1 -1
  37. package/templates/skills/hai-build/paths.md +1 -1
  38. package/templates/skills/hai-core/reference.md +2 -11
  39. package/templates/skills/hai-crypto/SKILL.md +1 -1
  40. package/templates/skills/hai-crypto/reference.md +3 -3
  41. package/templates/skills/hai-deploy/SKILL.md +8 -8
  42. package/templates/skills/hai-deploy/middleware.md +2 -2
  43. package/templates/skills/hai-framework-sync/SKILL.md +4 -4
  44. package/templates/skills/hai-iam/SKILL.md +1 -1
  45. package/templates/skills/hai-iam/reference.md +1 -1
  46. package/templates/skills/hai-kit/SKILL.md +2 -2
  47. package/templates/skills/hai-kit/reference.md +3 -3
  48. package/templates/skills/hai-reach/SKILL.md +1 -1
  49. package/templates/skills/hai-reldb/SKILL.md +1 -1
  50. package/templates/skills/hai-reldb/reference.md +2 -3
  51. package/templates/skills/hai-scheduler/reference.md +1 -1
  52. package/templates/skills/hai-serv/SKILL.md +1 -1
  53. package/templates/skills/hai-serv/reference.md +2 -2
  54. package/templates/skills/hai-storage/SKILL.md +1 -0
  55. package/templates/skills/hai-storage/reference.md +14 -4
  56. package/templates/skills/hai-vecdb/reference.md +1 -1
  57. package/dist/chunk-2ZL5ORQ2.js.map +0 -1
  58. package/dist/chunk-L53P43B2.js.map +0 -1
  59. package/dist/cli-config-templates-LWMD2I6V.js +0 -5
  60. package/templates/skills/hai-deploy/credentials.md +0 -19
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@h-ai/cli",
3
3
  "type": "module",
4
- "version": "1.0.0-beta.21",
4
+ "version": "1.0.0-beta.23",
5
5
  "description": "Hai Framework CLI for project scaffolding and code generation.",
6
6
  "license": "Apache-2.0",
7
7
  "keywords": [
@@ -30,7 +30,7 @@
30
30
  "node": "^22.23.4 || ^24.22.1 || ^26.12.1"
31
31
  },
32
32
  "dependencies": {
33
- "@h-ai/core": "1.0.0-beta.21",
33
+ "@h-ai/core": "1.0.0-beta.23",
34
34
  "cac": "^7.1.0",
35
35
  "chalk": "^6.0.1",
36
36
  "fast-wrap-ansi": "^0.2.2",
@@ -41,18 +41,18 @@
41
41
  "yaml": "^2.9.1"
42
42
  },
43
43
  "devDependencies": {
44
- "@h-ai/api-client": "1.0.0-beta.21",
45
- "@h-ai/cache": "1.0.0-beta.21",
46
- "@h-ai/crypto": "1.0.0-beta.21",
47
- "@h-ai/deploy": "1.0.0-beta.21",
48
- "@h-ai/feedback": "1.0.0-beta.21",
49
- "@h-ai/iam": "1.0.0-beta.21",
50
- "@h-ai/kit": "1.0.0-beta.21",
51
- "@h-ai/payment": "1.0.0-beta.21",
52
- "@h-ai/reach": "1.0.0-beta.21",
53
- "@h-ai/reldb": "1.0.0-beta.21",
54
- "@h-ai/storage": "1.0.0-beta.21",
55
- "@h-ai/vecdb": "1.0.0-beta.21",
44
+ "@h-ai/api-client": "1.0.0-beta.23",
45
+ "@h-ai/cache": "1.0.0-beta.23",
46
+ "@h-ai/crypto": "1.0.0-beta.23",
47
+ "@h-ai/deploy": "1.0.0-beta.23",
48
+ "@h-ai/feedback": "1.0.0-beta.23",
49
+ "@h-ai/iam": "1.0.0-beta.23",
50
+ "@h-ai/kit": "1.0.0-beta.23",
51
+ "@h-ai/payment": "1.0.0-beta.23",
52
+ "@h-ai/reach": "1.0.0-beta.23",
53
+ "@h-ai/reldb": "1.0.0-beta.23",
54
+ "@h-ai/storage": "1.0.0-beta.23",
55
+ "@h-ai/vecdb": "1.0.0-beta.23",
56
56
  "@types/fs-extra": "^11.0.4",
57
57
  "@types/node": "^26.6.6",
58
58
  "@types/prompts": "^2.4.9",
@@ -31,7 +31,7 @@ HAI_AUDIT_BACKUP_ENABLED=true
31
31
 
32
32
  这里列的是 fullstack 模板默认值,不是所有项目或底层 Schema 的普适限制。
33
33
 
34
- 加密请求默认有效 300 秒,允许客户端时间领先服务器最多 30 秒。可在 `_serv.yml` 的 `transport` 下设置 `maxRequestAgeSeconds`(1~600)和 `maxFutureSkewSeconds`(0~300),单位为整数秒。超限时,前端显示校验时的服务器 UTC 时间,提示用户开启设备自动校时或手工修改时间,然后刷新。升级旧版本后,从最后一个旧节点停止处理请求起等待 12 分钟,再把请求有效期调到 300 秒以上。
34
+ 加密请求默认有效 300 秒,允许客户端时间领先服务器最多 30 秒。可在 `_serv.yml` 的 `transport` 下设置 `maxRequestAgeSeconds`(1~600)和 `maxFutureSkewSeconds`(0~300),单位为整数秒。超限时,前端显示校验时的服务器 UTC 时间,提示用户开启设备自动校时或手工修改时间,然后刷新。
35
35
 
36
36
  | 条目 | fullstack 默认及启用入口 | 使用影响 |
37
37
  | ---------------------------- | --------------------------------------------------------------- | -------------------------------------------------------- |
@@ -65,7 +65,7 @@ HAI_AUDIT_BACKUP_ENABLED=true
65
65
 
66
66
  在 `_iam.yml` 设置 `register.enabled: false` 会隐藏访客注册入口,同时由服务端拒绝自助注册;已有账号仍可登录,管理员也仍可创建用户。设回 true 恢复访客注册。
67
67
 
68
- `register.defaultAccountStatus: dormant` 表示自助注册成功后创建休眠账号,等待系统管理员激活后才能登录。默认 active 表示无需审批;仅影响新自助注册账号,管理员创建可另选状态。环境变量为 `HAI_IAM_REGISTER_ENABLED` / `HAI_IAM_REGISTER_DEFAULTACCOUNTSTATUS`。账号只有激活、休眠、注销三种生命周期,不再使用独立启用/停用开关。系统管理员可管理所有职责账号的资料、状态、角色并重置密码;审计与业务配置权限仍归各职责。
68
+ `register.defaultAccountStatus: dormant` 表示自助注册成功后创建休眠账号,等待系统管理员激活后才能登录。默认 active 表示无需审批;仅影响新自助注册账号,管理员创建可另选状态。环境变量为 `HAI_IAM_REGISTER_ENABLED` / `HAI_IAM_REGISTER_DEFAULTACCOUNTSTATUS`。账号状态包括激活、休眠和注销。系统管理员可管理所有职责账号的资料、状态、角色并重置密码;审计与业务配置权限仍归各职责。
69
69
 
70
70
  ## 账号类型、状态和自动休眠
71
71
 
@@ -330,7 +330,7 @@ alerts:
330
330
 
331
331
  反馈 console 通知默认 consoleContent: metadata,只打印编号、应用版本等元数据;summary 是显式选项,可能包含业务信息。Reach API 默认拒绝重定向,敏感发送始终拒绝重定向,生产敏感发送默认要求 HTTPS;可信内网例外必须显式配置 requireHttps: false 并评估网络边界。
332
332
 
333
- 升级前可用 `hai upgrade <精确版本>` 检查实际消费的包和已发布迁移导出。报告出现 migrateAudit({ dryRun: true }) 时先只读预检,再按结果安排备份和实际迁移;报告本身不执行数据库迁移。旧部署缺少受管凭据状态时仍拒绝普通部署,按 hai-deploy skill 的凭据导入流程处理,不删除旧卷或生成新密码替代。
333
+ 升级前可用 `hai upgrade <精确版本>` 核对消费包、安装状态和技能差异。发布前在独立环境验证数据库结构与应用启动,备份数据库、会话缓存、数据卷和受保护的部署状态。部署预检发现已有卷缺少匹配状态时,从核验过的备份恢复。
334
334
 
335
335
  ## 其他模板和业务项目如何复用
336
336
 
@@ -1,12 +1,14 @@
1
1
  {{! @skipIfEmpty }}
2
2
  {{#if fullstack.frontends.web}}
3
+ {{{nginxDownloadErrors "http"}}}
3
4
  server {
4
5
  listen 8080;
5
6
  server_name _;
6
7
  root /usr/share/nginx/html;
7
8
 
9
+ {{{nginxDownloadErrors "server"}}}
8
10
  location / {
9
- try_files $uri $uri/ /index.html;
11
+ try_files $uri $uri/ @hai_spa_fallback;
10
12
  }
11
13
 
12
14
  location = /runtime-config.js {
@@ -80,7 +80,7 @@ LDAP/Reach 均可选:基础认证无需安装。默认 LDAP 客户端需在 IA
80
80
 
81
81
  feedback.init(config, { collectEvidence }) 支持等待业务 text/data 与文本/ZIP/GZIP 附件;_feedback.yml 提供业务采集超时和 Webhook URL/签名密钥/请求超时。密钥仅放服务端私有环境。Webhook 持久重试,详情展示状态并允许管理员重试。
82
82
 
83
- 反馈的 console/disabled/reach 独立选择;SDK getView/ratio/saveDraft 与 createFeedbackReview 统一预览、标注、保存及轮询。退出身份清理草稿和观察器。宿主生命周期由 core.module.createRuntime 处理,框架表升级前备份停旧写入并运行各模块 dryRun 预检。
83
+ 反馈的 console/disabled/reach 独立选择;SDK getView/ratio/saveDraft 与 createFeedbackReview 统一预览、标注、保存及轮询。退出身份清理草稿和观察器。宿主生命周期由 core.module.createRuntime 处理。
84
84
 
85
85
  ## 开发测试
86
86
 
@@ -1,3 +1,4 @@
1
+ import type { KeyExchangeResponse } from '@h-ai/crypto'
1
2
  import { HaiApiClientError } from '@h-ai/api-client'
2
3
  import { authenticateTransportRequest, createInMemoryKeyStore, crypto, TRANSPORT_PROTOCOL } from '@h-ai/crypto'
3
4
  import { afterAll, beforeAll, describe, expect, it, vi } from 'vitest'
@@ -41,7 +42,9 @@ function createServerFetch() {
41
42
  serverKeyId: server.getServerKeyId(),
42
43
  serverPublicKey: server.getServerPublicKey(),
43
44
  clientId,
44
- })
45
+ maxPlaintextBytes: server.bodyLimits.plaintextBytes,
46
+ clientKeyTtlSeconds: server.clientKeyTtlSeconds,
47
+ } satisfies KeyExchangeResponse)
45
48
  }
46
49
  const clientId = request.headers.get(TRANSPORT_PROTOCOL.CLIENT_ID_HEADER)
47
50
  if (!clientId)
@@ -1,12 +1,14 @@
1
1
  {{! @skipIfEmpty }}
2
2
  {{#if isSvelteOnlyApp}}
3
+ {{{nginxDownloadErrors "http"}}}
3
4
  server {
4
5
  listen 8080;
5
6
  server_name _;
6
7
  root /usr/share/nginx/html;
7
8
 
9
+ {{{nginxDownloadErrors "server"}}}
8
10
  location / {
9
- try_files $uri $uri/ /index.html;
11
+ try_files $uri $uri/ @hai_spa_fallback;
10
12
  }
11
13
 
12
14
  location = /runtime-config.js {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "engines": { "node": "^22.23.4 || ^24.22.1 || ^26.12.1" },
3
3
  "name": "{{projectName}}",
4
- "version": "1.0.0-beta.21",
4
+ "version": "1.0.0-beta.23",
5
5
  "private": true,
6
6
  "packageManager": "pnpm@12.11.2",
7
7
  "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}}",
@@ -0,0 +1,19 @@
1
+ map "$request_method:$args" $hai_signed_download {
2
+ default 0;
3
+ "~*^(GET|HEAD):(?:[^&]*&)*X-Amz-Signature=[^&]+(?:&|$)" 1;
4
+ }
5
+ map $http_accept_language $hai_download_language {
6
+ default zh-CN;
7
+ ~*^en en-US;
8
+ ~*^ja ja-JP;
9
+ }
10
+ map $hai_download_language $hai_download_title {
11
+ default "下载链接无法访问";
12
+ en-US "Download link unavailable";
13
+ ja-JP "ダウンロードリンクにアクセスできません";
14
+ }
15
+ map $hai_download_language $hai_download_instruction {
16
+ default "请返回下载页面重新获取链接。若仍无法下载,请联系管理员。";
17
+ en-US "Return to the download page to get a new link. If the download still fails, contact your administrator.";
18
+ ja-JP "ダウンロードページに戻り、新しいリンクを取得してください。解決しない場合は管理者にお問い合わせください。";
19
+ }
@@ -0,0 +1,52 @@
1
+ location @hai_download_unavailable {
2
+ access_log off;
3
+ error_log /dev/null;
4
+ types { }
5
+ default_type text/html;
6
+ charset utf-8;
7
+ add_header Cache-Control "private, no-store" always;
8
+ add_header Referrer-Policy no-referrer always;
9
+ add_header X-Content-Type-Options nosniff always;
10
+ add_header Content-Security-Policy "default-src 'none'; style-src 'unsafe-inline'; base-uri 'none'; form-action 'none'; frame-ancestors 'none'" always;
11
+ return 404 '<!doctype html>
12
+ <html lang="$hai_download_language">
13
+ <head>
14
+ <meta charset="utf-8">
15
+ <meta name="viewport" content="width=device-width, initial-scale=1">
16
+ <meta name="robots" content="noindex">
17
+ <meta name="referrer" content="no-referrer">
18
+ <link rel="icon" href="data:,">
19
+ <title>$hai_download_title</title>
20
+ <style>
21
+ * { box-sizing: border-box; }
22
+ body {
23
+ margin: 0;
24
+ min-height: 100vh;
25
+ display: grid;
26
+ place-items: center;
27
+ padding: 24px;
28
+ background: #f1f5f9;
29
+ color: #172033;
30
+ font-family: Inter, system-ui, "Microsoft YaHei", sans-serif;
31
+ line-height: 1.7;
32
+ }
33
+ main {
34
+ width: min(100%, 640px);
35
+ padding: clamp(24px, 6vw, 48px);
36
+ border: 1px solid #dbe3ee;
37
+ border-top: 4px solid #2563eb;
38
+ border-radius: 12px;
39
+ background: #fff;
40
+ }
41
+ h1 { margin: 0 0 16px; font-size: clamp(24px, 5vw, 32px); line-height: 1.4; }
42
+ p { margin: 0; color: #475569; font-size: 16px; }
43
+ </style>
44
+ </head>
45
+ <body><main><h1>$hai_download_title</h1><p>$hai_download_instruction</p></main></body>
46
+ </html>';
47
+ }
48
+ location @hai_spa_fallback {
49
+ error_page 418 = @hai_download_unavailable;
50
+ if ($hai_signed_download) { return 418; }
51
+ try_files /index.html =404;
52
+ }
@@ -122,7 +122,7 @@ Podman 不自动继承远程服务的代理变量。需要代理时,使用 Age
122
122
 
123
123
  发布仅允许框架创建的隔离 session,避免将共享目录中别人的修改算入当前任务。操作前停止受管进程/容器,避免背景工具继续写入;要继续编码显式 `sessions.resume(id)`。原生 Agent 自己创建的 worktree 不自动纳入框架发布范围。
124
124
 
125
- 私有仓库把 Git 凭据引用写入 workspace 的 `gitEnvironmentRefs`,把 Agent 专用变量写入 agent 的 `environmentRefs`。旧 workspace 的 `environmentRefs` 仍共用,迁移时移走其中的 Git 凭据。启用 Git 隔离后,远程克隆、拉取、推送只使用登记的远端地址和可信环境注入,在独立 Git 目录运行;需要的网络代理或 CA 设置也随 Git 引用配置。认证脚本和全局 Git 配置应由可信执行器提供。任务之间的访问权限仍由应用和运行环境隔离。
125
+ 私有仓库把 Git 凭据引用写入 workspace 的 `gitEnvironmentRefs`,把 Agent 专用变量写入 agent 的 `environmentRefs`。启用 Git 隔离后,远程克隆、拉取、推送只使用登记的远端地址和可信环境注入,在独立 Git 目录运行;需要的网络代理或 CA 设置也随 Git 引用配置。认证脚本和全局 Git 配置应由可信执行器提供。任务之间的访问权限仍由应用和运行环境隔离。
126
126
 
127
127
  多个工程不是原子事务。失败时返回 `ACP_PARTIAL_FAILURE`,`error.cause.reports` 列出每个工程 success、phase、commit 和 error;已完成工程不回滚,冲突保留现场。失败于 push 后,排除权限/网络问题可重试;远端结果不明时先核对实际分支。普通文件的修改留在 session 目录,不自动纳入 Git 发布。失败创建的 worktree 也保留,避免删除已有成果。
128
128
 
@@ -82,7 +82,7 @@ await ai.close()
82
82
 
83
83
  ## 配置要点
84
84
 
85
- LLM 协议使用 `llm.protocol`、`llm.models[].protocol` 和 `tempModel.protocol`,值为 `chat | responses | anthropic`;环境变量使用 `HAI_AI_LLM_PROTOCOL`、`HAI_AI_LLM_MODELS_0_PROTOCOL` 等。旧 `api` 字段及旧协议变量会报迁移错误,密钥字段 `apiKey` 不变。
85
+ LLM 协议使用 `llm.protocol`、`llm.models[].protocol` 和 `tempModel.protocol`,值为 `chat | responses | anthropic`;环境变量使用 `HAI_AI_LLM_PROTOCOL`、`HAI_AI_LLM_MODELS_0_PROTOCOL` 等。
86
86
 
87
87
  ```yaml
88
88
  llm:
@@ -73,7 +73,7 @@ await mcp.connect(new StdioServerTransport())
73
73
 
74
74
  ### `ai.mcp`
75
75
 
76
- 这是进程内注册表,不会自动绑定独立 MCP Server 的 transport。工具注册时编译 JSON Schema、调用时校验输入;非法输入不执行 handler。资源必须恰好含 text/blob 之一;提示词必填参数必须是自身字符串属性,输出使用 SDK 消息类型(含 image/audio)。同名注册覆盖,缺失 requestId 自动补 UUID。已移除未使用的 MCPProvider 与 AIMCPFunctionsDeps 类型;使用 MCPOperations 即可。
76
+ 这是进程内注册表,不会自动绑定独立 MCP Server 的 transport。工具注册时编译 JSON Schema、调用时校验输入;非法输入不执行 handler。资源必须恰好含 text/blob 之一;提示词必填参数必须是自身字符串属性,输出使用 SDK 消息类型(含 image/audio)。同名注册覆盖,缺失 requestId 自动补 UUID。调用接口类型为 `MCPOperations`。
77
77
 
78
78
  | 方法 | 说明 |
79
79
  | --- | --- |
@@ -23,13 +23,13 @@ description: "接入或排查 @h-ai/api-client typed API、登录 Token、自动
23
23
  4. 同步服务端与客户端的刷新、加密和错误处理;切换配置前 close,任务结束释放客户端。
24
24
  5. 验证登录、刷新重试、失败、退出与会话恢复。
25
25
 
26
- 错误提示与诊断分离:HTTP/UI 边界用 core.error.toPublic,仅公开 i18n 安全消息及 error.traceId;重新翻译后用 core.error.formatMessage 保留关联。ToastContainer 默认只显示可读说明,自动将编号移入排查信息图标,展开后可查看、复制并提供给客户支持或开发人员。前后端日志传完整 Error 和操作上下文,使用同一 requestId/traceId 并脱敏;禁止只写通用错误标题,也不能把 cause/ext/SQL/绑定值直接返回用户。数据库日志保留安全的驱动错误码、消息和堆栈。
26
+ 保留服务端错误的 `traceId`,UI 使用公开安全消息,重新翻译后用 `core.error.formatMessage` 保留编号。4xx 和可关联的服务端错误使用 `debug`。客户端自己的网络、协议或未关联的 5xx 失败使用 `error`,记录可用的传输诊断字段。具体见 [日志与错误诊断](../hai-app-create/logging.md)。
27
27
 
28
28
  非 2xx HTTP 响应中的合法失败 HaiResult 保留服务端业务码、消息与 traceId,以真实 HTTP 状态执行脱敏。验证 409 冲突、422 输入错误、429 限流和 5xx,不只测试 HTTP 200 业务失败;UI 包装 Error 时通过 cause 保留 HaiError。
29
29
 
30
30
  ## 按需参考
31
31
 
32
- Serv/Kit 对通过请求绑定、签名和 MAC 校验但超出时间窗口的请求返回加密的 HTTP 400 错误。客户端抛出 `TransportTimeError`(`hai:crypto:071`),显示校验时的服务器 UTC 时间,提示开启设备的自动校时或手工修改时间后刷新。`serverTime` 为 Unix 毫秒,旧服务端未提供该值时仍显示校时提示。`api-client` 映射为 `VALIDATION_FAILED`,在 `cause.transportCode` 和 `cause.serverTime` 保留诊断信息。
32
+ Serv/Kit 对通过请求绑定、签名和 MAC 校验但超出时间窗口的请求返回加密的 HTTP 400 错误。客户端抛出 `TransportTimeError`(`hai:crypto:071`),显示校验时的服务器 UTC 时间,提示开启设备的自动校时或手工修改时间后刷新。`serverTime` 为 Unix 毫秒。`api-client` 映射为 `VALIDATION_FAILED`,在 `cause.transportCode` 和 `cause.serverTime` 保留诊断信息。
33
33
 
34
34
  按本次操作读取对应小节;以已安装版本的公开类型核验签名,示例中的业务变量由应用提供。
35
35
 
@@ -19,6 +19,8 @@ description: "在 hai 应用中新增、修改或修复功能,按需求复用
19
19
 
20
20
  沟通、问题状态与文档/配置/截图/测试/模板/调用方同步遵循[沟通与同步维护](collaboration.md)。
21
21
 
22
+ 生成或修改错误处理和日志时,应用 [日志与错误诊断](logging.md) 中的级别、根因与脱敏规则。
23
+
22
24
  范围、验收、复用依据和证据按 [工作交接契约](../hai-spec-implement/handoff.md) 贯穿实现、测试和审查;小改动使用简短记录,文档驱动任务沿既有任务记录续跑。
23
25
 
24
26
  业务以用户当前明确要求及其指定文档为准;模板规则、生成代码与测试只描述初始方案。需求改变角色、流程、限额或架构时,在已有授权范围内同步调整冲突实现及过期 AGENTS/skills/README,不要求把需求改回模板。具体判定见 [development.md](development.md#需求与模板的关系)。
@@ -22,13 +22,13 @@ README 编辑先读章节,把功能说明与示例合并到对应章节;新
22
22
 
23
23
  ### 页面与布局
24
24
 
25
- 每个模板只安装实际使用的模块:admin/h5 包含认证、验证码及找回密码通知,以及数据库、缓存、加密依赖;website 只包含 Core/Kit/UI;API 包含认证、通知、上传和反馈;fullstack 保留管理后台的认证、审计、存储、通知、调度和反馈;mobile-app 只使用客户端能力。AI、payment、vecdb、datapipe 不默认安装。所有项目保留完整模块 YAML 和 `.env.example`,未启用项以注释提供,只有已启用项进入公共环境默认值。后续添加对应依赖、取消所需配置注释,再接入初始化、关闭及业务路由;添加依赖不会自动启用功能。`--template`、`--features`、`--examples/--no-examples` 已删除,`create --yes/-y` 改为 `--defaults`,旧参数明确报错;`deploy --yes` 保持原义。已有工程不会自动裁剪依赖。
25
+ 每个模板只安装实际使用的模块:admin/h5 包含认证、验证码及找回密码通知,以及数据库、缓存、加密依赖;website 只包含 Core/Kit/UI;API 包含认证、通知、上传和反馈;fullstack 保留管理后台的认证、审计、存储、通知、调度和反馈;mobile-app 只使用客户端能力。AI、payment、vecdb、datapipe 不默认安装。所有项目保留完整模块 YAML 和 `.env.example`,未启用项以注释提供,只有已启用项进入公共环境默认值。后续添加对应依赖、取消所需配置注释,再接入初始化、关闭及业务路由;添加依赖不会自动启用功能。无交互创建项目使用 `hai create --defaults`。执行 `hai deploy --yes` 时使用已保存的部署配置。
26
26
 
27
27
  website默认SSR;静态发布须先移除服务端业务依赖、初始化及API/actions。静态构建与实际路径验证按[hai-build](../hai-build/SKILL.md)。
28
28
 
29
29
  admin、website、h5 和 Fullstack Web 在入口 HTML 加载 `_hai/browser-check/startup.js` 后才进入应用。检测到 Chrome/Edge 低于 111、Firefox 低于 114、Safari 或 iOS/iPadOS 低于 16.4 时,浏览器跳转到同目录的静态升级页。保留经典脚本标签和静态资源,验证根目录和发布子目录都能在应用包加载失败时显示升级提示。
30
30
 
31
- 网站挂载到`/yyy/`等目录时,按[网站挂载目录](../hai-build/paths.md)同步资源、链接、认证端点和服务端守卫。SvelteKit在编译时固定base,Vite默认相对资源加hash导航;API基址单独配置。SDK子目录制品使用格式5,健康与增量检查随制品处理,不手工修改旧格式清单来代替重新构建。
31
+ 网站挂载到`/yyy/`等目录时,按[网站挂载目录](../hai-build/paths.md)同步资源、链接、认证端点和服务端守卫。SvelteKit在编译时固定base,Vite默认相对资源加hash导航;API基址单独配置。镜像清单使用格式 5,部署健康检查和增量更新按清单中的 `webBasePath` 处理。
32
32
 
33
33
  页面共用根布局的 header/footer。独立移动应用的页面放在 `src/lib/pages/**/*.svelte`,Fullstack 放在 `packages/<name>-shared/src/lib/pages/**/*.svelte`,由 `HashRouter` 和 `import.meta.glob` 自动发现;新增页面无需修改路由表,菜单按业务配置入口。SvelteKit 保留 `src/routes/**/+page.svelte` 自动文件路由、服务端鉴权和刷新定位。
34
34
 
@@ -85,7 +85,7 @@ Fullstack 的环境文件在工程根;service 脚本通过 `../../.env*` 加
85
85
 
86
86
  先把原来被忽略的 `.env` 私有值迁入 `.env.local`,再提交公共基础和环境文件。本机文件只保留需要覆盖的值,避免整份复制示例后固定开发环境。旧的 `HAI_AI_LLM_API_KEY` / `HAI_AI_LLM_BASE_URL` 改为 `HAI_AI_LLM_APIKEY` / `HAI_AI_LLM_BASEURL`;音频和图像厂商回退变量同样使用 `APIKEY` 后缀。Reach 和 deploy 凭据按当前 YAML 路径命名,以项目 `.env.example` 为准,同步迁移部署平台变量和本机文件。
87
87
 
88
- 原 `HAI_AI_ANTHROPIC_API_KEY` / `HAI_AI_ANTHROPIC_BASE_URL` 不再使用。默认协议为 `anthropic` 时改用标准 LLM 变量;混合模型池中有独立凭据或端点时,将 `apiKey` / `baseUrl` 配置在对应模型项。厂商回退 `ANTHROPIC_API_KEY` / `ANTHROPIC_BASE_URL` 保留,优先级以 AI 模块说明为准。
88
+ 默认协议为 `anthropic` 时使用标准 LLM 配置。混合模型池的独立凭据或端点配置在对应模型项的 `apiKey` 和 `baseUrl`。厂商环境变量及优先级见 AI 模块说明。
89
89
 
90
90
  #### 常用变量及约束
91
91
 
@@ -0,0 +1,32 @@
1
+ # 日志与错误诊断
2
+
3
+ 框架模块、服务端业务代码和浏览器代码统一使用 `core.logger`。用 `core.logger.child({ module, scope })` 标记稳定的来源,英文日志消息说明发生的操作。应用与常规 CLI 操作沿用 Core 日志配置,应用可通过 `_core.yml` 的 `logging.level` 或 `HAI_CORE_LOGGING_LEVEL` 设置阈值。提供 JSON 输出的命令按其机器可读输出契约处理诊断。
4
+
5
+ ## 日志级别
6
+
7
+ | 级别 | 使用场景 |
8
+ | --- | --- |
9
+ | `trace` | 请求和响应摘要、循环细节等按需诊断。请求查询只记录参数数量。 |
10
+ | `debug` | 常规读写的安全摘要、预期的 HTTP 4xx、客户端对已带服务端追踪编号的错误进行关联。 |
11
+ | `info` | 初始化完成、服务就绪和有意义的低频状态变化。 |
12
+ | `warn` | 模块所拥有的业务操作失败、可恢复的外部故障或清理失败。 |
13
+ | `error` | 初始化或连接失败、影响资源状态的关闭失败、未预期异常和服务端 5xx。 |
14
+ | `fatal` | 进程无法继续运行的故障。 |
15
+
16
+ 由能看到根因的层记录失败。上层复用错误码和追踪编号,按需要补充诊断摘要。接口和 UI 使用现有的公开错误映射与本地化提示。客户端自己的网络、协议或未处理异常需要独立诊断。操作日志的读写开关控制正常摘要,错误日志由对应级别和 Core 阈值控制。持久化审计遵循审计契约。
17
+
18
+ ## 保留根因与关联信息
19
+
20
+ - 对 `HaiResult` 先判断 `success`。业务调用方应得到有区分度的错误码和消息,以及可获得的原始 `error.cause`。把后续失败单独保存在 `error.ext.cleanupError` 等既有诊断字段,保留取消原因和业务分支所需的错误码。HTTP 对外使用安全消息和追踪编号。
21
+ - 日志使用结构化上下文。沿用模块已有的操作名和错误码字段,并按需记录失败阶段、状态和可用的 `traceId` 或 `requestId`。上游请求编号独立记录为 `providerRequestId`。需要堆栈时保留可排查的脱敏异常链。
22
+ - 用明确的字段白名单构造日志。省略密码、token、密钥、连接凭据、完整配置、请求正文和查询值、联系方式、消息主题和正文、签名 URL、对象或文件内容、SQL 绑定值、文档、向量和过滤值。连接地址复用 `core.sanitize.sanitizeUrl`。字段脱敏不能识别自由文本里的全部私密值,外部错误正文应提取有界的错误码、状态和请求编号。
23
+
24
+ ## Storage
25
+
26
+ `storage` 默认开启写操作摘要,读操作按需开启。常规操作使用 `debug`,上传、下载、删除等失败返回业务错误并记录 `warn`,初始化、连接或关闭失败记录 `error`。日志省略对象键、签名地址和内容,由 `HAI_CORE_LOGGING_LEVEL` 过滤。
27
+
28
+ 流式上传保留源读取错误、取消原因和 S3 原始写入错误。分片清理也失败时单独保留清理错误,并记录可用的 S3 操作名和请求编号。条件写入冲突按 `ALREADY_EXISTS` 处理。使用 Storage 时查阅项目中 `hai-storage` 的配置与错误说明。
29
+
30
+ ## 验证
31
+
32
+ 修改日志或错误处理时,从实际公共入口验证受影响的成功和失败分支。按场景核对日志级别、原始原因、关联编号和脱敏字段。涉及上传、取消或回滚的双重失败,用受控故障验证业务结果和日志均保留首次错误与清理诊断。可使用 `core.logger.observe` 捕获结构化记录。
@@ -15,6 +15,7 @@
15
15
  - 服务端校验输入、身份、权限及资源/租户归属;SQL 值参数化、标识符白名单、文件路径防逃逸。不把隐藏按钮当授权,不执行不可信 HTML/AI 内容。
16
16
  - 密钥不进入 PUBLIC_/VITE_、浏览器或日志,敏感 token 不写 localStorage;沿现有 Cookie/内存或原生安全存储流程。用户提示准确且脱敏,日志保留英文操作上下文及可关联错误,未知提交不自动重放。
17
17
  - 禁止 any、无说明的 as unknown as、console.log、硬编码密钥。注释中文,结构化日志英文且脱敏,用户文本沿现有 i18n 同步全部 locale。
18
+ - 实现或审查错误处理和日志时,核对 [日志与错误诊断](logging.md) 的业务返回、日志级别、字段白名单与追踪编号。
18
19
 
19
20
  ## 代码注释与示例
20
21
 
@@ -228,9 +228,7 @@ export async function listArticles(params: ListParams): Promise<HaiResult<Pagina
228
228
  ### 服务层规则
229
229
 
230
230
  - 应用领域操作沿现有契约返回 `HaiResult<T>`;纯 getter、工具、工厂与流按实际签名,不统一改造
231
- - 写操作需 `debug`(进入)+ `info`(成功)日志
232
- - 读操作仅 `debug` 日志
233
- - 禁止 `console.log`,使用 `core.logger`
231
+ - 需要记录服务层操作时,使用 `core.logger` 和 [日志与错误诊断](logging.md) 约定的安全摘要。传播框架模块的 `HaiResult` 错误时保留原因和关联信息。
234
232
  - 服务层不处理 HTTP 响应格式,只返回 HaiResult
235
233
 
236
234
  ## 创建数据模型
@@ -357,7 +355,7 @@ core.init() → crypto.init() → reldb.init() → cache.init() → iam.init()
357
355
  - [ ] 页面使用 Svelte 5 Runes 语法
358
356
  - [ ] API 端点有权限守卫与输入校验
359
357
  - [ ] 服务层和调用方遵循实际返回/抛错/流契约,不能以 HTTP 成功替代业务状态成功
360
- - [ ] 日志级别正确(读 debug / 写 debug+info / 失败 warn|error)
358
+ - [ ] 已验证 [日志与错误诊断](logging.md) 的日志级别、失败原因、关联编号和脱敏字段
361
359
  - [ ] 禁止 `console.log`、`any`、硬编码密钥
362
360
  - [ ] 用户可见文本走 i18n
363
361
  - [ ] 涉及容器交付时,镜像启动及配置/环境变量/数据目录行为已验证
@@ -60,7 +60,7 @@ typecheck/lint 不能证明越权、租户隔离、事务、错误状态或资
60
60
 
61
61
  - 禁止 `any`、无说明的 `as unknown as T`、`console.log`、硬编码密钥;外部 `unknown` 先校验/缩窄。必要第三方断言有真实依据。
62
62
  - 遵循当前 ESLint、TypeScript、目录与命名规则,函数返回类型按真实契约标注;不把所有返回类型都强制成 HaiResult。
63
- - 注释中文,结构化日志英文并脱敏。读操作用 debug,写操作成功用 info,失败按业务/系统性质用 warn/error;避免记录整份请求、凭据或未经脱敏的 error/context。
63
+ - 注释中文,结构化日志英文。按 [日志与错误诊断](../hai-app-create/logging.md) 核对级别、字段白名单、原始原因和重复记录。
64
64
  - 用户可见文案沿现有 i18n 入口,同步中英日文;包括错误、空态、降级、校验和状态文案。禁止手改 Paraglide 生成文件;框架组件内置翻译沿公开 Props 使用。
65
65
  - 业务逻辑不塞入 UI/hook;优先 early return、按职责拆分复杂函数。遵守实际 lint 的复杂度/长度限制,不为了数字拆出无意义转发链。
66
66
  - 循环串行/并发按依赖、事务、限流与资源限制选择;列表分页/流式处理,避免热路径同步 I/O、重复连接和无界并发。
@@ -29,7 +29,7 @@ description: "接入 @h-ai/audit 记录业务操作,查询分页日志与统
29
29
 
30
30
  `audit.log(input,tx)`、`audit.capture(input,{tx})` 可使用调用者现有 RelDB 事务,事件和变更一并提交或回滚;失败尝试须在回滚后由入口记录。自动记录失败输出诊断并保留已生效业务结果;强制审计的业务检查同事务显式 log 的 HaiResult 后再提交。list 支持 requestId/traceId 关联筛选。
31
31
 
32
- `retention:{enabled:true,minDays:90}` 将最低保留写入共享数据库,cleanup(0) 也必须遵守;数据库触发器保护近期行的改删与覆盖。框架首次未配置不强制保护;未传 retention 的重初始化保留既有策略,显式可信配置可关闭。DB 所有者 DDL/权限与独立归档是部署边界。升级前停旧写入、备份并用 `migrateAudit({dryRun:true})` 预检。写失败返回失败并输出诊断;close 等待在途写入后再关闭数据库。
32
+ `retention:{enabled:true,minDays:90}` 将最低保留写入共享数据库,cleanup(0) 也必须遵守;数据库触发器保护近期行的改删与覆盖。框架首次未配置不强制保护;未传 retention 的重初始化保留既有策略,显式可信配置可关闭。DB 所有者 DDL/权限与独立归档是部署边界。`audit.init()` 校验当前表结构。用户名取自事件时的身份快照。写失败返回失败并输出诊断;close 等待在途写入后再关闭数据库。
33
33
 
34
34
  常见事件使用 `audit.helper` 与公开 `AuditHelper` 类型;内部记录器装配工厂不从包入口导出。
35
35
 
@@ -69,7 +69,7 @@ if (!closeResult.success) {
69
69
 
70
70
  `audit.log(input,tx)`、`audit.capture(input,{tx})` 可使用调用者现有 RelDB 事务,事件和变更一并提交或回滚;失败尝试须在回滚后由入口记录。自动记录失败输出诊断并保留已生效业务结果;强制审计的业务检查同事务显式 log 的 HaiResult 后再提交。list 支持 requestId/traceId 关联筛选。
71
71
 
72
- `retention:{enabled:true,minDays:90}` 启用共享最低保留,cleanup(0) 不能绕过。触发器拒绝保护期内改删及冲突覆盖;未传 retention 的重初始化沿用已有策略,只有显式可信配置能关闭。默认清理90天不等于不可变保护,首次未配置时不强制启用。DB 所有者 DDL、权限与独立归档需部署侧控制。升级前停旧写入并备份,`migrateAudit({dryRun:true})` 只读预检,init 自动升级;close 会等待已开始的写入。
72
+ `retention:{enabled:true,minDays:90}` 启用共享最低保留,cleanup(0) 不能绕过。触发器拒绝保护期内改删及冲突覆盖;未传 retention 的重初始化沿用已有策略,只有显式可信配置能关闭。默认清理90天不等于不可变保护,首次未配置时不强制启用。DB 所有者 DDL、权限与独立归档需部署侧控制。`audit.init()` 校验当前表结构。用户名取自事件时的身份快照。`audit.close()` 等待已开始的写入。
73
73
 
74
74
  `backup.enabled` 默认关闭;开启后注入已初始化的私有 StorageFunctions。backup.run({id?,from?,to?,signal?}) 流式条件创建 JSONL,返回记录数/SHA-256/截止时间;稳定 ID 只复用完整且条件相同的归档,不覆盖。backup.verify(key) 有界核对;batchSize/maxRecordBytes 控制内存,超限不截断证据。AUDIT_BACKUP_PREFIX 固定为 hai-private/audit-backups/,公开读取入口只允许指定资源,关闭备份不解除私有边界;已配置公开 publicUrl 的 S3 不能启用备份。
75
75
 
@@ -58,7 +58,7 @@ description: "构建或排查 hai 应用的源码、静态网站、容器镜像
58
58
 
59
59
  交付角色统一为 `serv`(独立 API 后端)、`web`(静态或 SSR 浏览器站点)、`app`(Android/iOS 手机安装包)、`desktop`(桌面安装包)。Fullstack 容器为 serv + 可选 web,API 为 serv,admin/website/h5/mobile-app 的浏览器镜像为 web;原生 app/desktop 不作为服务器 Compose 服务。`--images` 与 `--services` 使用 serv/web,`--target app` 与 `--target desktop` 生成原生包。API workspace 的服务目录为 `apps/<项目>-serv`。镜像与容器共用工程根 `package.json.name` 的归一化身份(去 scope、转小写、非法字符合并为连字符);名称缺失或无效时报错,不回退目录名。镜像为 `localhost/hai-<项目>-<角色>:<buildId>`,容器为 `hai-<项目>-<环境>-<角色>`,镜像可跨环境复用。
60
60
 
61
- 制品格式4和5都记录primaryService(serv/web),hasWeb表示完整工程是否包含浏览器端;格式5另记录非根目录webBasePath。只有serv + web生成同源API代理;格式1至3须重新构建。CLI的--project-name与SDK的DeployAppOptions.projectName覆盖入口已移除,旧参数明确报错,请在工程根package.json.name维护身份。迁移旧工程先同步build.images、Dockerfile及API workspace的-serv包名与引用,重新安装依赖并重建完整制品。旧部署先备份、核对项目名、卷和受管凭据,再建立完整基线。工具不自动跨项目迁移数据;完整部署仍按Compose规则清理同项目旧服务,不能仅改deploy-target.json.directory完成迁移。网站目录与格式5迁移见[网站挂载目录](paths.md)。
61
+ 镜像制品统一使用格式 5,`webBasePath` 为空字符串时表示域名根目录。`primaryService` 记录主服务,`hasWeb` 记录完整 Web 拓扑。部署身份来自工程根 `package.json.name`。只有 `serv` 与 `web` 同时存在时才生成同源 API 代理。 公开挂载目录见[网站挂载目录](paths.md)。
62
62
 
63
63
  ## 按需参考
64
64
 
@@ -21,7 +21,7 @@ SvelteKit 3 的宿主在 `sveltekit({ paths: { base: 挂载目录, relative: fal
21
21
  ## SDK容器交付
22
22
 
23
23
  - `PUBLIC_BASE_PATH=/yyy`用于compile或image网站构建;HAI_BUILD_STATIC仍只用于website的compile静态预渲染。
24
- - 根目录制品沿用格式4,非根目录制品使用格式5并记录webBasePath。使用支持格式5的CLI/Deploy;旧工具拒绝新格式,不能手改编号绕过。
24
+ - 镜像清单使用格式 5,`webBasePath` 为必填项。域名根目录写空字符串。
25
25
  - SDK生成匹配的Nginx目录、运行配置入口、健康检查和访问URL。外部网关转发到此入口时保留目录,避免再次剥离。
26
26
  - 每次完整或部分构建保持同一PUBLIC_BASE_PATH,即使只构建serv。目录变化须全量部署;遗漏或改变目录的增量包会在替换前拒绝。失败恢复使用旧发布的健康路径;容器恢复不撤销业务数据。
27
27
 
@@ -192,7 +192,7 @@ logger.info('Module initialized')
192
192
 
193
193
  // 创建携带固定上下文的子 logger(自动合并到每条日志)
194
194
  const reqLogger = core.logger.child({ requestId: 'req-001' })
195
- reqLogger.info('Request started') // 日志中自动包含 requestId
195
+ reqLogger.debug('Request started') // 日志中自动包含 requestId
196
196
 
197
197
  // 配置输出格式和级别
198
198
  core.logger.configure({
@@ -204,16 +204,7 @@ core.logger.setLevel('debug')
204
204
  const level = core.logger.getLevel() // 'debug'
205
205
  ```
206
206
 
207
- **日志级别规范**:
208
-
209
- | 级别 | 适用场景 |
210
- | ------- | ---------------------------------- |
211
- | `trace` | 循环内详细调试数据 |
212
- | `debug` | 函数进入、参数概要、读操作 |
213
- | `info` | 业务事件(初始化完成、写操作成功) |
214
- | `warn` | 异常但可恢复(校验失败、重试) |
215
- | `error` | 操作失败(需人工排查) |
216
- | `fatal` | 致命错误(服务无法继续) |
207
+ 日志级别、业务错误处理和字段选择见 [日志与错误诊断](../hai-app-create/logging.md)。
217
208
 
218
209
  ### 国际化 — `core.i18n`
219
210
 
@@ -32,7 +32,7 @@ transport 仅支持 v2:SM2 发送方签名、独立密钥的 Encrypt-then-HMAC
32
32
 
33
33
  业务容量统一配置 transport.maxPlaintextBytes:UTF-8 JSON 明文字节,默认 1 MiB,有效整数范围 1 KiB~64 MiB,请求和响应共用。客户端从握手获取服务端预算,可选本地上限取较小值;resolveTransportBodyLimits 自动推导密文预算为两倍明文加 32 KiB。握手 JSON 16 KiB、公钥 4 KiB、认证头 16 KiB、路径 8 KiB、认证及防重放保持独立。原流超限/取消立即停止、取消并释放 reader;请求 413、响应返回加密的 500 容量错误,客户端识别 TransportBodyTooLargeError / hai:crypto:070。 自定义适配器使用 manager.bodyLimits 和 readTransportBody 消费原流,不 clone/tee;握手返回 maxPlaintextBytes。
34
34
 
35
- Serv/Kit 对通过请求绑定、签名和 MAC 校验但超出时间窗口的请求返回加密的 HTTP 400 错误。客户端抛出 `TransportTimeError`(`hai:crypto:071`),显示校验时的服务器 UTC 时间,提示开启设备的自动校时或手工修改时间后刷新。`serverTime` 为 Unix 毫秒,旧服务端未提供该值时仍显示校时提示。`api-client` 映射为 `VALIDATION_FAILED`,在 `cause.transportCode` 和 `cause.serverTime` 保留诊断信息。
35
+ Serv/Kit 对通过请求绑定、签名和 MAC 校验但超出时间窗口的请求返回加密的 HTTP 400 错误。客户端抛出 `TransportTimeError`(`hai:crypto:071`),显示校验时的服务器 UTC 时间,提示开启设备的自动校时或手工修改时间后刷新。`serverTime` 为 Unix 毫秒。`api-client` 映射为 `VALIDATION_FAILED`,在 `cause.transportCode` 和 `cause.serverTime` 保留诊断信息。
36
36
 
37
37
  ## 按需参考
38
38
 
@@ -206,8 +206,8 @@ const sharedServer = crypto.transport.createServer({
206
206
 
207
207
  使用流程:
208
208
  1. 先 `await crypto.init()`。
209
- 2. 服务端显式传入受管 serverKeyRing 和共享 keyStore,协商返回 `serverKeyId + serverPublicKey + clientId`。
210
- 3. 客户端 `createClient({ keyExchangeUrl })` 后,首次 `init()` / `encryptedFetch()` 会自动做密钥协商。
209
+ 2. 服务端显式传入受管 `serverKeyRing` 和共享 `keyStore`。协商响应包含 `serverKeyId`、`serverPublicKey`、`clientId`、`maxPlaintextBytes` 和可选的 `clientKeyTtlSeconds`。自定义适配器读取 `manager.clientKeyTtlSeconds` 填充登记有效期。
210
+ 3. 客户端 `createClient({ keyExchangeUrl })` 后,首次使用或登记临近到期时自动协商。并发请求共享握手,协商完成后再发送业务请求。
211
211
  4. 所有请求均发送 v2 认证封装;包括 GET/HEAD 和无 input POST,不能只附带 clientId。
212
212
  5. 自定义适配器先 await authenticateTransportRequest(manager, request),再用 manager.encryptResponse(verified.data.context, data, status) 关联响应。
213
213
  6. 客户端收到 `X-Encrypted: true` 的响应后自动解密;`destroy()` 可清空当前会话。
@@ -235,7 +235,7 @@ const manager = crypto.transport.createServer({ serverKeyRing: ring.data, keySto
235
235
 
236
236
  业务容量统一配置 transport.maxPlaintextBytes:UTF-8 JSON 明文字节,默认 1 MiB,有效整数范围 1 KiB~64 MiB,请求和响应共用。客户端从握手获取服务端预算,可选本地上限取较小值;resolveTransportBodyLimits(limit, { requiredPlaintextBytes }) 合并模块需求:保留显式上限,不足时报配置冲突;省略时按需要推导,无模块需要仍为 1 MiB。Serv/Kit 的 auto 规范化为 undefined。密文预算为两倍明文加 32 KiB。握手 JSON 16 KiB、公钥 4 KiB、认证头 16 KiB、路径 8 KiB、认证及防重放保持独立。原流超限/取消立即停止、取消并释放 reader;请求 413、响应返回加密的 500 容量错误,客户端识别 TransportBodyTooLargeError / hai:crypto:070。 自定义适配器使用 manager.bodyLimits 和 readTransportBody 消费原流,不 clone/tee;握手返回 maxPlaintextBytes。
237
237
 
238
- Serv/Kit 对通过请求绑定、签名和 MAC 校验但超出时间窗口的请求返回加密的 HTTP 400 错误。客户端抛出 `TransportTimeError`(`hai:crypto:071`),显示校验时的服务器 UTC 时间,提示开启设备的自动校时或手工修改时间后刷新。`serverTime` 为 Unix 毫秒,旧服务端未提供该值时仍显示校时提示。`api-client` 映射为 `VALIDATION_FAILED`,在 `cause.transportCode` 和 `cause.serverTime` 保留诊断信息。
238
+ Serv/Kit 对通过请求绑定、签名和 MAC 校验但超出时间窗口的请求返回加密的 HTTP 400 错误。客户端抛出 `TransportTimeError`(`hai:crypto:071`),显示校验时的服务器 UTC 时间,提示开启设备的自动校时或手工修改时间后刷新。`serverTime` 为 Unix 毫秒。`api-client` 映射为 `VALIDATION_FAILED`,在 `cause.transportCode` 和 `cause.serverTime` 保留诊断信息。
239
239
 
240
240
  自定义 HTTP 适配器检查失败原因 `error instanceof TransportTimeError && error.context` 后,使用 `createTransportErrorResponse(error, { manager, context: error.context })` 加密拒绝响应;不能根据未认证报文的时间戳给出可信时间诊断。
241
241
 
@@ -20,13 +20,11 @@ description: "构建并发布或交付已有镜像:完整离线包、按服务
20
20
 
21
21
  Vercel API/文件上传只请求官方入口,拒绝重定向,每次请求和完整 JSON 读取共用 30 秒期限;超时先查询平台实际状态,不无条件重复发布。此期限没有配置开关。
22
22
 
23
- --plan 和 --inspect-remote 从 CLI 完整透传,只读失败不得退回部署。target.runtime 只表示远端配置。旧凭据用服务器本机 `hai deploy-secrets <directory> --import <private.json>` 预检,--apply 才导入;SDK importDeploymentSecrets 默认 dryRun:true,验证项目/卷/实际凭据、共享 flock、v2 状态特殊字符与原子 0600 写入,缺状态的普通部署仍拒绝。完整格式和恢复步骤见 [凭据导入](credentials.md)。
23
+ --plan 和 --inspect-remote 从 CLI 完整透传,只读失败不得退回部署。target.runtime 只表示远端配置。
24
24
 
25
25
  ## 操作流程
26
26
 
27
- 全量与增量都须在变更前核对实际卷和后端家族。服务器 `persistent-backends` 在任何启动前保存绑定,服务移除、首次失败及恢复均保留;与 `managed-secrets.env`、数据卷成套保护和备份。不能删除或编辑账本强行认领,不把“移除 PostgreSQL 再加 MySQL”视为新环境。旧 active 只有已知家族和唯一真实服务容器的 Mounts、Config.Image 同时吻合才能补证;未知 dormant 旧卷拒绝猜测,活动卷缺失拒绝空库自愈。新 release 的 `previous-state-backends` 仅供恢复前核验旧 tuple、账本与实际卷,不回退永久记录。此私有元数据没有新增公开配置 API。
28
-
29
- 受控导入 v2 可为未认领旧卷补证,但须项目/凭据组/精确卷绑定与唯一运行原容器的真实角色标签、Mounts、Image 家族完全一致;Docker/Podman namespace冲突拒绝,已知 ledger/context/daemon 冲突不覆盖。导入不创建 current,缺成功基线时必须当前 CLI 新 ID 完整全量包,完整成功后才可增量。详见[凭据导入](credentials.md),不得把导入或接管称作数据库数据恢复。
27
+ 全量和增量部署都须在变更服务前核对实际卷及后端家族。已有卷须匹配 `persistent-backends`,缺少记录或活动卷时停止部署,先恢复核验过的备份。服务启动前写入账本,移除服务、首次安装失败和恢复都会保留。账本与 `managed-secrets.env`、数据卷成套备份。`previous-state-backends` 用于恢复前重新验证原绑定。
30
28
 
31
29
  身份限制只发生在 SSH 目标 Linux 服务器执行 `apply.sh`:固定 SSH uid、本地默认 Docker context 或实际本地 Podman,以及 daemon/store;远程连接、非默认 context、显式存储覆盖拒绝。生产显式 pin `remote.runtime`;auto 因新装 Podman 换引擎时,先恢复原 runtime/用户/store。Windows/macOS 操作机用 Docker Desktop/Podman VM 构建打包再 SSH 发布不受限。有意更换 daemon、服务器或恢复/替换数据时单独迁移,不能用 uid、路径或家族相同证明数据格式兼容;主版本和 Schema 兼容仍须单独确认。
32
30
 
@@ -34,12 +32,12 @@ Vercel API/文件上传只请求官方入口,拒绝重定向,每次请求和
34
32
 
35
33
  1. 读取项目 scripts、images/ 中的 Dockerfile、项目根 hai.json 的 build/deploy 与服务目录运行配置。构建与部署属于整个项目;服务目录只用于扫描运行依赖。创建默认带开发期部署工具;用户明确 --no-deploy 时不生成部署入口。构建细节见 [hai-build](../hai-build/SKILL.md)。
36
34
  2. 选择环境,验证 .env.<mode> 存在,再处理部署目标与必要中间件。SSH 支持 key/agent 或 password,缺项按说明补齐,有效完整配置直接复用;--bundle-only 在构建和中间件配置后、打包前最后询问是否添加或修改 SSH 信息,保存并用于本次和后续 target;--yes 或跳过时复用已有配置。新私有参数写 .env.<mode>.local,共享 .env.local 保留本机私有值;私钥仅引用路径,密码隐藏输入并保留原值。Core、Node/Vite 和部署统一加载模式私有层,按来源先父后细项。优先级与认证边界见 [middleware.md](middleware.md)。
37
- 3. 先校验所选制品、版本、服务和SHA-256。--build只构建所选应用镜像;不带它时缺产物明确失败,不隐式补构建。根目录使用格式4,非根目录使用格式5及webBasePath;两者都记录appVersion、buildId、partial、hasWeb、镜像归档及显式配置的servContainerPort/webContainerPort,部分构建也保留未选镜像端口,缺省使用部署默认值。网站目录由构建进程PUBLIC_BASE_PATH确定,所有部分构建须保持一致;目录变化必须全量,健康与恢复按制品记录处理。具体步骤见[网站挂载目录](../hai-build/paths.md)。同一应用版本可重建,镜像引用区分实际内容;禁止latest与无版本镜像。
35
+ 3. 先校验所选制品、版本、服务和SHA-256。--build只构建所选应用镜像;不带它时缺产物明确失败,不隐式补构建。制品使用格式 5,记录 `webBasePath`(域名根目录为空字符串)、appVersion、buildId、partial、hasWeb、镜像归档及显式配置的servContainerPort/webContainerPort,部分构建也保留未选镜像端口,缺省使用部署默认值。网站目录由构建进程PUBLIC_BASE_PATH确定,所有部分构建须保持一致;目录变化必须全量,健康与恢复按制品记录处理。具体步骤见[网站挂载目录](../hai-build/paths.md)。同一应用版本可重建,镜像引用区分实际内容;禁止latest与无版本镜像。
38
36
  4. 完整包自动导出实际自建中间件镜像并去重;只更新 Web 不重新打包 API/中间件。打包机需容器引擎导出中间件,服务器仅 load,禁止 pull/build。外部连接及云开通不能伪称为离线自包含服务。
39
37
  5. --services 使用目标服务器最后成功状态为基线,检查端口/拓扑及未选依赖,保留未选配置、凭据、网络和卷。脚本先按项目/服务标签仅删除选中旧容器(保留卷),再 up -d --no-deps --no-build --pull never --no-recreate 创建新容器并保留其他容器;先等待选中中间件健康,再启动 serv/Web,失败恢复同序,避免 Compose Provider 连带重建反向依赖,禁止整个项目 down。S3 可选 createBucket 在 storage.init 中检查并创建缺失桶,自建 S3 注入 HAI_STORAGE_CREATEBUCKET=true;仅更新 storage 无须应用镜像,新桶在下次应用初始化时创建。Compose 网络统一 hai-app,序列化不使用 YAML 锚点。
40
38
  6. 每次交付只生成工程根 .hai/deploy/<环境>/bundle-<项目>-<部署ID>-<版本>/ 一个目录,外层仅含 tar、deploy.sh、deploy.ps1、deploy-target.json、read-target.awk、deploy-local.sh 和 README.txt;Compose、运行环境、web.conf 和 manifest 仅在 tar 内,README 记录部署 ID。--output release.tar 生成同目录的 release-<版本>/release-<版本>.tar,不能覆盖已有交付。--bundle-only 不连接 SSH,生成带逐项说明的 deploy-target.json,只引用 passwordEnv 而不含 SSH 密码值;修改 target 仅影响本包。Windows PowerShell 5+ deploy.ps1、Linux/macOS deploy.sh 读取该文件,上传并自动解包部署;隔离服务器复制整个目录后执行 sh deploy-local.sh,自动解包再执行 apply.sh。只复制归档时,解压后阅读同目录的 `APPLY.txt`,在该目录执行 `sh ./apply.sh`。脚本不依赖源码、Node 或 pnpm;本机运行 deploy.sh 需 sh、awk、ssh、scp。manifest.partial/selectedServices 固化模式:完整构建且无 --services 为全量;部分构建或显式 --services 为增量,必须匹配服务器成功基线。
41
39
  7. 工具预检、基线、上传、校验、镜像导入、服务更新和 HTTP 检查分别报告。失败返回非零退出码,结果文件保留失败阶段;只有检查成功才更新服务器 current。已有部署失败尝试恢复所选服务的旧容器和配置,并按旧发布端口/路径验证容器和 HTTP;result 的 recoveryStatus 为 not-needed/passed/failed/stopped。数据卷保留,已写业务数据和不兼容 Schema 迁移不回退。恢复验证失败不能表述为已回滚成功。禁止把本地打包记录当服务器成功状态。
42
- 8. 部署及恢复全程用 Linux flock 持有固定 `.deploy.lock`;不得删除/替换文件或按时间抢锁。父进程强杀后,继承 FD 的活跃子命令仍持锁;所有持有进程退出或重启后自动释放。旧 `.deploy-lock` 目录保持原样,升级前结束旧版部署进程。强杀/断电不执行恢复 trap,缺结果文件须人工核对容器、镜像和 current 后用新包恢复,锁释放不代表副作用已回退。
40
+ 8. 部署及恢复全程用 Linux flock 持有固定 `.deploy.lock`;不得删除/替换文件或按时间抢锁。父进程强杀后,继承 FD 的活跃子命令仍持锁;所有持有进程退出或重启后自动释放。强杀/断电不执行恢复 trap,缺结果文件须人工核对容器、镜像和 current 后用新包恢复,锁释放不代表副作用已回退。
43
41
 
44
42
  成功后手工回退使用**当前 CLI**、旧应用 build 制品和匹配配置,省略 `--build`、`--services` 生成新全量 ID;离线也须重新打包,不能运行旧 bundle 或旧工具绕过账本。旧制品通常只保留 serv/web 镜像,中间件仍按当前 CLI 固定版本规划,不承诺精确回退全部中间件版本、DDL 或业务数据。确认主版本、Schema、拓扑和数据兼容并准备备份,再发布和验证。
45
43
 
@@ -73,7 +71,7 @@ Node 后端(Serv / SvelteKit SSR)每次启动读取服务器 `<部署目录>
73
71
 
74
72
  每个部署包的外层 `README.txt` 和归档内 `APPLY.txt` 都包含文件索引、环境优先级、当前包的变量名和排障步骤。解包目录的 `config/.env.runtime` 是发布输入,部署根下的是当前运行配置,`releases/<ID>/` 下的是历史记录。外部 PostgreSQL 改密后更新项目源环境并发布后端。部署校验拒绝 URL 与分字段密码冲突,增量切换外部与随包中间件需要改用全量发布。 `HAI_SERV_REFRESH_COOKIE_SECURE` 独立于 `HAI_SERV` 父配置,容器部署接受 `true`、`false` 或空值。
75
73
 
76
- 部署归档顶层包含 `apply.sh`、`APPLY.txt`、`manifest.json`、`config/`、`images/` 和 `.hai/`。`config/.env.runtime` 是唯一应用环境文件;`manifest.json.metadata` 集中保存依赖、布局、健康路径、镜像列表、受管绑定等非敏感元数据。`.hai/compose.json` 是完整服务快照;局部更新在服务边界替换并保留未选服务,不串联历史 Compose 文件。`.hai/` 还存原生环境、加载器、Nginx/TLS、策略和校验和,秘密不写入 manifest。辅助脚本合入 `apply.sh`。失败时先恢复原应用配置再恢复服务,凭据、后端账本和数据卷继续保护。新包格式为 2,不实现旧目录兼容或迁移。
74
+ 部署归档顶层包含 `apply.sh`、`APPLY.txt`、`manifest.json`、`config/`、`images/` 和 `.hai/`。`config/.env.runtime` 是唯一应用环境文件;`manifest.json.metadata` 集中保存依赖、布局、健康路径、镜像列表、受管绑定等非敏感元数据。`.hai/compose.json` 是完整服务快照;局部更新在服务边界替换并保留未选服务,不串联历史 Compose 文件。`.hai/` 还存原生环境、加载器、Nginx/TLS、策略和校验和,秘密不写入 manifest。辅助脚本合入 `apply.sh`。失败时先恢复原应用配置再恢复服务,凭据、后端账本和数据卷继续保护。部署归档的 `bundleFormat` 为 `2`。
77
75
 
78
76
  ## CLI
79
77
 
@@ -86,7 +84,7 @@ pnpm build --target image --images web
86
84
  pnpm run deploy --env prod --services web
87
85
  ```
88
86
 
89
- 制品格式4和5都记录primaryService(serv/web),hasWeb表示完整工程是否包含浏览器端;格式5另记录非根目录webBasePath。只有serv + web生成同源API代理;格式1至3须重新构建。CLI的--project-name与SDK的DeployAppOptions.projectName覆盖入口已移除,旧参数明确报错,请在工程根package.json.name维护身份。迁移旧工程先同步build.images、Dockerfile及API workspace的-serv包名与引用,重新安装依赖并重建完整制品。旧部署先备份、核对项目名、卷和受管凭据,再建立完整基线。工具不自动跨项目迁移数据;完整部署仍按Compose规则清理同项目旧服务,不能仅改deploy-target.json.directory完成迁移。
87
+ 镜像制品统一使用格式 5,`webBasePath` 为空字符串时表示域名根目录。`primaryService` 记录主服务,`hasWeb` 记录完整 Web 拓扑。部署身份来自工程根 `package.json.name`。只有 `serv` 与 `web` 同时存在时才生成同源 API 代理。
90
88
 
91
89
  上传脚本先复用操作机密码;缺失或为空时直接隐藏输入并继续本次部署,输入不落盘;非交互调用明确失败且不等待。无效输入显示变量名和约束;SCP 上传期间通过 SSH 查询远端归档大小,显示已接收/总字节与百分比,成功后才显示 100%。增量部署先恢复已停止或 Compose down 的既有完整基线,复用本地镜像且不重建正在运行的容器,再检查依赖和更新选中服务;恢复失败须检查诊断,不能推进 current。
92
90
 
@@ -98,6 +96,8 @@ Serv/SvelteKit 后端的策略快照、只读挂载、环境覆盖、代理信
98
96
 
99
97
  ## 自建对象存储直连
100
98
 
99
+ 签名下载误入静态网站回退或在 SSR/API 网关收到应用 404 时,Nginx 返回独立的 404 提示页。重复失败时,检查生效的 `HAI_STORAGE_PUBLICENDPOINT` 和实际代理路由,再签发新链接验证。升级已有代理规则使用完整交付包,独立静态 Web 镜像从更新后的模板重建。
100
+
101
101
  S3 的内部 endpoint 与预签名 publicEndpoint 分开;后者无默认,填用户可访问的 HTTP(S) origin,不含桶名或网站目录。全栈 Web Nginx 及 SSR/API gateway 将实际桶路径原样代理至 storage,不经过 Serv,保留签名 Host/URI/query,关闭对象缓冲和签名日志。桶与网站/API 路径不能重叠;外层反向代理也须转发桶路径,不能把网站 base 加到签名 URL。跨源 CORS 在 S3 配置,私有桶保持签名授权;外部 S3 不生成本地路由,Local 不开放目录。
102
102
 
103
103
  自建保留 bucket/region/prefix/publicEndpoint/publicUrl/operationLog。增加 gateway 或改变这些访问配置需全量基线,增量摘要不一致在更新前拒绝;选中范围不改变完整拓扑规划。应用/Storage 增量刷新保留代理 DNS,恢复使用旧发布拓扑。验证真实 S3+Nginx 大文件、Range、PUT、匿名/篡改/过期拒绝,并核对外层日志和回滚;不能用只检查生成字符串代替网络验证。