@luckypiggy/zhihu-ai-cli 0.2.1-test.4 → 0.2.1-test.5
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 +14 -7
- package/assets/codex-skills/zhihu-aiops/SKILL.md +44 -22
- package/assets/codex-skills/zhihu-aiops/references/commands.md +30 -18
- package/assets/codex-skills/zhihu-aiops/references/errors.md +2 -1
- package/assets/codex-skills/zhihu-aiops/references/safety.md +7 -2
- package/dist/cli.js +70 -5
- package/dist/codex-skill.js +50 -8
- package/dist/credentials.js +30 -10
- package/dist/install.js +11 -4
- package/dist/local-runner.js +1717 -0
- package/dist/profile-lock.js +7 -0
- package/dist/release.js +7 -4
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -27,19 +27,24 @@
|
|
|
27
27
|
|
|
28
28
|
当前客户端只访问当前 Profile 的固定 `/api/zhihu/integrations/v1` 路径,拒绝任意 HTTP 重定向和非回环明文 HTTP;Profile 的 CA bundle 用于严格 TLS 校验。macOS 使用 `/usr/bin/security`,Linux 使用固定路径 `/usr/bin/secret-tool`,不提供明文文件 fallback。Refresh rotation 通过 profile 锁和进程内 single-flight 合并,并在 Keychain 写入失败时尝试撤销新会话和清理本机旧值。
|
|
29
29
|
|
|
30
|
-
本切片提供数字员工、资产、指标、告警、指定告警诊断、巡检和 A2A 四类只读委派的 `list`/`get`/`query`/`chat`/`diagnose`/`run` 能力,并在包内实现无密钥的外部 `zhihu-aiops` Codex Skill 及其受管安装/卸载,以及公网测试版 `zhihu install` 安装向导。自然语言任务默认通过 `zhihu a2a delegate` 进入 A2A;底层命令仍保留为显式结构化查询和受控诊断入口。CLI/Skill 不提交或猜测 target、source、root、turn、tool、user、tenant 或员工映射。canonical 与离线包的 Skill 使用当前构建提供的本地 `zhihu` runner,不读取本机认证材料,不提供写操作或 MCP
|
|
30
|
+
本切片提供数字员工、资产、指标、告警、指定告警诊断、巡检和 A2A 四类只读委派的 `list`/`get`/`query`/`chat`/`diagnose`/`run` 能力,并在包内实现无密钥的外部 `zhihu-aiops` Codex Skill 及其受管安装/卸载,以及公网测试版 `zhihu install` 安装向导。自然语言任务默认通过 `zhihu a2a delegate` 进入 A2A;底层命令仍保留为显式结构化查询和受控诊断入口。CLI/Skill 不提交或猜测 target、source、root、turn、tool、user、tenant 或员工映射。canonical 与离线包的 Skill 使用当前构建提供的本地 `zhihu` runner,不读取本机认证材料,不提供写操作或 MCP。公网测试包在 staging 中仍把命令写成精确版本 npx;安装时会把该命令改写为本机持久化 runner,后续调用不再访问 npm registry。测试版支持客户 URL 和离线交付,正式 GA 与真实客户联调遵循各自发布门禁。安装向导严格验证 `/info` 的服务、协议、版本、能力顺序和终端登录契约,拒绝非回环明文 HTTP、重定向、userinfo、query/fragment、符号链接 CA;Profile 与 root current_profile 采用快照恢复,Skill 安装遵守 foreign/modified/force 语义。指定告警诊断只允许 `cpu`/`memory`/`load`/`io`/`disk`/`online`/`unknown` 确定性分类,巡检只允许固定模型/窗口、四个 Scope、资产/告警各 100 条和五类 VM IP 标签,证据不足时保守返回;两条链路均不会接受客户端 URL、PromQL、任意标签或写操作,不调用 LLM,均为 30 秒总超时且 401 最多刷新一次。CLI 只解析签名 JWT 的非敏感展示字段,不以本地解析结果作为授权决策。Chat conversation 映射当前是服务端单 worker 的有界 TTL 内存状态,同进程并发有锁保护;多 worker/多副本部署需要共享状态存储。
|
|
31
31
|
|
|
32
32
|
## 公网测试版安装
|
|
33
33
|
|
|
34
|
-
|
|
34
|
+
本包身份是 `@luckypiggy/zhihu-ai-cli@0.2.1-test.5`,dist-tag 为 `test`。这是智护 Pilot 的公网测试包,不是 latest,也不是正式 GA。首次入口使用精确版本 npx;安装成功后使用本机持久化 runner。
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
当前候选的第一次安装命令为:
|
|
35
38
|
|
|
36
39
|
```bash
|
|
37
|
-
npx --yes @luckypiggy/zhihu-ai-cli@0.2.1-test.
|
|
40
|
+
npx --yes @luckypiggy/zhihu-ai-cli@0.2.1-test.5 install
|
|
38
41
|
```
|
|
39
42
|
|
|
40
|
-
这是智护 Pilot
|
|
43
|
+
这是智护 Pilot 的公网测试包(不是 latest,也不是正式 GA)。上面的精确版本 npx 只负责第一次进入安装向导。安装成功后,Skill 只使用本机持久化 runner。之后即使 registry 不可达,只读调用仍通过这个本地 runner 工作。已经安装的 `0.2.1-test.4` 仍是 npx runner,要等包含本地 runner 的后续安装后才会切换。服务地址、权限和凭据仍由用户在安装向导中配置。客户 URL 和离线 `.tgz` 适用于受控网络或隔离环境。
|
|
41
44
|
|
|
42
|
-
|
|
45
|
+
安装完成后,Codex 通过这个本地 Runner 工作。macOS 沙箱第一次执行某条精确只读命令时,可能显示一次权限确认。这次确认只允许同一条白名单命令按原样重试一次,不表示 CLI 读取或上传 Keychain、密码、Token 或验证码;这些材料仍不得进入 Agent 对话。用户拒绝或宿主不支持该确认时,Agent 应停止并说明当前沙箱无法确认登录状态。
|
|
46
|
+
|
|
47
|
+
一次 `npx ... install` 不会把 `zhihu` 命令永久写入 PATH。若用户要手动直接输入 `zhihu`,必须先全局安装同一精确版本:`npm install --global --ignore-scripts @luckypiggy/zhihu-ai-cli@0.2.1-test.5`。离线包则应使用其自身的 `zhihu` 安装方式,不要为了绕过离线约束改用公网 runner。
|
|
43
48
|
|
|
44
49
|
`install` 的固定顺序是:
|
|
45
50
|
|
|
@@ -61,9 +66,11 @@ AI/Codex Skill 调用必须继续使用上面的精确 npx runner;一次 `npx
|
|
|
61
66
|
| `ZHIHU_CA_BUNDLE` | 客户 CA bundle 绝对路径 |
|
|
62
67
|
| `ZHIHU_TIMEOUT` | 请求超时秒数(1~300) |
|
|
63
68
|
|
|
64
|
-
配置优先级固定为“CLI 参数 > 非空 env > 交互/default”。不支持也不建议 `ZHIHU_ACCESS_TOKEN`、`ZHIHU_USER` 或 `ZHIHU_PASSWORD`;密码和可选验证码只能通过隐藏/终端提示本次传入,令牌只由终端登录接口获得并由系统凭据库保存 Refresh Credential。安装失败或服务尚未配置时可再次运行相同的 `npx --yes @luckypiggy/zhihu-ai-cli@0.2.1-test.
|
|
69
|
+
配置优先级固定为“CLI 参数 > 非空 env > 交互/default”。不支持也不建议 `ZHIHU_ACCESS_TOKEN`、`ZHIHU_USER` 或 `ZHIHU_PASSWORD`;密码和可选验证码只能通过隐藏/终端提示本次传入,令牌只由终端登录接口获得并由系统凭据库保存 Refresh Credential。安装失败或服务尚未配置时可再次运行相同的 `npx --yes @luckypiggy/zhihu-ai-cli@0.2.1-test.5 install` 命令,向导会保留/恢复一致的本地 Profile。npm `install` 没有交互钩子,不会在依赖安装阶段启动向导、读取密码或打开浏览器。
|
|
70
|
+
|
|
71
|
+
安装完成后请新开一个 Codex 会话,让它重新发现 Skill;可以直接用自然语言尝试“列出当前活动告警”“查看资产 123 最近一小时 CPU 趋势”或“分析告警 456 的根因”。需要排查安装时运行本地持久化 runner 的 `doctor`,该命令只输出脱敏检查结果,不会访问 npm registry。尚未完成安装时,仍使用上面的精确版本 npx 进入安装向导。
|
|
65
72
|
|
|
66
|
-
|
|
73
|
+
如果第一次安装在本地 runner 生成前失败,错误提示里的恢复命令就是当前这条精确版本命令,不要改成尚未生成的裸 zhihu,也不要改成浮动 tag。先查看状态:`npx --yes @luckypiggy/zhihu-ai-cli@0.2.1-test.5 runner lock status --json`。状态为 `held` 时表示可能仍有安装在进行,等待结束后重试。状态为 `stale` 时,使用返回的 fingerprint 显式恢复:`npx --yes @luckypiggy/zhihu-ai-cli@0.2.1-test.5 runner lock recover --confirm recover-stale-runner-lock --fingerprint <fingerprint> --json`,然后再运行原来的安装命令。不要手工删除锁文件。本地 runner 已经安装成功后,诊断和 `doctor` 才改用安装结果里的本地绝对命令,不再指向 npm 上的版本。符号链接、损坏锁、年轻锁和错误 fingerprint 都会拒绝恢复。`doctor` 的 `local_runner_lock` 给出同样的下一步。
|
|
67
74
|
|
|
68
75
|
开发和发布需要 Node.js 22 或更新版本:
|
|
69
76
|
|
|
@@ -5,13 +5,13 @@ description: 通过智护 Pilot CLI 将自然语言运维问题路由到智护 A
|
|
|
5
5
|
|
|
6
6
|
# 智护 AI 运维
|
|
7
7
|
|
|
8
|
-
本 Skill
|
|
8
|
+
本 Skill 只把明确的自然语言问题交给安装时写入的本机持久化 runner,并依据单一 JSON 结果给出分析。默认路径先调用一次 `npx --yes @luckypiggy/zhihu-ai-cli@0.2.1-test.5 a2a capabilities --json`,再调用一次 `npx --yes @luckypiggy/zhihu-ai-cli@0.2.1-test.5 a2a delegate`,由 Pilot 服务端的 A2A 注册中心选择专业数字员工;Skill 只提交 capability_id 和用户原始问题,不提交或猜测 employee_id、target、root、turn、tool、user 或 tenant。安装完成后只执行下面列出的本地命令,不再通过 npx 或 npm registry 重新解析 CLI。先确认用户要看的租户、资产、告警或时间范围,再选择一个最小查询;多个查询之间保留用户给出的精确 ID。
|
|
9
9
|
|
|
10
10
|
## 工作流程
|
|
11
11
|
|
|
12
12
|
1. 确认目标和范围。缺少资产 ID、告警 ID、模型或时间窗时提问;不要用名称猜测唯一对象。只有用户明确指定可信员工 ID 并要求直接对话时,才使用 `employees chat`。
|
|
13
|
-
2. 只调用 `npx --yes @luckypiggy/zhihu-ai-cli@0.2.1-test.
|
|
14
|
-
3.
|
|
13
|
+
2. 只调用 `npx --yes @luckypiggy/zhihu-ai-cli@0.2.1-test.5 ... --json`,每次调用都等待完整 JSON envelope。不要把诊断参数改写成 URL、查询表达式或任意参数。
|
|
14
|
+
3. 默认 A2A 路径先调用 `npx --yes @luckypiggy/zhihu-ai-cli@0.2.1-test.5 a2a capabilities --json`,成功后再调用一次对应的 `npx --yes @luckypiggy/zhihu-ai-cli@0.2.1-test.5 a2a delegate`。`npx --yes @luckypiggy/zhihu-ai-cli@0.2.1-test.5 auth status --json` 和 `npx --yes @luckypiggy/zhihu-ai-cli@0.2.1-test.5 capabilities list --json` 只在用户明确查询登录或能力、或排查错误时使用,不作为每次委派的前置步骤。认证失败时按下面的沙箱权限重试处理,不要要求用户粘贴任何凭据。
|
|
15
15
|
4. 只依据 `ok`、稳定错误码和 `data` 字段回答。字段缺失、对象不唯一或结果过期时明确说明并停止推断。
|
|
16
16
|
5. 对告警或巡检结论引用返回的证据、范围和时间窗;建议动作必须保持在只读排查范围内。
|
|
17
17
|
6. 回复语言跟随当前用户语言;命令和稳定错误码保持原样,解释、确认和结论使用用户选择的中文或英文。
|
|
@@ -22,34 +22,56 @@ description: 通过智护 Pilot CLI 将自然语言运维问题路由到智护 A
|
|
|
22
22
|
|
|
23
23
|
| 用户意图 | capability_id | 默认命令 |
|
|
24
24
|
| --- | --- | --- |
|
|
25
|
-
| 资产、指标、采集状态 | `resource_query` | `npx --yes @luckypiggy/zhihu-ai-cli@0.2.1-test.
|
|
26
|
-
| 告警、故障、根因和恢复验证建议 | `incident_analysis` | `npx --yes @luckypiggy/zhihu-ai-cli@0.2.1-test.
|
|
27
|
-
| 巡检、健康检查和状态摘要 | `inspection_summary` | `npx --yes @luckypiggy/zhihu-ai-cli@0.2.1-test.
|
|
28
|
-
| 知识、案例、手册和 SOP | `knowledge_qa` | `npx --yes @luckypiggy/zhihu-ai-cli@0.2.1-test.
|
|
25
|
+
| 资产、指标、采集状态 | `resource_query` | `npx --yes @luckypiggy/zhihu-ai-cli@0.2.1-test.5 a2a delegate --capability resource_query --message <原始问题> --json` |
|
|
26
|
+
| 告警、故障、根因和恢复验证建议 | `incident_analysis` | `npx --yes @luckypiggy/zhihu-ai-cli@0.2.1-test.5 a2a delegate --capability incident_analysis --message <原始问题> --json` |
|
|
27
|
+
| 巡检、健康检查和状态摘要 | `inspection_summary` | `npx --yes @luckypiggy/zhihu-ai-cli@0.2.1-test.5 a2a delegate --capability inspection_summary --message <原始问题> --json` |
|
|
28
|
+
| 知识、案例、手册和 SOP | `knowledge_qa` | `npx --yes @luckypiggy/zhihu-ai-cli@0.2.1-test.5 a2a delegate --capability knowledge_qa --message <原始问题> --json` |
|
|
29
29
|
|
|
30
|
-
先调用 `npx --yes @luckypiggy/zhihu-ai-cli@0.2.1-test.
|
|
30
|
+
先调用 `npx --yes @luckypiggy/zhihu-ai-cli@0.2.1-test.5 a2a capabilities --json` 检查 Pilot 是否声明 A2A;A2A 关闭、能力不可用、歧义或下游失败时报告稳定错误并停止,不静默绕过治理。`asset_onboarding`、`asset_discovery` 等写能力始终禁用。
|
|
31
|
+
|
|
32
|
+
## 沙箱权限重试
|
|
33
|
+
|
|
34
|
+
`AUTH_RELOGIN_REQUIRED` 第一次出现时,不要直接要求重新登录。只有下面六项同时成立,才请求宿主对这一条命令授权,并原样重试一次,且只重试一次:
|
|
35
|
+
|
|
36
|
+
1. 失败命令是安装后写入的绝对本地 Runner。
|
|
37
|
+
2. 命令属于本 Skill 明确列出的只读命令。
|
|
38
|
+
3. 返回的稳定错误码是 `AUTH_RELOGIN_REQUIRED`。
|
|
39
|
+
4. 宿主提供明确的权限申请机制。
|
|
40
|
+
5. 用户尚未拒绝。
|
|
41
|
+
6. 这条完全相同的命令在本次任务中还没有做过权限重试。
|
|
42
|
+
|
|
43
|
+
原样重试必须保持可执行文件、参数、消息、标识、环境、工作目录和顺序不变,不循环。不得读取或展示 Keychain、Secret Service、Profile、Token、密码或配置。不得把范围扩大到其他命令、任意 shell、直接 API、网络探测或文件读取。宿主已经批准过完全相同的范围时可以复用,不得自行扩大范围。
|
|
44
|
+
|
|
45
|
+
即使返回认证错误,也不得对下列命令使用权限重试:`npx --yes @luckypiggy/zhihu-ai-cli@0.2.1-test.5 auth login`、`npx --yes @luckypiggy/zhihu-ai-cli@0.2.1-test.5 auth logout`、`npx --yes @luckypiggy/zhihu-ai-cli@0.2.1-test.5 install`、`npx --yes @luckypiggy/zhihu-ai-cli@0.2.1-test.5 uninstall`、`npx --yes @luckypiggy/zhihu-ai-cli@0.2.1-test.5 runner lock recover`、任何未列出的命令、`asset_onboarding`、`asset_discovery`、任何写能力、任意 shell、脚本、直接服务调用、任意 URL,以及 Agent 改过参数的命令。`npx --yes @luckypiggy/zhihu-ai-cli@0.2.1-test.5 runner lock status --json` 只属于既有显式锁恢复流程,不属于业务 A2A 权限重试。
|
|
46
|
+
|
|
47
|
+
用户拒绝、宿主不支持明确权限机制,或命令不在白名单时,停止并说明 Agent 无法在当前沙箱确认登录状态。不得声称凭据已失效。没有权限机制时,可以建议用户在自己的终端核对登录状态,但不要在对话中索取密码、Token 或验证码。
|
|
48
|
+
|
|
49
|
+
同一条命令第二次仍返回 `AUTH_RELOGIN_REQUIRED` 时,才提示用户在自己的终端重新登录,并使用安装结果给出的登录命令。其他错误码按各自规则处理,不得二次提权,也不得改走直接 API。权限申请失败、取消或超时不得编造查询结果。
|
|
50
|
+
|
|
51
|
+
macOS 上的 Codex 沙箱使用这一次性重试。第一条命令已经成功时,不做权限流程。Linux Secret Service、非沙箱终端或其他 Agent 没有明确权限机制时,按上面的停止规则处理。这不改变 CLI 的凭据存储、Token 有效期或服务端授权。回复语言跟随用户,稳定错误码保持原文。服务端仍负责选择数字员工;不要新增或猜测 employee、target、root、turn、tenant、user 或 tool。
|
|
31
52
|
|
|
32
53
|
## 可用查询
|
|
33
54
|
|
|
34
|
-
- `npx --yes @luckypiggy/zhihu-ai-cli@0.2.1-test.
|
|
35
|
-
- `npx --yes @luckypiggy/zhihu-ai-cli@0.2.1-test.
|
|
36
|
-
- `npx --yes @luckypiggy/zhihu-ai-cli@0.2.1-test.
|
|
37
|
-
- `npx --yes @luckypiggy/zhihu-ai-cli@0.2.1-test.
|
|
38
|
-
- `npx --yes @luckypiggy/zhihu-ai-cli@0.2.1-test.
|
|
39
|
-
- `npx --yes @luckypiggy/zhihu-ai-cli@0.2.1-test.
|
|
40
|
-
- `npx --yes @luckypiggy/zhihu-ai-cli@0.2.1-test.
|
|
41
|
-
- `npx --yes @luckypiggy/zhihu-ai-cli@0.2.1-test.
|
|
42
|
-
- `npx --yes @luckypiggy/zhihu-ai-cli@0.2.1-test.
|
|
43
|
-
- `npx --yes @luckypiggy/zhihu-ai-cli@0.2.1-test.
|
|
44
|
-
- `npx --yes @luckypiggy/zhihu-ai-cli@0.2.1-test.
|
|
55
|
+
- `npx --yes @luckypiggy/zhihu-ai-cli@0.2.1-test.5 auth status --json`
|
|
56
|
+
- `npx --yes @luckypiggy/zhihu-ai-cli@0.2.1-test.5 capabilities list --json`
|
|
57
|
+
- `npx --yes @luckypiggy/zhihu-ai-cli@0.2.1-test.5 a2a capabilities --json`
|
|
58
|
+
- `npx --yes @luckypiggy/zhihu-ai-cli@0.2.1-test.5 a2a delegate --capability <resource_query|incident_analysis|inspection_summary|knowledge_qa> --message <用户原始问题> --json`
|
|
59
|
+
- `npx --yes @luckypiggy/zhihu-ai-cli@0.2.1-test.5 a2a task get --id <opaque-task-id> --json`
|
|
60
|
+
- `npx --yes @luckypiggy/zhihu-ai-cli@0.2.1-test.5 employees list --json`
|
|
61
|
+
- `npx --yes @luckypiggy/zhihu-ai-cli@0.2.1-test.5 assets list --json` 或 `npx --yes @luckypiggy/zhihu-ai-cli@0.2.1-test.5 assets get --id <id> --json`
|
|
62
|
+
- `npx --yes @luckypiggy/zhihu-ai-cli@0.2.1-test.5 metrics query --asset <id|ip> --metric <online|cpu|memory|load|disk|io|snmp> --window <5m..24h> --json`
|
|
63
|
+
- `npx --yes @luckypiggy/zhihu-ai-cli@0.2.1-test.5 alarms list --status <all|firing|resolved> --json` 或 `npx --yes @luckypiggy/zhihu-ai-cli@0.2.1-test.5 alarms get --id <id> --json`
|
|
64
|
+
- `npx --yes @luckypiggy/zhihu-ai-cli@0.2.1-test.5 incidents diagnose --alarm-id <id> --json`
|
|
65
|
+
- `npx --yes @luckypiggy/zhihu-ai-cli@0.2.1-test.5 inspections run [--model <name|code|id>] [--window <5m..24h>] --json`
|
|
66
|
+
- `npx --yes @luckypiggy/zhihu-ai-cli@0.2.1-test.5 runner lock status --json`,以及仅在状态为 stale 时使用的 `npx --yes @luckypiggy/zhihu-ai-cli@0.2.1-test.5 runner lock recover --confirm recover-stale-runner-lock --fingerprint <fingerprint> --json`。不要手工删除锁文件。本地 runner 尚未生成时,重复安装错误里的精确版本命令,不要改用全局 zhihu 或浮动 tag。本地 runner 已经存在后,再使用安装结果里的本地命令。
|
|
45
67
|
|
|
46
|
-
筛选、分页、模型和时间窗必须使用 CLI 已声明的取值;不要自行拼接未列出的选项。底层资产、指标、告警、诊断和巡检命令只在用户明确要求原始结构化数据、排查 CLI 本身或 A2A 明确不可用且用户同意受控降级时使用;A2A 错误不得自动降级。员工对话只有在用户明确给出可信员工 ID 和问题时才可使用 `npx --yes @luckypiggy/zhihu-ai-cli@0.2.1-test.
|
|
68
|
+
筛选、分页、模型和时间窗必须使用 CLI 已声明的取值;不要自行拼接未列出的选项。底层资产、指标、告警、诊断和巡检命令只在用户明确要求原始结构化数据、排查 CLI 本身或 A2A 明确不可用且用户同意受控降级时使用;A2A 错误不得自动降级。员工对话只有在用户明确给出可信员工 ID 和问题时才可使用 `npx --yes @luckypiggy/zhihu-ai-cli@0.2.1-test.5 employees chat <employee-id> --message <text> --json`,不得把对话当作默认路由或执行变更的入口。
|
|
47
69
|
|
|
48
70
|
## 输出和安全边界
|
|
49
71
|
|
|
50
72
|
- 只展示 CLI envelope 中必要的非敏感字段;不要复述认证材料、配置内容、环境变量或本机凭据。
|
|
51
|
-
- 只读命令不等于服务端一定有数据。遇到 `AUTH_RELOGIN_REQUIRED
|
|
73
|
+
- 只读命令不等于服务端一定有数据。遇到 `AUTH_RELOGIN_REQUIRED` 时按沙箱权限重试处理,不要在第一次出现时要求重新登录。遇到 `FORBIDDEN`、`CAPABILITY_NOT_AVAILABLE`、`A2A_DISABLED`、`A2A_CAPABILITY_NOT_FOUND`、`A2A_CAPABILITY_DISABLED`、`A2A_TASK_NOT_FOUND`、`A2A_FORBIDDEN`、`A2A_TIMEOUT`、`A2A_EXECUTION_INDETERMINATE`、`ASSET_NOT_FOUND`、`ALARM_NOT_FOUND`、`DATA_SOURCE_UNAVAILABLE`、`TIMEOUT` 或 `PROTOCOL_ERROR`,说明稳定错误码和下一步,不编造结果。
|
|
52
74
|
- 不直接访问智护服务、监控服务或容器;不执行额外命令,不访问本机配置/凭据存储,不联系外部地址。
|
|
53
|
-
- 本 Skill 不提供 MCP、变更、部署或公网包发布能力;`npx --yes @luckypiggy/zhihu-ai-cli@0.2.1-test.
|
|
75
|
+
- 本 Skill 不提供 MCP、变更、部署或公网包发布能力;`npx --yes @luckypiggy/zhihu-ai-cli@0.2.1-test.5 install codex` 只安装本地 Skill 文件。
|
|
54
76
|
|
|
55
77
|
详细命令字段见 [references/commands.md](references/commands.md),错误处理见 [references/errors.md](references/errors.md),边界规则见 [references/safety.md](references/safety.md)。
|
|
@@ -1,16 +1,24 @@
|
|
|
1
1
|
# CLI 命令参考
|
|
2
2
|
|
|
3
|
-
所有调用都必须以 `--json` 结束或等价地启用 JSON
|
|
3
|
+
所有调用都必须以 `--json` 结束或等价地启用 JSON 模式;命令使用安装时写入的本机持久化 runner。后续调用直接执行该本地程序,不再通过 npx 访问 npm registry。只解析一个完整 envelope:成功看 `ok: true` 与 `data`,失败看 `ok: false`、`error.code`、`error.retryable`。
|
|
4
|
+
|
|
5
|
+
## 默认路径与沙箱权限重试
|
|
6
|
+
|
|
7
|
+
默认 A2A 路径先调用 `npx --yes @luckypiggy/zhihu-ai-cli@0.2.1-test.5 a2a capabilities --json`,成功后再调用一次 `npx --yes @luckypiggy/zhihu-ai-cli@0.2.1-test.5 a2a delegate`。`npx --yes @luckypiggy/zhihu-ai-cli@0.2.1-test.5 auth status --json` 和 `npx --yes @luckypiggy/zhihu-ai-cli@0.2.1-test.5 capabilities list --json` 只在用户明确查询登录或能力、或排查错误时使用,不作为每次委派的前置步骤。
|
|
8
|
+
|
|
9
|
+
`AUTH_RELOGIN_REQUIRED` 第一次出现时不要直接要求重新登录。只有绝对本地 Runner、本文件列出的只读命令、该稳定错误码、宿主明确权限机制、用户尚未拒绝,且这条命令尚未重试过,才对同一条命令原样重试,且只重试一次。原样重试保持可执行文件、参数、消息、标识、环境、工作目录和顺序不变。不得读取 Keychain、Secret Service、Profile、Token、密码或配置,不得扩大到任意 shell 或直接 API。
|
|
10
|
+
|
|
11
|
+
不得对 `npx --yes @luckypiggy/zhihu-ai-cli@0.2.1-test.5 auth login`、`npx --yes @luckypiggy/zhihu-ai-cli@0.2.1-test.5 auth logout`、`npx --yes @luckypiggy/zhihu-ai-cli@0.2.1-test.5 install`、`npx --yes @luckypiggy/zhihu-ai-cli@0.2.1-test.5 uninstall`、`npx --yes @luckypiggy/zhihu-ai-cli@0.2.1-test.5 runner lock recover`、未列出的命令、`asset_onboarding`、`asset_discovery` 或写能力使用权限重试。`npx --yes @luckypiggy/zhihu-ai-cli@0.2.1-test.5 runner lock status --json` 不属于业务 A2A 权限重试。用户拒绝或宿主不支持时,说明 Agent 无法在当前沙箱确认登录状态,不得声称凭据已失效。同一条命令第二次仍返回 `AUTH_RELOGIN_REQUIRED` 时,才提示用户在自己的终端重新登录。第一条命令已经成功时,不做权限流程。权限申请失败、取消或超时不得编造结果。
|
|
4
12
|
|
|
5
13
|
## 会话与能力
|
|
6
14
|
|
|
7
15
|
| 目的 | 命令 |
|
|
8
16
|
| --- | --- |
|
|
9
|
-
| 查看当前登录状态 | `npx --yes @luckypiggy/zhihu-ai-cli@0.2.1-test.
|
|
10
|
-
| 查看能力范围 | `npx --yes @luckypiggy/zhihu-ai-cli@0.2.1-test.
|
|
11
|
-
| 查看 A2A 只读能力 | `npx --yes @luckypiggy/zhihu-ai-cli@0.2.1-test.
|
|
12
|
-
| 查看可用数字员工 | `npx --yes @luckypiggy/zhihu-ai-cli@0.2.1-test.
|
|
13
|
-
| 发起只读员工问答 | `npx --yes @luckypiggy/zhihu-ai-cli@0.2.1-test.
|
|
17
|
+
| 查看当前登录状态 | `npx --yes @luckypiggy/zhihu-ai-cli@0.2.1-test.5 auth status --json` |
|
|
18
|
+
| 查看能力范围 | `npx --yes @luckypiggy/zhihu-ai-cli@0.2.1-test.5 capabilities list --json` |
|
|
19
|
+
| 查看 A2A 只读能力 | `npx --yes @luckypiggy/zhihu-ai-cli@0.2.1-test.5 a2a capabilities --json` |
|
|
20
|
+
| 查看可用数字员工 | `npx --yes @luckypiggy/zhihu-ai-cli@0.2.1-test.5 employees list --json` |
|
|
21
|
+
| 发起只读员工问答 | `npx --yes @luckypiggy/zhihu-ai-cli@0.2.1-test.5 employees chat <employee-id> --message <text> --json` |
|
|
14
22
|
|
|
15
23
|
## 自然语言默认路由
|
|
16
24
|
|
|
@@ -18,12 +26,12 @@
|
|
|
18
26
|
|
|
19
27
|
| 用户意图 | capability_id | 命令 |
|
|
20
28
|
| --- | --- | --- |
|
|
21
|
-
| 资产、指标、采集状态 | `resource_query` | `npx --yes @luckypiggy/zhihu-ai-cli@0.2.1-test.
|
|
22
|
-
| 告警、故障、根因和恢复验证建议 | `incident_analysis` | `npx --yes @luckypiggy/zhihu-ai-cli@0.2.1-test.
|
|
23
|
-
| 巡检、健康检查和状态摘要 | `inspection_summary` | `npx --yes @luckypiggy/zhihu-ai-cli@0.2.1-test.
|
|
24
|
-
| 知识、案例、手册和 SOP | `knowledge_qa` | `npx --yes @luckypiggy/zhihu-ai-cli@0.2.1-test.
|
|
29
|
+
| 资产、指标、采集状态 | `resource_query` | `npx --yes @luckypiggy/zhihu-ai-cli@0.2.1-test.5 a2a delegate --capability resource_query --message <原始问题> --json` |
|
|
30
|
+
| 告警、故障、根因和恢复验证建议 | `incident_analysis` | `npx --yes @luckypiggy/zhihu-ai-cli@0.2.1-test.5 a2a delegate --capability incident_analysis --message <原始问题> --json` |
|
|
31
|
+
| 巡检、健康检查和状态摘要 | `inspection_summary` | `npx --yes @luckypiggy/zhihu-ai-cli@0.2.1-test.5 a2a delegate --capability inspection_summary --message <原始问题> --json` |
|
|
32
|
+
| 知识、案例、手册和 SOP | `knowledge_qa` | `npx --yes @luckypiggy/zhihu-ai-cli@0.2.1-test.5 a2a delegate --capability knowledge_qa --message <原始问题> --json` |
|
|
25
33
|
|
|
26
|
-
委派返回 opaque `task`、目标员工、状态、追踪字段和只读结论;需要复查时使用 `npx --yes @luckypiggy/zhihu-ai-cli@0.2.1-test.
|
|
34
|
+
委派返回 opaque `task`、目标员工、状态、追踪字段和只读结论;需要复查时使用 `npx --yes @luckypiggy/zhihu-ai-cli@0.2.1-test.5 a2a task get --id <opaque-task-id> --json`。不添加 `--employee`、`--target-agent`、`--source-agent`、`--tool`、`--root-session`、`--tenant` 或 `--user`。
|
|
27
35
|
|
|
28
36
|
普通 A2A 问答不要求用户知道员工 ID;只有用户明确要求直接与数字员工对话时,才确认可信员工 ID。`--message` 只描述查询,不描述变更。
|
|
29
37
|
|
|
@@ -31,17 +39,21 @@
|
|
|
31
39
|
|
|
32
40
|
| 目的 | 命令 |
|
|
33
41
|
| --- | --- |
|
|
34
|
-
| 查资产 | `npx --yes @luckypiggy/zhihu-ai-cli@0.2.1-test.
|
|
35
|
-
| 取资产 | `npx --yes @luckypiggy/zhihu-ai-cli@0.2.1-test.
|
|
36
|
-
| 查指标 | `npx --yes @luckypiggy/zhihu-ai-cli@0.2.1-test.
|
|
37
|
-
| 列告警 | `npx --yes @luckypiggy/zhihu-ai-cli@0.2.1-test.
|
|
38
|
-
| 取告警 | `npx --yes @luckypiggy/zhihu-ai-cli@0.2.1-test.
|
|
42
|
+
| 查资产 | `npx --yes @luckypiggy/zhihu-ai-cli@0.2.1-test.5 assets list [--keyword <text>] [--model <name|code|id>] --json` |
|
|
43
|
+
| 取资产 | `npx --yes @luckypiggy/zhihu-ai-cli@0.2.1-test.5 assets get --id <id> --json` |
|
|
44
|
+
| 查指标 | `npx --yes @luckypiggy/zhihu-ai-cli@0.2.1-test.5 metrics query --asset <id|ip> --metric <allowed-name> --window <5m..24h> --json` |
|
|
45
|
+
| 列告警 | `npx --yes @luckypiggy/zhihu-ai-cli@0.2.1-test.5 alarms list --status <all|firing|resolved> --json` |
|
|
46
|
+
| 取告警 | `npx --yes @luckypiggy/zhihu-ai-cli@0.2.1-test.5 alarms get --id <id> --json` |
|
|
39
47
|
|
|
40
48
|
资产和告警 ID 必须来自用户或此前 CLI 返回;IP、指标和窗口必须满足 CLI 的白名单与上限。底层查询命令是显式结构化数据入口,不是自然语言任务的默认路径。
|
|
41
49
|
|
|
50
|
+
## 安装锁
|
|
51
|
+
|
|
52
|
+
安装异常退出后不要手工删除锁文件。先运行 `npx --yes @luckypiggy/zhihu-ai-cli@0.2.1-test.5 runner lock status --json`。`held` 表示安装可能仍在进行,等待后重试。只有 `stale` 才使用 status 返回的 fingerprint 运行 `npx --yes @luckypiggy/zhihu-ai-cli@0.2.1-test.5 runner lock recover --confirm recover-stale-runner-lock --fingerprint <fingerprint> --json`,成功后再重试安装。本地 runner 尚未生成时,重复安装错误里的精确版本命令,不要改用全局 zhihu 或浮动 tag。本地 runner 已经存在后,再使用本地命令。
|
|
53
|
+
|
|
42
54
|
## 诊断与巡检
|
|
43
55
|
|
|
44
|
-
- 指定告警分析:`npx --yes @luckypiggy/zhihu-ai-cli@0.2.1-test.
|
|
45
|
-
- 只读巡检摘要:`npx --yes @luckypiggy/zhihu-ai-cli@0.2.1-test.
|
|
56
|
+
- 指定告警分析:`npx --yes @luckypiggy/zhihu-ai-cli@0.2.1-test.5 incidents diagnose --alarm-id <id> --json`
|
|
57
|
+
- 只读巡检摘要:`npx --yes @luckypiggy/zhihu-ai-cli@0.2.1-test.5 inspections run [--model <name|code|id>] [--window <5m..24h>] --json`
|
|
46
58
|
|
|
47
59
|
诊断和巡检结果是保守的结构化摘要。证据不足时报告不足,不补全设备、租户、权限或因果关系。
|
|
@@ -4,11 +4,12 @@
|
|
|
4
4
|
|
|
5
5
|
常见处理:
|
|
6
6
|
|
|
7
|
-
- `AUTH_RELOGIN_REQUIRED
|
|
7
|
+
- `AUTH_RELOGIN_REQUIRED`:不要把第一次出现直接解释成登录失效,也不要直接要求重新登录。只有绝对本地 Runner、Skill 只读白名单、该稳定错误码、宿主明确权限机制、用户尚未拒绝、且这条命令尚未重试过六项同时成立时,才对同一条命令原样重试,且只重试一次。原样重试保持可执行文件、参数、消息、标识、环境、工作目录和顺序不变。不得读取 Keychain、Secret Service、Profile、Token、密码或配置。`npx --yes @luckypiggy/zhihu-ai-cli@0.2.1-test.5 auth login`、`npx --yes @luckypiggy/zhihu-ai-cli@0.2.1-test.5 auth logout`、`npx --yes @luckypiggy/zhihu-ai-cli@0.2.1-test.5 install`、`npx --yes @luckypiggy/zhihu-ai-cli@0.2.1-test.5 uninstall`、`npx --yes @luckypiggy/zhihu-ai-cli@0.2.1-test.5 runner lock recover`、白名单外命令、`asset_onboarding`、`asset_discovery`、写能力、任意 shell 和直接 API 都不得使用该机制。`npx --yes @luckypiggy/zhihu-ai-cli@0.2.1-test.5 runner lock status --json` 不属于业务 A2A 权限重试。用户拒绝或宿主不支持时,说明当前沙箱无法确认登录状态,不得声称凭据已失效;可以建议用户在自己的终端核对登录状态。同一条命令第二次仍返回 `AUTH_RELOGIN_REQUIRED` 时,才提示用户在自己的终端重新登录,并使用安装结果中的登录命令;不要索取密码、Token 或验证码。其他错误码不得二次提权。权限申请失败、取消或超时不得编造结果,也不得改走直接 API。第一条命令已经成功时,不做权限流程。
|
|
8
8
|
- `FORBIDDEN`、`CAPABILITY_NOT_AVAILABLE`:说明当前账号没有所需范围,请用户联系管理员;不要尝试其他身份。
|
|
9
9
|
- `EMPLOYEE_NOT_FOUND`、`ASSET_NOT_FOUND`、`ALARM_NOT_FOUND`:让用户确认精确 ID;不要从相似名称猜测。
|
|
10
10
|
- `INCIDENT_ASSET_IDENTITY_UNAVAILABLE`、`ASSET_METRIC_IDENTITY_UNAVAILABLE`、`METRIC_DATA_NOT_FOUND`:报告目标无法安全关联或没有数据。
|
|
11
11
|
- `DATA_SOURCE_UNAVAILABLE`、`TIMEOUT`、`NETWORK_ERROR`、`RATE_LIMITED`:保留原查询边界,按 `retryable` 决定是否建议稍后重试。
|
|
12
12
|
- `INVALID_ARGUMENT`、`PROTOCOL_ERROR`、`VERSION_INCOMPATIBLE`:说明 CLI 参数或版本问题,先停止分析。
|
|
13
|
+
- `LOCAL_RUNNER_LOCK_TIMEOUT`:安装锁超时。运行 `npx --yes @luckypiggy/zhihu-ai-cli@0.2.1-test.5 runner lock status --json`。状态为 `held` 时不要恢复,等待安装结束后重试。状态为 `stale` 时,使用返回的 fingerprint 运行 `npx --yes @luckypiggy/zhihu-ai-cli@0.2.1-test.5 runner lock recover --confirm recover-stale-runner-lock --fingerprint <fingerprint> --json`,然后再重试安装。不要手工删除锁文件。本地 runner 尚未生成时,重复安装错误里的精确版本命令,不要改用全局 zhihu 或浮动 tag。本地 runner 已经存在后,再使用本地命令。符号链接、损坏锁和错误 fingerprint 必须保持失败。
|
|
13
14
|
|
|
14
15
|
认证、权限、数据源和协议错误都不能被改写成“正常”或“已修复”。
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# 安全边界
|
|
2
2
|
|
|
3
|
-
-
|
|
3
|
+
- 唯一入口是安装时写入的本机持久化 runner 的 JSON 命令,例如 `npx --yes @luckypiggy/zhihu-ai-cli@0.2.1-test.5 auth status --json`。后续调用直接执行该本地程序,不再通过 npx 访问 npm registry。不要从本机配置、环境变量、凭据服务或日志读取认证材料。
|
|
4
4
|
- 不要直接访问智护 API、监控后端、容器地址或任意网络地址;不要改写请求地址、查询表达式、标签、租户或权限。
|
|
5
5
|
- 不执行额外系统命令,不操作主机、文件、进程、网络设备或部署;不要把 CLI 结果发送给外部服务。
|
|
6
6
|
- 只读范围包括认证状态、能力、A2A 四类只读委派、数字员工、资产、指标、告警、指定告警诊断和巡检。任何创建、删除、更新、重启、发布或变更请求都应拒绝并说明当前能力限制;`asset_onboarding`、`asset_discovery` 等写 capability 不可调用。
|
|
@@ -8,4 +8,9 @@
|
|
|
8
8
|
- 用户意图映射固定为 `resource_query`(资产/指标/采集)、`incident_analysis`(告警/故障/根因)、`inspection_summary`(巡检/健康摘要)和 `knowledge_qa`(知识/案例/SOP)。命中多个意图、缺少必要对象或包含“查询并处理”等写/歧义请求时先澄清。
|
|
9
9
|
- 用户没有给出唯一目标时先询问:资产 ID、告警 ID、模型、指标、窗口和语言都不能凭空补全;只有用户明确要求直接员工对话时才询问员工 ID。
|
|
10
10
|
- 结果只保留回答所需的字段。不要输出本机路径、内部地址、完整请求、会话材料或未脱敏正文。
|
|
11
|
-
-
|
|
11
|
+
- 安装锁异常退出后,只通过 `npx --yes @luckypiggy/zhihu-ai-cli@0.2.1-test.5 runner lock status --json` 和 `npx --yes @luckypiggy/zhihu-ai-cli@0.2.1-test.5 runner lock recover --confirm recover-stale-runner-lock --fingerprint <fingerprint> --json` 处理。不要手工删除锁文件,不要在 `held`、符号链接、损坏锁或 fingerprint 不一致时继续恢复。本地 runner 尚未生成时,重复安装错误里的精确版本命令,不要改用全局 zhihu 或浮动 tag。本地 runner 已经存在后,再使用本地命令。恢复成功后再重试原来的安装命令。
|
|
12
|
+
- 默认 A2A 路径先调用 `npx --yes @luckypiggy/zhihu-ai-cli@0.2.1-test.5 a2a capabilities --json`,成功后再调用一次 `npx --yes @luckypiggy/zhihu-ai-cli@0.2.1-test.5 a2a delegate`。`npx --yes @luckypiggy/zhihu-ai-cli@0.2.1-test.5 auth status --json` 和 `npx --yes @luckypiggy/zhihu-ai-cli@0.2.1-test.5 capabilities list --json` 只在用户明确查询登录或能力、或排查错误时使用,不作为每次委派的前置步骤。
|
|
13
|
+
- `AUTH_RELOGIN_REQUIRED` 只有在绝对本地 Runner、只读白名单、该稳定错误码、明确权限机制、用户尚未拒绝且尚未重试过时,才允许原样重试,且只重试一次。原样重试保持可执行文件、参数、消息、标识、环境、工作目录和顺序不变。不得读取 Keychain、Secret Service、Profile、Token、密码或配置。
|
|
14
|
+
- 不得对 `npx --yes @luckypiggy/zhihu-ai-cli@0.2.1-test.5 auth login`、`npx --yes @luckypiggy/zhihu-ai-cli@0.2.1-test.5 auth logout`、`npx --yes @luckypiggy/zhihu-ai-cli@0.2.1-test.5 install`、`npx --yes @luckypiggy/zhihu-ai-cli@0.2.1-test.5 uninstall`、`npx --yes @luckypiggy/zhihu-ai-cli@0.2.1-test.5 runner lock recover`、`asset_onboarding`、`asset_discovery`、写能力、任意 shell 或直接 API 使用权限重试。`npx --yes @luckypiggy/zhihu-ai-cli@0.2.1-test.5 runner lock status --json` 不属于业务 A2A 权限重试。
|
|
15
|
+
- 用户拒绝、宿主不支持或命令不在白名单时,停止并说明 Agent 无法在当前沙箱确认登录状态,不得声称凭据已失效。同一条命令第二次仍返回 `AUTH_RELOGIN_REQUIRED` 时,才提示用户在自己的终端重新登录。第一条命令已经成功时,不做权限流程。权限申请失败、取消或超时不得编造结果,也不得改走直接 API。
|
|
16
|
+
- 这是只读 Skill,不宣称 MCP、变更能力或公网包发布能力。安装后的命令指向本机持久化 runner,不要改回 npx、npm 或其他未列出的入口。
|
package/dist/cli.js
CHANGED
|
@@ -3,7 +3,8 @@ import { isIP } from "node:net";
|
|
|
3
3
|
import { isAbsolute, resolve } from "node:path";
|
|
4
4
|
import { fileURLToPath } from "node:url";
|
|
5
5
|
import { CLI_VERSION, CLI_PACKAGE_NAME, MIN_NODE_MAJOR, } from "./contracts.js";
|
|
6
|
-
import {
|
|
6
|
+
import { CLI_RUNNER } from "./release.js";
|
|
7
|
+
import { existingLocalRunnerCommand, inspectLocalRunner, inspectLocalRunnerLock, LocalRunnerError, recoverLocalRunnerLock, } from "./local-runner.js";
|
|
7
8
|
import { ConfigError, DEFAULT_PROFILE, DEFAULT_PROFILE_NAME, checkProfile, configRoot, guardProfileConnectionChange, initializeProfile, ProfileConnectionGuardError, readConfigSnapshot, readProfileRaw, validateProfile, } from "./config.js";
|
|
8
9
|
import { withProfileLock } from "./profile-lock.js";
|
|
9
10
|
import { emitError, emitSuccess } from "./output.js";
|
|
@@ -277,9 +278,22 @@ export async function doctorData(environment = process.env, options = {}) {
|
|
|
277
278
|
};
|
|
278
279
|
const runtime = { ok: runtimeMajor >= MIN_NODE_MAJOR, node_version: process.versions.node, minimum_node_major: MIN_NODE_MAJOR };
|
|
279
280
|
const codexCheck = { ok: codex.state === "managed", state: codex.state, ...(codex.target ? { target: codex.target } : {}) };
|
|
281
|
+
const runnerInspection = inspectLocalRunner(environment);
|
|
282
|
+
const lockInspection = inspectLocalRunnerLock(environment);
|
|
283
|
+
const localRunnerLock = {
|
|
284
|
+
ok: lockInspection.state === "missing",
|
|
285
|
+
state: lockInspection.state,
|
|
286
|
+
...(lockInspection.pid !== undefined ? { pid: lockInspection.pid } : {}),
|
|
287
|
+
...(lockInspection.age_ms !== undefined ? { age_ms: lockInspection.age_ms } : {}),
|
|
288
|
+
...(lockInspection.fingerprint ? { fingerprint: lockInspection.fingerprint } : {}),
|
|
289
|
+
...(lockInspection.detail ? { detail: lockInspection.detail } : {}),
|
|
290
|
+
};
|
|
280
291
|
return {
|
|
281
292
|
cli_version: CLI_VERSION,
|
|
282
293
|
stage: "p1-readonly",
|
|
294
|
+
local_runner: runnerInspection.command,
|
|
295
|
+
local_runner_health: runnerInspection.health,
|
|
296
|
+
local_runner_lock: localRunnerLock,
|
|
283
297
|
checks: {
|
|
284
298
|
runtime,
|
|
285
299
|
config: configCheckResult,
|
|
@@ -288,6 +302,7 @@ export async function doctorData(environment = process.env, options = {}) {
|
|
|
288
302
|
authentication,
|
|
289
303
|
capabilities: capabilityCheck,
|
|
290
304
|
codex: codexCheck,
|
|
305
|
+
local_runner_lock: localRunnerLock,
|
|
291
306
|
// Compatibility aliases retained for existing automation; the stable
|
|
292
307
|
// Stage5B contract is the seven checks above.
|
|
293
308
|
base_url: { ok: profileReady, state: profileReady ? "configured" : "not_configured" },
|
|
@@ -1063,7 +1078,11 @@ async function installCommand(flags, json) {
|
|
|
1063
1078
|
process.removeListener("SIGTERM", onSignal);
|
|
1064
1079
|
}
|
|
1065
1080
|
}
|
|
1081
|
+
function displayedRunner() {
|
|
1082
|
+
return existingLocalRunnerCommand(process.env) ?? CLI_RUNNER;
|
|
1083
|
+
}
|
|
1066
1084
|
export function installHumanSummary(data) {
|
|
1085
|
+
const runner = displayedRunner();
|
|
1067
1086
|
const zh = data.language === "zh";
|
|
1068
1087
|
const profile = typeof data.profile === "string" ? data.profile : "-";
|
|
1069
1088
|
const baseUrl = typeof data.base_url === "string" ? data.base_url : "-";
|
|
@@ -1090,8 +1109,8 @@ export function installHumanSummary(data) {
|
|
|
1090
1109
|
"- 用智护列出当前活动告警。",
|
|
1091
1110
|
"- 用智护查看资产 123 最近一小时 CPU 趋势。",
|
|
1092
1111
|
"- 用智护分析告警 456 的根因。",
|
|
1093
|
-
`排查安装:${
|
|
1094
|
-
`Skill runner:${
|
|
1112
|
+
`排查安装:${runner} doctor`,
|
|
1113
|
+
`Skill runner:${runner}`,
|
|
1095
1114
|
].join("\n");
|
|
1096
1115
|
}
|
|
1097
1116
|
return [
|
|
@@ -1105,10 +1124,52 @@ export function installHumanSummary(data) {
|
|
|
1105
1124
|
"- List the current firing alarms.",
|
|
1106
1125
|
"- Show the last hour of CPU trend for asset 123.",
|
|
1107
1126
|
"- Diagnose the root cause of alarm 456.",
|
|
1108
|
-
`Troubleshoot installation: ${
|
|
1109
|
-
`Skill runner: ${
|
|
1127
|
+
`Troubleshoot installation: ${runner} doctor`,
|
|
1128
|
+
`Skill runner: ${runner}`,
|
|
1110
1129
|
].join("\n");
|
|
1111
1130
|
}
|
|
1131
|
+
function runnerLockCommand(command, flags) {
|
|
1132
|
+
const [, second, third] = command;
|
|
1133
|
+
if (second !== "lock" || command.length !== 3 || (third !== "status" && third !== "recover")) {
|
|
1134
|
+
invalid("Use runner lock status or runner lock recover");
|
|
1135
|
+
}
|
|
1136
|
+
if (third === "status") {
|
|
1137
|
+
for (const key of flags.keys())
|
|
1138
|
+
invalid(`Unknown runner option: --${key}`);
|
|
1139
|
+
const inspection = inspectLocalRunnerLock(process.env);
|
|
1140
|
+
return {
|
|
1141
|
+
state: inspection.state,
|
|
1142
|
+
...(inspection.pid !== undefined ? { pid: inspection.pid } : {}),
|
|
1143
|
+
...(inspection.age_ms !== undefined ? { age_ms: inspection.age_ms } : {}),
|
|
1144
|
+
...(inspection.fingerprint ? { fingerprint: inspection.fingerprint } : {}),
|
|
1145
|
+
...(inspection.detail ? { detail: inspection.detail } : {}),
|
|
1146
|
+
};
|
|
1147
|
+
}
|
|
1148
|
+
for (const key of flags.keys()) {
|
|
1149
|
+
if (key !== "confirm" && key !== "fingerprint")
|
|
1150
|
+
invalid(`Unknown runner option: --${key}`);
|
|
1151
|
+
}
|
|
1152
|
+
const confirm = flags.get("confirm");
|
|
1153
|
+
const fingerprint = flags.get("fingerprint");
|
|
1154
|
+
if (typeof confirm !== "string" || typeof fingerprint !== "string") {
|
|
1155
|
+
invalid("runner lock recover requires --confirm and --fingerprint");
|
|
1156
|
+
}
|
|
1157
|
+
try {
|
|
1158
|
+
recoverLocalRunnerLock(process.env, { confirm, fingerprint });
|
|
1159
|
+
}
|
|
1160
|
+
catch (error) {
|
|
1161
|
+
if (error instanceof LocalRunnerError) {
|
|
1162
|
+
throw new CliFailure({
|
|
1163
|
+
code: error.code,
|
|
1164
|
+
message: error.message,
|
|
1165
|
+
retryable: error.code === "LOCAL_RUNNER_LOCK_TIMEOUT",
|
|
1166
|
+
exit_code: 2,
|
|
1167
|
+
});
|
|
1168
|
+
}
|
|
1169
|
+
throw error;
|
|
1170
|
+
}
|
|
1171
|
+
return { state: "recovered" };
|
|
1172
|
+
}
|
|
1112
1173
|
function helpData() {
|
|
1113
1174
|
return {
|
|
1114
1175
|
commands: [
|
|
@@ -1135,6 +1196,8 @@ function helpData() {
|
|
|
1135
1196
|
"install [--profile <name>] [--base-url <https-url>] [--timeout N] [--language zh-CN|en] [--ca-file <absolute-file>] [--codex-skill-dir <absolute-path>] [--skip-login] [--skip-codex] [--force]",
|
|
1136
1197
|
"install codex [--profile <name>] [--codex-skill-dir <absolute-path>] [--force]",
|
|
1137
1198
|
"uninstall codex [--profile <name>] [--codex-skill-dir <absolute-path>] [--force]",
|
|
1199
|
+
"runner lock status",
|
|
1200
|
+
"runner lock recover --confirm recover-stale-runner-lock --fingerprint <fingerprint>",
|
|
1138
1201
|
],
|
|
1139
1202
|
json: "Append --json for the stable success/error envelope",
|
|
1140
1203
|
note: "Authentication plus read-only employee, asset, metric, alarm, incident diagnosis, inspection queries, local installation, and the Codex Skill installer are available; login is terminal-only and passwords are never accepted through flags or environment variables; formal GA, write operations, and MCP are not available",
|
|
@@ -1163,6 +1226,8 @@ async function run(parsed) {
|
|
|
1163
1226
|
return { data: showConfig(parsed.flags), exit_code: 0 };
|
|
1164
1227
|
if (first === "config" && second === "init")
|
|
1165
1228
|
return { data: await configInit(parsed.flags), exit_code: 0 };
|
|
1229
|
+
if (first === "runner")
|
|
1230
|
+
return { data: runnerLockCommand(parsed.command, parsed.flags), exit_code: 0 };
|
|
1166
1231
|
if (first === "install") {
|
|
1167
1232
|
if (!second && parsed.command.length === 1) {
|
|
1168
1233
|
const data = await installCommand(parsed.flags, parsed.json);
|
package/dist/codex-skill.js
CHANGED
|
@@ -3,7 +3,8 @@ import { createHash, randomBytes } from "node:crypto";
|
|
|
3
3
|
import { homedir } from "node:os";
|
|
4
4
|
import { dirname, isAbsolute, join, parse, resolve } from "node:path";
|
|
5
5
|
import { fileURLToPath } from "node:url";
|
|
6
|
-
import { CLI_PACKAGE_NAME, CLI_VERSION, } from "./contracts.js";
|
|
6
|
+
import { CLI_PACKAGE_NAME, CLI_RUNNER, CLI_VERSION, } from "./contracts.js";
|
|
7
|
+
import { LocalRunnerError, persistLocalCliRunner, retargetPackagedRunner, runnerCommandToken } from "./local-runner.js";
|
|
7
8
|
import { ConfigError, readConfigSnapshot, validateProfile, } from "./config.js";
|
|
8
9
|
export const CODEX_SKILL_NAME = "zhihu-aiops";
|
|
9
10
|
export const CODEX_SKILL_VERSION = CLI_VERSION;
|
|
@@ -431,12 +432,58 @@ function inspectTarget(target) {
|
|
|
431
432
|
localError("CODEX_SKILL_FOREIGN", "Codex Skill target is not a directory");
|
|
432
433
|
return "directory";
|
|
433
434
|
}
|
|
435
|
+
function installedSkillFiles(environment) {
|
|
436
|
+
let command;
|
|
437
|
+
try {
|
|
438
|
+
command = runnerCommandToken(persistLocalCliRunner(environment));
|
|
439
|
+
}
|
|
440
|
+
catch (error) {
|
|
441
|
+
if (error instanceof CodexSkillError)
|
|
442
|
+
throw error;
|
|
443
|
+
if (error instanceof LocalRunnerError && error.code === "LOCAL_RUNNER_SYMLINK") {
|
|
444
|
+
localError("CODEX_SKILL_SYMLINK", "Local CLI runner path is a symbolic link");
|
|
445
|
+
}
|
|
446
|
+
if (error instanceof LocalRunnerError && error.code === "LOCAL_RUNNER_UNSAFE_PATH") {
|
|
447
|
+
localError("CODEX_SKILL_PATH_INVALID", "Local CLI runner path cannot be embedded in a command");
|
|
448
|
+
}
|
|
449
|
+
if (error instanceof LocalRunnerError && error.code === "LOCAL_RUNNER_LOCK_TIMEOUT") {
|
|
450
|
+
localError("LOCAL_RUNNER_LOCK_TIMEOUT", error.message);
|
|
451
|
+
}
|
|
452
|
+
localError("CODEX_SKILL_IO_ERROR", "Local CLI runner could not be installed");
|
|
453
|
+
}
|
|
454
|
+
const files = sourceFiles();
|
|
455
|
+
let replaced = false;
|
|
456
|
+
for (const file of CODEX_SKILL_FILES) {
|
|
457
|
+
const current = files.get(file);
|
|
458
|
+
if (!current)
|
|
459
|
+
localError("CODEX_SKILL_IO_ERROR", "Codex Skill assets are incomplete");
|
|
460
|
+
const text = current.toString("utf8");
|
|
461
|
+
const packagedRunner = CLI_RUNNER;
|
|
462
|
+
const next = retargetPackagedRunner(text, packagedRunner, command);
|
|
463
|
+
if (next !== text)
|
|
464
|
+
replaced = true;
|
|
465
|
+
if (packagedRunner !== "zhihu" && next.includes(packagedRunner)) {
|
|
466
|
+
localError("CODEX_SKILL_IO_ERROR", "Installed Skill still references the packaged runner");
|
|
467
|
+
}
|
|
468
|
+
files.set(file, Buffer.from(next, "utf8"));
|
|
469
|
+
}
|
|
470
|
+
if (!replaced)
|
|
471
|
+
localError("CODEX_SKILL_IO_ERROR", "Codex Skill assets do not contain the packaged runner");
|
|
472
|
+
return files;
|
|
473
|
+
}
|
|
434
474
|
export function installCodexSkill(options = {}) {
|
|
435
475
|
const target = resolveCodexSkillTarget(options);
|
|
436
|
-
const files = sourceFiles();
|
|
437
|
-
const marker = markerFor(files);
|
|
438
476
|
ensureParent(target);
|
|
439
477
|
const state = inspectTarget(target);
|
|
478
|
+
const existingMarker = state === "missing" ? undefined : readMarker(target);
|
|
479
|
+
if (state !== "missing" && !existingMarker)
|
|
480
|
+
localError("CODEX_SKILL_FOREIGN", "Codex Skill target is not managed by this CLI");
|
|
481
|
+
const managedState = existingMarker ? regularManagedState(target, existingMarker) : undefined;
|
|
482
|
+
if (managedState === "modified" && !options.force) {
|
|
483
|
+
localError("CODEX_SKILL_MODIFIED", "Codex Skill files were modified; use --force to replace them");
|
|
484
|
+
}
|
|
485
|
+
const files = installedSkillFiles(options.environment ?? process.env);
|
|
486
|
+
const marker = markerFor(files);
|
|
440
487
|
if (state === "missing") {
|
|
441
488
|
const temporary = temporaryDirectory(dirname(target), CODEX_SKILL_NAME);
|
|
442
489
|
try {
|
|
@@ -449,13 +496,8 @@ export function installCodexSkill(options = {}) {
|
|
|
449
496
|
}
|
|
450
497
|
return { action: "install", status: "installed", skill: CODEX_SKILL_NAME, version: CODEX_SKILL_VERSION, target };
|
|
451
498
|
}
|
|
452
|
-
const existingMarker = readMarker(target);
|
|
453
499
|
if (!existingMarker)
|
|
454
500
|
localError("CODEX_SKILL_FOREIGN", "Codex Skill target is not managed by this CLI");
|
|
455
|
-
const managedState = regularManagedState(target, existingMarker);
|
|
456
|
-
if (managedState === "modified" && !options.force) {
|
|
457
|
-
localError("CODEX_SKILL_MODIFIED", "Codex Skill files were modified; use --force to replace them");
|
|
458
|
-
}
|
|
459
501
|
const sourceMatches = existingMarker.version === CODEX_SKILL_VERSION
|
|
460
502
|
&& CODEX_SKILL_FILES.every((file) => existingMarker.files[file] === marker.files[file]);
|
|
461
503
|
if (managedState === "complete" && sourceMatches) {
|
package/dist/credentials.js
CHANGED
|
@@ -20,9 +20,20 @@ function safeProfile(profile) {
|
|
|
20
20
|
const MAC_SECURITY_EXECUTABLE = "/usr/bin/security";
|
|
21
21
|
const MAC_EXPECT_EXECUTABLE = "/usr/bin/expect";
|
|
22
22
|
const MAC_EXPECT_FAILURE = 70;
|
|
23
|
-
|
|
23
|
+
/** Reserved exit status of the fixed macOS Expect helper. Other children may also exit 124. */
|
|
24
|
+
export const MAC_EXPECT_TIMEOUT = 124;
|
|
25
|
+
/** Read, delete, availability, and Linux writes keep the original five-second bound. */
|
|
26
|
+
export const CREDENTIAL_COMMAND_TIMEOUT_MS = 5000;
|
|
27
|
+
/**
|
|
28
|
+
* Bounded Expect wait for the whole macOS Keychain handshake. Both prompts
|
|
29
|
+
* share this clock because exp_continue does not restart it.
|
|
30
|
+
*/
|
|
31
|
+
export const MAC_KEYCHAIN_WRITE_EXPECT_TIMEOUT_SECONDS = 15;
|
|
32
|
+
/** Lets Expect exit and the child be reaped after its own timeout. */
|
|
33
|
+
export const MAC_KEYCHAIN_WRITE_CLEANUP_MARGIN_MS = 2000;
|
|
34
|
+
export const MAC_KEYCHAIN_WRITE_PROCESS_TIMEOUT_MS = MAC_KEYCHAIN_WRITE_EXPECT_TIMEOUT_SECONDS * 1000 + MAC_KEYCHAIN_WRITE_CLEANUP_MARGIN_MS;
|
|
24
35
|
const MAC_EXPECT_SCRIPT_BODY = `
|
|
25
|
-
set timeout
|
|
36
|
+
set timeout ${MAC_KEYCHAIN_WRITE_EXPECT_TIMEOUT_SECONDS}
|
|
26
37
|
log_user 0
|
|
27
38
|
if {[gets stdin secret] < 0} { exit ${MAC_EXPECT_FAILURE} }
|
|
28
39
|
set prompt_count 0
|
|
@@ -32,13 +43,13 @@ expect {
|
|
|
32
43
|
if {$prompt_count != 0} { exit ${MAC_EXPECT_FAILURE} }
|
|
33
44
|
set prompt_count 1
|
|
34
45
|
send -- "$secret\\r"
|
|
35
|
-
exp_continue
|
|
46
|
+
exp_continue -continue_timer
|
|
36
47
|
}
|
|
37
48
|
"retype password for new item:" {
|
|
38
49
|
if {$prompt_count != 1} { exit ${MAC_EXPECT_FAILURE} }
|
|
39
50
|
set prompt_count 2
|
|
40
51
|
send -- "$secret\\r"
|
|
41
|
-
exp_continue
|
|
52
|
+
exp_continue -continue_timer
|
|
42
53
|
}
|
|
43
54
|
timeout { exit ${MAC_EXPECT_TIMEOUT} }
|
|
44
55
|
eof {}
|
|
@@ -153,8 +164,8 @@ export const processCommandRunner = (command, args, options = {}) => new Promise
|
|
|
153
164
|
const failUnavailable = (message) => {
|
|
154
165
|
if (settled)
|
|
155
166
|
return;
|
|
156
|
-
terminateChild();
|
|
157
167
|
settled = true;
|
|
168
|
+
terminateChild();
|
|
158
169
|
clearMainTimer();
|
|
159
170
|
reject(new CredentialStoreError("CREDENTIAL_STORE_UNAVAILABLE", message));
|
|
160
171
|
};
|
|
@@ -189,7 +200,14 @@ export const processCommandRunner = (command, args, options = {}) => new Promise
|
|
|
189
200
|
// that exited during the handshake cannot become an uncaught error.
|
|
190
201
|
child.stdin.on("error", () => failUnavailable("System credential store is unavailable"));
|
|
191
202
|
child.on("error", () => failUnavailable("System credential store is unavailable"));
|
|
192
|
-
child.once("close", (code) =>
|
|
203
|
+
child.once("close", (code) => {
|
|
204
|
+
const status = code ?? 1;
|
|
205
|
+
if (useMacSecurityExpect && status === MAC_EXPECT_TIMEOUT) {
|
|
206
|
+
failUnavailable("System credential store timed out");
|
|
207
|
+
return;
|
|
208
|
+
}
|
|
209
|
+
finish({ code: status, stdout, stderr });
|
|
210
|
+
});
|
|
193
211
|
if (options.timeoutMs) {
|
|
194
212
|
timer = setTimeout(() => {
|
|
195
213
|
failUnavailable("System credential store timed out");
|
|
@@ -238,7 +256,7 @@ export class SecureCredentialStore {
|
|
|
238
256
|
async available() {
|
|
239
257
|
try {
|
|
240
258
|
const command = this.command();
|
|
241
|
-
const result = await this.runner(command.executable, command.checkArgs, { timeoutMs:
|
|
259
|
+
const result = await this.runner(command.executable, command.checkArgs, { timeoutMs: CREDENTIAL_COMMAND_TIMEOUT_MS });
|
|
242
260
|
if (result.code !== 0)
|
|
243
261
|
throw new CredentialStoreError("CREDENTIAL_STORE_UNAVAILABLE", "System credential store is unavailable");
|
|
244
262
|
return true;
|
|
@@ -252,7 +270,7 @@ export class SecureCredentialStore {
|
|
|
252
270
|
async get() {
|
|
253
271
|
try {
|
|
254
272
|
const command = this.command();
|
|
255
|
-
const result = await this.runner(command.executable, command.readArgs, { timeoutMs:
|
|
273
|
+
const result = await this.runner(command.executable, command.readArgs, { timeoutMs: CREDENTIAL_COMMAND_TIMEOUT_MS });
|
|
256
274
|
if (result.code !== 0) {
|
|
257
275
|
const diagnostic = result.stderr.toLowerCase();
|
|
258
276
|
if (missingResult(result) || diagnostic.includes("could not be found") || diagnostic.includes("no such secret") || diagnostic.includes("not found"))
|
|
@@ -282,7 +300,9 @@ export class SecureCredentialStore {
|
|
|
282
300
|
// fixed macOS Expect helper, which answers both security prompts in
|
|
283
301
|
// the child PTY without placing the secret in argv or the environment.
|
|
284
302
|
input: `${credential}\n`,
|
|
285
|
-
timeoutMs:
|
|
303
|
+
timeoutMs: this.platform === "darwin"
|
|
304
|
+
? MAC_KEYCHAIN_WRITE_PROCESS_TIMEOUT_MS
|
|
305
|
+
: CREDENTIAL_COMMAND_TIMEOUT_MS,
|
|
286
306
|
});
|
|
287
307
|
if (result.code !== 0)
|
|
288
308
|
throw new CredentialStoreError("CREDENTIAL_STORE_ERROR", "Unable to write system credential");
|
|
@@ -296,7 +316,7 @@ export class SecureCredentialStore {
|
|
|
296
316
|
async delete() {
|
|
297
317
|
try {
|
|
298
318
|
const command = this.command();
|
|
299
|
-
const result = await this.runner(command.executable, command.deleteArgs, { timeoutMs:
|
|
319
|
+
const result = await this.runner(command.executable, command.deleteArgs, { timeoutMs: CREDENTIAL_COMMAND_TIMEOUT_MS });
|
|
300
320
|
const diagnostic = result.stderr.toLowerCase();
|
|
301
321
|
if (result.code !== 0 && !missingResult(result) && !diagnostic.includes("could not be found") && !diagnostic.includes("no such secret") && !diagnostic.includes("not found")) {
|
|
302
322
|
throw new CredentialStoreError("CREDENTIAL_STORE_ERROR", "Unable to delete system credential");
|