@numa-tech/numa 1.20.2 → 1.20.4

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 (142) hide show
  1. package/README.md +13 -1
  2. package/dist/application-onboarding/schemas.d.ts +24 -0
  3. package/dist/application-onboarding/schemas.js +4 -0
  4. package/dist/application-onboarding/schemas.js.map +1 -1
  5. package/dist/application-onboarding/tui-model.d.ts +1 -0
  6. package/dist/application-onboarding/tui-model.js +10 -0
  7. package/dist/application-onboarding/tui-model.js.map +1 -1
  8. package/dist/application-onboarding/tui.js +1 -1
  9. package/dist/application-onboarding/tui.js.map +1 -1
  10. package/dist/authorization/application-client.d.ts +46 -5
  11. package/dist/authorization/application-client.js +21 -4
  12. package/dist/authorization/application-client.js.map +1 -1
  13. package/dist/authorization/application-commands.js +10 -3
  14. package/dist/authorization/application-commands.js.map +1 -1
  15. package/dist/authorization/application-schemas.d.ts +52 -6
  16. package/dist/authorization/application-schemas.js +25 -3
  17. package/dist/authorization/application-schemas.js.map +1 -1
  18. package/dist/business-issuer.js +8 -0
  19. package/dist/business-issuer.js.map +1 -1
  20. package/dist/cli.js +49 -31
  21. package/dist/cli.js.map +1 -1
  22. package/dist/command-catalog.js +9 -0
  23. package/dist/command-catalog.js.map +1 -1
  24. package/dist/kb/catalog.d.ts +2 -0
  25. package/dist/kb/catalog.js +14 -0
  26. package/dist/kb/catalog.js.map +1 -0
  27. package/dist/kb/client.d.ts +46 -0
  28. package/dist/kb/client.js +272 -0
  29. package/dist/kb/client.js.map +1 -0
  30. package/dist/kb/commands.d.ts +14 -0
  31. package/dist/kb/commands.js +121 -0
  32. package/dist/kb/commands.js.map +1 -0
  33. package/dist/kb/config.d.ts +15 -0
  34. package/dist/kb/config.js +76 -0
  35. package/dist/kb/config.js.map +1 -0
  36. package/dist/kb/errors.d.ts +9 -0
  37. package/dist/kb/errors.js +31 -0
  38. package/dist/kb/errors.js.map +1 -0
  39. package/dist/kb/transfers.d.ts +67 -0
  40. package/dist/kb/transfers.js +257 -0
  41. package/dist/kb/transfers.js.map +1 -0
  42. package/dist/kb/types.d.ts +81 -0
  43. package/dist/kb/types.js +2 -0
  44. package/dist/kb/types.js.map +1 -0
  45. package/dist/kb/upload-state.d.ts +20 -0
  46. package/dist/kb/upload-state.js +135 -0
  47. package/dist/kb/upload-state.js.map +1 -0
  48. package/dist/login-recovery.js +15 -1
  49. package/dist/login-recovery.js.map +1 -1
  50. package/dist/maintenance/modules.js +1 -1
  51. package/dist/maintenance/modules.js.map +1 -1
  52. package/dist/mcp/catalog.js +6 -1
  53. package/dist/mcp/catalog.js.map +1 -1
  54. package/dist/oss/file-operations.d.ts +8 -0
  55. package/dist/oss/file-operations.js +48 -6
  56. package/dist/oss/file-operations.js.map +1 -1
  57. package/dist/oss/signed-url-transfers.d.ts +41 -0
  58. package/dist/oss/signed-url-transfers.js +207 -0
  59. package/dist/oss/signed-url-transfers.js.map +1 -0
  60. package/dist/oss/transfer-pool.d.ts +2 -0
  61. package/dist/oss/transfer-pool.js +27 -0
  62. package/dist/oss/transfer-pool.js.map +1 -0
  63. package/dist/oss/transfer-worker.js +8 -17
  64. package/dist/oss/transfer-worker.js.map +1 -1
  65. package/dist/oss/web-page.js +48 -8
  66. package/dist/oss/web-page.js.map +1 -1
  67. package/dist/platform-profile.d.ts +1 -1
  68. package/dist/server.d.ts +2 -0
  69. package/dist/server.js +2 -0
  70. package/dist/server.js.map +1 -1
  71. package/dist/tasks/commands.d.ts +9 -0
  72. package/dist/tasks/commands.js +60 -0
  73. package/dist/tasks/commands.js.map +1 -0
  74. package/dist/tasks/oss-query.d.ts +56 -0
  75. package/dist/tasks/oss-query.js +125 -0
  76. package/dist/tasks/oss-query.js.map +1 -0
  77. package/dist/tasks/runtime.d.ts +151 -0
  78. package/dist/tasks/runtime.js +71 -0
  79. package/dist/tasks/runtime.js.map +1 -0
  80. package/dist/tasks/tools.d.ts +3 -0
  81. package/dist/tasks/tools.js +16 -0
  82. package/dist/tasks/tools.js.map +1 -0
  83. package/dist/tasks/types.d.ts +14 -0
  84. package/dist/tasks/types.js +4 -0
  85. package/dist/tasks/types.js.map +1 -0
  86. package/dist/tasks/wecom-notify.d.ts +26 -0
  87. package/dist/tasks/wecom-notify.js +150 -0
  88. package/dist/tasks/wecom-notify.js.map +1 -0
  89. package/docs/agent-tasks.md +97 -0
  90. package/docs/application-default-groups.md +36 -0
  91. package/docs/design/kb-cli.md +94 -0
  92. package/docs/kb-access.md +85 -0
  93. package/package.json +12 -6
  94. package/scripts/export-kb-contract.py +158 -0
  95. package/skills/numa-ai-registry/SKILL.md +2 -2
  96. package/skills/numa-cli/SKILL.md +24 -9
  97. package/skills/numa-cli/agents/openai.yaml +2 -2
  98. package/skills/numa-cli/evals/evals.json +13 -1
  99. package/skills/numa-cli/numa-skill.json +1 -1
  100. package/skills/numa-cli/references/cli-bootstrap.md +8 -8
  101. package/skills/numa-cli/references/cli-mcp.md +28 -20
  102. package/skills/numa-cli/references/pipeline-release.md +9 -9
  103. package/skills/numa-cli/references/skills-command-contract.md +40 -40
  104. package/skills/numa-cli/references/skills-updates.md +13 -13
  105. package/skills/numa-cli/references/skills-workflows.md +4 -4
  106. package/skills/numa-create-application/SKILL.md +1 -1
  107. package/skills/numa-create-application/numa-skill.json +2 -2
  108. package/skills/numa-create-application/references/cli-bootstrap.md +3 -3
  109. package/skills/numa-create-application/references/numa-cli.md +16 -16
  110. package/skills/numa-jenkins-deployment/SKILL.md +8 -8
  111. package/skills/numa-jenkins-deployment/numa-skill.json +2 -2
  112. package/skills/numa-jenkins-deployment/references/cli-bootstrap.md +3 -3
  113. package/skills/numa-jenkins-deployment/references/cli-contract.md +7 -7
  114. package/skills/numa-jenkins-deployment/references/diagnostics.md +2 -2
  115. package/skills/numa-local-flux-deploy/SKILL.md +1 -1
  116. package/skills/numa-local-flux-deploy/numa-skill.json +2 -2
  117. package/skills/numa-local-flux-deploy/references/cli-bootstrap.md +3 -3
  118. package/skills/numa-nacos/SKILL.md +4 -4
  119. package/skills/numa-nacos/numa-skill.json +2 -2
  120. package/skills/numa-nacos/references/cli-bootstrap.md +3 -3
  121. package/skills/numa-nacos/references/command-contract.md +17 -17
  122. package/skills/numa-notion/SKILL.md +4 -4
  123. package/skills/numa-notion/numa-skill.json +2 -2
  124. package/skills/numa-notion/references/agents-automation.md +2 -2
  125. package/skills/numa-notion/references/cli-bootstrap.md +3 -3
  126. package/skills/numa-notion/references/cli.md +17 -17
  127. package/skills/numa-oss-access/SKILL.md +27 -81
  128. package/skills/numa-oss-access/agents/openai.yaml +2 -2
  129. package/skills/numa-oss-access/evals/evals.json +12 -0
  130. package/skills/numa-oss-access/numa-skill.json +2 -2
  131. package/skills/numa-oss-access/references/cli-bootstrap.md +5 -5
  132. package/skills/numa-oss-access/references/object-operations.md +95 -0
  133. package/skills/numa-oss-access/references/search-and-mcp.md +12 -11
  134. package/skills/numa-wecom/SKILL.md +27 -62
  135. package/skills/numa-wecom/agents/openai.yaml +2 -2
  136. package/skills/numa-wecom/evals/evals.json +25 -0
  137. package/skills/numa-wecom/numa-skill.json +2 -2
  138. package/skills/numa-wecom/references/cli-bootstrap.md +5 -5
  139. package/skills/numa-wecom/references/legacy-operations.md +71 -0
  140. package/skills/numa-wecom/references/machine-auth.md +7 -7
  141. package/skills/numa-wecom/references/mcp.md +9 -8
  142. package/src/kb/openapi-contract.json +1432 -0
