@open-weave/cli
v0.1.0
Published
open-weave 平台的命令行入口:登录、能力清单与声明式面(get / plan / diff / apply)。open-weave —— 多工作区组合与能力治理平台。
Readme
platform-developer-tools
Owner:open-weave maintainers。未来 CLI/SDK/显式 MCP 的责任边界;运行实现尚未进入当前阶段。
状态:local-tooling(S5 首片 2026-09-14;红线 23「可见即可调」只读面补齐 2026-09-15;机器身份 B19-1 2026-09-15;B19 凭据落盘与 auth 命令 2026-09-17)。wanweave 提供 profile / 凭据(auth)/ 登录(浏览器 OIDC 或 AccessKey 机器身份)/ 网关能力清单 / 控制面断言只读面:
npm install
npm run wanweave -- profile init --environment test --organization <org> --workspace <workspace> --dataspace <space> --scope capability.read
npm run wanweave -- profile show # 含 machineIdentity:是否启用机器身份、来自哪些环境变量
npm run wanweave -- auth login --organization <org> --workspace <ws> # 交互式隐藏输入,或 --stdin 走管道
npm run wanweave -- auth status # 已存凭据与生效来源(env > keychain > file),不含 secret
npm run wanweave -- auth rotate --organization <org> --workspace <ws> # 轮换 secret(保留 createdAt)
npm run wanweave -- auth logout --organization <org> --workspace <ws> # 删除;--all 清空
# 机器身份(免浏览器、只读)三种来源:env(优先级最高)/ keychain / ~/.wanweave/credentials.json
# export WANWEAVE_ACCESS_KEY_ID=ak_... WANWEAVE_ACCESS_KEY_SECRET=...
npm run wanweave -- capability list # 无机器身份时起浏览器登录(浏览器 OIDC 公共客户端 + PKCE + loopback 8976/8977;OIDC client id 仍是协议标识 `harnessctl`,属独立迁移切片)
npm run wanweave -- capability list --offline # 离线:只读本地能力映射表 + profile,不换 token、不发请求、没有 profile 也能答
npm run wanweave -- doctor # 结构化自诊断(环境 / profile / 认证路径 / 入口可达性;默认不联网,--probe 才探测)
npm run wanweave -- access-scopes
npm run wanweave -- get RoleBinding [name] [-o yaml|json] [--limit 25 | --version v]
npm run wanweave -- audit list [-o yaml|json] [--limit 25] [--cursor <cursor>] [--code <code>] [--resource-kind <kind>] [--resource-name <name>] [--subject-type <type>]
npm run wanweave -- policy snapshot [-o yaml|json]
npm run wanweave -- catalog list --view entitled [-o yaml|json] [--limit 25]
npm run wanweave -- workspace list [-o yaml|json] [--limit 25]
npm run wanweave -- plan -f declaration.yaml
npm run wanweave -- diff -f declaration.yaml
npm run wanweave -- apply -f declaration.yaml # 机器/AI 写通道(B1):plan → apply,先取服务端计划摘要
npm run wanweave -- authorization propose -f proposal.yaml # 机器/AI 提议(不改变状态;裁决必须是人)
npm run wanweave -- authorization apply -f apply.yaml # 机器/AI 执行绑定自己、由人裁决签发的一次性 Grant
npm run wanweave -- authorization status <proposalId> # 机器/AI 读回自己的提议进展(只读)可见即可调(红线 23)
网关 GET /gateway/v1/capabilities 广告的每个只读能力都有可达的 wanweave 子命令,机器可检对拍见 tests/capability-parity.test.ts。能力 id 的唯一事实源是 platform-gateway/src/capabilities.ts(测试按 Workspace 布局只读定位,本仓不复制第二份能力表):
| 网关能力 | wanweave 子命令 | 出口 |
|---|---|---|
| access-scopes.read | access-scopes | 网关 GET /gateway/v1/access-scopes |
| policy.read | policy snapshot | 断言面 GET /control/v1/assertion/policy-snapshot |
| resource.list | get <Kind> | 断言面 GET /control/v1/assertion/resources/{kind} |
| resource.get | get <Kind> [name] | 断言面 GET /control/v1/assertion/resources/{kind}/{name} |
| audit.read | audit list | 断言面 GET /control/v1/assertion/audit |
| workspace.list | workspace list | 网关 GET /gateway/v1/workspaces |
另有断言面的能力目录投影 catalog list --view entitled(网关不广告该能力,故不参与上面的对拍)。写通道按 B1 裁决(2026-09-19)开放:B1 裁决(2026-09-19):人只做决策者,AI/机器是执行单元——低风险写由机器凭据直接执行(RBAC + Plan-Digest + 幂等 + 机器主体审计),达到/高于风险下限的仍需人类裁决签发的一次性 Grant,且执行仍由发起它的机器完成。
过滤器边界(旧缺口已修复):audit list 的 --outcome/--action/--request-id/--resource/--from/--until 与断言面 GET /control/v1/assertion/audit 的 canonical 过滤器同名同义,
语义与会话面 /control/v1/audit 一致(from 含、until 不含,action/requestId 精确,resource 字面子串)。
spec 16(契约 0.8.0)另加四个精确过滤器:--code(如 apply_out_of_grant)、--resource-kind(结构化 kind,如 Workspace)、--resource-name、--subject-type(human|service|agent|access-key|local-admin|unauthenticated,仅审计标注)。
租户 scope 仍只来自 Workspace Token 与 profile,请求侧不接受任何 organization/workspace/environment 参数;未知参数与非法取值一律拒绝(非法取值在换 token 前就失败)。
CLI 的过滤器集合由 tests/capability-parity.test.ts 与 api/control-plane.openapi.json 的断言面参数对拍(历史缺口记录在内部归档中保留,未经公开;现行结论以本节为准)。
机器身份模式(AccessKey → STS,免浏览器)
凭据来源优先级固定为 env > keychain > file:WANWEAVE_ACCESS_KEY_ID/WANWEAVE_ACCESS_KEY_SECRET(CI/无人值守)>
macOS 钥匙串(auth login --store keychain)> ~/.wanweave/credentials.json(0600,多工作区多 profile,键 = <organizationId>/<workspaceId>)。
只给一个 env 变量是配置错误:明确报错,绝不回退到其它来源或浏览器登录。
当两个 env 变量同时存在时,wanweave 直接用 AccessKey 调
POST /control/v1/service-principals:access-key-exchange(请求体只有 {accessKeyId, secret})换取同一枚 TTL ≤300s 的
Workspace Token,后续请求与浏览器路径完全一致(Authorization: Bearer <token>):
- 不起浏览器、不开 loopback 端口、不写任何凭据到磁盘:secret 只从环境变量读进内存,只用于这一次 POST; token 只活在单次命令内存里。
- 机器主体按 RoleBinding/CredentialPolicy 收敛:上表只读子命令可用,
apply在授权范围内可写(低风险直写;需要裁决的服务端会拒绝) (不换 token、不发请求)。 - 两个变量只给一个 → 退出码 1 的明确报错,绝不静默回退浏览器登录;两个都不给 → 保持浏览器 OIDC 路径不变。
- 可见性:stderr 打一行
machine identity <accessKeyId>(只有标识符,绝不含 secret);profile show的machineIdentity字段显示enabled/ 来源环境变量variables,配错时显示incomplete与missing。 - 失败映射(退出码仍是 0/1/2):
401(未知 key、错 secret、已吊销、已过期)→ 找签发该 AccessKey 的平台管理员 确认 ServicePrincipal 状态并用新凭证轮换;422→ 检查两个变量是否为完整原值(ak_+ 26 位 / 43 位 base64url);429→ 按retry-after退避,不要忙等;网络失败保持既有风格。
秘密边界:secret 与 token 都不进 stdout/stderr/错误消息/异常堆栈,也不落盘(profile 仍只存环境与地址); 机器模式不改变既有禁止事项——不要向用户索要 GitLab/Jira 凭据。
AI Agent 用法
本仓 skills/ 与 CLI 同仓同提交(决策台账 A-11):agent 的入口是 skills/harness-capabilities/SKILL.md——入口只给命令索引与全局约束(凭据优先级、安全边界、禁止事项),每条命令的参数与边界在 skills/harness-capabilities/references/ 按域按需加载。命令集合由 tests/capability-skill-parity.test.ts 与 CLI 帮助面、网关广告能力三方对拍(A-12),skill 不手写第二份命令事实。
推荐流程(agent):
- 读 skill 找命令与参数,不凭记忆拼命令;
- 只读优先:
capability list/access-scopes/get/audit list/policy snapshot/catalog list/workspace list;需要判断影响时用plan/diff出计划; - 需要写时:CLI 直接执行(
plan→apply,服务端按风险分级判定)。B1 裁决(2026-09-19):人只做决策者,AI/机器是执行单元——低风险写由机器凭据直接执行(RBAC + Plan-Digest + 幂等 + 机器主体审计),达到/高于风险下限的仍需人类裁决签发的一次性 Grant,且执行仍由发起它的机器完成。 仍不向用户索要 GitLab/Jira 凭据。
命令面扩容触发器(已触发,已拆分):阈值写死并由 tests/capability-skill-parity.test.ts 守护——命令条目 ≤ 16 且顶层域 ≤ 12;B1/S3 新增 authorization 域、B1/S4 新增 authorization decide 后为当前 21 条 / 14 域,两个阈值都已越过,因此入口文件 skills/harness-capabilities/SKILL.md 已拆成 references/(按域一文件:auth / profile / capability / control-read / declarative / diagnostics / errors),入口文件只留命令索引与全局约束。
AI-friendly 约定:结构化输出(默认 JSON,-o json|yaml);默认安全(未知参数拒绝、catalog 必须显式 --view entitled、audit 过滤器非法取值在换 token 前拒绝、写通道默认拒绝);错误可分级(stderr 首行 wanweave:error code=<code> retry=<no|yes|backoff|relogin>:usage 参数错 / token_expired 重新登录 / unauthenticated 凭证无效 / forbidden 权限不足 / not_found / conflict / validation_failed / rate_limited 退避 / server_error 与 network 可有限重试;退出码仍是 0/1/2,2 = 入口/用法);终端输出脱敏(Workspace Token 只在单次命令内存里,只走 Authorization 头,不落盘、不进 URL、不进错误与终端输出);机器身份可选(WANWEAVE_ACCESS_KEY_ID + WANWEAVE_ACCESS_KEY_SECRET 同时存在时免浏览器,只读;只给一个是配置错误,不静默回退浏览器)。
唯一运行时依赖是 yaml(声明式文档解析);配置位于 ~/.wanweave/config.json(目录 0700 / 文件 0600,WANWEAVE_HOME 可覆盖),只存环境与地址,不存 token/refresh/私钥;凭据(AccessKey)在 ~/.wanweave/credentials.json(0600)或钥匙串,auth 的 secret 只从 TTY 隐藏输入或 --stdin 进入内存、不进 argv;Workspace Token 只在单次命令的内存里,TTL ≤300s,且只走 Authorization 头。
当前组合状态和人工交接以 Workspace 根 README 与当前 spec 为准;本仓 当前日志 记录本次审计。已完成实施 spec/log 位于各自 .archived/,保留历史,不覆盖现行契约。
本仓边界检查:python3 ../platform-testkit/scripts/check_boundaries.py --module platform-developer-tools(cwd 本仓)。这只验证所有权、声明及证据引用,不能代替业务验收。module.json 指向历史依据;架构正文由 platform-contracts 唯一维护,UI 合同由 Workspace 根唯一维护。
