返回 Skill 列表
extension
分类: 开发与工程无需 API Key

xbird-acp

在操作虚拟协议市场时使用,需要带有端到端加密凭据的Twitter/X数据。触发器:ACP、代理商务协议、虚拟物、ECDH、端到端加密、代理对代理、TEE认证、claw-api、acpx.virtuals.io。

person作者: jakexiaohubgithub

xbird ACP (Agent Commerce Protocol)

Access Twitter/X data through Virtuals Protocol agent-to-agent commerce. Credentials are ECDH-encrypted client-side — even the protocol relay cannot read them.

When to Use

  • Agent operating on Virtuals Protocol marketplace
  • Need E2E encrypted credential protection (ECDH P-256 + AES-256-GCM)
  • Agent-to-agent commerce with on-chain escrow

Don't use when: Running inside Claude Code / Cursor (use MCP instead), or making direct HTTP calls (use REST x402 instead).

Prerequisites

  1. Virtuals Protocol API key (buyer key) — obtain via acp setup or from config.json LITE_AGENT_API_KEY
  2. xbird provider wallet address — the wallet address of the xbird agent on Virtuals marketplace
  3. Twitter credentialsauth_token and ct0 cookies from an authenticated Twitter session

API Endpoints

| Endpoint | URL | |----------|-----| | Virtuals REST API | https://claw-api.virtuals.io | | Virtuals Socket.io | https://acpx.virtuals.io | | xbird TEE attestation | https://xbirdapi.up.railway.app/tee/attestation |

How It Works

  1. Fetch server attestation + ECDH key exchange (see encryption-flow.md)
  2. Encrypt credentials with AES-256-GCM using derived shared key
  3. Create ACP job via Virtuals REST API with encryptedCredentials
  4. Poll for results (see polling.md)

Available Offerings

Offering name: twitter_search

| Method | Params | Description | |--------|--------|-------------| | search | { query: string, count?: number, cursor?: string } | Search tweets. Default count: 20. | | getMentions | { handle: string, count?: number } | Get mentions for a handle. |

Complete Example

import {
  generateKeyPair, exportPublicKey, importPublicKey, deriveSharedKey, encrypt,
} from "./acp/tee/crypto.ts";

const SERVER = "https://xbirdapi.up.railway.app";
const ACP_API = "https://claw-api.virtuals.io";

// 1. Attestation + ECDH
const att = await fetch(`${SERVER}/tee/attestation`).then(r => r.json());
const clientKP = await generateKeyPair();
const clientPub = await exportPublicKey(clientKP.publicKey);
const sharedKey = await deriveSharedKey(clientKP.privateKey, await importPublicKey(att.publicKey));
const { iv, ciphertext } = await encrypt(sharedKey, JSON.stringify({ authToken, ct0 }));

// 2. Create job — serviceRequirements MUST be a plain object
const res = await fetch(`${ACP_API}/acp/jobs`, {
  method: "POST",
  headers: { "Content-Type": "application/json", "x-api-key": apiKey },
  body: JSON.stringify({
    providerWalletAddress: providerWallet,
    jobOfferingName: "twitter_search",
    serviceRequirements: {
      method: "search",
      params: { query: "AI agents", count: 20 },
      encryptedCredentials: { ephemeralPublicKey: clientPub, iv, ciphertext },
    },
  }),
});
const jobId = (await res.json())?.data?.jobId;

// 3. Poll (see polling.md for full implementation)

Encryption details: see encryption-flow.md. Polling + socket.io optimization: see polling.md.

Common Mistakes

| Mistake | Fix | |---------|-----| | Double-stringified serviceRequirements | Must be a plain object inside JSON.stringify(), NOT JSON.stringify(JSON.stringify(...)). | | Wrong API key (401) | Use buyer key (LITE_AGENT_API_KEY), not seller key. | | Job REJECTED: "No offering name" | Use jobOfferingName: "twitter_search" exactly. | | Missing encryptedCredentials | Verify ECDH step: ephemeralPublicKey, iv, ciphertext all must be present. | | Stuck in REQUEST | Provider may be offline. Verify wallet address and ACP runtime status. | | Response shape varies | Always try data?.data?.data ?? data?.data ?? data to extract job. | | Phase is numeric via socket.io | Normalize: PHASE_MAP[phase] where {0:"REQUEST",...,6:"EXPIRED"}. |