@@ -0,0 +1,97 @@
1
+ # Agent 业务任务入口
2
+
3
+ `task run` 将参数校验、当前进程身份配置检查和业务操作组合成一次调用。CLI 与 MCP 直接调用同一份任务函数;MCP 不启动 CLI 子进程。模型负责将用户意图转换成参数,缺项、歧义或执行失败时才继续判断。
4
+
5
+ 这是新增运行时能力。已安装的 Skill、CLI npm 版本、MCP 配置里固定的版本和已连接工具是不同状态;升级 Skill 不会更新运行时。安装/初始化时核对工具或 schema,正常任务无需再次逐项调用版本、help、auth status、whoami、config、capabilities 或 Grant 列表。
6
+
7
+ ## 入口与结果
8
+
9
+ | 任务 | MCP 工具 | 模块 |
10
+ | --- | --- | --- |
11
+ | `wecom.notify` | `numa_wecom_notify` | `wecom` |
12
+ | `oss.query` | `numa_oss_query` | `oss` |
13
+
14
+ ```sh
15
+ # 本地 schema 查询,无需登录或业务 API;省略名称可查看全部任务
16
+ numa task inspect wecom.notify --json
17
+ numa task inspect oss.query --json
18
+
19
+ # 业务调用;task.json 为 UTF-8 JSON 对象,最大 1 MiB
20
+ numa task run wecom.notify --input-file /ABS/notify.json --json
21
+ numa task run oss.query --input-file /ABS/query.json --json
22
+ # --input-file - 也可从 stdin 读取 JSON
23
+ ```
24
+
25
+ 运行结果是 JSON 对象,公共字段为 `task`、`status`;按结果可包含 `data`、`error:{code,message}`、`missing`、`candidates`、`request_id`。
26
+
27
+ | status | CLI 退出码 | 含义与后续动作 |
28
+ | --- | --- | --- |
29
+ | `succeeded` | 0 | 任务完成,解释业务结果与完整性限制 |
30
+ | `needs_input` | 2 | 输入缺失、无效或目标存在歧义,补齐字段或选择候选 |
31
+ | `blocked` | 3 | 配置、权限、范围或业务条件不满足,按脱敏错误处理 |
32
+ | `pending` | 4 | 发送结果尚未确定,保留请求号,仅查询回执 |
33
+
34
+ 进程中断、连接丢失或没有输出不能证明通知未发送。无论 CLI 退出码如何,已尝试的发送只能按原请求号恢复,不能自动重发。
35
+
36
+ ## 企微通知
37
+
38
+ 在用户已授权以下目标与内容的前提下,通知输入示例:
39
+
40
+ ```json
41
+ {
42
+ "recipient_email": "user@example.com",
43
+ "email_type": 2,
44
+ "message_type": "text",
45
+ "content": "构建已完成。",
46
+ "request_id": "notice-20260921-001",
47
+ "confirmed": true
48
+ }
49
+ ```
50
+
51
+ - 收件人模式二选一:精确 `recipient_email` + 显式 `email_type`,或精确 `recipient_user_ids` 数组。`email_type:1` 表示企微企业邮箱,`2` 表示成员资料邮箱,不能从邮箱域名猜测。
52
+ - `message_type` 默认 `text`,支持 `markdown`;`safe` 默认 `false`,Markdown 不支持 `safe:true`。
53
+ - `request_id` 是必需的 8–128 位稳定请求号,只使用字母、数字或 `._:-`。发送前保存,同一意图不换号。
54
+ - `confirmed:true` 表示用户已授权这次目标与正文。已明确授权时无需再问一次;它不是 dry-run。
55
+
56
+ 执行器在内部校验消息、能力与限额、按精确邮箱解析账号,并发送一次。未知结果最多查询一次回执,不自动再次发送。`succeeded` 中的 `data.receipt_status:ACCEPTED` 只表示企微 API 接受,不代表送达或已读;`PARTIAL` 为 `blocked` 并携带失败对象,不能重发全体。
57
+
58
+ `pending` 后调用 `numa_wecom_status` 或同一 CLI 的 `numa wecom status --request-id NOTICE_ID --json`,保持原身份。若恢复查询返回权限错误,仍可能已发送,先恢复原身份访问能力再查;不切换身份重新发送。平台 subject 模式、目录管理和映射维护继续使用低层工具,不属于此通知任务。
59
+
60
+ ## OSS 查询
61
+
62
+ 按精确 bucket/prefix 查询,由执行器选择唯一启用且已同步、覆盖该范围的 Grant:
63
+
64
+ ```json
65
+ {
66
+ "bucket": "example-bucket",
67
+ "prefix": "reports/",
68
+ "suffix": ".pdf",
69
+ "sort": "modified",
70
+ "order": "desc",
71
+ "limit": 20,
72
+ "scan_limit": 1000,
73
+ "fields": ["key", "sizeBytes", "lastModified"]
74
+ }
75
+ ```
76
+
77
+ 也可直接传数值 `grant_id`;可选 `prefix` 限定查询,省略时沿用该 Grant 的授权范围。没有 `grant_id` 时必须提供 `bucket` 和 `prefix`;显式 `prefix:""` 表示请求 bucket 根目录。多个覆盖 Grant 返回 `needs_input` 和有限候选列表,不能自动选择最大权限。指定 Grant 的有效性和最终范围仍受平台与现有 OSS 客户端检查约束。
78
+
79
+ 筛选参数与 `numa_oss_search` 一致,包括目录、key/大小、修改时间、元数据、content-type、约定的创建时间字段、排序和输出列。`scan_limit` 默认 1000、最多 100000;`limit` 默认 100、最多 1000。需要元数据/创建时间时会对候选对象执行 HEAD,并要求 GetObject 权限。
80
+
81
+ 结果在 `data` 中包含搜索摘要与对象。`complete:false`、`truncated:true` 表示扫描未覆盖完整范围,`resultsLimited:true` 表示输出截断;排序只覆盖本次扫描。`nextMarker` 仅用于继续尚未扫描的对象,不能恢复当前 limit 截掉的匹配结果。`LastModified` 是修改时间,不能冒充创建时间;创建时间必须使用已有自定义元数据键。详见 [OSS 搜索与 MCP 契约](../skills/numa-oss-access/references/search-and-mcp.md)。
82
+
83
+ ## WorkBuddy 的身份与环境
84
+
85
+ `mcp.json` 的 `env` 传递给指定 MCP 服务启动进程:
86
+
87
+ ```text
88
+ WorkBuddy → npx → numa serve → 内部任务函数
89
+ ↑
90
+ mcp.json 的 env
91
+ ```
92
+
93
+ 机器模式由 `NUMA_KEYCLOAK_AUTH_MODE=client_credentials`、专用 `NUMA_KEYCLOAK_CLIENT_ID` 以及唯一 Secret 来源(`NUMA_KEYCLOAK_CLIENT_SECRET_FILE` 或 `NUMA_KEYCLOAK_CLIENT_SECRET`)提供。API/Issuer 遵循当前受治理的平台 profile,不通过随意覆盖环境变量切换平台。Secret 文件由认证库读取,Agent 无需读取正文。详情见 [MCP 安装](../skills/numa-cli/references/cli-mcp.md)。
94
+
95
+ 这些环境变量可供 `numa serve` 内部直接使用,不会自动反向导入 WorkBuddy 主进程或其他终端。业务 Skill 在初始化时选定 MCP 路径后保持使用它;缺少工具时报告版本或 `--modules`/`--tools` 配置问题,不自动改走终端、不复制 Secret、不回退个人登录。普通任务的身份检查内置于执行器,权限由实际业务请求的服务端检查。
96
+
97
+ 现有低层 CLI/MCP 工具仍可用于诊断、专门对象操作或用户明确选择的兼容流程。Skills 库上传、提交审核与发布仍是独立治理流程,任务入口不绕过两人制发布审核。
@@ -71,3 +71,39 @@ rotate 还需完整 `--confirm-client-id` 与 `--acknowledge-impact`。必须显
71
71
  Skills/MCP 业务新路径为 `/api/v1/skills`;实例管理和 namespace 写操作为 `/api/v1/admin/nacos/registry`。CLI 不在 401/403 或写超时后退回旧路径重发。发布顺序为:后端兼容 alias 和新路径 → 网关方法映射及权限发布 → CLI 包发布。旧 CLI 继续使用后端保留的 `/api/v1/ai-registry` alias;服务 profile 中的显式 capabilityUrl 仍予以兼容。
