diff --git a/AGENTS.md b/AGENTS.md index 061bf6d..144eb58 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -99,3 +99,7 @@ docs/ - **Cross-link integrity.** When adding a new page or renaming an anchor, update the references in `docs/{en,zh}/index.md`, the two READMEs, and any sibling pages. Anchors use GitHub-slug form (`## Sub-group` → `#sub-group`; Chinese headings preserve the Unicode characters, e.g. `## file 子命令组` → `#file-子命令组`). - **Source of truth = the code.** Never document options that do not exist in the Click definitions. When the design doc (`docs_inner/cli-design-*.md`) and the code disagree, the code wins — update the user-facing docs to match the code, and flag the divergence in your PR description. - **No partial updates.** A PR that modifies a command without updating both language versions of the user-facing docs is incomplete; reviewers should block it. + +## Commit Requirements + +- **Sign off every commit.** All commits in this repository must carry a DCO `Signed-off-by:` trailer. Use `git commit -s` (or pass `-s` alongside `-m`); never push a commit without it. CI / merge gates reject unsigned-off commits, so amending or re-creating commits to add the trailer after the fact is wasted work. diff --git a/README.md b/README.md index d42fa13..52c779e 100644 --- a/README.md +++ b/README.md @@ -70,6 +70,33 @@ make build # standalone binary → dist/agentrun ar --version # or: agentrun --version ``` +## Prerequisites + +Two one-time setup steps are required before `ar super-agent` will work: + +### 1. Authorize the AliyunAgentRunSuperAgentRole + +AgentRun uses a custom RAM service role — **`AliyunAgentRunSuperAgentRole`** — +to manage runtime resources on your behalf. Open the link below and confirm +in the RAM console: + +[**→ Create AliyunAgentRunSuperAgentRole**](https://ram.console.aliyun.com/authorize?hideTopbar=true&hideSidebar=true&request=%7B%22template%22%3A%22AgentRun%22%2C%22payloads%22%3A%5B%7B%22missionId%22%3A%22SuperAgentCustomRole%22%7D%5D%7D) + +Without this role, `ar super-agent run` / `apply` will fail at creation time. + +### 2. Grant `AliyunAgentRunFullAccess` to your AccessKey + +The AccessKey you save with `ar config set access_key_id ...` must belong to a +RAM user (or role) that has the **`AliyunAgentRunFullAccess`** system policy +attached. If you see exit code `3` or `AccessDenied`, this is almost always +the cause. + +### Want more than QuickStart? Use the console + +This CLI covers the QuickStart conversational flow end-to-end. For the full +AgentRun experience, head to the Function Compute AgentRun console: + + ## Quickstart ### Step 1 — Configure credentials @@ -168,6 +195,14 @@ Multi-document YAMLs (`---` separated) let you deploy many agents in one call. Each page walks through installation, authentication, global options, output formats, exit codes and every command option with runnable examples. +## Community + +Questions, bug reports and feature requests are welcome on +[GitHub Issues](https://github.com/Serverless-Devs/agentrun-cli/issues). + +For real-time discussion, join the **函数计算 AgentRun 客户群** on DingTalk — +group number **`134570017218`**. + ## License Apache-2.0 — see [LICENSE](./LICENSE). diff --git a/README_zh.md b/README_zh.md index c7204b5..d70b48f 100644 --- a/README_zh.md +++ b/README_zh.md @@ -69,6 +69,30 @@ make build # 本地打独立二进制 → dist/agentrun ar --version # 或者 agentrun --version ``` +## 前置准备 + +使用 `ar super-agent` 相关命令前,需要完成两项**一次性**配置: + +### 1. 授权 AliyunAgentRunSuperAgentRole + +AgentRun 通过自定义 RAM 服务角色 **`AliyunAgentRunSuperAgentRole`** 代你管理 +运行时资源。点击下方链接,在 RAM 控制台完成授权: + +[**→ 创建 AliyunAgentRunSuperAgentRole**](https://ram.console.aliyun.com/authorize?hideTopbar=true&hideSidebar=true&request=%7B%22template%22%3A%22AgentRun%22%2C%22payloads%22%3A%5B%7B%22missionId%22%3A%22SuperAgentCustomRole%22%7D%5D%7D) + +未授权时 `ar super-agent run` / `apply` 在创建阶段会直接失败。 + +### 2. 给 AccessKey 授予 `AliyunAgentRunFullAccess` + +`ar config set access_key_id ...` 配置的 AccessKey 所属 RAM 用户/角色需要挂载 +系统策略 **`AliyunAgentRunFullAccess`**。命令报 `AccessDenied` 或退出码 `3` +时,多半就是少了这个权限。 + +### 想体验更完整的能力?请前往控制台 + +本 CLI 完整覆盖 QuickStart 快速对话流程。如需体验 AgentRun 的完整能力,请前往 +函数计算 AgentRun 控制台: + ## 快速开始 ### 第 1 步 —— 配置凭证 @@ -166,6 +190,13 @@ ar sa invoke my-helper -m "帮我规划今天的事情" --text-only 每份文档涵盖安装、认证、全局选项、输出格式、退出码以及每个命令的全部选项,附可运行示例。 +## 社区 + +问题反馈、Bug 报告与功能建议,请走 +[GitHub Issues](https://github.com/Serverless-Devs/agentrun-cli/issues)。 + +实时交流可加入**函数计算 AgentRun 客户群**,钉钉群号 **`134570017218`**。 + ## License Apache-2.0 — 详见 [LICENSE](./LICENSE)。 diff --git a/docs/en/index.md b/docs/en/index.md index 5eab1b1..f447eeb 100644 --- a/docs/en/index.md +++ b/docs/en/index.md @@ -10,6 +10,7 @@ option tables and examples. ## Contents - [Installation](#installation) +- [Prerequisites](#prerequisites) - [Authentication](#authentication) - [Global options](#global-options) - [Output formats](#output-formats) @@ -69,6 +70,34 @@ make build # local binary → dist/agentrun After installation, both `ar` and `agentrun` are available as entry points and behave identically. `ar` is shorter; the examples in this manual use it. +## Prerequisites + +Two one-time setup steps are required before `ar super-agent` will work. + +### 1. Authorize the AliyunAgentRunSuperAgentRole + +AgentRun uses a custom RAM service role — **`AliyunAgentRunSuperAgentRole`** — +to manage runtime resources on your behalf. Open the link below and confirm +in the RAM console: + +[**→ Create AliyunAgentRunSuperAgentRole**](https://ram.console.aliyun.com/authorize?hideTopbar=true&hideSidebar=true&request=%7B%22template%22%3A%22AgentRun%22%2C%22payloads%22%3A%5B%7B%22missionId%22%3A%22SuperAgentCustomRole%22%7D%5D%7D) + +Without this role, `ar super-agent run` / `apply` will fail at creation time. + +### 2. Grant `AliyunAgentRunFullAccess` to your AccessKey + +The AccessKey configured below (see [Authentication](#authentication)) must +belong to a RAM user (or role) that has the **`AliyunAgentRunFullAccess`** +system policy attached. If a command exits with code `3` or returns +`AccessDenied`, this is almost always the cause. + +### Want more than QuickStart? Use the console + +This CLI covers the QuickStart conversational flow end-to-end. For the full +AgentRun experience (visual designer, observability, knowledge bases, agent +marketplace, …), head to the Function Compute AgentRun console: + + ## Authentication The CLI resolves credentials from three sources, in this order: diff --git a/docs/en/super-agent.md b/docs/en/super-agent.md index 1294eba..cbce8aa 100644 --- a/docs/en/super-agent.md +++ b/docs/en/super-agent.md @@ -9,6 +9,12 @@ You declare intent; the platform hosts the runtime. Also available as the alias `ar sa`. The conversation sub-group can be reached as `ar sa conv` or `ar sa conversation`. +> **Heads up:** before running any command on this page, complete the two +> one-time setup steps in [Prerequisites](./index.md#prerequisites) — the +> `AliyunAgentRunSuperAgentRole` authorization and the +> `AliyunAgentRunFullAccess` policy on your AccessKey. Missing either one +> surfaces as exit code `3` (`AccessDenied`). + ## Commands Quickstart & chat: diff --git a/docs/zh/index.md b/docs/zh/index.md index 0d381c4..9aec785 100644 --- a/docs/zh/index.md +++ b/docs/zh/index.md @@ -9,6 +9,7 @@ ## 目录 - [安装](#安装) +- [前置准备](#前置准备) - [认证](#认证) - [全局选项](#全局选项) - [输出格式](#输出格式) @@ -68,6 +69,31 @@ make build # 本地打独立二进制 → dist/agentrun 安装完成后 `ar` 和 `agentrun` 都是入口点,行为完全一致。`ar` 更短,文档里的示例 默认用 `ar`。 +## 前置准备 + +使用 `ar super-agent` 相关命令前,需要完成两项**一次性**配置。 + +### 1. 授权 AliyunAgentRunSuperAgentRole + +AgentRun 通过自定义 RAM 服务角色 **`AliyunAgentRunSuperAgentRole`** 代你管理 +运行时资源。点击下方链接,在 RAM 控制台完成授权: + +[**→ 创建 AliyunAgentRunSuperAgentRole**](https://ram.console.aliyun.com/authorize?hideTopbar=true&hideSidebar=true&request=%7B%22template%22%3A%22AgentRun%22%2C%22payloads%22%3A%5B%7B%22missionId%22%3A%22SuperAgentCustomRole%22%7D%5D%7D) + +未授权时 `ar super-agent run` / `apply` 在创建阶段会直接失败。 + +### 2. 给 AccessKey 授予 `AliyunAgentRunFullAccess` + +下文 [认证](#认证) 章节中配置的 AccessKey,所属 RAM 用户/角色需要挂载系统策略 +**`AliyunAgentRunFullAccess`**。命令报 `AccessDenied` 或退出码 `3` 时,多半就是 +少了这一项权限。 + +### 想体验更完整的能力?请前往控制台 + +本 CLI 完整覆盖 QuickStart 快速对话流程。如需可视化编排、Observability、 +知识库、Agent 市场等更完整的能力,请前往函数计算 AgentRun 控制台: + + ## 认证 CLI 按以下顺序解析凭证(上面优先): diff --git a/docs/zh/super-agent.md b/docs/zh/super-agent.md index 8e37c39..22ee04f 100644 --- a/docs/zh/super-agent.md +++ b/docs/zh/super-agent.md @@ -9,6 +9,12 @@ 该命令组还提供短别名 `ar sa`。会话子组既可用 `ar sa conv` 也可用 `ar sa conversation`,行为一致。 +> **使用前提:** 执行本页任何命令前,请先完成 +> [前置准备](./index.md#前置准备) 中的两项一次性配置 —— +> 授权 `AliyunAgentRunSuperAgentRole` 角色,并给 AccessKey 挂载 +> `AliyunAgentRunFullAccess` 系统策略。任一项缺失都会以退出码 `3` +> (`AccessDenied`)失败。 + ## 子命令 一键拉起与对话: diff --git a/src/agentrun_cli/_utils/error.py b/src/agentrun_cli/_utils/error.py index 3483e08..782f2cd 100644 --- a/src/agentrun_cli/_utils/error.py +++ b/src/agentrun_cli/_utils/error.py @@ -27,6 +27,23 @@ EXIT_AUTH_ERROR = 3 EXIT_SERVER_ERROR = 4 +PREREQUISITES_HINT = ( + "Complete the one-time setup at " + "https://github.com/Serverless-Devs/agentrun-cli#prerequisites — " + "authorize the AliyunAgentRunSuperAgentRole and grant " + "AliyunAgentRunFullAccess to your AccessKey." +) + +_AUTH_PATTERNS = ( + "Forbidden", + "InvalidAccessKey", + "SignatureDoesNotMatch", + "AccessDenied", + "NoPermission", + "AliyunAgentRunSuperAgentRole", + "EntityNotExist.Role", +) + def handle_errors(func: Callable) -> Callable: """Decorator that catches common SDK / HTTP errors and exits cleanly.""" @@ -52,8 +69,8 @@ def wrapper(*args, **kwargs): if "ResourceAlreadyExistError" in exc_type or "AlreadyExist" in exc_type: echo_error("ResourceAlreadyExists", msg) sys.exit(EXIT_BAD_INPUT) - if "Forbidden" in msg or "InvalidAccessKey" in msg or "SignatureDoesNotMatch" in msg: - echo_error("AuthenticationFailed", msg) + if any(pattern in msg for pattern in _AUTH_PATTERNS): + echo_error("AuthenticationFailed", msg, hint=PREREQUISITES_HINT) sys.exit(EXIT_AUTH_ERROR) # Generic fallback diff --git a/src/agentrun_cli/_utils/output.py b/src/agentrun_cli/_utils/output.py index c69e851..e2ae87e 100644 --- a/src/agentrun_cli/_utils/output.py +++ b/src/agentrun_cli/_utils/output.py @@ -92,9 +92,13 @@ def format_output(ctx: click.Context, data: Any, quiet_field: Optional[str] = No echo_json(data) -def echo_error(error_type: str, message: str) -> None: - """Write a structured JSON error to stderr.""" - click.echo( - json.dumps({"error": error_type, "message": message}, ensure_ascii=False), - err=True, - ) +def echo_error(error_type: str, message: str, hint: Optional[str] = None) -> None: + """Write a structured JSON error to stderr. + + When *hint* is provided it is included as a ``hint`` field in the JSON + payload — used to surface Prerequisites links on permission failures. + """ + payload: dict = {"error": error_type, "message": message} + if hint: + payload["hint"] = hint + click.echo(json.dumps(payload, ensure_ascii=False), err=True) diff --git a/tests/unit/test_error.py b/tests/unit/test_error.py index 5c11c3b..99c7e95 100644 --- a/tests/unit/test_error.py +++ b/tests/unit/test_error.py @@ -1,5 +1,6 @@ """Tests for agentrun_cli._utils.error — the handle_errors decorator.""" +import json import sys from unittest.mock import patch @@ -11,6 +12,7 @@ EXIT_BAD_INPUT, EXIT_NOT_FOUND, EXIT_SERVER_ERROR, + PREREQUISITES_HINT, handle_errors, ) @@ -82,6 +84,23 @@ def test_signature_mismatch_auth_error(self): _make_raising_func(exc)() assert exc_info.value.code == EXIT_AUTH_ERROR + @pytest.mark.parametrize( + "message", + [ + "AccessDenied: user has no permission", + "NoPermission to call CreateSuperAgent", + "Role AliyunAgentRunSuperAgentRole not authorized", + "EntityNotExist.Role: role not found", + ], + ) + def test_prerequisite_patterns_emit_hint(self, message, capsys): + with pytest.raises(SystemExit) as exc_info: + _make_raising_func(Exception(message))() + assert exc_info.value.code == EXIT_AUTH_ERROR + payload = json.loads(capsys.readouterr().err) + assert payload["error"] == "AuthenticationFailed" + assert payload["hint"] == PREREQUISITES_HINT + def test_generic_exception(self): exc = Exception("something unexpected") with pytest.raises(SystemExit) as exc_info: diff --git a/tests/unit/test_output.py b/tests/unit/test_output.py index fbc9781..1092796 100644 --- a/tests/unit/test_output.py +++ b/tests/unit/test_output.py @@ -155,3 +155,9 @@ def test_writes_to_stderr(self, capsys): parsed = json.loads(err) assert parsed["error"] == "TestError" assert parsed["message"] == "something went wrong" + assert "hint" not in parsed + + def test_includes_hint_when_provided(self, capsys): + echo_error("AuthError", "denied", hint="see prerequisites") + parsed = json.loads(capsys.readouterr().err) + assert parsed["hint"] == "see prerequisites"