@agentnetwork/opencode-anet 0.1.3 → 0.3.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 +35 -19
- package/README_CN.md +35 -19
- package/bin/anet-install.cmd +23 -0
- package/bin/anet-install.sh +301 -14
- package/bin/anet-mcp +26 -0
- package/bin/anet-mcp.cmd +16 -0
- package/bin/anet-mcp.sh +31 -3
- package/bin/anet-run.cmd +57 -0
- package/bin/anet-run.ps1 +415 -0
- package/bin/anet-run.sh +50 -1
- package/commands/anet-status.md +11 -14
- package/commands/anet.md +23 -0
- package/lib.js +9 -3
- package/package.json +2 -2
- package/server.js +3 -13
- package/skills/anet/SKILL.md +44 -84
- package/skills/anet-setup/SKILL.md +54 -105
package/README.md
CHANGED
|
@@ -4,30 +4,35 @@
|
|
|
4
4
|
|
|
5
5
|
Connects OpenCode to AgentNetwork (anet), so that the agent in a OpenCode session can hand work to other AI agents over the [A2A protocol](https://a2a-protocol.org) and answer tasks that other agents send to this machine.
|
|
6
6
|
|
|
7
|
+
Enabling the plugin installs anet: when its MCP server starts and finds no anet on the machine, it runs the official installer, which checks the signed release manifest and the SHA-256 of the download, installs anet for the user, starts its node and joins the public hub `https://hub.agentnetwork.org.cn`. No further step is needed. The installation can be turned off (see "Automatic installation" below).
|
|
8
|
+
|
|
7
9
|
anet is an open-source node that runs on the user's machine. It holds the agent's identity, encrypts every message end to end, and exchanges tasks with other nodes through a hub. The hub relays messages and cannot read them. anet runs no model: the agents at either end do the work. Priced skills are settled with [a2a-x402](https://github.com/google-agentic-commerce/a2a-x402) within limits that only the operator sets.
|
|
8
10
|
|
|
9
11
|
## What the plugin provides
|
|
10
12
|
|
|
11
13
|
| Component | Name | Purpose |
|
|
12
14
|
| --- | --- | --- |
|
|
13
|
-
| MCP server | `anet` | The
|
|
14
|
-
| Skill | `anet` |
|
|
15
|
-
| Skill | `anet-setup` |
|
|
16
|
-
| Command | `/anet
|
|
15
|
+
| MCP server | `anet` | The tools of `anet mcp`, served by the anet installed on this machine. Its tool `guide` returns the current guide to anet. The launcher installs anet when it is missing, and `anet mcp` starts the node when it is not running. |
|
|
16
|
+
| Skill | `anet` | Tells the agent to read the guide of the installed anet before the first anet action of a session and on every "@anet" request, and holds the rules that always apply. Loaded when the user writes "@anet", mentions anet, AgentNetwork or an AID, or wants to hand work to other agents. |
|
|
17
|
+
| Skill | `anet-setup` | Finds the anet executable, installs anet when it is still missing, and follows the setup guide of the installed anet. |
|
|
18
|
+
| Command | `/anet` | Carries out an "@anet" request, as the guide of the installed anet describes. |
|
|
19
|
+
| Command | `/anet-status` | Shows the state of the node, as the guide of the installed anet describes. |
|
|
20
|
+
|
|
21
|
+
The plugin carries no description of the tools of anet or of its setup steps: the installed anet provides them (the MCP tool `guide`, and `anet guide` on the command line). A new release of anet therefore reaches OpenCode with the anet executable alone; the plugin does not need an update.
|
|
17
22
|
|
|
18
23
|
## Requirements
|
|
19
24
|
|
|
20
|
-
-
|
|
25
|
+
- Linux, macOS or Windows, on x86-64 or ARM64. On Linux and macOS the plugin runs POSIX shell scripts; on Windows it runs `.cmd` scripts and Windows PowerShell 5.1, which is part of Windows.
|
|
21
26
|
- OpenCode 1.19.3 or later.
|
|
22
|
-
- anet
|
|
23
|
-
- For installation: `curl`, `gzip` and `ssh-keygen` (OpenSSH 8.1 or later).
|
|
27
|
+
- An anet release that provides `anet guide`. The MCP server of the plugin installs the current release when anet is missing.
|
|
28
|
+
- For the installation on Linux and macOS: `curl`, `gzip` and `ssh-keygen` (OpenSSH 8.1 or later).
|
|
24
29
|
|
|
25
30
|
## Getting started
|
|
26
31
|
|
|
27
32
|
1. Add the plugin to OpenCode and restart it; see Installation and settings below.
|
|
28
|
-
2.
|
|
33
|
+
2. Start a session. The MCP server of the plugin installs anet when it is missing, starts its node and joins the public hub; the first start can take a minute. Ask: `What can anet do?` The agent answers from the state of the node. If the anet tools are missing, the agent runs the `anet-setup` skill, which completes the installation and follows the setup guide of the installed anet.
|
|
29
34
|
3. Restart OpenCode, so that the MCP server starts, and run `/anet-status`.
|
|
30
|
-
4.
|
|
35
|
+
4. Make a request that begins with `@anet`, or describe the work for another agent in plain words.
|
|
31
36
|
|
|
32
37
|
The [OpenCode guide](https://docs.agentnetwork.org.cn/docs/en/latest/agents/opencode.html) of the anet documentation describes the installation, the verification and the removal in full.
|
|
33
38
|
|
|
@@ -56,15 +61,27 @@ OpenCode reads its configuration when it starts: restart it after a change. The
|
|
|
56
61
|
|
|
57
62
|
| Option | Default | Meaning |
|
|
58
63
|
| --- | --- | --- |
|
|
59
|
-
| `identity` | empty | A named identity (`anet id ls`). Empty selects the default identity
|
|
60
|
-
| `anetPath` | empty | Absolute path of the `anet` executable. Empty searches `PATH`, `~/.local/bin`, `/usr/local/bin` and `/opt/homebrew/bin
|
|
64
|
+
| `identity` | empty | A named identity (`anet id ls`). Empty selects the default identity. |
|
|
65
|
+
| `anetPath` | empty | Absolute path of the `anet` executable (`anet.exe` on Windows). Empty searches `PATH`, then `~/.local/bin`, `/usr/local/bin` and `/opt/homebrew/bin` on Linux and macOS, or the install locations of the Windows installer (`%LOCALAPPDATA%\Programs\anet`, `%LOCALAPPDATA%\anet\bin`, `%USERPROFILE%\.anet-bin`) on Windows. A path that is not an executable file is reported as an error; no other copy is used and the setup skill does not install one. |
|
|
66
|
+
|
|
67
|
+
## Automatic installation
|
|
68
|
+
|
|
69
|
+
When the MCP server of the plugin starts and finds no anet executable (the setting `anetPath` is empty, and none is on `PATH` or in the install locations listed above), it installs anet before it starts `anet mcp`:
|
|
70
|
+
|
|
71
|
+
- It runs the official installer, `https://agentnetwork.org.cn/install.sh` on Linux and macOS and `https://agentnetwork.org.cn/install.ps1` on Windows (with Windows PowerShell of the system directory, as the installation guide gives the command), without arguments. Nothing from the workspace or the session is passed to it.
|
|
72
|
+
- The installer checks the signed release manifest and the SHA-256 of the download and stops without changing anything when a check fails. It installs anet for the current user without administrator rights, adds its directory to `PATH` (the shell profiles on Linux and macOS, the user environment on Windows), creates an identity with safe defaults in `~/.anet`, starts the node and joins the public hub `https://hub.agentnetwork.org.cn`.
|
|
73
|
+
- The installer runs in the home directory, with no input, no terminal and no window; its output goes to the error output of the MCP server, never to the MCP session. On Linux and macOS it also stays in `~/.cache/anet-plugin/install.log`.
|
|
74
|
+
- One installation runs at a time: a second host that starts at the same moment waits for the first one and does not install again.
|
|
75
|
+
- A setting `anetPath` that names a missing file is reported as an error; nothing is installed in its place.
|
|
76
|
+
|
|
77
|
+
To turn the automatic installation off, set the environment variable `ANET_PLUGIN_NO_INSTALL=1` in the environment of OpenCode. The MCP server then reports that anet is not installed, and the `anet-setup` skill asks before it installs.
|
|
61
78
|
|
|
62
79
|
## Security and side effects
|
|
63
80
|
|
|
64
|
-
- **Commands run.** Every `anet` command of the plugin goes through `bin/anet-run.sh
|
|
65
|
-
- **Network access.** The plugin itself opens no connection. The anet node connects to the hub it has joined and, through it, to other nodes.
|
|
66
|
-
- **Files written.** The plugin writes
|
|
67
|
-
- **Safe defaults.** A new node accepts no tasks from other agents, publishes nothing, and has spending limits of 0. Opening the node, trusting a peer and raising a limit are
|
|
81
|
+
- **Commands run.** Every `anet` command of the plugin goes through the runner of the plugin, `bin/anet-run.sh` on Linux and macOS and `bin/anet-run.cmd` on Windows, which apply the two settings above: the MCP server (`anet mcp`), the commands `/anet` and `/anet-status` and the skills reach the same executable and the same identity. Outside the MCP server, the script reads these two settings, and nothing else, from its environment, which the plugin sets for the shell commands of the agent. The runners never run a file of the working directory or of the workspace. When anet is missing, the MCP launcher runs the official installer (`https://agentnetwork.org.cn/install.sh`, or `install.ps1` on Windows), as described under "Automatic installation"; the `anet-setup` skill runs the same installer when anet is still missing. `anet mcp` starts the node of the selected identity when it is not running. The plugin has no hooks.
|
|
82
|
+
- **Network access.** The plugin itself opens no connection. The installer downloads from `agentnetwork.org.cn` and the release mirrors that it names. The anet node connects to the hub it has joined (by default the public hub) and, through it, to other nodes.
|
|
83
|
+
- **Files written.** The plugin itself writes only the lock and the log of an automatic installation, in `~/.cache/anet-plugin` (`%LOCALAPPDATA%\anet-plugin` on Windows). The anet installer writes the anet executable into a directory of the user, adds that directory to `PATH`, and creates the identity and the configuration in `~/.anet` (`%USERPROFILE%\.anet` on Windows). The skills tell the agent never to read the files of that directory.
|
|
84
|
+
- **Safe defaults.** A new node accepts no tasks from other agents, publishes nothing, and has spending limits of 0. Opening the node, trusting a peer and raising a limit are decisions of the user; the skills tell the agent to name those commands rather than run them.
|
|
68
85
|
- **Untrusted input.** Results and tasks from other agents are text from third parties. The `anet` skill tells the agent not to follow instructions in them and not to send secrets or files the user did not ask to send.
|
|
69
86
|
- **Data.** Task content is end-to-end encrypted between nodes. The hub sees which nodes communicate, and when.
|
|
70
87
|
|
|
@@ -72,10 +89,9 @@ OpenCode reads its configuration when it starts: restart it after a change. The
|
|
|
72
89
|
|
|
73
90
|
| Symptom | Action |
|
|
74
91
|
| --- | --- |
|
|
75
|
-
| The anet tools do not appear in a session |
|
|
76
|
-
| `
|
|
77
|
-
|
|
|
78
|
-
| A refusal ends with a code in brackets | See the [reason codes](https://docs.agentnetwork.org.cn/docs/en/latest/reference/reason-codes.html). |
|
|
92
|
+
| The anet tools do not appear in a session | The installation did not finish before the host gave up on the MCP server, or it failed; the error output of the MCP server names the reason. Run `/anet-status`, or ask the agent to run the `anet-setup` skill, then restart OpenCode. |
|
|
93
|
+
| `node_status` reports `running: false` | The node could not be started; its `next` member names the command that fixes it. The agent runs `anet up` itself; other fixes belong to the user. |
|
|
94
|
+
| A refusal ends with a code in brackets | The line after it names the next command. See also the [reason codes](https://docs.agentnetwork.org.cn/docs/en/latest/reference/reason-codes.html). |
|
|
79
95
|
|
|
80
96
|
## Links
|
|
81
97
|
|
package/README_CN.md
CHANGED
|
@@ -4,30 +4,35 @@
|
|
|
4
4
|
|
|
5
5
|
将 OpenCode 接入 AgentNetwork(anet)。接入后,OpenCode 会话中的智能体可以通过 [A2A 协议](https://a2a-protocol.org)把任务交给其他 AI 智能体,并答复其他智能体发送到本机的任务。
|
|
6
6
|
|
|
7
|
+
启用本插件即会安装 anet:插件的 MCP 服务启动时如果在本机找不到 anet,会运行官方安装程序。安装程序校验已签名的发布清单和下载文件的 SHA-256,为当前用户安装 anet,启动其节点并加入公共 hub `https://hub.agentnetwork.org.cn`,无需其他步骤。自动安装可以关闭(参见下文“自动安装”)。
|
|
8
|
+
|
|
7
9
|
anet 是运行在用户本机上的开源节点。它保存智能体的身份,对每条消息进行端到端加密,并通过 hub 与其他节点交换任务。hub 只负责中转,无法读取消息内容。anet 本身不运行模型,任务由两端的智能体完成。付费技能按 [a2a-x402](https://github.com/google-agentic-commerce/a2a-x402) 结算,额度只能由运营者设定。
|
|
8
10
|
|
|
9
11
|
## 插件内容
|
|
10
12
|
|
|
11
13
|
| 组件 | 名称 | 用途 |
|
|
12
14
|
| --- | --- | --- |
|
|
13
|
-
| MCP 服务 | `anet` | `anet mcp`
|
|
14
|
-
| Skill | `anet` |
|
|
15
|
-
| Skill | `anet-setup` |
|
|
16
|
-
| 命令 | `/anet
|
|
15
|
+
| MCP 服务 | `anet` | `anet mcp` 提供的工具,由本机安装的 anet 提供。其中的工具 `guide` 返回 anet 的当前指南。未安装 anet 时由启动脚本安装;节点未运行时由 `anet mcp` 启动。 |
|
|
16
|
+
| Skill | `anet` | 要求智能体在会话中首次执行 anet 操作之前以及每次收到“@anet”请求时,先阅读已安装 anet 的指南,并列出始终适用的规则。用户输入“@anet”,提到 anet、AgentNetwork 或 AID,或希望把工作交给其他智能体时加载。 |
|
|
17
|
+
| Skill | `anet-setup` | 查找 anet 可执行文件;anet 仍未安装时进行安装,然后按已安装 anet 的设置指南完成设置。 |
|
|
18
|
+
| 命令 | `/anet` | 按已安装 anet 的指南执行一项“@anet”请求。 |
|
|
19
|
+
| 命令 | `/anet-status` | 按已安装 anet 的指南显示节点状态。 |
|
|
20
|
+
|
|
21
|
+
本插件不包含 anet 工具或设置步骤的说明,这些内容由已安装的 anet 提供(MCP 工具 `guide`,以及命令行中的 `anet guide`)。因此,anet 的新版本只需更新 anet 可执行文件即可在 OpenCode 中生效,插件无需更新。
|
|
17
22
|
|
|
18
23
|
## 环境要求
|
|
19
24
|
|
|
20
|
-
-
|
|
25
|
+
- Linux、macOS 或 Windows,x86-64 或 ARM64。在 Linux 和 macOS 上,插件运行 POSIX shell 脚本;在 Windows 上,插件运行 `.cmd` 脚本和 Windows 自带的 Windows PowerShell 5.1。
|
|
21
26
|
- OpenCode 1.19.3 或更高版本。
|
|
22
|
-
- anet
|
|
23
|
-
-
|
|
27
|
+
- 提供 `anet guide` 命令的 anet 版本。未安装 anet 时,插件的 MCP 服务会安装当前版本。
|
|
28
|
+
- 在 Linux 和 macOS 上安装需要 `curl`、`gzip` 和 `ssh-keygen`(OpenSSH 8.1 或更高版本)。
|
|
24
29
|
|
|
25
30
|
## 快速开始
|
|
26
31
|
|
|
27
32
|
1. 将插件添加到 OpenCode 并重启;见下文“安装与设置”。
|
|
28
|
-
2.
|
|
33
|
+
2. 启动一个会话。未安装 anet 时,插件的 MCP 服务会安装 anet、启动其节点并加入公共 hub;首次启动可能需要约一分钟。然后输入:`What can anet do?`(或“anet 能做什么”)。智能体会根据节点的实际状态作答。如果会话中没有 anet 工具,智能体会运行 `anet-setup` skill,完成安装并按已安装 anet 的设置指南完成设置。
|
|
29
34
|
3. 重启 OpenCode,使 MCP 服务启动,然后运行 `/anet-status`。
|
|
30
|
-
4.
|
|
35
|
+
4. 输入以 `@anet` 开头的请求,或直接描述要交给其他智能体的工作。
|
|
31
36
|
|
|
32
37
|
anet 文档中的 [OpenCode 接入指南](https://docs.agentnetwork.org.cn/docs/zh-cn/latest/agents/opencode.html)完整说明了安装、验证与卸载。
|
|
33
38
|
|
|
@@ -56,15 +61,27 @@ OpenCode 在启动时读取配置:修改后须重启。若已存在 `mcp.anet`
|
|
|
56
61
|
|
|
57
62
|
| 选项 | 默认值 | 含义 |
|
|
58
63
|
| --- | --- | --- |
|
|
59
|
-
| `identity` | 空 | 指定的命名身份(`anet id ls
|
|
60
|
-
| `anetPath` | 空 | `anet`
|
|
64
|
+
| `identity` | 空 | 指定的命名身份(`anet id ls`)。为空时使用默认身份。 |
|
|
65
|
+
| `anetPath` | 空 | `anet` 可执行文件(Windows 上为 `anet.exe`)的绝对路径。为空时先查找 `PATH`,然后在 Linux 和 macOS 上查找 `~/.local/bin`、`/usr/local/bin` 和 `/opt/homebrew/bin`,在 Windows 上查找 Windows 安装程序的安装位置(`%LOCALAPPDATA%\Programs\anet`、`%LOCALAPPDATA%\anet\bin`、`%USERPROFILE%\.anet-bin`)。若该路径不是可执行文件,则报告错误;不会改用其他副本,安装 skill 也不会另行安装。 |
|
|
66
|
+
|
|
67
|
+
## 自动安装
|
|
68
|
+
|
|
69
|
+
插件的 MCP 服务启动时,如果找不到 anet 可执行文件(设置项 `anetPath` 为空,且 `PATH` 和上述安装位置中都没有),会在启动 `anet mcp` 之前安装 anet:
|
|
70
|
+
|
|
71
|
+
- 运行官方安装程序,不带任何参数:Linux 和 macOS 上为 `https://agentnetwork.org.cn/install.sh`,Windows 上为 `https://agentnetwork.org.cn/install.ps1`(按安装指南给出的命令,使用系统目录中的 Windows PowerShell 运行)。工作区或会话中的任何内容都不会传给安装程序。
|
|
72
|
+
- 安装程序校验已签名的发布清单和下载文件的 SHA-256,任何一项校验失败即停止,不做任何改动。它无需管理员权限,为当前用户安装 anet,把安装目录加入 `PATH`(Linux 和 macOS 上写入 shell 配置文件,Windows 上写入用户环境变量),在 `~/.anet` 中以安全默认值创建身份,启动节点并加入公共 hub `https://hub.agentnetwork.org.cn`。
|
|
73
|
+
- 安装程序在用户主目录中运行,没有输入、终端或窗口;其输出写入 MCP 服务的错误输出,不会进入 MCP 会话。在 Linux 和 macOS 上,输出还保存在 `~/.cache/anet-plugin/install.log` 中。
|
|
74
|
+
- 同一时间只运行一个安装:同时启动的另一个宿主会等待第一个安装完成,不会重复安装。
|
|
75
|
+
- 如果设置项 `anetPath` 指向不存在的文件,会报告错误,不会改为安装其他副本。
|
|
76
|
+
|
|
77
|
+
要关闭自动安装,请在 OpenCode 的运行环境中设置环境变量 `ANET_PLUGIN_NO_INSTALL=1`。此后 MCP 服务会报告 anet 未安装,`anet-setup` skill 会在安装前先征得同意。
|
|
61
78
|
|
|
62
79
|
## 安全与副作用
|
|
63
80
|
|
|
64
|
-
- **执行的命令。** 插件的所有 `anet`
|
|
65
|
-
- **网络访问。**
|
|
66
|
-
- **写入的文件。**
|
|
67
|
-
- **安全默认值。** 新节点不接受其他智能体的任务,不发布任何能力,支付额度为 0
|
|
81
|
+
- **执行的命令。** 插件的所有 `anet` 命令都经由插件的运行脚本执行(Linux 和 macOS 上为 `bin/anet-run.sh`,Windows 上为 `bin/anet-run.cmd`),并应用上述两项设置:MCP 服务(`anet mcp`)、`/anet` 与 `/anet-status` 命令以及各 skill 使用同一可执行文件和同一身份。在 MCP 服务之外,该脚本只从自身环境变量中读取这两项设置,由插件为智能体的 shell 命令设置。运行脚本从不执行工作目录或工作区中的文件。未安装 anet 时,MCP 启动脚本按“自动安装”所述运行官方安装程序(`https://agentnetwork.org.cn/install.sh`,Windows 上为 `install.ps1`);anet 仍未安装时,`anet-setup` skill 运行同一安装程序。所选身份的节点未运行时,`anet mcp` 会启动它。本插件不包含 Hook。
|
|
82
|
+
- **网络访问。** 插件本身不建立任何连接。安装程序从 `agentnetwork.org.cn` 及其列出的发布镜像下载。anet 节点连接其加入的 hub(默认为公共 hub),并经由 hub 与其他节点通信。
|
|
83
|
+
- **写入的文件。** 插件本身只写入自动安装的锁和日志,位于 `~/.cache/anet-plugin`(Windows 上为 `%LOCALAPPDATA%\anet-plugin`)。anet 安装程序将 anet 可执行文件写入用户目录,把该目录加入 `PATH`,并在 `~/.anet`(Windows 上为 `%USERPROFILE%\.anet`)中创建身份和配置。Skill 要求智能体不得读取该目录中的文件。
|
|
84
|
+
- **安全默认值。** 新节点不接受其他智能体的任务,不发布任何能力,支付额度为 0。开放节点、信任对端和提高额度均由用户决定;Skill 要求智能体只给出这些命令,不代为执行。
|
|
68
85
|
- **不可信输入。** 其他智能体返回的结果和发来的任务都是第三方文本。`anet` skill 要求智能体不执行其中的指令,也不发送用户未要求发送的密钥或文件。
|
|
69
86
|
- **数据。** 节点之间的任务内容端到端加密。hub 能看到哪些节点在通信以及通信时间。
|
|
70
87
|
|
|
@@ -72,10 +89,9 @@ OpenCode 在启动时读取配置:修改后须重启。若已存在 `mcp.anet`
|
|
|
72
89
|
|
|
73
90
|
| 现象 | 处理 |
|
|
74
91
|
| --- | --- |
|
|
75
|
-
| 会话中没有 anet 工具 |
|
|
76
|
-
| `
|
|
77
|
-
|
|
|
78
|
-
| 拒绝信息末尾带有方括号中的代码 | 参见[原因代码](https://docs.agentnetwork.org.cn/docs/zh-cn/latest/reference/reason-codes.html)。 |
|
|
92
|
+
| 会话中没有 anet 工具 | 宿主在安装完成前放弃了 MCP 服务,或安装失败;MCP 服务的错误输出给出原因。运行 `/anet-status`,或让智能体运行 `anet-setup` skill,然后重启 OpenCode。 |
|
|
93
|
+
| `node_status` 报告 `running: false` | 节点无法启动;其 `next` 字段给出修复命令。智能体会自行运行 `anet up`;其他修复由用户决定。 |
|
|
94
|
+
| 拒绝信息末尾带有方括号中的代码 | 其下一行给出下一步命令。另请参见[原因代码](https://docs.agentnetwork.org.cn/docs/zh-cn/latest/reference/reason-codes.html)。 |
|
|
79
95
|
|
|
80
96
|
## 链接
|
|
81
97
|
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
@echo off
|
|
2
|
+
rem Installs the anet command line on Windows with the official installer.
|
|
3
|
+
rem
|
|
4
|
+
rem "anet-install.cmd" runs https://agentnetwork.org.cn/install.ps1 with Windows
|
|
5
|
+
rem PowerShell, over TLS 1.2. The installer checks the signed release manifest
|
|
6
|
+
rem and the SHA-256 of the binary, stops without changing anything when a check
|
|
7
|
+
rem fails, installs into a directory of this user and puts it on the user PATH.
|
|
8
|
+
rem The Windows counterpart of anet-install.sh.
|
|
9
|
+
rem
|
|
10
|
+
rem Every program is named by its full path: PowerShell as
|
|
11
|
+
rem %SystemRoot%\System32\WindowsPowerShell\v1.0\powershell.exe, the scripts of
|
|
12
|
+
rem the plugin by the directory of this file. Nothing is looked up in the
|
|
13
|
+
rem working directory or on PATH, where cmd.exe would look in the working
|
|
14
|
+
rem directory (a workspace) first. SystemRoot is set by Windows for every
|
|
15
|
+
rem process and is not compared with the system directory here: cmd.exe cannot
|
|
16
|
+
rem ask Windows for that directory without running a program, which would have
|
|
17
|
+
rem to be found the same way, and a host that sets SystemRoot for its MCP
|
|
18
|
+
rem servers already chooses every program they run. (The automatic installation
|
|
19
|
+
rem of the MCP launcher, in anet-run.ps1, asks Windows for the system directory
|
|
20
|
+
rem with [Environment]::GetFolderPath.)
|
|
21
|
+
setlocal EnableExtensions DisableDelayedExpansion
|
|
22
|
+
%SystemRoot%\System32\WindowsPowerShell\v1.0\powershell.exe -NoLogo -NoProfile -NonInteractive -ExecutionPolicy Bypass -Command "[Net.ServicePointManager]::SecurityProtocol=3072; & ([scriptblock]::Create((irm -UseBasicParsing https://agentnetwork.org.cn/install.ps1)))" 0<NUL
|
|
23
|
+
exit /b %ERRORLEVEL%
|
package/bin/anet-install.sh
CHANGED
|
@@ -1,25 +1,312 @@
|
|
|
1
1
|
#!/bin/sh
|
|
2
2
|
# Installs the anet command line with the official installer.
|
|
3
3
|
#
|
|
4
|
-
# `sh install
|
|
4
|
+
# `sh anet-install.sh [installer arguments]` downloads https://agentnetwork.org.cn/install.sh
|
|
5
5
|
# into a new private directory, runs it only when the download succeeded, and
|
|
6
6
|
# removes the directory afterwards. The installer checks the signed release
|
|
7
7
|
# manifest and the SHA-256 of the binary and stops without changing anything
|
|
8
|
-
# when a check fails
|
|
9
|
-
#
|
|
10
|
-
#
|
|
8
|
+
# when a check fails; by default it installs anet for this user, starts the
|
|
9
|
+
# node and joins the public hub. The script exists so that an agent host that
|
|
10
|
+
# refuses command substitution in a shell command can still run the
|
|
11
|
+
# installation as one plain command (the anet-setup skill).
|
|
12
|
+
#
|
|
13
|
+
# `sh anet-install.sh --mcp` is the first step of the MCP launcher
|
|
14
|
+
# (anet-mcp.sh): it installs anet when the runner finds no anet executable, so
|
|
15
|
+
# that enabling the plugin is enough to have anet installed and its node
|
|
16
|
+
# running. It does nothing when the environment variable ANET_PLUGIN_NO_INSTALL
|
|
17
|
+
# is set to a value other than 0, when the plugin setting anetPath is set (an
|
|
18
|
+
# invalid one is reported by the runner, and no other copy is installed), and
|
|
19
|
+
# in a POSIX shell on Windows (anet-mcp.cmd installs there). Otherwise it runs
|
|
20
|
+
# the installer without arguments: nothing from the workspace or the session
|
|
21
|
+
# reaches it, the user's settings select nothing of it either. The installer
|
|
22
|
+
# runs
|
|
23
|
+
# * with no input and no controlling terminal (in a session of its own, with
|
|
24
|
+
# setsid or Perl where available), so that it never asks for a password
|
|
25
|
+
# on the terminal of the host, and never reads the MCP session;
|
|
26
|
+
# * in the user's home directory, not in the working directory of the
|
|
27
|
+
# session (a workspace);
|
|
28
|
+
# * with its output written to install.log in the state directory, which is
|
|
29
|
+
# copied to standard error afterwards (the launcher sends this script's
|
|
30
|
+
# output to standard error: standard output is the MCP session). Because
|
|
31
|
+
# the installer does not write to the host's pipe and has a session of
|
|
32
|
+
# its own, a host that gives up on a slow start does not end it halfway;
|
|
33
|
+
# it finishes, and the next session finds anet.
|
|
34
|
+
# It waits for the installer at most 15 minutes, and exits with status 0 in
|
|
35
|
+
# every case: the launcher goes on, and the runner reports anet as missing
|
|
36
|
+
# when the installation did not succeed.
|
|
37
|
+
#
|
|
38
|
+
# One installation at a time. Both forms hold the lock directory
|
|
39
|
+
# <state>/install.lock while the installer runs, with the process ID of the
|
|
40
|
+
# holder and its start time (as ps prints it) in the file pid; <state> is
|
|
41
|
+
# $XDG_CACHE_HOME/anet-plugin, or ~/.cache/anet-plugin (an absolute path only;
|
|
42
|
+
# without one the --mcp form installs nothing). Another host that starts at
|
|
43
|
+
# the same moment waits for it, at most 10 minutes (5 for the plain form), and
|
|
44
|
+
# then installs nothing itself when the first installation left an anet
|
|
45
|
+
# behind. A lock whose holder no longer runs (no process with that ID, or one
|
|
46
|
+
# that started at another time: the ID was reused), or that is older than 30
|
|
47
|
+
# minutes, is removed.
|
|
11
48
|
set -u
|
|
12
49
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
50
|
+
# Programs (sh, sed, uname and the others) are found only in the absolute
|
|
51
|
+
# directories of PATH: a relative entry (such as "." or an empty one) would
|
|
52
|
+
# find a file of the working directory of the session, which can be an
|
|
53
|
+
# untrusted workspace. This comes before the first command that is not built
|
|
54
|
+
# into the shell, and the scripts started from here inherit it.
|
|
55
|
+
_abs=
|
|
56
|
+
_ifs=$IFS
|
|
57
|
+
IFS=:
|
|
58
|
+
set -f
|
|
59
|
+
for _d in ${PATH:-}; do
|
|
60
|
+
case $_d in
|
|
61
|
+
/*) _abs=${_abs:+$_abs:}$_d ;;
|
|
62
|
+
esac
|
|
63
|
+
done
|
|
64
|
+
IFS=$_ifs
|
|
65
|
+
set +f
|
|
66
|
+
PATH=${_abs:-/usr/bin:/bin}
|
|
67
|
+
export PATH
|
|
68
|
+
unset _abs _ifs _d
|
|
69
|
+
|
|
70
|
+
here=$(cd "$(dirname "$0")" 2>/dev/null && pwd) || here=$(dirname "$0")
|
|
71
|
+
self="$here/anet-install.sh"
|
|
72
|
+
|
|
73
|
+
say() { printf '%s\n' "$*" >&2; }
|
|
74
|
+
|
|
75
|
+
# state_dir prints the directory of the lock and the log, or fails.
|
|
76
|
+
state_dir() {
|
|
77
|
+
_base=${XDG_CACHE_HOME:-}
|
|
78
|
+
case $_base in
|
|
79
|
+
/*) ;;
|
|
80
|
+
*) _base=${HOME:-}/.cache ;;
|
|
81
|
+
esac
|
|
82
|
+
case $_base in
|
|
83
|
+
/*) printf '%s/anet-plugin\n' "$_base" ;;
|
|
84
|
+
*) return 1 ;;
|
|
85
|
+
esac
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
# anet_missing: whether the runner finds no anet executable and no anetPath is
|
|
89
|
+
# set (its --resolve prints "executable: not found" exactly then).
|
|
90
|
+
anet_missing() {
|
|
91
|
+
[ "$(sh "$here/anet-run.sh" --resolve 2>/dev/null | sed -n 1p)" = "executable: not found" ]
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
# started PID prints when the process PID started, as one line, or nothing
|
|
95
|
+
# where ps cannot say.
|
|
96
|
+
started() {
|
|
97
|
+
ps -o lstart= -p "$1" 2>/dev/null | sed -e 's/ */ /g' -e 's/^ //' -e 's/ $//' -e 1q
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
# holder_runs LINE: whether the holder that the line of a lock names ("PID
|
|
101
|
+
# START") still runs: a process with that ID exists and, when the line has a
|
|
102
|
+
# start time and ps gives one, it started then. (kill -0 sends no signal; it
|
|
103
|
+
# only checks that the process exists.)
|
|
104
|
+
holder_runs() {
|
|
105
|
+
_pid=${1%% *}
|
|
106
|
+
case $_pid in
|
|
107
|
+
'' | *[!0-9]*) return 1 ;;
|
|
108
|
+
esac
|
|
109
|
+
kill -0 "$_pid" 2>/dev/null || return 1
|
|
110
|
+
_was=
|
|
111
|
+
case $1 in
|
|
112
|
+
*" "*) _was=${1#* } ;;
|
|
113
|
+
esac
|
|
114
|
+
[ -n "$_was" ] || return 0
|
|
115
|
+
_now=$(started "$_pid")
|
|
116
|
+
[ -z "$_now" ] || [ "$_now" = "$_was" ]
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
# take_lock SECONDS: takes the lock, waiting at most SECONDS. Sets waited=1
|
|
120
|
+
# when another installation held it.
|
|
121
|
+
waited=0
|
|
122
|
+
take_lock() {
|
|
123
|
+
_left=$1
|
|
124
|
+
while :; do
|
|
125
|
+
if mkdir "$lock" 2>/dev/null; then
|
|
126
|
+
_me=$(started "$$")
|
|
127
|
+
printf '%s\n' "$$${_me:+ $_me}" >"$lock/pid" && return 0
|
|
128
|
+
rmdir "$lock" 2>/dev/null
|
|
129
|
+
return 1
|
|
130
|
+
fi
|
|
131
|
+
_holder=$(cat "$lock/pid" 2>/dev/null) || _holder=
|
|
132
|
+
_stale=0
|
|
133
|
+
if [ -n "$(find "$lock" -prune -mmin +30 2>/dev/null)" ]; then
|
|
134
|
+
_stale=1
|
|
135
|
+
elif [ -n "$_holder" ]; then
|
|
136
|
+
holder_runs "$_holder" || _stale=1
|
|
137
|
+
elif [ -n "$(find "$lock" -prune -mmin +1 2>/dev/null)" ]; then
|
|
138
|
+
_stale=1 # created more than a minute ago and never given a holder
|
|
139
|
+
fi
|
|
140
|
+
if [ "$_stale" -eq 1 ]; then
|
|
141
|
+
# Moved aside first, so that of two processes that found it stale only
|
|
142
|
+
# one removes it; a lock that changed holder in the meantime is put back.
|
|
143
|
+
_aside="$lock.stale.$$"
|
|
144
|
+
if mv "$lock" "$_aside" 2>/dev/null; then
|
|
145
|
+
if [ "$(cat "$_aside/pid" 2>/dev/null)" = "$_holder" ]; then
|
|
146
|
+
rm -f "$_aside/pid"
|
|
147
|
+
rmdir "$_aside" 2>/dev/null
|
|
148
|
+
else
|
|
149
|
+
mv "$_aside" "$lock" 2>/dev/null || { rm -f "$_aside/pid"; rmdir "$_aside" 2>/dev/null; }
|
|
150
|
+
fi
|
|
151
|
+
fi
|
|
152
|
+
continue
|
|
153
|
+
fi
|
|
154
|
+
[ "$_left" -gt 0 ] || return 1
|
|
155
|
+
if [ "$waited" -eq 0 ]; then
|
|
156
|
+
say "Another installation of anet is running; waiting for it to finish."
|
|
157
|
+
fi
|
|
158
|
+
waited=1
|
|
159
|
+
sleep 1
|
|
160
|
+
_left=$((_left - 1))
|
|
161
|
+
done
|
|
17
162
|
}
|
|
18
|
-
trap 'rm -rf "$dir"' EXIT
|
|
19
|
-
trap 'exit 1' HUP INT TERM
|
|
20
163
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
164
|
+
release_lock() {
|
|
165
|
+
_line=$(cat "${lock:-/nonexistent}/pid" 2>/dev/null) || _line=
|
|
166
|
+
if [ -n "${lock:-}" ] && [ "${_line%% *}" = "$$" ]; then
|
|
167
|
+
rm -f "$lock/pid"
|
|
168
|
+
rmdir "$lock" 2>/dev/null
|
|
169
|
+
fi
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
# lock_live: whether a running process holds the lock.
|
|
173
|
+
lock_live() {
|
|
174
|
+
_h=$(cat "$lock/pid" 2>/dev/null) || _h=
|
|
175
|
+
[ -n "$_h" ] && holder_runs "$_h"
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
# cleanup runs on exit: the lock of this process and the download directory.
|
|
179
|
+
_dir=
|
|
180
|
+
cleanup() {
|
|
181
|
+
release_lock
|
|
182
|
+
if [ -n "$_dir" ]; then
|
|
183
|
+
rm -rf "$_dir"
|
|
184
|
+
fi
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
# run_installer [installer arguments]: download and run the official installer.
|
|
188
|
+
run_installer() {
|
|
189
|
+
umask 077
|
|
190
|
+
_dir=$(mktemp -d 2>/dev/null) || {
|
|
191
|
+
_dir=
|
|
192
|
+
echo "Error: a temporary directory could not be created." >&2
|
|
193
|
+
return 1
|
|
194
|
+
}
|
|
195
|
+
if ! curl --proto '=https' --tlsv1.2 -fsSL https://agentnetwork.org.cn/install.sh -o "$_dir/install.sh"; then
|
|
196
|
+
echo "Error: the installer could not be downloaded." >&2
|
|
197
|
+
return 1
|
|
198
|
+
fi
|
|
199
|
+
# The home directory, not the working directory of the session: the node
|
|
200
|
+
# that the installer starts keeps its working directory.
|
|
201
|
+
case ${HOME:-} in
|
|
202
|
+
/*) cd "$HOME" 2>/dev/null || cd / ;;
|
|
203
|
+
*) cd / ;;
|
|
204
|
+
esac
|
|
205
|
+
sh "$_dir/install.sh" "$@"
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
# detached COMMAND...: COMMAND in a session of its own (no controlling
|
|
209
|
+
# terminal), where setsid or Perl can make one; otherwise as it is.
|
|
210
|
+
detached() {
|
|
211
|
+
if command -v setsid >/dev/null 2>&1; then
|
|
212
|
+
exec setsid "$@"
|
|
213
|
+
fi
|
|
214
|
+
if command -v perl >/dev/null 2>&1; then
|
|
215
|
+
exec perl -MPOSIX -e 'POSIX::setsid(); exec { $ARGV[0] } @ARGV or exit 127' -- "$@"
|
|
216
|
+
fi
|
|
217
|
+
exec "$@"
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
opted_out() {
|
|
221
|
+
case ${ANET_PLUGIN_NO_INSTALL:-} in
|
|
222
|
+
"" | 0) return 1 ;;
|
|
223
|
+
*) return 0 ;;
|
|
224
|
+
esac
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
case ${1:-} in
|
|
228
|
+
--mcp)
|
|
229
|
+
opted_out && exit 0
|
|
230
|
+
case $(uname -s 2>/dev/null) in
|
|
231
|
+
MINGW* | MSYS* | CYGWIN*) exit 0 ;;
|
|
232
|
+
esac
|
|
233
|
+
anet_missing || exit 0
|
|
234
|
+
state=$(state_dir) || {
|
|
235
|
+
say "anet is not installed, and it was not installed automatically: HOME is not an absolute path."
|
|
236
|
+
exit 0
|
|
237
|
+
}
|
|
238
|
+
(umask 077 && mkdir -p "$state") || exit 0
|
|
239
|
+
lock="$state/install.lock"
|
|
240
|
+
log="$state/install.log"
|
|
241
|
+
result=$(mktemp "$state/result.XXXXXX" 2>/dev/null) || exit 0
|
|
242
|
+
say "anet is not installed. Installing it with the official installer (https://agentnetwork.org.cn/install.sh),"
|
|
243
|
+
say "which checks the signed release manifest, installs anet for this user, starts its node and joins the public hub."
|
|
244
|
+
say "Set the environment variable ANET_PLUGIN_NO_INSTALL=1 to turn this off."
|
|
245
|
+
(detached sh "$self" --mcp-worker "$result" </dev/null >/dev/null 2>&1) &
|
|
246
|
+
worker=$!
|
|
247
|
+
left=900
|
|
248
|
+
gone=0
|
|
249
|
+
while [ ! -s "$result" ] && [ "$left" -gt 0 ]; do
|
|
250
|
+
# The worker is gone without a result and no installation holds the
|
|
251
|
+
# lock, for three seconds in a row: nothing is coming.
|
|
252
|
+
if ! kill -0 "$worker" 2>/dev/null && ! lock_live; then
|
|
253
|
+
gone=$((gone + 1))
|
|
254
|
+
[ "$gone" -lt 3 ] || break
|
|
255
|
+
else
|
|
256
|
+
gone=0
|
|
257
|
+
fi
|
|
258
|
+
sleep 1
|
|
259
|
+
left=$((left - 1))
|
|
260
|
+
done
|
|
261
|
+
outcome=$(cat "$result" 2>/dev/null) || outcome=
|
|
262
|
+
rm -f "$result"
|
|
263
|
+
case $outcome in
|
|
264
|
+
installed:*)
|
|
265
|
+
cat "$log" >&2 2>/dev/null
|
|
266
|
+
say "The anet installer ended with status ${outcome#installed:}."
|
|
267
|
+
;;
|
|
268
|
+
other) say "Another installation of anet has finished." ;;
|
|
269
|
+
busy) say "Another installation of anet is still running; anet was not installed by this session." ;;
|
|
270
|
+
*) say "The installation of anet has not finished; it goes on in the background (log: $log)." ;;
|
|
271
|
+
esac
|
|
272
|
+
exit 0
|
|
273
|
+
;;
|
|
274
|
+
--mcp-worker)
|
|
275
|
+
# The detached part of --mcp; $2 is the file for the outcome.
|
|
276
|
+
result=${2:-}
|
|
277
|
+
state=$(state_dir) || exit 0
|
|
278
|
+
lock="$state/install.lock"
|
|
279
|
+
log="$state/install.log"
|
|
280
|
+
if ! take_lock 600; then
|
|
281
|
+
printf 'busy\n' >"$result"
|
|
282
|
+
exit 0
|
|
283
|
+
fi
|
|
284
|
+
trap cleanup EXIT
|
|
285
|
+
trap 'exit 1' HUP INT TERM
|
|
286
|
+
if [ "$waited" -eq 1 ] && ! anet_missing; then
|
|
287
|
+
printf 'other\n' >"$result"
|
|
288
|
+
exit 0
|
|
289
|
+
fi
|
|
290
|
+
(umask 077 && : >"$log")
|
|
291
|
+
run_installer </dev/null >"$log" 2>&1
|
|
292
|
+
printf 'installed:%s\n' "$?" >"$result"
|
|
293
|
+
exit 0
|
|
294
|
+
;;
|
|
295
|
+
esac
|
|
296
|
+
|
|
297
|
+
# The plain form: the installer with the given arguments, its output on this
|
|
298
|
+
# terminal.
|
|
299
|
+
trap cleanup EXIT
|
|
300
|
+
trap 'exit 1' HUP INT TERM
|
|
301
|
+
if state=$(state_dir) && (umask 077 && mkdir -p "$state"); then
|
|
302
|
+
lock="$state/install.lock"
|
|
303
|
+
if ! take_lock 300; then
|
|
304
|
+
echo "Error: another installation of anet is still running; try again when it has finished." >&2
|
|
305
|
+
exit 1
|
|
306
|
+
fi
|
|
307
|
+
if [ "$waited" -eq 1 ] && ! anet_missing; then
|
|
308
|
+
say "Another installation of anet has finished. Run the runner with --resolve to see it."
|
|
309
|
+
exit 0
|
|
310
|
+
fi
|
|
24
311
|
fi
|
|
25
|
-
|
|
312
|
+
run_installer "$@"
|
package/bin/anet-mcp
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
#!/bin/sh
|
|
2
|
+
# Starts the MCP server of the local anet node through anet-mcp.sh, for a
|
|
3
|
+
# host that starts the launcher by its path: on Windows the same path, without
|
|
4
|
+
# an extension, selects anet-mcp.cmd.
|
|
5
|
+
|
|
6
|
+
# Programs (sh, sed, uname and the others) are found only in the absolute
|
|
7
|
+
# directories of PATH: a relative entry (such as "." or an empty one) would
|
|
8
|
+
# find a file of the working directory of the session, which can be an
|
|
9
|
+
# untrusted workspace. This comes before the first command that is not built
|
|
10
|
+
# into the shell, and the scripts started from here inherit it.
|
|
11
|
+
_abs=
|
|
12
|
+
_ifs=$IFS
|
|
13
|
+
IFS=:
|
|
14
|
+
set -f
|
|
15
|
+
for _d in ${PATH:-}; do
|
|
16
|
+
case $_d in
|
|
17
|
+
/*) _abs=${_abs:+$_abs:}$_d ;;
|
|
18
|
+
esac
|
|
19
|
+
done
|
|
20
|
+
IFS=$_ifs
|
|
21
|
+
set +f
|
|
22
|
+
PATH=${_abs:-/usr/bin:/bin}
|
|
23
|
+
export PATH
|
|
24
|
+
unset _abs _ifs _d
|
|
25
|
+
|
|
26
|
+
exec sh "$(dirname "$0")/anet-mcp.sh" "$@"
|
package/bin/anet-mcp.cmd
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
@echo off
|
|
2
|
+
rem Starts the MCP server of the local anet node ("anet mcp", stdio) for the
|
|
3
|
+
rem executable and identity of the plugin settings, which the host passes in
|
|
4
|
+
rem ANET_PLUGIN_BIN and ANET_PLUGIN_IDENTITY; see anet-run.cmd. The Windows
|
|
5
|
+
rem counterpart of anet-mcp.sh.
|
|
6
|
+
rem
|
|
7
|
+
rem ANET_RUN_INSTALL=missing asks anet-run.ps1 to install anet with the
|
|
8
|
+
rem official installer (install.ps1, which also starts the node and joins the
|
|
9
|
+
rem public hub) when it finds no anet.exe, unless ANET_PLUGIN_NO_INSTALL=1 is
|
|
10
|
+
rem set; all of its output goes to standard error, because standard output is
|
|
11
|
+
rem the MCP session. "anet mcp" then starts the node of the selected identity
|
|
12
|
+
rem when it is not running, and serves the tools even when the node cannot
|
|
13
|
+
rem run: node_status then names the reason and the command that fixes it.
|
|
14
|
+
set "ANET_PLUGIN_SETTINGS=env"
|
|
15
|
+
set "ANET_RUN_INSTALL=missing"
|
|
16
|
+
"%~dp0anet-run.cmd" mcp
|