72
72
 
73
73
  Codex 模块自己的登录使用独立服务 audience 与 token cache,不把该服务令牌转发给 DevOps enrollment;其应用入组应在该服务的可信登录回调完成。这里的自动入组只使用当前明确的 DevOps API 配置。
74
+
75
+ ## 应用调用关系与前端页面迁移
76
+
77
+ Client 列表的默认关系是本应用拥有的身份。查看外部调用方或本应用调用的服务时显式选择关系:
78
+
79
+ ```sh
80
+ numa authorization application --app 1 identity client list --relation CALLERS --json
81
+ numa authorization application --app 2 identity client list --relation SERVICES --json
82
+ ```
83
+
84
+ 应用分类使用 `applicationType: FRONTEND | BACKEND | FULLSTACK | UNCLASSIFIED`,可在 onboarding 的 application 配置中选择。仅渲染页面的 SSR 应用属于 FRONTEND,同时提供页面和业务 API 的应用属于 FULLSTACK;不根据框架或 Client 数量推断分类。
85
+
86
+ 调用方在本应用拥有的 Client 草稿中配置 `audienceTargets: [{ applicationId: 1, clientId: "backend-api" }]`,精确指定目标后端应用及应用 Client。一个应用可有多个应用 Client,Audience 只由调用方修改;`CALLERS` 是被调用方的只读反查。旧 `consumerApplicationIds` 和 `demand.audience` 写字段已移除。Audience 不授予角色,角色授予仍需独立配置,变更统一通过“环境与发布”生效。
87
+
88
+ API 资源通过 `resources[].clientId` 声明资源所属应用 Client,角色通过 `roles[].clientId` 声明所属 Client。同名角色必须按所属 Client 区分;`defaultRoleClientId` 只作为未显式指定时的默认值。PAGE 不配置资源 Client。
89
+
90
+ PAGE 草稿可以引用其他应用的固定功能版本,格式为
91
+ `permissionSource: { applicationId, definitionVersion, functionCode }` 与 `actions`。
92
+ 前端环境使用 `enforcementMode: "PAGE"`,只发布页面规则,不创建 Keycloak 资源 Client。
93
+
94
+ 从后端转移现有页面时,先创建并核对前端应用,再读取两份草稿版本和当前实际发布的后端权限版本。
95
+ `transfer.json` 必须包含 `targetApplicationId`、`sourceDraftVersion`、`targetDraftVersion` 和
96
+ `permissionDefinitionVersion`。预览不会修改草稿;应用请求还必须携带预览的 `expectedDigest`。
97
+
98
+ ```sh
99
+ numa authorization application --app 1 definition page-transfer preview --file transfer.json --json
100
+ numa authorization application --app 1 definition page-transfer apply --file reviewed-transfer.json --yes --json
101
+ ```
102
+
103
+ 迁移只更新平台草稿,历史版本和线上配置不变。固定前端版本并通过“环境与发布”发布后,页面规则才生效。
104
+ 后端权限版本升级后,应相应更新前端的固定版本引用再发布。CLI 需要配合提供 PAGE 与迁移接口的后端版本使用。
105
+
106
+
107
+ 多 Client 的角色关系写入使用 `ownerClientId` 和裸 `roleName` 明确角色归属;`clientInstanceId` 始终表示接收角色的服务账号所属 Client 实例。省略 owner 只在应用拥有唯一候选 Client 时可解析,歧义会阻断。查询指定 Client 的已发布角色证据可用 `identity subject-access <subject> --env <uuid> --owner-client <client-id>`。
108
+
109
+ 调用方 Client 的 `scopeRoleRefs: ["backend-api/reader"]` 单独限定 Token 可携带的角色范围;它不等于 Audience,也不授予用户或服务账号角色。角色授予继续通过角色关系草稿和统一发布完成。
@@ -0,0 +1,94 @@
1
+ # Numa KB CLI 与 OSS 传输协作设计
2
+
3
+ 日期:2026-09-21。状态:开发完成,验收记录见文末。代码依据:kb-management 当前 Agent REST、upload_sessions、direct_downloads,以及 Numa OSS transfer-worker/queue。已有未提交修改保留;本次不发布 npm、不部署、不操作真实知识库。
4
+
5
+ ## 目标及边界
6
+
7
+ 首版交付可用的 `numa kb`:知识库列表、创建任务知识库、检索、本地文件/目录上传、恢复上传、下载原件、查询/等待/重试入库任务。默认使用 REST;MCP 继续由同一 OpenAPI 自动生成。文件字节走签名 OSS URL;后端无 OSS 时使用 multipart 代理上传接口,并以本次新增的 `conflict_policy=reject` 保持冲突默认拒绝。业务权限、版本冲突、文件校验、解析、索引均由 KB 服务端负责。
8
+
9
+ 复用 OSS 模块的传输调度基础,增加签名 URL 传输适配。现有 STS TransferJob 绑定 grantId/key/binding,不能伪造 STS 会话把 KB 塞入原队列。提取通用有界并发执行器供 OSS worker 和 KB 共用;KB 保存自己的入库会话检查点。首版恢复粒度为文件:已上传对象由 KB status(renew=true) 确认并跳过,未完成 PUT 从文件起点重传。Multipart 分片授权和 OSS 来源导入为后续扩展,不能声称首版支持。
10
+
11
+ ## 命令与 REST 映射
12
+
13
+ 下列 Agent 路径均位于服务 base URL 下 `/api/v1/external/agent`。
14
+
15
+ | 命令 | 方法/路径 | 语义 |
16
+ | --- | --- | --- |
17
+ | kb collections list | GET /collections | 返回当前凭据可见知识库 |
18
+ | kb collections create --name NAME [--description TEXT] | POST /task-collections | task_name 与 collection_name 均取 name,默认私有 |
19
+ | kb search QUERY --collection-id ID [--top-k N] [--min-score N] | POST /search | 输出内容与来源,隐藏下载能力 URL |
20
+ | kb upload PATH --collection-id ID [--state-file PATH] [--wait] | POST /upload-sessions | 扫描、校验、分批创建会话并直传,complete 后提交解析 |
21
+ | kb upload PATH --collection-id ID --resume --state-file PATH | GET /upload-sessions/{id}?renew=true | 核对本地清单及目标身份,恢复文件级进度 |
22
+ | kb download DOCUMENT --output PATH | GET /documents/{id}/download-ticket | 下载到临时文件,校验后原子提交,不覆盖已有目标 |
23
+ | kb tasks status ID [--wait] | GET /archives/{id} | 进度及逐文件回执 |
24
+ | kb tasks retry ID | POST /archives/{id}/retry | 显式重试失败入库项,不自动重放 |
25
+
26
+ 所有命令支持根级 `--json`,stdout 返回 `{ok:true,data:...}`;进度写 stderr。`--wait` 有明确超时,超时保留任务 ID 与恢复信息;终态部分失败不能以全成功返回。上传 PATH 接受普通文件或递归目录,固定排序,拒绝符号链接和空文件;普通文件的解析能力由服务端回执表达;重复 basename 拒绝并说明,因为当前 KB 文件名不承载相对目录结构。文件上限 100 MiB,单会话默认不超过 50 个/1 GiB,允许用户调低 batch-size 以适配环境配置。
27
+
28
+ ## 认证与地址
29
+
30
+ 平台 profile 可声明 `services.kb`,由服务自己的 authority/audience 选择 Numa 已有 Keycloak 凭据。无 profile 时显式使用 `NUMA_KB_BASE_URL`;不把平台管理 API 地址推断为 KB 地址。有 profile 时不允许 env 把 KB 请求换到其他服务。API Key 由 `NUMA_KB_API_KEY` 或 `NUMA_KB_API_KEY_FILE` 提供(二者互斥);不进入命令参数、检查点、日志。
31
+
32
+ 独立服务登录使用 `numa login --service kb --no-console --no-config`;同一选项也用于 auth status/logout、profile,确保登录与请求使用同一 authority/audience 缓存。
33
+
34
+ 默认 `NUMA_KB_AUTH_MODE=keycloak`:KB 控制请求携带当前 Numa Keycloak Bearer 与 X-Api-Key。明确配置 `api-key` 模式时只用 KB API Key,适合不要求 Keycloak 的部署;不得在鉴权失败后自动降级。HTTP 仅允许 loopback 测试地址,其他地址 HTTPS,禁止 URL 内嵌凭据、查询或 fragment。代理下载必须落在同一 KB 服务路径内才可附带身份;OSS 签名 URL 不带 KB/Keycloak 凭据,不跟随重定向。
35
+
36
+ ## API client 与契约
37
+
38
+ `src/kb/client.ts` 提供 KbClient;`errors.ts` 提供 KbError;`types.ts` 提供明确请求、会话、票据类型。离线导出选定 OpenAPI 到 `src/kb/openapi-contract.json`,保留 operationId、请求 schema、响应 schema、参数与引用,添加生成/漂移检查命令。当前后端响应多为开放 dict,需运行时验证会话/票据关键字段,不能把手写类型当成完整服务端保证。
39
+
40
+ 公共方法约定:listCollections(), createCollection({name,description?}), search({query,collection_id,top_k?,min_score?}), beginUpload({collection_id,request_id,files,options?}), getUpload(sessionId,renew?), completeUpload(sessionId), cancelUpload(sessionId), downloadTicket(documentId), getTask(taskId), retryTask(taskId), proxyUpload({collectionId,requestId,files:[{path,filename}],signal?})。所有 Promise;client.binding 为不含秘密的目标/身份绑定摘要,client.baseUrl 为规范服务根。代理下载方法 downloadProxy(ticket,signal?) 返回 Response,检查同服务后才附加身份。
41
+
42
+ 客户端不自动重放写入。网络断开后的创建结果未知错误携带固定错误码;有 request_id 的上传可由显式恢复流程重建同一会话。服务端错误不直接拼接原始响应;保留安全 code/status 及必要恢复提示。任何输出递归移除 URL 中的凭据能力、upload ticket、认证字段。
43
+
44
+ ## 上传状态机
45
+
46
+ 扫描/计算 SHA256 与 Base64 MD5 → 保存私有 manifest → 创建/恢复会话 → 读取待传文件签名 → 并发 PUT → complete → submitted → 可选轮询解析。
47
+
48
+ manifest 版本化,包含目标绑定、collectionId、本地绝对路径和内容摘要、按批固定 requestId、sessionId/batchId;不含 Token、API Key、签名 URL。文件 0600、父目录 0700、原子更新;持有互斥锁防止两个进程使用同一状态文件。恢复时重新验证文件内容和目标绑定,源变化拒绝;创建响应丢失时复用原 requestId。异常保留 state 路径;Ctrl-C 中止活动传输并保留恢复记录。已经 submitted 的会话不重新上传或创建任务。
49
+
50
+ PUT 过期/结果未知后先向 KB 查询并续签,已存在对象按服务端状态处理。本地读取期间检测变化;遵守票据 method/headers/size,校验错误不继续 complete。检查点表示传输进度,不替代服务端的 SHA256 验证。代理模式使用外部 `/api/v1/external/documents/batch-upload`,显式传 `conflict_policy=reject`,按批同 request_id 和原内容恢复,任务 ID 保存在相同 manifest。后端保留旧 detect/force_upload 默认和行为;reject 在有候选冲突时返回 409,不自动切换版本。旧后端不支持 reject 时明确失败,不降级 detect。
51
+
52
+ ## 下载
53
+
54
+ 先通过 KB 获取原件票据。OSS URL 使用无身份 fetch;proxy URL 同服务验证后按票据要求调用。串流落盘,校验 size_bytes 和有效 sha256;通过排他创建/原子链接提交,失败删除本次临时文件,保留既有目标。错误、进度、结果不输出短期 URL。
55
+
56
+ ## OSS 来源导入及 Multipart 后续设计
57
+
58
+ `kb import oss` 后续接收 Grant+prefix/对象清单,由平台确认来源读取权、KB 确认目标写入权。保存 source Grant/bucket/key/versionId 或 ETag 与 hash,创建 KB 自有不可变副本,然后走既有解析任务。不能把任意 oss:// 直接注册到数据库,不能使用普通 OSS rm/move 改变 KB 已入库对象。当前同 Grant copy 实现不足以支持跨 Grant 导入;跨 Grant 服务端复制必须建立明确的双侧授权机制。
59
+
60
+ Multipart 后续由 KB 发起 uploadId,签发分片 URL,确认 parts/校验内容,持久保存会话并负责取消清理;在这套契约存在前不承诺字节级断点续传。
61
+
62
+ ## 分工与验收
63
+
64
+ 1. 契约 subagent:核对后端实际形状、导出可再生 OpenAPI 契约;不改业务后端。
65
+ 2. 客户端 subagent:REST client、配置、错误、类型、认证与协议测试。
66
+ 3. 传输 subagent:通用调度复用、签名 URL 文件传输、私有 manifest、上传/下载闭环与恢复测试。
67
+ 4. 主 agent:命令注册/发现、用户文档、集成与审查,解决接口差异。
68
+
69
+ 必须验证:路径前缀/参数映射;Bearer 与 Key 分离;鉴权失败不降级;签名 URL 不带身份/不重定向;内容及长度校验;批处理、已上传跳过、请求结果未知恢复、文件变化拒绝;代理回退;入库 partial/failed/timeout;JSON 脱敏;CLI help/catalog 可发现。使用受控 HTTP/文件夹夹具,执行类型检查、相关 OSS 回归、构建与打包检查;不以夹具结果声称远端真实联调。
70
+
71
+ ## 实施验收记录(2026-09-21)
72
+
73
+ | 要求 | 实现及验证证据 |
74
+ | --- | --- |
75
+ | REST 契约与命令 | `src/kb/client.ts`、`commands.ts`、`catalog.ts`;真实 CLI 子进程连接 loopback HTTP,所有请求按导出的 OpenAPI 校验;7 个命令叶子进入目录 |
76
+ | 配置和独立登录 | `config.ts`、`business-issuer.ts`;KB 请求与 `login --service kb` authority/audience/tokenFile 一致,支持根级参数置前的恢复指令 |
77
+ | 文件传输复用 | `oss/transfer-pool.ts` 同时用于原 OSS worker 与 KB;签名 PUT/GET 无 KB 身份头,拒绝重定向,默认 10 分钟传输超时 |
78
+ | 上传恢复 | `upload-state.ts`、`transfers.ts`;真实 PUT/GET、创建/complete 响应丢失、源变更、并发陈旧锁恢复、已提交会话不重复提交测试通过 |
79
+ | 代理模式一致性 | multipart 显式 reject;后端 Form 请求真实验证 409/UPLOAD_REQUIRES_REVIEW,不创建批次,保留旧 detect 自动版本行为 |
80
+ | 下载完整性 | 长度/有效 SHA256、临时文件清理、原子提交、不覆盖目标,以及代理下载凭据边界测试通过 |
81
+ | 等待与中断 | 原始 task_status;旧回执 processing counts 与混合取消兼容;多批共享等待时限;用户取消和网络结果未知区分 |
82
+ | 日志及产物 | JSON/错误详情脱敏,state_file 可用于恢复;文档、契约导出脚本和快照进入 package files |
83
+
84
+ 执行结果:
85
+
86
+ - `npm run test:kb`:41 passed,含 3 项契约/真实 CLI 集成测试。
87
+ - OSS worker/queue、business-issuer、login-recovery、KB login、CLI options 相关回归:44 passed(其中 KB login 与上一组有重叠,不累加成总数)。
88
+ - 后端 `test_kb_cli_contract.py`、`test_mcp_auto_integration.py`、`test_mcp_upload.py`:63 passed、1 skipped;跳过的是未配置专用 PostgreSQL 的真实数据库场景。
89
+ - 后端 `test_external_batch_upload.py`:22 passed,包含新增 reject 策略回归和实际 FastAPI multipart 请求。
90
+ - `export-kb-contract.py --check`:15 个操作匹配;引用闭包无缺失。
91
+ - `npm run root:typecheck`、`npm run build`、两仓库 `git diff --check` 通过。
92
+ - `npm run root:pack:check`:KB 模块、文档和契约打包清单断言通过;完整检查随后因既有 Skill 版本断言失败,`skills/numa-cli/numa-skill.json` 为 1.4.1,而脚本要求 1.4.0。该版本变化和对应其他 Skill 改动在本任务开始前已存在,本次未更改 Skill 内容或放宽其版本断言。发布前需协调解决。
93
+
94
+ 本次完成本地设计、代码和验证,未发布 npm、提交 Git、部署后端或执行真实环境联调。目标环境采用代理上传时须先部署支持 reject 的后端;真实 PostgreSQL/Keycloak/OSS 网关联调及整体包版本一致性属于发布前待办。OSS 来源导入和 Multipart 分片续传仍按上文后续设计实施。
@@ -0,0 +1,85 @@
1
+ # 知识库 CLI
2
+
3
+ `numa kb` 通过知识库 REST API 管理知识库、检索内容、传输原件和查询入库任务。OSS 传输与 KB 入库由同一命令衔接,文件上传完成后仍会调用 KB 完成接口,交由服务端解析、管理版本并建立索引。
4
+
5
+ ## 配置与登录
6
+
7
+ 平台 Profile 提供 `services.kb` 时,CLI 使用其服务地址、身份源与 audience。没有 Profile 时需显式配置 `NUMA_KB_BASE_URL`,其值是包含网关服务前缀的根地址,例如 `https://gateway.example.com/kb-management`,不追加 `/api/v1`。有 Profile 时不能用环境变量改到其他服务。
8
+
9
+ KB API Key 使用 `NUMA_KB_API_KEY_FILE` 指向已有的私有凭据文件,或使用 `NUMA_KB_API_KEY` 环境变量,二者不能同时配置。Key 应由知识库授权主体签发,范围覆盖目标知识库和所需操作。不要将 Key 写入命令参数或检查点。
10
+
11
+ 默认同时使用 Keycloak 和 API Key。独立 KB 服务使用对应的登录缓存:
12
+
13
+ ```bash
14
+ numa login --service kb --no-console --no-config
15
+ numa auth status --service kb --json
16
+ numa profile --service kb --json
17
+ ```
18
+
19
+ 机器身份沿用 Numa 的 `NUMA_KEYCLOAK_AUTH_MODE=client_credentials` 和专用 client 配置,无需浏览器登录。对只要求 KB API Key 的部署,可显式设置 `NUMA_KB_AUTH_MODE=api-key`。CLI 不会在认证失败时自动降级或更换身份。
20
+
21
+ 退出 KB 登录使用 `numa auth logout --service kb`。这不会删除其他服务的缓存。
22
+
23
+ ## 基本操作
24
+
25
+ ```bash
26
+ numa kb collections list --json
27
+ numa kb collections create --name "项目资料" --description "项目归档" --json
28
+ numa kb search "部署流程" --collection-id COLLECTION --top-k 5 --json
29
+ numa kb download DOCUMENT --output ./report.pdf --json
30
+ ```
31
+
32
+ 创建命令使用 KB 的任务知识库接口,默认私有。搜索保留正文、文档标识和来源信息,输出移除短期下载 URL;下载用文档 ID 单独请求授权。下载会校验服务端提供的大小与 SHA256,验证后保存,目标已存在时不会覆盖。
33
+
34
+ ## 文件和目录上传
35
+
36
+ ```bash
37
+ numa kb upload ./docs --collection-id COLLECTION \
38
+ --state-file ./kb-upload-state.json --batch-size 50 --concurrency 3 --wait --json
39
+ ```
40
+
41
+ PATH 可以是普通文件或目录;目录递归扫描并排序。目录结构不映射为知识库文件夹,两个不同子目录中的同名文件会被拒绝,避免静默冲突。符号链接、空文件、单文件超过 100 MiB 的输入会在上传前拒绝。解析是否支持某种格式由服务端结果说明。
42
+
43
+ 每批最多 50 个文件、总大小不超过 1 GiB;`--batch-size` 可以调低以适应目标环境的配置。`--concurrency` 为 1–10,默认 3。进度写入 stderr,stdout 为 JSON 结果,适合脚本重定向。
44
+
45
+ KB 启用 OSS 时,CLI 通过签名 PUT 直接传输原件,OSS 请求不附带 KB API Key 或 Keycloak Bearer。KB 返回 `transport=proxy` 时,CLI 使用现有 multipart 批量上传接口,不把文件转为 Base64。代理模式显式使用 `conflict_policy=reject`,与直传一样在遇到候选冲突时拒绝本批上传,保留错误详情;CLI 不自动选择覆盖或切换版本。该策略需要部署本次配套后端更新,旧后端不支持时明确失败,不降级到会自动处理版本的 `detect`。
46
+
47
+ 传输提交后,不带 `--wait` 的命令返回会话/任务信息;带 `--wait` 时等待解析终态,默认最长 600 秒,`--timeout <seconds>` 可调整。解析失败、部分失败、取消、需要解决冲突和等待超时会返回非零退出码及恢复信息。
48
+
49
+ ## 恢复与任务
50
+
51
+ ```bash
52
+ numa kb upload ./docs --collection-id COLLECTION \
53
+ --state-file ./kb-upload-state.json --resume --wait --json
54
+ numa kb tasks status TASK --wait --timeout 600 --json
55
+ numa kb tasks retry TASK --json
56
+ ```
57
+
58
+ 首次上传在传输前保存检查点;不指定 `--state-file` 时自动生成,并在回执中返回路径。检查点只保存目标绑定、文件摘要、固定请求 ID、会话和任务 ID,不保存 API Key、Token 或签名 URL。上传中断后保留该文件,使用相同文件源和凭据配置运行 `--resume`;本地文件变化或目标身份变化时拒绝恢复。
59
+
60
+ 恢复是文件级:向 KB 查询哪些对象已上传,跳过已上传项,只重新传输缺失项。单次 PUT 中断后从该文件起点重新开始。CLI 不会为已提交的会话重复创建解析任务。Ctrl-C 中止当前工作并保留恢复记录。
61
+
62
+ `ext_` 前缀的任务自动使用 external batch 接口,其他任务使用 archive 接口;需要时可以显式指定 `--kind batch` 或 `--kind archive`。读取任务本身成功时,普通 `tasks status` 返回当前状态;`--wait` 才以任务完成结果决定成功或失败。
63
+
64
+ ## 返回与退出码
65
+
66
+ 成功输出为 `{ "ok": true, "data": ... }`。错误包含稳定 `error.code`,如有恢复记录、任务或服务端冲突详情则放入 `error.details`。常用退出码:0 成功,1 操作失败或未完成,2 输入无效,3 需要认证,4 无权访问,130 用户中断。所有业务输出隐藏短期能力 URL 和认证字段。
67
+
68
+ ## 与 OSS 命令的关系
69
+
70
+ Numa OSS 模块和 KB 上传共用有界并发传输基础;KB 保留自己的入库会话与检查点。现有 OSS STS 分片队列的记录不能用于 KB 签名 PUT。首版没有 KB Multipart 分片续传,也没有 `kb import oss`;其双侧授权、复制与分片会话设计见 [详细设计](design/kb-cli.md)。
71
+
72
+ 已入库原件的删除、替换和版本切换必须通过 KB 业务接口,以维护文档、权限及索引一致性。
73
+
74
+ ## 开发与契约核验
75
+
76
+ `src/kb/openapi-contract.json` 是从 KB 的 FastAPI 路由离线导出的契约快照。它覆盖使用的请求路径、参数和 Schema;当前响应多为开放对象,客户端额外验证关键返回字段。
77
+
78
+ ```bash
79
+ /path/to/kb-management/.venv/bin/python scripts/export-kb-contract.py \
80
+ --kb-root /path/to/kb-management --check
81
+ npm run test:kb
82
+ npm run root:typecheck
83
+ ```
84
+
85
+ 去掉 `--check` 可在审查服务端变化后更新快照。导出不连接部署环境,不加载真实业务数据。受控本地 HTTP 测试覆盖协议和文件行为,不能代替目标环境的真实网关、Keycloak、API Key 和 OSS 联调。
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@numa-tech/numa",
3
- "version": "1.20.2",
3
+ "version": "1.20.4",
4
4
  "description": "Cross-platform CLI for the Numa internal developer platform with Keycloak authentication and MCP support.",
