@numa-tech/numa 1.14.52 → 1.16.0
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/README.md +161 -24
- package/dist/ai-registry/commands.d.ts +8 -1
- package/dist/ai-registry/commands.js +70 -15
- package/dist/ai-registry/commands.js.map +1 -1
- package/dist/ai-registry/mcp-installer.d.ts +1 -1
- package/dist/ai-registry/mcp-installer.js +26 -0
- package/dist/ai-registry/mcp-installer.js.map +1 -1
- package/dist/ai-registry/sync.js +3 -2
- package/dist/ai-registry/sync.js.map +1 -1
- package/dist/cli.js +42 -14
- package/dist/cli.js.map +1 -1
- package/dist/command-catalog.js +28 -3
- package/dist/command-catalog.js.map +1 -1
- package/dist/interactive/entry.d.ts +2 -0
- package/dist/interactive/entry.js +91 -0
- package/dist/interactive/entry.js.map +1 -0
- package/dist/interactive/mcp.d.ts +4 -0
- package/dist/interactive/mcp.js +88 -0
- package/dist/interactive/mcp.js.map +1 -0
- package/dist/interactive/prompts.d.ts +26 -0
- package/dist/interactive/prompts.js +79 -0
- package/dist/interactive/prompts.js.map +1 -0
- package/dist/interactive/registry.d.ts +28 -0
- package/dist/interactive/registry.js +147 -0
- package/dist/interactive/registry.js.map +1 -0
- package/dist/maintenance/modules.js +1 -1
- package/dist/maintenance/modules.js.map +1 -1
- package/dist/mcp/catalog.d.ts +35 -0
- package/dist/mcp/catalog.js +203 -0
- package/dist/mcp/catalog.js.map +1 -0
- package/dist/mcp/commands.d.ts +10 -0
- package/dist/mcp/commands.js +96 -0
- package/dist/mcp/commands.js.map +1 -0
- package/dist/mcp/environment.d.ts +5 -0
- package/dist/mcp/environment.js +35 -0
- package/dist/mcp/environment.js.map +1 -0
- package/dist/mcp/inspect.d.ts +35 -0
- package/dist/mcp/inspect.js +17 -0
- package/dist/mcp/inspect.js.map +1 -0
- package/dist/mcp/install.d.ts +55 -0
- package/dist/mcp/install.js +151 -0
- package/dist/mcp/install.js.map +1 -0
- package/dist/mcp/modules.d.ts +4 -0
- package/dist/mcp/modules.js +11 -0
- package/dist/mcp/modules.js.map +1 -0
- package/dist/oss/commands.d.ts +3 -0
- package/dist/oss/commands.js +74 -7
- package/dist/oss/commands.js.map +1 -1
- package/dist/oss/search.d.ts +70 -0
- package/dist/oss/search.js +172 -0
- package/dist/oss/search.js.map +1 -0
- package/dist/oss/tools.d.ts +8 -0
- package/dist/oss/tools.js +69 -0
- package/dist/oss/tools.js.map +1 -0
- package/dist/oss/transfers.d.ts +17 -2
- package/dist/oss/transfers.js +29 -7
- package/dist/oss/transfers.js.map +1 -1
- package/dist/server.d.ts +4 -1
- package/dist/server.js +192 -169
- package/dist/server.js.map +1 -1
- package/dist/wecom/tools.js +15 -4
- package/dist/wecom/tools.js.map +1 -1
- package/package.json +4 -4
- package/skills/numa-cli/SKILL.md +4 -2
- package/skills/numa-cli/agents/openai.yaml +1 -1
- package/skills/numa-cli/evals/evals.json +31 -1
- package/skills/numa-cli/numa-skill.json +1 -1
- package/skills/numa-cli/references/cli-mcp.md +121 -0
- package/skills/numa-oss-access/SKILL.md +7 -3
- package/skills/numa-oss-access/agents/openai.yaml +1 -1
- package/skills/numa-oss-access/evals/evals.json +18 -0
- package/skills/numa-oss-access/numa-skill.json +2 -2
- package/skills/numa-oss-access/references/cli-bootstrap.md +2 -2
- package/skills/numa-oss-access/references/search-and-mcp.md +63 -0
- package/skills/numa-wecom/SKILL.md +7 -3
- package/skills/numa-wecom/numa-skill.json +2 -2
- package/skills/numa-wecom/references/cli-bootstrap.md +2 -2
- package/skills/numa-wecom/references/mcp.md +12 -0
package/README.md
CHANGED
|
@@ -253,7 +253,7 @@ numa mail archive search kenneth.davis@mail.microconnect.com \
|
|
|
253
253
|
| `numa media asset upload` | 需登录 | 流式 multipart 上传资产 |
|
|
254
254
|
| `numa media library search/inspect/pull/publish` | 需登录 | 查询、锁定、下载和发布素材 |
|
|
255
255
|
| `numa oss grants/session` | 需登录 | 查看当前身份的 OSS Policy 授权并申请短期 STS(不输出密钥) |
|
|
256
|
-
| `numa oss ls/stat/get/put/rm` | 需登录 | 使用内存中的 STS 通过 Aliyun OSS SDK 读写受限 Bucket/Prefix |
|
|
256
|
+
| `numa oss ls/search/stat/get/put/rm` | 需登录 | 使用内存中的 STS 通过 Aliyun OSS SDK 读写受限 Bucket/Prefix |
|
|
257
257
|
| `numa codex list` | 需登录 | 列出服务端账号元数据,不下载 refresh token |
|
|
258
258
|
| `numa codex login --assign-to <username>` | 需角色 | 通过一次隔离的官方 Codex 登录,为指定用户补录或替换失效凭据 |
|
|
259
259
|
| `numa codex admin accounts renew <account> --expires-at <ISO-8601>` | 需角色 | 续订已过期账号;新到期时间必须晚于当前时间和原到期时间 |
|
|
@@ -505,11 +505,11 @@ capability 发布且健康的企业 Router,不安装 Nacos 中的下游 endpoi
|
|
|
505
505
|
### 客户端安装适配
|
|
506
506
|
|
|
507
507
|
`numa-cli` 是基础运行与 Skill 发现入口;业务 Skills 通过 `numa-skill.json` 声明同来源、
|
|
508
|
-
精确版本依赖。`skills install --plan`
|
|
509
|
-
|
|
508
|
+
精确版本依赖。`skills install --plan` 只读展示完整安装计划;安装向导询问是否立即安装,
|
|
509
|
+
完整参数调用必须在用户同意后提供 `--with-dependencies`。依赖冲突/本地修改不自动覆盖,
|
|
510
510
|
自动更新不会静默增加依赖。此元数据由 Numa CLI 解析,不代表所有 Agent 原生支持依赖加载。
|
|
511
511
|
|
|
512
|
-
用户级 Skills 现在支持自愿开启 `latest`
|
|
512
|
+
用户级 Skills 现在支持自愿开启 `latest` 追踪。首次符合条件且未固定版本的交互安装会询问:仅当前
|
|
513
513
|
Skill、全部受管用户级 Skills、或不自动升级且不再询问。脚本可明确指定
|
|
514
514
|
`--updates current|all|never`;JSON/非交互调用不会询问,也不会默认同意。
|
|
515
515
|
|
|
@@ -1065,6 +1065,66 @@ numa oss rm 123 projects/old.png --yes --json
|
|
|
1065
1065
|
`oss grants` 文本输出显示 Grant ID、Policy、Bucket/Prefix、主体与配置状态;未就绪的
|
|
1066
1066
|
Grant 不计入就绪数。JSON 字段保持兼容,`oss session` 增加安全的 Actions / Effect 元数据。
|
|
1067
1067
|
|
|
1068
|
+
### OSS 目录与组合检索
|
|
1069
|
+
|
|
1070
|
+
`oss ls` 列出一页对象;`--delimiter /` 将子目录汇总到 `directories`,不递归展开。
|
|
1071
|
+
使用返回的 `nextMarker` 配合 `--marker` 续页,单页最多 1000 项(对象与目录合计)。
|
|
1072
|
+
不传 `--fields` 时,`ls --json` 保留原 `listing.objects` 结构,增加 `directories` 与对象存储类型。
|
|
1073
|
+
|
|
1074
|
+
```bash
|
|
1075
|
+
# Grant ID 和路径按 oss grants 的实际授权选择
|
|
1076
|
+
numa oss ls 123 projects/ --delimiter / --fields key,kind,sizeBytes --format table
|
|
1077
|
+
numa oss ls 123 projects/ --marker projects/last-key --json
|
|
1078
|
+
numa oss search 123 projects/ --directories-only --contains finance
|
|
1079
|
+
numa oss search 123 projects/ --contains report --suffix .pdf --ignore-case \
|
|
1080
|
+
--modified-after 2026-09-01 --modified-before 2026-10-01 \
|
|
1081
|
+
--min-size 1024 --max-size 10485760 --sort modified --order desc \
|
|
1082
|
+
--scan-limit 10000 --limit 50 --fields key,sizeBytes,lastModified --json
|
|
1083
|
+
numa oss search 123 projects/ --meta team=finance --meta status=approved \
|
|
1084
|
+
--content-type application/pdf --fields key,contentType,metadata --format jsonl
|
|
1085
|
+
numa oss search 123 projects/ --suffix .pdf --format keys
|
|
1086
|
+
```
|
|
1087
|
+
|
|
1088
|
+
`search` 默认递归扫描指定前缀;`--delimiter /` 限制到当前层级,
|
|
1089
|
+
`--directories-only` 只返回这一层的子目录,支持名称筛选与按 key 排序。
|
|
1090
|
+
`--contains`/`--suffix` 匹配完整对象 key,`--ignore-case` 仅影响这两项。
|
|
1091
|
+
`--meta KEY=VALUE` 匹配自定义 `x-oss-meta-*`,可重复,所有条件必须同时满足。
|
|
1092
|
+
元数据键不区分大小写,值和 Content-Type 精确匹配;仅指定值不是全文或模糊查询。
|
|
1093
|
+
大小按字节、上下界均包含;日期下界包含、上界不包含。日期格式 `YYYY-MM-DD`
|
|
1094
|
+
表示 UTC 零点;时间戳需显式提供 `Z` 或时区偏移。
|
|
1095
|
+
|
|
1096
|
+
**创建时间与最后修改时间不同。** OSS 对象列表返回 `lastModified`,不提供可保留的
|
|
1097
|
+
原始创建时间。只有业务已在自定义元数据中记录创建时间时,才能指定字段检索:
|
|
1098
|
+
|
|
1099
|
+
```bash
|
|
1100
|
+
numa oss search 123 projects/ --created-time-key created-at \
|
|
1101
|
+
--created-after 2026-01-01 --created-before 2027-01-01 \
|
|
1102
|
+
--sort created --fields key,createdAt --json
|
|
1103
|
+
```
|
|
1104
|
+
|
|
1105
|
+
该字段必须符合上述日期格式。缺失或格式无效的创建时间不会通过创建日期筛选,
|
|
1106
|
+
排序时缺失值放末尾。CLI 不会用 `lastModified` 替代,也不会回填或修改对象元数据。
|
|
1107
|
+
|
|
1108
|
+
`--sort` 支持 `key`、`size`、`modified`、`created`,`--order` 为 `asc`/`desc`。
|
|
1109
|
+
`--fields` 可选 `key,kind,sizeBytes,lastModified,createdAt,contentType,etag,type,storageClass,metadata`。
|
|
1110
|
+
`--format` 支持表格 `table`、带执行摘要的 `json`、逐行对象 `jsonl`、仅 key 的 `keys`。
|
|
1111
|
+
后两者将扫描摘要写到 stderr,方便 stdout 管道处理。`--json` 不能与非 JSON 的
|
|
1112
|
+
`--format` 同时使用。`ls` 不读取对象头;需要 `metadata`/`contentType` 时使用 `search` 或 `stat --json`。
|
|
1113
|
+
`stat` 从 SDK 的响应头读取标准属性,同时返回自定义 `metadata`;敏感字段继续脱敏。
|
|
1114
|
+
|
|
1115
|
+
**检索是有界扫描,不是服务端全文索引。** `--scan-limit` 默认 1000、最高 100000,
|
|
1116
|
+
`--limit` 默认输出 100、最高 1000。排序只覆盖本次扫描集合;达到扫描上限时返回
|
|
1117
|
+
`complete:false`、`truncated:true` 和 `nextMarker`。`resultsLimited:true` 表示匹配数
|
|
1118
|
+
超过输出上限;`nextMarker` 继续扫描尚未扫描的范围,不会补回已经扫描但因输出上限省略的匹配项。
|
|
1119
|
+
不同扫描批次分别排序,不能拼接成全量全局排序结果。需要精确全量排序时,先确认
|
|
1120
|
+
扫描完成且未截断;更大规模需要另外建设索引。扫描期间对象可能变化,不是时间点快照。
|
|
1121
|
+
|
|
1122
|
+
普通筛选使用 ListObjects 返回的属性;自定义元数据、Content-Type、创建时间以及
|
|
1123
|
+
输出 `metadata`/`contentType` 会对候选对象额外执行 HEAD,需要 Grant 的 `oss:GetObject`
|
|
1124
|
+
权限,并产生额外请求。`headRequests` 显示请求数量。遇到鉴权失败、对象消失、会话过期
|
|
1125
|
+
或上游错误时整次命令失败,不会把跳过的对象伪装成完整结果。未指定元数据相关选项
|
|
1126
|
+
时不会逐个 HEAD,更不会下载文件内容。
|
|
1127
|
+
|
|
1068
1128
|
### 管理员初始化默认 OSS Role
|
|
1069
1129
|
|
|
1070
1130
|
需要新版 DevOps 后端(V171)及账号已完成系统和共享业务身份初始化。执行前用
|
|
@@ -1470,36 +1530,113 @@ numa --json request /api/projects
|
|
|
1470
1530
|
numa --json request /api/admin --require-role platform-admin
|
|
1471
1531
|
```
|
|
1472
1532
|
|
|
1473
|
-
## MCP
|
|
1533
|
+
## MCP 服务与 Agent 安装
|
|
1474
1534
|
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1535
|
+
### 模块检索与交互安装(下一版本)
|
|
1536
|
+
|
|
1537
|
+
在交互终端运行 `numa` 会进入安装向导;`numa mcp`、`numa skills` 分别进入 MCP 与 Skills 入口。
|
|
1538
|
+
`numa mcp install-cli`、`numa mcp install`、`numa skills install` 缺少目标时也会引导选择。
|
|
1539
|
+
可输入业务关键词选择模块,查看工具和相关 Skills,选择 Agent、范围与身份,核对完整计划后安装。
|
|
1540
|
+
企业资源向导只展示当前身份可安装的来源;资源搜索匹配当前页,支持翻页与输入精确名称。
|
|
1541
|
+
Skills 只选择已发布版本,计划包含依赖、来源和安装路径。
|
|
1542
|
+
|
|
1543
|
+
```sh
|
|
1544
|
+
numa
|
|
1545
|
+
numa modules --search 文件 --json
|
|
1546
|
+
numa mcp modules 通知
|
|
1547
|
+
numa mcp install-cli --interactive
|
|
1548
|
+
numa skills install --interactive
|
|
1484
1549
|
```
|
|
1485
1550
|
|
|
1486
|
-
|
|
1551
|
+
参数齐全时使用默认值并直接执行;显式 `--interactive` 可补选其他选项,已给定参数会保留。
|
|
1552
|
+
`--no-interactive` 禁止提示。JSON 或非 TTY 不进入向导,缺少必要参数会报错;Agent/CI 应先用
|
|
1553
|
+
`--plan --json` 核对,执行时给齐参数,Skills 依赖通过 `--with-dependencies` 明确安装。
|
|
1554
|
+
在有默认 Agent 的 profile 下,非交互 Skills 安装仍可沿用该默认配置。
|
|
1555
|
+
|
|
1556
|
+
`--modules` 决定业务模块;`--tools` 和 `--exclude-tools` 进一步控制实际暴露的工具。
|
|
1557
|
+
模块检索不启用任何工具。`--server-name` 和 `--server-description` 设置 MCP 自描述,CLI 自动附加
|
|
1558
|
+
实际模块和工具范围;命令目录也随筛选收窄。向导支持按工具标注选择只读集合。
|
|
1487
1559
|
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
-
|
|
1491
|
-
-
|
|
1492
|
-
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1560
|
+
```sh
|
|
1561
|
+
numa mcp tools --modules oss --tools numa_oss_search,numa_oss_stat --json
|
|
1562
|
+
numa mcp install-cli --agent workbuddy --modules oss \
|
|
1563
|
+
--tools numa_oss_search,numa_oss_stat --server-name numa-files \
|
|
1564
|
+
--server-description '查询授权 OSS 文件' --plan --json
|
|
1565
|
+
```
|
|
1566
|
+
|
|
1567
|
+
MCP 安装支持重复的 `--env NAME=VALUE`,合并到本地服务的启动环境;值可包含 `=` 或为空。
|
|
1568
|
+
重复变量名会报错。预览隐藏敏感字段,实际值只写入所选客户端配置(Tunnel 写入私有 launcher)。
|
|
1569
|
+
向导中的环境变量值采用隐藏输入。用户自定义变量不写进全局 shell 配置。
|
|
1570
|
+
|
|
1571
|
+
```sh
|
|
1572
|
+
numa mcp install-cli --agent workbuddy --modules auth,oss \
|
|
1573
|
+
--env LOG_LEVEL=debug --env 'EXAMPLE_URL=https://example.com?a=b' --plan --json
|
|
1574
|
+
# 确认计划后去掉 --plan 执行;Registry 本地桥接也支持
|
|
1575
|
+
numa mcp install internal-docs --namespace TEAM --agent workbuddy \
|
|
1576
|
+
--transport bridge --env LOG_LEVEL=debug --plan --json
|
|
1577
|
+
```
|
|
1578
|
+
|
|
1579
|
+
远程 HTTP endpoint 没有本地进程环境,不接受 `--env`。CLI 服务的身份必须通过 `--auth-mode`、
|
|
1580
|
+
`--client-id`、`--client-secret-file` 设置,不能用自定义环境变量覆盖这四个身份字段。
|
|
1581
|
+
Registry 的自定义环境保存在目标客户端,不进入可共享的 registry manifest/lock;在新机器重建配置时应重新提供。
|
|
1582
|
+
|
|
1583
|
+
|
|
1584
|
+
CLI 1.15.0 支持将 OSS 与企微能力安装为 Agent 的 stdio MCP 服务:
|
|
1585
|
+
|
|
1586
|
+
```sh
|
|
1587
|
+
numa login
|
|
1588
|
+
numa mcp tools --modules auth,oss,wecom --json
|
|
1589
|
+
numa mcp install-cli --agent workbuddy --scope user --plan --json
|
|
1590
|
+
numa mcp install-cli --agent workbuddy --scope user --json
|
|
1591
|
+
# 其他目标按需选择
|
|
1592
|
+
numa mcp install-cli --agent claude --scope user --plan --json
|
|
1593
|
+
numa mcp install-cli --agent claude-desktop --scope user --plan --json
|
|
1594
|
+
numa mcp install-cli --agent codex --scope user --plan --json
|
|
1595
|
+
```
|
|
1596
|
+
|
|
1597
|
+
`install-cli` 管理 CLI 自身的工具;现有 `mcp install NAME` / `bridge` 管理企业 Registry 资源。
|
|
1598
|
+
支持 WorkBuddy、Claude Code、Claude Desktop(macOS/Windows)、Codex、Cursor、OpenClaw、custom 和 ChatGPT。
|
|
1599
|
+
默认 user scope;Claude/WorkBuddy/Cursor 可显式选择 project。JSON 安装会合并配置、保留备份并拒绝默认覆盖同名服务。
|
|
1600
|
+
配置固定运行当前 CLI 的精确 npm 版本,升级 CLI 后需要重新更新 Agent 配置才能使用新版 MCP。
|
|
1601
|
+
|
|
1602
|
+
默认安装 `auth,oss,wecom` 模块,可用 `--modules` 调整;`notion` 与 `platform` 按需加入。
|
|
1603
|
+
`numa serve` 是 stdio 入口,不传模块时保留全部工具以兼容现有配置;stdout 仅用于 MCP 协议。
|
|
1604
|
+
`numa mcp tools` 通过真实 tools/list 列举 schema,不需要调用业务 API。
|
|
1605
|
+
|
|
1606
|
+
| 模块 | 工具 |
|
|
1607
|
+
| --- | --- |
|
|
1608
|
+
| 默认公共工具(也可筛选) | `numa_command_catalog`、`numa_module_catalog` |
|
|
1609
|
+
| auth | `numa_auth_status/start_login/complete_login/whoami/logout` |
|
|
1610
|
+
| oss | `numa_oss_grants/list/search/stat/download/upload/delete` |
|
|
1611
|
+
| wecom | `numa_wecom_capabilities/recipients/recipient_by_email/send/status`、4 个 mappings 工具 |
|
|
1612
|
+
| notion | 现有 Notion Grant、检索及内容工具 |
|
|
1613
|
+
| platform | `numa_backend_request`,服务端执行最终授权 |
|
|
1614
|
+
|
|
1615
|
+
默认 `--auth-mode browser` 复用同一系统用户的登录缓存,明确清除 MCP 进程继承的机器身份设置。
|
|
1616
|
+
机器模式指定 `--auth-mode client_credentials --client-id ID --client-secret-file /ABS/SECRET_FILE`,只存文件路径,
|
|
1617
|
+
不读取或复制 Secret。Issuer/API 继续使用 platform profile 与既有配置优先级,安装器不会写入本地开发地址。
|
|
1618
|
+
发送消息、上传、下载和删除工具必须基于用户明确授权传 `confirmed:true`;下载/上传的路径属于 MCP 服务机器。
|
|
1619
|
+
|
|
1620
|
+
ChatGPT 可以通过 [官方 Secure MCP Tunnel](https://developers.openai.com/api/docs/guides/secure-mcp-tunnels)
|
|
1621
|
+
连接本地 stdio。先安装官方 `tunnel-client` 并在 OpenAI Platform 取得当前工作区可用的 Tunnel ID:
|
|
1622
|
+
|
|
1623
|
+
```sh
|
|
1624
|
+
numa mcp install-cli --agent chatgpt --tunnel-id CONFIRMED_TUNNEL_ID --plan --json
|
|
1625
|
+
numa mcp install-cli --agent chatgpt --tunnel-id CONFIRMED_TUNNEL_ID --json
|
|
1626
|
+
tunnel-client doctor --profile numa-cli --explain
|
|
1627
|
+
tunnel-client run --profile numa-cli
|
|
1628
|
+
```
|
|
1495
1629
|
|
|
1496
|
-
|
|
1630
|
+
自动初始化支持 macOS/Linux,不创建 API key,不自动运行后台服务。保持 Tunnel 运行后,在 ChatGPT Plugins
|
|
1631
|
+
创建开发者应用,选择对应 Tunnel 并完成授权与工具审核。`--endpoint` 可为已有 HTTPS MCP 生成连接指引;
|
|
1632
|
+
不提供 ID 或 endpoint 时返回 `manual_required`,不能视为已安装。完整路径、恢复流程与身份说明见
|
|
1633
|
+
[CLI MCP Skill 指引](skills/numa-cli/references/cli-mcp.md)。
|
|
1497
1634
|
|
|
1498
1635
|
### 企业微信消息与用户映射
|
|
1499
1636
|
|
|
1500
1637
|
`numa wecom` 通过 DevOps 平台向企业成员发送 text/markdown 消息,CLI 和 MCP 均不接触企微应用密钥。支持明确企微 userid,或通过 Keycloak subject 的邮箱自动/人工映射选择收件人。`numa wecom mappings sync` 默认预览,`--apply --yes` 才应用唯一邮箱映射;冲突保留审核结果,人工映射不会被自动覆盖。
|
|
1501
1638
|
|
|
1502
|
-
发送使用 `--text-file`(`-` 表示标准输入)、稳定 `--request-id` 和 `--yes`。结果未知时用 `numa wecom status --request-id ...` 查询,不能换幂等键盲目重发。`numa serve` 提供
|
|
1639
|
+
发送使用 `--text-file`(`-` 表示标准输入)、稳定 `--request-id` 和 `--yes`。结果未知时用 `numa wecom status --request-id ...` 查询,不能换幂等键盲目重发。`numa serve` 提供 9 个相应 MCP 工具(包括精确邮箱查询和发送),发送工具要求用户明确授权及 `confirmed:true`。完整命令、API 契约与恢复说明见 [企业微信消息指南](docs/wecom-messaging.md)。
|
|
1503
1640
|
|
|
1504
1641
|
## 云账号与 OSS 初始化
|
|
1505
1642
|
|
|
@@ -1,2 +1,9 @@
|
|
|
1
|
+
import { type InstallUi } from "../interactive/prompts.js";
|
|
1
2
|
import type { Command } from "commander";
|
|
2
|
-
|
|
3
|
+
import { AiRegistryClient } from "./client.js";
|
|
4
|
+
export declare function registerAiRegistryCommands(program: Command, dependencies?: {
|
|
5
|
+
ui?: InstallUi;
|
|
6
|
+
tty?: boolean;
|
|
7
|
+
clientFactory?: () => AiRegistryClient;
|
|
8
|
+
initializeProfile?: () => Promise<void>;
|
|
9
|
+
}): void;
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import { appendEnvironment, collectEnvironment, parseEnvironment, safeMcpOutput } from "../mcp/environment.js";
|
|
2
|
+
import { completeRegistryInstall } from "../interactive/registry.js";
|
|
3
|
+
import { terminalUi, useInteractive, requireInput, confirmInstall, chooseEnvironment } from "../interactive/prompts.js";
|
|
1
4
|
import { lstat, readFile } from "node:fs/promises";
|
|
2
5
|
import { resolve } from "node:path";
|
|
3
6
|
import process from "node:process";
|
|
@@ -81,7 +84,7 @@ function itemName(item, fallback) {
|
|
|
81
84
|
const value = item.name ?? item.skillName ?? item.mcpName;
|
|
82
85
|
return typeof value === "string" ? value : fallback;
|
|
83
86
|
}
|
|
84
|
-
export function registerAiRegistryCommands(program) {
|
|
87
|
+
export function registerAiRegistryCommands(program, dependencies = {}) {
|
|
85
88
|
const registry = program
|
|
86
89
|
.command("registry")
|
|
87
90
|
.alias("ai-registry")
|
|
@@ -265,7 +268,7 @@ export function registerAiRegistryCommands(program) {
|
|
|
265
268
|
const result = await client.getSkill(targetNamespace.id, name);
|
|
266
269
|
await output(command, { ok: true, namespace: targetNamespace, skill: result }, JSON.stringify(sanitizeRegistryOutput(result), null, 2));
|
|
267
270
|
}), ["numa skills inspect release-helper --namespace team-ai --json"]);
|
|
268
|
-
addExamples(skills.command("install
|
|
271
|
+
addExamples(skills.command("install [name]")
|
|
269
272
|
.option("-n, --namespace <key>", "namespaceKey")
|
|
270
273
|
.option("-i, --instance <id-or-code>", "同名 namespace 的实例消歧")
|
|
271
274
|
.option("-a, --agent <agent>", "覆盖 profile 默认客户端:chatgpt|codex|claude|cursor|workbuddy|openclaw|agents|custom")
|
|
@@ -278,18 +281,31 @@ export function registerAiRegistryCommands(program) {
|
|
|
278
281
|
.option("--updates <choice>", "current 仅当前 Skill 自动升级;all 全部受管用户级 Skills;never 不自动升级且不再询问")
|
|
279
282
|
.option("--plan", "仅解析并显示安装及依赖计划,不修改本地安装")
|
|
280
283
|
.option("--with-dependencies", "明确同意安装计划中的依赖;不覆盖已有冲突依赖")
|
|
284
|
+
.option("--interactive", "交互选择来源、Skill、版本、Agent 和安装范围")
|
|
285
|
+
.option("--no-interactive", "不询问;依赖安装须显式 --with-dependencies")
|
|
281
286
|
.action(async (name, options, command) => {
|
|
287
|
+
const json = jsonEnabled(command, (await loadAppConfig()).output), ui = dependencies.ui ?? terminalUi;
|
|
288
|
+
const interactive = useInteractive(options, !name || !options.agent || options.agent === "custom" && !options.customRoot, json, dependencies.tty);
|
|
289
|
+
if (!interactive)
|
|
290
|
+
requireInput(name, "缺少 Skill 名称");
|
|
291
|
+
await dependencies.initializeProfile?.();
|
|
292
|
+
const client = dependencies.clientFactory?.() ?? new AiRegistryClient(loadConfig("aiRegistry"));
|
|
293
|
+
if (interactive) {
|
|
294
|
+
const selected = await completeRegistryInstall("skill", name, { ...options, scope: command.getOptionValueSource("scope") === "default" ? undefined : options.scope }, client, ui);
|
|
295
|
+
name = selected.name;
|
|
296
|
+
options = { ...options, ...selected.options, scope: selected.options.scope ?? options.scope };
|
|
297
|
+
}
|
|
298
|
+
requireInput(name, "缺少 Skill 名称");
|
|
282
299
|
const targets = await resolveSkillInstallTargets(options);
|
|
283
300
|
if (targets.scope === "project" && options.updates === "current")
|
|
284
301
|
throw new Error("Project Skills remain locked; use registry upgrade.");
|
|
285
|
-
const client = new AiRegistryClient(loadConfig("aiRegistry"));
|
|
286
302
|
const context = await resolveContext(client, options, "install");
|
|
287
303
|
const detail = await client.getSkill(context.namespace.id, name);
|
|
288
304
|
const trackedLabel = options.version ? undefined : options.label ?? "latest";
|
|
289
305
|
const version = resolveVersion(detail, options.version, trackedLabel, name);
|
|
290
306
|
const download = await client.downloadSkill(context.namespace.id, name, { version });
|
|
291
307
|
const graph = await resolveSkillDependencies(client, context.namespace.id, { name, version, download, dependencies: [] });
|
|
292
|
-
const
|
|
308
|
+
const dependencyNodes = graph.filter(node => node.name !== name);
|
|
293
309
|
const source = { namespace: context.namespace.namespaceKey, instance: context.namespace.instanceCode,
|
|
294
310
|
namespaceId: context.namespace.id, instanceId: context.namespace.instanceId };
|
|
295
311
|
const plan = { source, scope: targets.scope, agents: targets.agents, packages: graph.map(node => ({
|
|
@@ -304,22 +320,26 @@ export function registerAiRegistryCommands(program) {
|
|
|
304
320
|
const target = { agent, scope: targets.scope, workspace: options.workspace, customRoot: options.customRoot, skillName: name };
|
|
305
321
|
const rootPath = resolve(skillTargetRoot(target), name);
|
|
306
322
|
await assertConsumersCompatible(name, version, rootPath);
|
|
307
|
-
if (
|
|
323
|
+
if (dependencyNodes.length) {
|
|
308
324
|
const existing = await lstat(rootPath).catch((e) => { if (e.code === "ENOENT")
|
|
309
325
|
return undefined; throw e; });
|
|
310
326
|
if (existing && !options.force)
|
|
311
327
|
throw new AiRegistryError("Root Skill already exists; inspect local changes and explicitly approve --force before installing dependencies.", "ai_registry_install_exists");
|
|
312
328
|
}
|
|
313
|
-
for (const dep of
|
|
329
|
+
for (const dep of dependencyNodes)
|
|
314
330
|
await reusableDependency(dep, target, source);
|
|
315
331
|
}
|
|
316
|
-
if (
|
|
317
|
-
await
|
|
332
|
+
if (interactive) {
|
|
333
|
+
await confirmInstall(ui, { ...plan, overwrite: Boolean(options.force) });
|
|
334
|
+
options.withDependencies = true;
|
|
335
|
+
}
|
|
336
|
+
if (dependencyNodes.length)
|
|
337
|
+
await confirmSkillDependencies(plan, { confirmed: options.withDependencies, json: true });
|
|
318
338
|
const choice = await chooseSkillUpdates({ choice: options.updates,
|
|
319
339
|
eligible: targets.scope === "user" && !options.version && (!options.label || options.label === "latest"),
|
|
320
|
-
json:
|
|
340
|
+
json: !interactive || json });
|
|
321
341
|
const identity = await maintenanceIdentity(loadConfig("aiRegistry"));
|
|
322
|
-
if (
|
|
342
|
+
if (dependencyNodes.length && !identity)
|
|
323
343
|
throw new AiRegistryError("Dependency provenance needs a verified current identity; login and retry.", "skills_dependency_identity_required");
|
|
324
344
|
if (choice && !identity)
|
|
325
345
|
throw new Error("Cannot associate update settings with the current identity; run numa login and retry.");
|
|
@@ -328,7 +348,7 @@ export function registerAiRegistryCommands(program) {
|
|
|
328
348
|
const dependencyInstallations = [];
|
|
329
349
|
try {
|
|
330
350
|
const target = { agent, scope: targets.scope, workspace: options.workspace, customRoot: options.customRoot, skillName: name };
|
|
331
|
-
for (const dep of
|
|
351
|
+
for (const dep of dependencyNodes) {
|
|
332
352
|
const depPath = resolve(skillTargetRoot(target), dep.name);
|
|
333
353
|
if (await reusableDependency(dep, target, source)) {
|
|
334
354
|
dependencyInstallations.push({ name: dep.name, version: dep.version, status: "REUSED", path: depPath });
|
|
@@ -507,19 +527,44 @@ export function registerAiRegistryCommands(program) {
|
|
|
507
527
|
const result = await client.getMcpServer(targetNamespace.id, name, options.version);
|
|
508
528
|
await output(command, { ok: true, namespace: targetNamespace, mcpServer: result }, JSON.stringify(sanitizeRegistryOutput(result), null, 2));
|
|
509
529
|
}), ["numa mcp inspect internal-docs --namespace team-ai --json"]);
|
|
510
|
-
addExamples(mcp.command("install
|
|
530
|
+
addExamples(mcp.command("install [name]")
|
|
511
531
|
.option("-n, --namespace <key>", "namespaceKey")
|
|
512
532
|
.option("-i, --instance <id-or-code>", "同名 namespace 的实例消歧")
|
|
513
|
-
.
|
|
533
|
+
.option("-a, --agent <agent>", "chatgpt|codex|claude|cursor|workbuddy|openclaw|custom")
|
|
514
534
|
.option("--scope <scope>", "project|user", "project")
|
|
515
535
|
.option("--version <semver>", "精确版本")
|
|
516
536
|
.option("--label <label>", "首次安装时解析的标签", "latest")
|
|
517
537
|
.option("--transport <transport>", "auto|remote|bridge", "auto")
|
|
518
538
|
.option("--workspace <path>", "项目根目录")
|
|
519
539
|
.option("--custom-config <path>", "自定义 mcp.json 路径")
|
|
540
|
+
.option("--env <NAME=VALUE>", "追加本地 bridge 的启动环境变量,可重复;远程 HTTP 不支持", collectEnvironment)
|
|
520
541
|
.option("--force", "备份并替换 JSON 配置中的同名项")
|
|
542
|
+
.option("--plan", "只核对 Router 和目标,输出计划,不写配置")
|
|
543
|
+
.option("--interactive", "交互选择企业 MCP、来源、Agent 和范围")
|
|
544
|
+
.option("--no-interactive", "禁止交互,缺少必要参数时报错")
|
|
521
545
|
.action(async (name, options, command) => {
|
|
522
|
-
|
|
546
|
+
parseEnvironment(options.env);
|
|
547
|
+
const json = jsonEnabled(command, (await loadAppConfig()).output), ui = dependencies.ui ?? terminalUi;
|
|
548
|
+
const interactive = useInteractive(options, !name || !options.agent || options.agent === "custom" && !options.customConfig, json, dependencies.tty);
|
|
549
|
+
if (!interactive) {
|
|
550
|
+
requireInput(name, "缺少 MCP 名称");
|
|
551
|
+
requireInput(options.agent, "缺少 --agent");
|
|
552
|
+
}
|
|
553
|
+
await dependencies.initializeProfile?.();
|
|
554
|
+
const client = dependencies.clientFactory?.() ?? new AiRegistryClient(loadConfig("aiRegistry"));
|
|
555
|
+
if (interactive) {
|
|
556
|
+
const selected = await completeRegistryInstall("mcp", name, { ...options, scope: command.getOptionValueSource("scope") === "default" ? undefined : options.scope }, client, ui);
|
|
557
|
+
name = selected.name;
|
|
558
|
+
options = { ...options, ...selected.options, scope: selected.options.scope ?? options.scope };
|
|
559
|
+
if (command.getOptionValueSource("transport") === "default") {
|
|
560
|
+
options.transport = options.agent === "chatgpt" ? "remote" : await ui.choose("MCP 连接方式", [
|
|
561
|
+
{ value: "remote", label: "远程 HTTP", hint: "由 Agent 连接企业 Router" },
|
|
562
|
+
{ value: "bridge", label: "本地 stdio 桥接", hint: "复用 Numa 登录,可追加环境变量" }
|
|
563
|
+
], options.env?.length ? "bridge" : "remote");
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
requireInput(name, "缺少 MCP 名称");
|
|
567
|
+
requireInput(options.agent, "缺少 --agent");
|
|
523
568
|
const context = await resolveContext(client, options);
|
|
524
569
|
const trackedLabel = options.version ? undefined : options.label ?? "latest";
|
|
525
570
|
const detail = await client.getMcpServer(context.namespace.id, name, options.version ? { version: options.version } : { label: trackedLabel });
|
|
@@ -533,7 +578,17 @@ export function registerAiRegistryCommands(program) {
|
|
|
533
578
|
throw new AiRegistryError(`Namespace ${context.namespace.namespaceKey} has no ready enterprise Router.`, "ai_registry_router_unavailable");
|
|
534
579
|
}
|
|
535
580
|
assertRouterHealth(await client.checkRouter(context.namespace.id));
|
|
536
|
-
|
|
581
|
+
if (interactive && entry.transport === "bridge")
|
|
582
|
+
options.env = await chooseEnvironment(ui, options.env);
|
|
583
|
+
const definition = appendEnvironment(routerMcpDefinition(context, entry), options.env);
|
|
584
|
+
const plan = { name, version, namespace: context.namespace.namespaceKey, instance: context.namespace.instanceCode, agent: entry.agent, scope: entry.scope, workspace: options.workspace, customConfig: options.customConfig, overwrite: Boolean(options.force), definition };
|
|
585
|
+
if (options.plan) {
|
|
586
|
+
await output(command, safeMcpOutput({ ok: true, plan }), JSON.stringify(safeMcpOutput(plan), null, 2));
|
|
587
|
+
return;
|
|
588
|
+
}
|
|
589
|
+
if (interactive)
|
|
590
|
+
await confirmInstall(ui, plan);
|
|
591
|
+
const result = await installMcpClientDefinition(name, definition, {
|
|
537
592
|
agent: entry.agent, scope: entry.scope, workspace: options.workspace,
|
|
538
593
|
customConfig: options.customConfig, force: options.force
|
|
539
594
|
});
|