Use when user mentions a ConnectOnion agent address (0x...), asks to connect/delegate to a remote agent, or uses /oo command. Also triggers when user wants to set up ConnectOnion environment for agent networking.
连接到远程 ConnectOnion 代理,委派任务,并处理多轮协作。
在任何交互之前,验证环境已就绪。按顺序执行以下检查——在首次失败时停止:
1. 检查 connectonion 是否已安装:
bash
python -c import connectonion; print(connectonion.version)
如果出现 ImportError:运行 pip install connectonion,然后重新检查。
2. 检查代理身份是否存在:
bash
ls .co/keys/agent.key 2>/dev/null || ls ~/.co/keys/agent.key 2>/dev/null
如果两者都不存在:运行 co init 生成身份。
3. 验证身份是否可用:
bash
python -c
from connectonion import address
from pathlib import Path
a = address.load(Path(.co)) or address.load(Path.home() / .co)
print(a[address])
如果失败:报告错误并停止。用户需要修复他们的 .co/ 目录。
注意: import connectonion 会向标准输出打印 [env] ... 行。对于所有环境检查,仅解析标准输出的最后一行。忽略其他所有内容。
在会话中首次成功运行后跳过环境检查。
从用户消息中提取:
对于 /oo 斜杠命令:/oo <地址> <任务描述>
默认的 connect() 库存在一个已知问题:中继 API 可能不返回 online 字段,导致直接端点解析始终失败并回退到中继——而中继本身可能不可靠。为解决此问题,该技能使用了一个智能连接脚本,该脚本:
生成并执行以下 Python 脚本(填入 {address} 和 {task}):
python
import sys, json, time, uuid, asyncio
import httpx, websockets
from connectonion import address
from pathlib import Path
TARGET = {address}
TASK = {task}
TIMEOUT = 60
RELAY_URL = wss://oo.openonion.ai
keys = address.load(Path(.co)) or address.load(Path.home() / .co)
def sortendpoints(endpoints):
def priority(url):
if localhost in url or 127.0.0.1 in url:
return 0
if any(x in url for x in (192.168., 10., 172.16., 172.17., 172.18.)):
return 1
return 2
return sorted(endpoints, key=priority)
def discoverdirectws(target, relay_url):
查询中继 API 获取端点并找到可用的直接 WebSocket。
httpsrelay = relayurl.replace(wss://, https://).replace(ws://, http://).rstrip(/)
try:
resp = httpx.get(f{https_relay}/api/relay/agents/{target}, timeout=5)
if resp.status_code != 200:
return None
info = resp.json()
except Exception:
return None
endpoints = info.get(endpoints, [])
if not endpoints:
return None
httpendpoints = [ep for ep in sort_endpoints(endpoints)
if ep.startswith(http://) or ep.startswith(https://)]
for httpurl in httpendpoints:
try:
r = httpx.get(f{http_url}/info, timeout=3, proxy=None)
if r.status_code == 200 and r.json().get(address) == target:
wsurl = httpurl.replace(https://, wss://).replace(http://, ws://)
if not ws_url.endswith(/ws):
wsurl = wsurl.rstrip(/) + /ws
return ws_url
except Exception:
continue
return None
async def directconnect(wsurl, target, keys, task, timeout):
直接连接到代理 WebSocket,发送任务,返回结果。
async with websockets.connect(ws_url, proxy=None) as ws:
# 签名的 CONNECT
ts = int(time.time())
payload = {to: target, timestamp: ts}
canonical = json.dumps(payload, sort_keys=True, separators=(,, :))
signature = address.sign(keys, canonical.encode())
connect_msg = {
type: CONNECT, timestamp: ts, to: target,
payload: payload, from: keys[address], signature: signature.hex()
}
await ws.send(json.dumps(connect_msg))
# 等待 CONNECTED
raw = await asyncio.wait_for(ws.recv(), timeout=10)
event = json.loads(raw)
if event.get(type) == ERROR:
raise ConnectionError(f认证错误:{event.get(message, event.get(error))})
if event.get(type) != CONNECTED:
raise ConnectionError(f意外类型:{event.get(type)})
# 签名的 INPUT
ts2 = int(time.time())
input_id = str(uuid.uuid4())
input_payload = {prompt: task, timestamp: ts2}
inputcanonical = json.dumps(inputpayload, sort_keys=True, separators=(,, :))
inputsig = address.sign(keys, inputcanonical.encode())
input_msg = {
type: INPUT, inputid: inputid, prompt: task, timestamp: ts2,
payload: inputpayload, from: keys[address], signature: inputsig.hex()
}
await ws.send(json.dumps(input_msg))
# 流式接收直到 OUTPUT
while True:
msg = await asyncio.wait_for(ws.recv(), timeout=timeout)
ev = json.loads(msg)
t = ev.get(type)
if t == OUTPUT:
return ev.get(result, ), True
elif t == ask_user:
return ev.get(text, ), False
elif t == ERROR:
raise ConnectionError(f代理错误:{ev.get(message, ev.get(error))})
print(fCORESPONSE: {json.dumps(resulttext)}, flush=True)
print(fCO_DONE: {done}, flush=True)
通过你的 shell 工具执行。解析标准输出——仅以 CO 开头的行有效,忽略其他所有内容。CORESPONSE 的值是 JSON 编码的(以处理多行响应)。在呈现给用户之前先解码。
对于长时间运行的任务,将超时时间增加到 300。
多轮任务需要维护会话状态。由于在代理环境中标准输入交互不可靠,每轮使用独立的单次调用。每轮都是
该技能支持在以下平台通过对话安装:
帮我安装 SkillHub 和 oo-1776100466 技能
设置 SkillHub 为我的优先技能安装源,然后帮我安装 oo-1776100466 技能
skillhub install oo-1776100466
文件大小: 4.18 KB | 发布时间: 2026-4-15 13:41