5
5
  "author": "numa-tech",
6
6
  "keywords": [
@@ -22,11 +22,16 @@
22
22
  "dist",
23
23
  "README.md",
24
24
  "docs/wecom-messaging.md",
25
+ "docs/agent-tasks.md",
25
26
  "docs/application-default-groups.md",
26
27
  "docs/module-maintenance-and-skills-updates.md",
27
28
  "examples",
28
29
  "skills",
29
- "THIRD_PARTY_NOTICES.md"
30
+ "THIRD_PARTY_NOTICES.md",
31
+ "docs/kb-access.md",
32
+ "docs/design/kb-cli.md",
33
+ "scripts/export-kb-contract.py",
34
+ "src/kb/openapi-contract.json"
30
35
  ],
31
36
  "scripts": {
32
37
  "clean": "node scripts/clean.mjs",
@@ -38,7 +43,7 @@
38
43
  "dev:oss-web": "tsx src/cli.ts oss web",
39
44
  "test:oss-web": "node --import tsx --test src/oss/web.test.ts src/oss/web-session.test.ts src/oss/web-transfers.test.ts src/oss/web-command.test.ts src/oss/local-picker.test.ts src/oss/transfer-paths.test.ts src/oss/transfer-worker.test.ts src/oss/transfer-queue.test.ts src/oss/web-page.test.ts src/oss/file-operations.test.ts",
40
45
  "test:oss-auth-code": "node scripts/test-oss-auth-code.mjs",
41
- "root:test": "node --import tsx --test --test-concurrency=4 src/mcp-gateway/profile.test.ts src/mcp-gateway/runtime.test.ts src/mcp-gateway/install.test.ts src/branding.test.ts src/app-config.test.ts src/platform-profile.test.ts src/identity.test.ts src/backend.test.ts src/oauth.test.ts src/web-console.test.ts src/cli-options.test.ts src/mail/client.test.ts src/wecom/client.test.ts src/wecom/commands.test.ts src/wecom/tools.test.ts src/ai-registry/client.test.ts src/ai-registry/bootstrap.test.ts src/ai-registry/install-defaults.test.ts src/ai-registry/workspace.test.ts src/ai-registry/namespace.test.ts src/ai-registry/sync.test.ts src/ai-registry/bridge.test.ts src/ai-registry/skill-installer.test.ts src/ai-registry/skill-uninstaller.test.ts src/ai-registry/skill-dependencies.test.ts src/ai-registry/updates.test.ts src/maintenance/scheduler.test.ts src/maintenance/codex.test.ts src/ai-registry/mcp-installer.test.ts src/media/client.test.ts src/media/commands.test.ts src/media/transfers.test.ts src/oss/client.test.ts src/oss/commands.test.ts src/oss/auth-code.test.ts src/oss/clipboard.test.ts src/oss/web.test.ts src/oss/web-session.test.ts src/oss/web-transfers.test.ts src/oss/web-command.test.ts src/oss/local-picker.test.ts src/oss/transfer-paths.test.ts src/oss/transfer-worker.test.ts src/oss/transfer-queue.test.ts src/oss/web-page.test.ts src/oss/file-operations.test.ts src/oss/bootstrap.test.ts src/oss/transfers.test.ts src/oss/search.test.ts src/oss/tools.test.ts src/mcp/install.test.ts src/mcp/catalog.test.ts src/interactive/wizard.test.ts src/pipeline/client.test.ts src/pipeline/commands.test.ts src/gitops/client.test.ts src/gitops/commands.test.ts src/repositories/client.test.ts src/repositories/commands.test.ts src/publications/scanner.test.ts src/publications/client.test.ts src/publications/commands.test.ts src/jenkins-jobs/client.test.ts src/jenkins-jobs/commands.test.ts src/clusters/client.test.ts src/clusters/commands.test.ts src/clusters/kubeconfig-discovery.test.ts src/teams/client.test.ts src/teams/commands.test.ts src/authorization/bootstrap-client.test.ts src/authorization/commands.test.ts src/authorization/access-request-client.test.ts src/authorization/access-request-commands.test.ts src/application-candidates/client.test.ts src/application-candidates/commands.test.ts src/application-onboarding/client.test.ts src/application-onboarding/commands.test.ts src/application-onboarding/tui.test.ts src/chatgpt-desktop.test.ts src/codex-integration.test.ts scripts/release-all.test.mjs src/authorization/enrollment.test.ts src/authorization/application-commands.test.ts src/authorization/application-client.test.ts src/authorization/application-schemas.test.ts src/authorization/enrollment-client.test.ts src/notion/client.test.ts src/notion/commands.test.ts src/notion/tools.test.ts src/notion/machine-cli.test.ts src/cloud/accounts.test.ts src/cloud/bootstrap.test.ts src/cloud/business-bootstrap.test.ts src/cloud/maintenance.test.ts src/cloud/wizard.test.ts src/cloud/oss-bootstrap.test.ts src/business-issuer.test.ts src/login-configuration.test.ts src/login-recovery.test.ts src/ai-registry/mcp-management.test.ts src/ai-registry/mcp-runtime.test.ts src/ai-registry/mcp-stdio.test.ts",
46
+ "root:test": "node --import tsx --test --test-concurrency=4 src/mcp-gateway/profile.test.ts src/mcp-gateway/runtime.test.ts src/mcp-gateway/install.test.ts src/branding.test.ts src/app-config.test.ts src/platform-profile.test.ts src/identity.test.ts src/backend.test.ts src/oauth.test.ts src/web-console.test.ts src/cli-options.test.ts src/mail/client.test.ts src/wecom/client.test.ts src/wecom/commands.test.ts src/wecom/tools.test.ts src/ai-registry/client.test.ts src/ai-registry/bootstrap.test.ts src/ai-registry/install-defaults.test.ts src/ai-registry/workspace.test.ts src/ai-registry/namespace.test.ts src/ai-registry/sync.test.ts src/ai-registry/bridge.test.ts src/ai-registry/skill-installer.test.ts src/ai-registry/skill-uninstaller.test.ts src/ai-registry/skill-dependencies.test.ts src/ai-registry/updates.test.ts src/maintenance/scheduler.test.ts src/maintenance/codex.test.ts src/ai-registry/mcp-installer.test.ts src/media/client.test.ts src/media/commands.test.ts src/media/transfers.test.ts src/oss/client.test.ts src/oss/commands.test.ts src/oss/auth-code.test.ts src/oss/clipboard.test.ts src/oss/web.test.ts src/oss/web-session.test.ts src/oss/web-transfers.test.ts src/oss/web-command.test.ts src/oss/local-picker.test.ts src/oss/transfer-paths.test.ts src/oss/transfer-worker.test.ts src/oss/transfer-queue.test.ts src/oss/web-page.test.ts src/oss/file-operations.test.ts src/oss/bootstrap.test.ts src/oss/transfers.test.ts src/oss/search.test.ts src/oss/tools.test.ts src/mcp/install.test.ts src/mcp/catalog.test.ts src/tasks/runtime.test.ts src/tasks/stdio.test.ts src/tasks/wecom-notify.test.ts src/tasks/oss-query.test.ts src/interactive/wizard.test.ts src/pipeline/client.test.ts src/pipeline/commands.test.ts src/gitops/client.test.ts src/gitops/commands.test.ts src/repositories/client.test.ts src/repositories/commands.test.ts src/publications/scanner.test.ts src/publications/client.test.ts src/publications/commands.test.ts src/jenkins-jobs/client.test.ts src/jenkins-jobs/commands.test.ts src/clusters/client.test.ts src/clusters/commands.test.ts src/clusters/kubeconfig-discovery.test.ts src/teams/client.test.ts src/teams/commands.test.ts src/authorization/bootstrap-client.test.ts src/authorization/commands.test.ts src/authorization/access-request-client.test.ts src/authorization/access-request-commands.test.ts src/application-candidates/client.test.ts src/application-candidates/commands.test.ts src/application-onboarding/client.test.ts src/application-onboarding/commands.test.ts src/application-onboarding/tui.test.ts src/chatgpt-desktop.test.ts src/codex-integration.test.ts scripts/release-all.test.mjs src/authorization/enrollment.test.ts src/authorization/application-commands.test.ts src/authorization/application-client.test.ts src/authorization/application-schemas.test.ts src/authorization/enrollment-client.test.ts src/notion/client.test.ts src/notion/commands.test.ts src/notion/tools.test.ts src/notion/machine-cli.test.ts src/cloud/accounts.test.ts src/cloud/bootstrap.test.ts src/cloud/business-bootstrap.test.ts src/cloud/maintenance.test.ts src/cloud/wizard.test.ts src/cloud/oss-bootstrap.test.ts src/business-issuer.test.ts src/login-configuration.test.ts src/login-recovery.test.ts src/ai-registry/mcp-management.test.ts src/ai-registry/mcp-runtime.test.ts src/ai-registry/mcp-stdio.test.ts src/kb/client.test.ts src/kb/config.test.ts src/kb/commands.test.ts src/kb/login.test.ts src/kb/transfers.test.ts src/kb/contract.test.ts",
42
47
  "pack:check": "turbo run pack:check root:pack:check",
43
48
  "root:pack:check": "node scripts/check-root-package.mjs",
44
49
  "boundaries": "node scripts/check-workspace-boundaries.mjs",
@@ -53,7 +58,8 @@
53
58
  "release": "node scripts/release-all.mjs",
54
59
  "release:resume": "node scripts/release-all.mjs --resume",
55
60
  "release:promote": "node scripts/release-all.mjs --promote",
56
- "prepack": "npm run root:build && npm run root:pack:check"
61
+ "prepack": "npm run root:build && npm run root:pack:check",
62
+ "test:kb": "node --import tsx --test --test-concurrency=4 src/kb/client.test.ts src/kb/config.test.ts src/kb/commands.test.ts src/kb/login.test.ts src/kb/transfers.test.ts src/kb/contract.test.ts"
57
63
  },
58
64
  "engines": {
59
65
  "node": ">=20.12.0"
@@ -61,8 +67,8 @@
61
67
  "dependencies": {
62
68
  "@clack/prompts": "^1.7.0",
63
69
  "@modelcontextprotocol/sdk": "^1.29.0",
64
- "@numa-tech/cli-auth": "1.20.2",
65
- "@tokensrc/codex": "1.20.2",
70
+ "@numa-tech/cli-auth": "1.20.4",
71
+ "@tokensrc/codex": "1.20.4",
66
72
  "ali-oss": "^6.23.0",
67
73
  "commander": "^14.0.3",
68
74
  "ink": "^6.8.0",
@@ -0,0 +1,158 @@
1
+ #!/usr/bin/env python3
2
+ """Export the Numa KB REST input contract without starting the KB application.
3
+
4
+ Run with the KB checkout's Python environment (no new dependencies):
5
+ /path/to/kb-management/.venv/bin/python scripts/export-kb-contract.py \
6
+ --kb-root /path/to/kb-management
7
+ Append --check to fail on contract drift without changing the snapshot.
8
+
9
+ The exporter imports only REST routers, never main or its lifespan.
10
+ It blocks network access and excludes credentials, deployment configuration,
11
+ servers, runtime responses, and machine-specific source paths from the snapshot.
12
+ Response schemas currently remain free-form because that is the server contract;
13
+ the client must validate the response fields it consumes separately.
14
+ """
15
+
16
+ from __future__ import annotations
17
+
18
+ import argparse
19
+ import json
20
+ import os
21
+ from pathlib import Path
22
+ import sys
23
+
24
+
25
+ AGENT_OPERATIONS = {
26
+ ("get", "/collections"): "list_knowledge_bases",
27
+ ("post", "/task-collections"): "create_task_knowledge_base",
28
+ ("post", "/search"): "search_knowledge_base",
29
+ ("post", "/upload-sessions"): "begin_direct_upload",
30
+ ("get", "/upload-sessions/{session_id}"): "get_direct_upload",
31
+ ("post", "/upload-sessions/{session_id}/complete"): "complete_direct_upload",
32
+ ("delete", "/upload-sessions/{session_id}"): "cancel_direct_upload",
33
+ ("get", "/documents/{document_id}/download-ticket"): "get_document_download",
34
+ ("post", "/archives"): "start_async_archive",
35
+ ("get", "/archives/{archive_id}"): "get_archive_status",
36
+ ("post", "/archives/{archive_id}/retry"): "retry_archive",
37
+ }
38
+ PREFIX = "/api/v1/external/agent"
39
+ OPERATIONS = {(method, PREFIX + suffix): name
40
+ for (method, suffix), name in AGENT_OPERATIONS.items()} | {
41
+ ("post", "/api/v1/external/documents/batch-upload"):
42
+ "upload_external_documents_api_v1_external_documents_batch_upload_post",
43
+ ("get", "/api/v1/external/batches/{batch_id}"):
44
+ "get_external_batch_api_v1_external_batches__batch_id__get",
45
+ ("post", "/api/v1/external/batches/{batch_id}/retry"):
46
+ "retry_external_batch_api_v1_external_batches__batch_id__retry_post",
47
+ ("post", "/api/v1/external/batches/{batch_id}/resolve"):
48
+ "resolve_external_batch_conflicts_api_v1_external_batches__batch_id__resolve_post",
49
+ }
50
+
51
+
52
+ def forbid_network(event: str, _args: tuple) -> None:
53
+ if event in {"socket.connect", "socket.connect_ex", "socket.getaddrinfo", "socket.bind"}:
54
+ raise RuntimeError("KB contract export must remain offline")
55
+
56
+
57
+ def schema_references(value):
58
+ if isinstance(value, dict):
59
+ reference = value.get("$ref")
60
+ if isinstance(reference, str) and reference.startswith("#/components/schemas/"):
61
+ yield reference.removeprefix("#/components/schemas/")
62
+ for child in value.values():
63
+ yield from schema_references(child)
64
+ elif isinstance(value, list):
65
+ for child in value:
66
+ yield from schema_references(child)
67
+
68
+
69
+ def export_contract(kb_root: Path) -> dict:
70
+ if not (kb_root / "backend/external/agent_routes.py").is_file():
71
+ raise ValueError("--kb-root must point to a KB checkout containing the Agent router")
72
+ # Canonical defaults: never freeze a developer's environment-specific limits.
73
+ os.environ["KB_MAX_FILES_PER_BATCH"] = "50"
74
+ os.environ["ALIYUN_OSS_ENABLED"] = "false"
75
+ os.environ.pop("KB_APPLICATION_AUTHORIZATION_CONFIG", None)
76
+ sys.addaudithook(forbid_network)
77
+ sys.path.insert(0, str(kb_root))
78
+ from fastapi import FastAPI
79
+ from backend.external.agent_routes import router
80
+ from backend.external.routes import router as external_router
81
+ from backend.knowledge_base.constants import (
82
+ MAX_EXTERNAL_FILES_PER_BATCH,
83
+ MAX_FILES_PER_BATCH,
84
+ MAX_SINGLE_FILE_SIZE_BYTES,
85
+ MAX_TOTAL_BATCH_SIZE_BYTES,
86
+ SUPPORTED_EXTS,
87
+ )
88
+
89
+ app = FastAPI(title="Knowledge Base Agent API")
90
+ app.include_router(router, prefix="/api/v1")
91
+ app.include_router(external_router, prefix="/api/v1")
92
+ original = app.openapi()
93
+ paths = {}
94
+ for (method, path), operation_id in OPERATIONS.items():
95
+ operation = original.get("paths", {}).get(path, {}).get(method)
96
+ if operation is None or operation.get("operationId") != operation_id:
97
+ raise ValueError(f"KB route contract changed: {method.upper()} {path}")
98
+ paths.setdefault(path, {})[method] = operation
99
+
100
+ schemas = original.get("components", {}).get("schemas", {})
101
+ selected = {}
102
+ pending = set(schema_references(paths))
103
+ while pending:
104
+ name = pending.pop()
105
+ if name in selected:
106
+ continue
107
+ selected[name] = schemas[name]
108
+ pending.update(schema_references(selected[name]))
109
+ return {
110
+ "openapi": original["openapi"],
111
+ "info": original["info"],
112
+ "paths": paths,
113
+ "components": {"schemas": selected},
114
+ "x-numa-contract": {
115
+ "sources": ["backend/external/agent_routes.py", "backend/external/routes.py"],
116
+ "generator": "scripts/export-kb-contract.py",
117
+ "response_validation": "Responses are free-form upstream; validate consumed fields at runtime.",
118
+ "authentication": "X-Api-Key; deployments may additionally require Bearer application authorization.",
119
+ "parse_supported_extensions": sorted(SUPPORTED_EXTS),
120
+ "default_upload_limits": {
121
+ "max_files_per_batch": MAX_FILES_PER_BATCH,
122
+ "max_external_files_per_batch": MAX_EXTERNAL_FILES_PER_BATCH,
123
+ "max_files_per_batch_environment_override": "KB_MAX_FILES_PER_BATCH",
124
+ "max_single_file_size_bytes": MAX_SINGLE_FILE_SIZE_BYTES,
125
+ "max_total_batch_size_bytes": MAX_TOTAL_BATCH_SIZE_BYTES,
126
+ },
127
+ },
128
+ }
129
+
130
+
131
+ def main() -> int:
132
+ parser = argparse.ArgumentParser(description=__doc__)
133
+ parser.add_argument("--kb-root", type=Path, required=True)
134
+ parser.add_argument("--output", type=Path,
135
+ default=Path(__file__).resolve().parents[1] / "src/kb/openapi-contract.json")
136
+ parser.add_argument("--check", action="store_true")
137
+ args = parser.parse_args()
138
+ try:
139
+ contract = export_contract(args.kb_root.resolve())
140
+ except (ImportError, ValueError, RuntimeError, KeyError) as exc:
141
+ print(f"KB contract export failed ({type(exc).__name__}). "
142
+ "Use the KB checkout's Python environment and inspect its Agent route declarations.", file=sys.stderr)
143
+ return 2
144
+ rendered = json.dumps(contract, ensure_ascii=False, indent=2, sort_keys=True) + "\n"
145
+ if args.check:
146
+ if not args.output.is_file() or args.output.read_text(encoding="utf-8") != rendered:
147
+ print("KB OpenAPI contract drift detected; regenerate and review the snapshot.", file=sys.stderr)
148
+ return 1
149
+ print(f"KB OpenAPI contract matches ({len(OPERATIONS)} operations).")
150
+ return 0
151
+ args.output.parent.mkdir(parents=True, exist_ok=True)
152
+ args.output.write_text(rendered, encoding="utf-8")
153
+ print(f"Exported KB OpenAPI contract ({len(OPERATIONS)} operations).")
154
+ return 0
155
+
156
+
157
+ if __name__ == "__main__":
158
+ raise SystemExit(main())
@@ -12,8 +12,8 @@ description: 旧 Numa AI Registry Skill 的兼容指引。仅在用户明确使
12
12
 
13
13
  按实际任务加载对应 Skill;混合任务按这两个边界分别处理。不要根据请求中出现 Nacos 一词,就把 Skill 安装归入基础设施管理。
14
14
 
15
- 若新 Skill 尚未安装,先 `numa auth status --json`、`numa config --show --json` 和 `numa registry capabilities --json`,核对身份、环境与可用 namespace,再用 `numa skills list/inspect/install --help` 检查命令。读取并安装授权库中的新 Skill 后继续;新 Skill 尚未发布时明确报告,不声称已具备它,也不照搬旧发布权限。
15
+ 若新 Skill 尚未安装,先 `npx -y @numa-tech/numa@latest auth status --json`、`npx -y @numa-tech/numa@latest config --show --json` 和 `npx -y @numa-tech/numa@latest registry capabilities --json`,核对身份、环境与可用 namespace,再用 `npx -y @numa-tech/numa@latest skills list/inspect/install --help` 检查命令。读取并安装授权库中的新 Skill 后继续;新 Skill 尚未发布时明确报告,不声称已具备它,也不照搬旧发布权限。
16
16
 
17
- 历史 `numa registry` 命令、`.numa/registry.json`、`registry.lock.json`、profile 的 `aiRegistry` 字段以及 `/api/v1/ai-registry/**` 保留兼容。不得为改名迁移配置、删除旧远端资源、修改路由或扩大授权。
17
+ 历史 `npx -y @numa-tech/numa@latest registry` 命令、`.numa/registry.json`、`registry.lock.json`、profile 的 `aiRegistry` 字段以及 `/api/v1/ai-registry/**` 保留兼容。不得为改名迁移配置、删除旧远端资源、修改路由或扩大授权。
18
18
 
19
19
  权限依据服务端当前 capabilities、功能 view/edit 和 namespace 业务授权,不再按历史普通用户角色名判定。不要读取 token 缓存、索取 Nacos 凭据、绕过 403;两人制发布禁止提交者自审自发。