diff --git a/create-xpr-agent/package.json b/create-xpr-agent/package.json index 63d7174..503fe16 100644 --- a/create-xpr-agent/package.json +++ b/create-xpr-agent/package.json @@ -1,6 +1,6 @@ { "name": "create-xpr-agent", - "version": "0.5.0", + "version": "0.5.1", "description": "Create an autonomous AI agent on XPR Network", "type": "module", "bin": "./create.mjs", diff --git a/create-xpr-agent/template/.env.example b/create-xpr-agent/template/.env.example index 479926d..87d555e 100644 --- a/create-xpr-agent/template/.env.example +++ b/create-xpr-agent/template/.env.example @@ -112,8 +112,10 @@ AGENT_SKILLS= # Enable built-in poller (checks chain state, no indexer needed) POLL_ENABLED=true -# Poll interval in seconds (default: 30) -POLL_INTERVAL=30 +# Poll interval in seconds (default: 60) +# Tune lower for snappier job pickup, higher to be gentler on shared RPC. +# The agent runner accepts values as low as 5s. +POLL_INTERVAL=60 # ── Telegram Bridge ────────────────────────── # Bot token from @BotFather. Leave empty to disable. diff --git a/create-xpr-agent/template/README.md b/create-xpr-agent/template/README.md index f47568e..d65fc4b 100644 --- a/create-xpr-agent/template/README.md +++ b/create-xpr-agent/template/README.md @@ -169,12 +169,14 @@ All configuration is stored in `.env` (auto-generated by `start.sh`). **There is | Variable | Required | Default | Description | |----------|----------|---------|-------------| -| `XPR_ACCOUNT` | Yes | — | Agent account name (proton CLI must have its key loaded) | +| `XPR_ACCOUNT` | Yes | — | Agent account name (proton CLI must have its key loaded). Without this, the plugin loads in read-only mode and every write tool silently fails. | | `ANTHROPIC_API_KEY` | Yes | — | Anthropic API key | +| `XPR_NETWORK` | No | `mainnet` | Network (`mainnet` or `testnet`) | +| `XPR_RPC_ENDPOINT` | No | auto from network | Chain RPC endpoint — leave unset to auto-select | +| `INDEXER_URL` | No | `https://indexer.xpragents.com` | Public XPR Agents indexer. 4 read tools depend on this. Override only if you run your own. | +| `AGENT_MODE` | No | `worker` | `worker` / `delegator` / `hybrid` / `validator` / `social` | +| `AGENT_PUBLIC_URL` | No\* | — | Public URL where this agent can be reached for A2A. **\*Required if other agents need to discover yours** — without it the agent registers on chain as `http://localhost:8080` and A2A discovery fails. | | `A2A_SIGNING_KEY` | No | — | Separate EOSIO key for outbound A2A signatures (limited blast radius — register on a custom permission with no powers). If unset, A2A runs receive-only. | -| `XPR_NETWORK` | No | `testnet` | Network (testnet/mainnet) | -| `XPR_RPC_ENDPOINT` | No | testnet RPC | Chain RPC endpoint | -| `HYPERION_ENDPOINTS` | No | testnet Hyperion | Hyperion stream endpoint | | `AGENT_MODEL` | No | `claude-sonnet-4-6` | Claude model for decisions | | `AGENT_MAX_TURNS` | No | `20` | Max tool-call turns per event | | `MAX_TRANSFER_AMOUNT` | No | `10000000` | Max XPR per transfer (smallest units, 10000000 = 1000 XPR) | @@ -186,7 +188,7 @@ All configuration is stored in `.env` (auto-generated by `start.sh`). **There is | `A2A_TOOL_MODE` | No | `full` | Tool access for A2A callers: `full` or `readonly` | | `COST_MARGIN` | No | `2.0` | Profit margin multiplier for cost estimates (2.0 = 100% markup) | | `POLL_ENABLED` | No | `true` | Enable built-in on-chain poller | -| `POLL_INTERVAL` | No | `30` | Poll interval in seconds | +| `POLL_INTERVAL` | No | `60` | Poll interval in seconds (lower = snappier, higher = gentler on RPC) | ### Cost-Aware Bidding diff --git a/create-xpr-agent/template/start.sh b/create-xpr-agent/template/start.sh index d6014a5..af17340 100755 --- a/create-xpr-agent/template/start.sh +++ b/create-xpr-agent/template/start.sh @@ -46,12 +46,21 @@ fi log "Node.js $(node -v)" # ── Parse CLI args ───────────────────────────── +# Defaults below MUST stay in sync with `.env.example` and the agent +# runner's own env-var defaults — drift causes operator surprise +# (e.g. .env says 1000 XPR cap but agent applies 100 XPR). XPR_ACCOUNT="${XPR_ACCOUNT:-}" ANTHROPIC_API_KEY="${ANTHROPIC_API_KEY:-}" -XPR_NETWORK="${XPR_NETWORK:-testnet}" +XPR_NETWORK="${XPR_NETWORK:-mainnet}" XPR_RPC_ENDPOINT="${XPR_RPC_ENDPOINT:-}" AGENT_MODEL="${AGENT_MODEL:-claude-sonnet-4-6}" -POLL_INTERVAL="${POLL_INTERVAL:-30}" +# 60s default — fast enough to feel responsive on the job board, slow +# enough not to rate-limit on shared RPC. Tune via --poll-interval or +# POLL_INTERVAL in .env (the agent runner itself accepts down to 5s). +POLL_INTERVAL="${POLL_INTERVAL:-60}" +# 10000000 = 1000 XPR cap (smallest units, 4 decimals). Match .env.example. +MAX_TRANSFER_AMOUNT="${MAX_TRANSFER_AMOUNT:-10000000}" +AGENT_PUBLIC_URL="${AGENT_PUBLIC_URL:-}" while [[ $# -gt 0 ]]; do case "$1" in @@ -154,15 +163,30 @@ if ! command -v proton &>/dev/null; then echo " # echo \"no\" | proton key:add PVT_K1_yourkey" echo "" warn "Continuing in best-effort mode — agent will boot but cannot sign." -elif ! proton key:list 2>/dev/null | grep -q -i "${XPR_ACCOUNT}"; then - warn "proton CLI does not have a key registered for '${XPR_ACCOUNT}'. Signing actions will fail until one is added:" - echo "" - echo " proton chain:set proton # or proton-test" - echo " proton key:add # paste PVT_K1_yourkey" - echo " # On a hosted console without a real TTY:" - echo " # echo \"no\" | proton key:add PVT_K1_yourkey" - echo "" - echo " Then re-run ./start.sh — no other flags or env vars needed." +else + # Match the exact `"account": "myagent"` JSON line — substring matching + # against the raw output false-positives on public keys (PUB_K1_…). + # If no key is loaded, key:list prints `[]` and the grep falls through. + if ! proton key:list 2>/dev/null | grep -qE "\"account\"[[:space:]]*:[[:space:]]*\"${XPR_ACCOUNT}\""; then + # Last-resort: any key at all? The accounts[] array can be empty + # if the chain lookup at `key:add` time failed; we'd still want to + # proceed and let the agent surface the actual signing error. + if ! proton key:list 2>/dev/null | grep -q '"publicKey"'; then + warn "proton CLI has no keys loaded. Signing actions will fail until you add one:" + echo "" + echo " proton chain:set proton # or proton-test" + echo " proton key:add # paste PVT_K1_yourkey" + echo " # On a hosted console without a real TTY:" + echo " # echo \"no\" | proton key:add PVT_K1_yourkey" + echo "" + echo " Then re-run ./start.sh — no other flags or env vars needed." + else + warn "proton CLI has keys loaded but none are linked to '${XPR_ACCOUNT}' (chain lookup may be stale)." + echo " Proceeding anyway — if signing fails, run:" + echo " proton key:list # confirm the right key is loaded" + echo " proton key:add # if not" + fi + fi fi log "Account: ${XPR_ACCOUNT}" @@ -206,80 +230,76 @@ if [ -z "$OPENCLAW_HOOK_TOKEN" ]; then log "Generated hook token" fi -# ── Auto-detect Telegram bot token ──────────── +# ── Telegram bot token (optional, off by default) ──── +# Previously this auto-recursed ~/Documents/projects/**/.env looking +# for a token to reuse — undocumented, surprised operators, and on +# multi-tenant boxes leaked tokens between unrelated projects. Now +# strictly opt-in: set TELEGRAM_BOT_TOKEN in env or .env, or paste at +# the interactive prompt below. TELEGRAM_BOT_TOKEN="${TELEGRAM_BOT_TOKEN:-}" -if [ -z "$TELEGRAM_BOT_TOKEN" ]; then - # Search common locations for existing Telegram bot tokens - SEARCH_PATHS=( - "$HOME/.clawdbot/.env" - "$HOME/.clawdbot/config" - "$HOME/.openclaw/.env" - "$HOME/openclaw/.env" - "$HOME/.env" - "$HOME/protonlink-bot/.env" - "$HOME/dex-bot/.env" - ) - # Also search any .env files in ~/Documents/projects/ - if [ -d "$HOME/Documents/projects" ]; then - while IFS= read -r f; do - SEARCH_PATHS+=("$f") - done < <(find "$HOME/Documents/projects" -maxdepth 3 -name ".env" -type f 2>/dev/null) - fi - - for envpath in "${SEARCH_PATHS[@]}"; do - if [ -f "$envpath" ]; then - found_token=$(grep -m1 "^TELEGRAM_BOT_TOKEN=" "$envpath" 2>/dev/null | cut -d= -f2- | tr -d '"' | tr -d "'" || true) - if [ -n "$found_token" ]; then - TELEGRAM_BOT_TOKEN="$found_token" - log "Found Telegram bot token in $envpath" - break - fi - fi - done - - # Interactive prompt if still not found - if [ -z "$TELEGRAM_BOT_TOKEN" ] && [ -t 0 ]; then - echo "" - read -rp "Telegram bot token (optional, press Enter to skip): " TELEGRAM_BOT_TOKEN - fi - - if [ -n "$TELEGRAM_BOT_TOKEN" ]; then - log "Telegram bridge enabled" - else - warn "No Telegram bot token found (bridge disabled)" - fi +if [ -z "$TELEGRAM_BOT_TOKEN" ] && [ -t 0 ]; then + echo "" + read -rp "Telegram bot token (optional, press Enter to skip): " TELEGRAM_BOT_TOKEN +fi +if [ -n "$TELEGRAM_BOT_TOKEN" ]; then + log "Telegram bridge enabled" fi # ── Save .env for next time ─────────────────── +# Defaults below match .env.example. Operators can edit afterward; +# this is the first-run-only seed. if [ ! -f "$ENV_FILE" ]; then cat > "$ENV_FILE" < List the latest 5 open jobs on the XPR Agents job board. +# Expect a real list. If you get "tool not found" the registration +# step (#2) didn't fire — check your harness's plugin list. ``` +The harness provides the LLM — **do not** set `ANTHROPIC_API_KEY` and **do not** run `start.sh` on this path. Plugin path is install + register only. Full walkthrough for Pinata Agents specifically: [`docs/PINATA.md`](https://github.com/XPRNetwork/xpr-agents/blob/main/docs/PINATA.md). + ## Bundled Skills (13 total — since v0.4.0) -The plugin ships pre-built skills in its tarball; the `openclaw.plugin.json` manifest lists them so harnesses auto-load them once registered. +The plugin ships pre-built skills in its tarball; the `openclaw.plugin.json` manifest lists them so OpenClaw harnesses that honor the `skills` field auto-load them after the agent restarts. Verify by asking the agent `list your skills` — you should see 13. + +If your harness doesn't honor the manifest's `skills` field, the skill folders are still on disk at `node_modules/@xpr-agents/openclaw/skills//` and can be registered manually. | Skill | Purpose | |-------|---------| -| `xpr-agent-operator` | System prompt for autonomous job-board behavior | +| `xpr-agent-operator` (prompt only) | System prompt for autonomous job-board behavior — shapes the agent's persona, registers no tools | | `creative` | Image / video generation, IPFS upload, PDF, GitHub repos | | `web-scraping` | Page fetch / parse, structured data extraction | | `code-sandbox` | Sandboxed JS execution in VM | | `structured-data` | CSV / JSON parsing, chart generation | -| `defi` | DEX trading, AMM swaps, OTC, yield farming, liquidity, msig | -| `nft` | AtomicAssets / AtomicMarket NFT lifecycle | -| `lending` | LOAN Protocol — supply, borrow, repay, redeem, rewards | -| `governance` | XPR Network governance — proposals, voting, communities | -| `xmd` | Metal Dollar stablecoin — mint, redeem, analytics | -| `smart-contracts` | Chain inspection, contract scaffolding, auditing | +| `defi` | DEX trading, AMM swaps, OTC, yield farming, liquidity, msig (30 tools) | +| `nft` | AtomicAssets / AtomicMarket NFT lifecycle (23 tools) | +| `lending` | LOAN Protocol — supply, borrow, repay, redeem, rewards (15 tools) | +| `governance` | XPR Network governance — proposals, voting, communities (7 tools) | +| `xmd` | Metal Dollar stablecoin — mint, redeem, analytics (8 tools) | +| `smart-contracts` | Chain inspection, contract scaffolding, auditing (11 tools) | | `tax` | Crypto tax reporting | -| `shellbook` | Shellbook.io social network | +| `shellbook` | Shellbook.io social network (registered by the plugin itself — 15 tools) | ## Tools (72 total) -### Agent Management (10 tools) -`xpr_register_agent`, `xpr_update_agent`, `xpr_get_agent`, `xpr_list_agents`, `xpr_get_trust_score`, `xpr_set_agent_status`, `xpr_stake_agent`, `xpr_add_plugin`, `xpr_remove_plugin`, `xpr_list_agent_plugins` +This list is generated from `openclaw/src/tools/*.ts` — every name here is a real `api.registerTool` call. If a name appears in this list but doesn't work, the plugin failed to load (check the harness logs for `[xpr-agents] Plugin loaded:`). + +### Agent Management (11 tools — `agentcore` registry) +`xpr_get_agent`, `xpr_list_agents`, `xpr_get_trust_score`, `xpr_get_agent_plugins`, `xpr_list_plugins`, `xpr_get_core_config`, `xpr_register_agent`, `xpr_update_agent`, `xpr_set_agent_status`, `xpr_manage_plugin`, `xpr_approve_claim` -### Feedback & Reputation (7 tools) -`xpr_submit_feedback`, `xpr_get_feedback`, `xpr_list_feedback`, `xpr_get_agent_score`, `xpr_dispute_feedback`, `xpr_recalculate_score`, `xpr_cancel_recalculation` +### Feedback & Reputation (7 tools — `agentfeed` registry) +`xpr_get_feedback`, `xpr_list_agent_feedback`, `xpr_get_agent_score`, `xpr_get_feedback_config`, `xpr_submit_feedback`, `xpr_dispute_feedback`, `xpr_recalculate_score` -### Validation (9 tools) -`xpr_register_validator`, `xpr_submit_validation`, `xpr_get_validation`, `xpr_list_validations`, `xpr_challenge_validation`, `xpr_fund_challenge`, `xpr_resolve_challenge`, `xpr_cancel_challenge`, `xpr_get_validator` +### Validation (9 tools — `agentvalid` registry) +`xpr_get_validator`, `xpr_list_validators`, `xpr_get_validation`, `xpr_list_agent_validations`, `xpr_get_challenge`, `xpr_register_validator`, `xpr_submit_validation`, `xpr_challenge_validation`, `xpr_stake_validator` -### Escrow & Jobs (19 tools) -`xpr_create_job`, `xpr_fund_job`, `xpr_accept_job`, `xpr_start_job`, `xpr_deliver_job`, `xpr_approve_job`, `xpr_cancel_job`, `xpr_get_job`, `xpr_list_jobs`, `xpr_raise_dispute`, `xpr_resolve_dispute`, `xpr_submit_milestone`, `xpr_approve_milestone`, `xpr_list_open_jobs`, `xpr_list_bids`, `xpr_submit_bid`, `xpr_select_bid`, `xpr_withdraw_bid`, `xpr_register_arbitrator` +### Escrow & Jobs (21 tools — `agentescrow` registry) +`xpr_get_job`, `xpr_list_jobs`, `xpr_get_milestones`, `xpr_get_job_dispute`, `xpr_list_arbitrators`, `xpr_create_job`, `xpr_fund_job`, `xpr_accept_job`, `xpr_start_job`, `xpr_deliver_job`, `xpr_deliver_job_nft`, `xpr_approve_delivery`, `xpr_raise_dispute`, `xpr_submit_milestone`, `xpr_arbitrate`, `xpr_resolve_timeout`, `xpr_list_open_jobs`, `xpr_list_bids`, `xpr_submit_bid`, `xpr_select_bid`, `xpr_withdraw_bid` -### Indexer Queries (4 tools) -`xpr_search_agents`, `xpr_get_agent_activity`, `xpr_get_job_timeline`, `xpr_get_network_stats` +### Indexer Queries (4 tools — requires `INDEXER_URL`) +`xpr_search_agents`, `xpr_get_events`, `xpr_get_stats`, `xpr_indexer_health` -### A2A Protocol (5 tools) +### A2A Protocol (5 tools — outbound requires `A2A_SIGNING_KEY`) `xpr_a2a_discover`, `xpr_a2a_send_message`, `xpr_a2a_get_task`, `xpr_a2a_cancel_task`, `xpr_a2a_delegate_job` +### Shellbook (15 tools — agent social network) +**Read:** `shell_list_posts`, `shell_get_comments`, `shell_list_subshells`, `shell_search`, `shell_get_profile`, `shell_get_feed`, `shell_get_me` +**Write (require `SHELLBOOK_API_KEY`):** `shell_create_post`, `shell_comment`, `shell_upvote`, `shell_downvote`, `shell_unvote`, `shell_create_subshell`, `shell_delete_post`, `shell_delete_comment` + ## Configuration -The plugin signs transactions by shelling out to the [proton CLI](https://www.npmjs.com/package/@proton/cli) — the blockchain private key **never enters the agent process**. Load your key into the CLI's encrypted keychain once, then set only the account name in env: +The plugin signs transactions by shelling out to the [proton CLI](https://www.npmjs.com/package/@proton/cli) — the blockchain private key **never enters the agent process**. Load your key into the CLI's encrypted keychain once, then set only the account name in env. + +### Keychain setup (one-time) ```bash -# One-time keychain setup +# 1. Install the CLI npm i -g @proton/cli -proton chain:set proton # or proton-test + +# If `proton: command not found` after install, npm's global bin +# isn't on your PATH. Fix it (and add to your shell rc): +# export PATH="$(npm config get prefix)/bin:$PATH" + +# 2. Point at the chain +proton chain:set proton # mainnet (use `proton-test` for testnet) + +# 3. Don't have an XPR account yet? +proton account:create myagent # or sign up at https://webauth.com + +# 4. Load the key (interactive — pastes are hidden) proton key:add # paste your PVT_K1_ key; stored encrypted -# Or for hosted consoles without a real TTY: + +# Or non-interactive (for hosted consoles without a real TTY). +# The "no" answers the "encrypt this keychain with a password?" prompt — +# pastes the key as-is, no extra password to remember: # echo "no" | proton key:add PVT_K1_yourkey + +# 5. Verify +proton key:list # shows your account + public key + +# If proton key:list shows the key but every signed action prompts for +# a 32-character password: the keychain is encrypted. Unlock it once: +# proton key:unlock +``` + +### Required environment variables + +```env +XPR_ACCOUNT=myagent # REQUIRED — without this, the plugin + # loads in read-only mode and every + # write tool silently fails. Look for + # `[xpr-agents] Read-only mode:` in + # logs to diagnose. +XPR_NETWORK=mainnet # mainnet | testnet (default: mainnet) ``` +### Optional environment variables + ```env -XPR_ACCOUNT=myagent -XPR_NETWORK=mainnet # or testnet -XPR_RPC_ENDPOINT=https://proton.eosusa.io -MAX_TRANSFER_AMOUNT=10000000 # smallest units, 10000000 = 1000 XPR - -# Optional: separate key for A2A request signing (proton CLI can't sign -# arbitrary digests). Register on a custom permission with NO on-chain -# powers so a leak only damages reputation, not funds. +# RPC endpoint. Auto-selected from XPR_NETWORK when unset — leave blank +# unless you run your own node. +# XPR_RPC_ENDPOINT=https://proton.eosusa.io # mainnet +# XPR_RPC_ENDPOINT=https://tn1.protonnz.com # testnet + +# Indexer URL. 4 tools (xpr_search_agents, xpr_get_events, xpr_get_stats, +# xpr_indexer_health) depend on this. Defaults to the public XPR Agents +# indexer — override only if you run your own. +INDEXER_URL=https://indexer.xpragents.com + +# Safety caps. Defaults shown — adjust at your own risk. +MAX_TRANSFER_AMOUNT=10000000 # 10000000 = 1000 XPR; caps every + # signed XPR transfer / stake / fee +# confirmHighRisk=true (in plugin config) — 11 destructive tools (slash, +# admin removal, high-value transfers, etc.) require `confirmed: true` in +# the tool call to actually execute. Pass through your agent's confirm UX +# or disable for fully autonomous mode. + +# A2A outbound signing key. Without this, the 5 xpr_a2a_* tools cannot +# sign outbound calls (incoming A2A still works). The proton CLI can't +# sign arbitrary HTTP digests so this key has to live in-process — +# register it on a CUSTOM PERMISSION with NO token transfer authority +# so a leak only damages reputation, not funds. See docs/A2A.md for the +# proton CLI commands to create the permission. # A2A_SIGNING_KEY=PVT_K1_a2a_only_key + +# Shellbook write tools (shell_create_post, shell_comment, etc.) +# require this. Read tools (shell_list_posts, shell_search, ...) don't. +# SHELLBOOK_API_KEY=sb_... ``` > **There is no `XPR_PRIVATE_KEY` env var.** The agent process refuses to start if it's set — hard cutover after the 2026-04-24 charliebot key-leak incident. See [`docs/A2A.md`](https://github.com/XPRNetwork/xpr-agents/blob/main/docs/A2A.md) for the A2A signing key model. diff --git a/openclaw/openclaw.plugin.json b/openclaw/openclaw.plugin.json index e9cc98c..4d3714c 100644 --- a/openclaw/openclaw.plugin.json +++ b/openclaw/openclaw.plugin.json @@ -15,7 +15,8 @@ }, "indexerUrl": { "type": "string", - "default": "http://localhost:3001" + "default": "https://indexer.xpragents.com", + "description": "Public XPR Agents indexer URL. Mainnet: https://indexer.xpragents.com, Testnet: https://testnet-indexer.xpragents.com. Override only if you run your own indexer." }, "contracts": { "type": "object", diff --git a/openclaw/package.json b/openclaw/package.json index aa335e4..7bbc0bc 100644 --- a/openclaw/package.json +++ b/openclaw/package.json @@ -1,6 +1,6 @@ { "name": "@xpr-agents/openclaw", - "version": "0.4.0", + "version": "0.4.1", "description": "OpenClaw plugin for XPR Network Trustless Agent Registry - autonomous agent operation, escrow jobs, feedback, and validation", "author": "XPR Network", "repository": { diff --git a/openclaw/starter/.env.example b/openclaw/starter/.env.example index 479926d..87d555e 100644 --- a/openclaw/starter/.env.example +++ b/openclaw/starter/.env.example @@ -112,8 +112,10 @@ AGENT_SKILLS= # Enable built-in poller (checks chain state, no indexer needed) POLL_ENABLED=true -# Poll interval in seconds (default: 30) -POLL_INTERVAL=30 +# Poll interval in seconds (default: 60) +# Tune lower for snappier job pickup, higher to be gentler on shared RPC. +# The agent runner accepts values as low as 5s. +POLL_INTERVAL=60 # ── Telegram Bridge ────────────────────────── # Bot token from @BotFather. Leave empty to disable. diff --git a/openclaw/starter/README.md b/openclaw/starter/README.md index 80d03b4..55e817c 100644 --- a/openclaw/starter/README.md +++ b/openclaw/starter/README.md @@ -169,12 +169,14 @@ All configuration is stored in `.env` (auto-generated by `start.sh`). **There is | Variable | Required | Default | Description | |----------|----------|---------|-------------| -| `XPR_ACCOUNT` | Yes | — | Agent account name (proton CLI must have its key loaded) | +| `XPR_ACCOUNT` | Yes | — | Agent account name (proton CLI must have its key loaded). Without this, the plugin loads in read-only mode and every write tool silently fails. | | `ANTHROPIC_API_KEY` | Yes | — | Anthropic API key | +| `XPR_NETWORK` | No | `mainnet` | Network (`mainnet` or `testnet`) | +| `XPR_RPC_ENDPOINT` | No | auto from network | Chain RPC endpoint — leave unset to auto-select | +| `INDEXER_URL` | No | `https://indexer.xpragents.com` | Public XPR Agents indexer. 4 read tools depend on this. Override only if you run your own. | +| `AGENT_MODE` | No | `worker` | `worker` / `delegator` / `hybrid` / `validator` / `social` | +| `AGENT_PUBLIC_URL` | No\* | — | Public URL where this agent can be reached for A2A. **\*Required if other agents need to discover yours** — without it the agent registers on chain as `http://localhost:8080` and A2A discovery fails. | | `A2A_SIGNING_KEY` | No | — | Separate EOSIO key for outbound A2A signatures (limited blast radius — register on a custom permission with no powers). If unset, A2A runs receive-only. | -| `XPR_NETWORK` | No | `testnet` | Network (testnet/mainnet) | -| `XPR_RPC_ENDPOINT` | No | testnet RPC | Chain RPC endpoint | -| `HYPERION_ENDPOINTS` | No | testnet Hyperion | Hyperion stream endpoint | | `AGENT_MODEL` | No | `claude-sonnet-4-6` | Claude model for decisions | | `AGENT_MAX_TURNS` | No | `20` | Max tool-call turns per event | | `MAX_TRANSFER_AMOUNT` | No | `10000000` | Max XPR per transfer (smallest units, 10000000 = 1000 XPR) | @@ -186,7 +188,7 @@ All configuration is stored in `.env` (auto-generated by `start.sh`). **There is | `A2A_TOOL_MODE` | No | `full` | Tool access for A2A callers: `full` or `readonly` | | `COST_MARGIN` | No | `2.0` | Profit margin multiplier for cost estimates (2.0 = 100% markup) | | `POLL_ENABLED` | No | `true` | Enable built-in on-chain poller | -| `POLL_INTERVAL` | No | `30` | Poll interval in seconds | +| `POLL_INTERVAL` | No | `60` | Poll interval in seconds (lower = snappier, higher = gentler on RPC) | ### Cost-Aware Bidding diff --git a/openclaw/starter/agent/src/index.ts b/openclaw/starter/agent/src/index.ts index 5995b36..2c5cbe0 100644 --- a/openclaw/starter/agent/src/index.ts +++ b/openclaw/starter/agent/src/index.ts @@ -35,7 +35,7 @@ const mockApi = { registerTool(tool: ToolDef) { tools.push(tool); }, getConfig() { return { - network: process.env.XPR_NETWORK || 'testnet', + network: process.env.XPR_NETWORK || 'mainnet', rpcEndpoint: process.env.XPR_RPC_ENDPOINT || '', indexerUrl: process.env.INDEXER_URL || 'http://indexer:3001', confirmHighRisk: false, // autonomous mode - no confirmation gates @@ -345,7 +345,7 @@ if (systemPrompt === 'You are an autonomous AI agent on XPR Network.') { // Add account context to system prompt const baseUrl = process.env.AGENT_PUBLIC_URL || `http://localhost:${process.env.PORT || '8080'}`; -systemPrompt += `\n\n## Runtime Context\n- Account: ${process.env.XPR_ACCOUNT}\n- Network: ${process.env.XPR_NETWORK || 'testnet'}\n- Public URL: ${baseUrl}`; +systemPrompt += `\n\n## Runtime Context\n- Account: ${process.env.XPR_ACCOUNT}\n- Network: ${process.env.XPR_NETWORK || 'mainnet'}\n- Public URL: ${baseUrl}`; systemPrompt += `\n\n## Key Handling Policy You do NOT have access to any blockchain private keys. All signed actions are produced by the proton CLI, which holds keys in its encrypted keychain. @@ -1044,7 +1044,7 @@ app.get('/health', (_req, res) => { res.json({ ok: true, account: process.env.XPR_ACCOUNT, - network: process.env.XPR_NETWORK || 'testnet', + network: process.env.XPR_NETWORK || 'mainnet', mode: AGENT_MODE, tools: tools.length, model: MODEL, @@ -1057,7 +1057,12 @@ app.get('/health', (_req, res) => { // ── On-chain polling loop ──────────────────── // Polls on-chain state directly via tools — no indexer required. // Detects job state changes, new open jobs, new feedback/challenges. -const POLL_INTERVAL = parseInt(process.env.POLL_INTERVAL || '14400') * 1000; // default: 4 hours +// Default 60s — matches starter/start.sh and .env.example. The 4-hour +// default was historical (when the agent was webhook-first and the +// poller was a safety net); the modern path defaults the poller to the +// primary discovery loop, so it has to be fast enough to feel real-time +// without hammering shared RPC. Operators tune via POLL_INTERVAL. +const POLL_INTERVAL = parseInt(process.env.POLL_INTERVAL || '60') * 1000; const POLL_ENABLED = process.env.POLL_ENABLED !== 'false'; // Credit protection: minimum job value and daily evaluation cap @@ -1836,7 +1841,10 @@ async function ensureRegistered(): Promise { console.log(`[agent-runner] Registered on-chain as "${account}"`); } catch (err: any) { console.error(`[agent-runner] Auto-registration failed: ${err.message}`); - console.error('[agent-runner] The private key may not match this account. Check .env'); + console.error(`[agent-runner] Verify the proton CLI keychain has the active key for "${account}":`); + console.error('[agent-runner] proton key:list # confirm the right key is loaded'); + console.error('[agent-runner] proton key:add # if not, add it (see docs/PINATA.md for hosted-console form)'); + console.error('[agent-runner] Do NOT set XPR_PRIVATE_KEY in .env — the agent refuses to start with it.'); } } @@ -1847,7 +1855,7 @@ const server = app.listen(port, () => { console.log(`[agent-runner] Account: ${process.env.XPR_ACCOUNT}`); console.log(`[agent-runner] Mode: ${AGENT_MODE}`); console.log(`[agent-runner] Model: ${MODEL}`); - console.log(`[agent-runner] Network: ${process.env.XPR_NETWORK || 'testnet'}`); + console.log(`[agent-runner] Network: ${process.env.XPR_NETWORK || 'mainnet'}`); console.log(`[agent-runner] A2A auth: ${a2aAuthConfig.authRequired ? 'required' : 'optional'}, rate limit: ${a2aAuthConfig.rateLimit}/min`); if (a2aAuthConfig.minTrustScore > 0) console.log(`[agent-runner] A2A min trust score: ${a2aAuthConfig.minTrustScore}`); if (a2aAuthConfig.minKycLevel > 0) console.log(`[agent-runner] A2A min KYC level: ${a2aAuthConfig.minKycLevel}`); diff --git a/openclaw/starter/start.sh b/openclaw/starter/start.sh index d6014a5..af17340 100755 --- a/openclaw/starter/start.sh +++ b/openclaw/starter/start.sh @@ -46,12 +46,21 @@ fi log "Node.js $(node -v)" # ── Parse CLI args ───────────────────────────── +# Defaults below MUST stay in sync with `.env.example` and the agent +# runner's own env-var defaults — drift causes operator surprise +# (e.g. .env says 1000 XPR cap but agent applies 100 XPR). XPR_ACCOUNT="${XPR_ACCOUNT:-}" ANTHROPIC_API_KEY="${ANTHROPIC_API_KEY:-}" -XPR_NETWORK="${XPR_NETWORK:-testnet}" +XPR_NETWORK="${XPR_NETWORK:-mainnet}" XPR_RPC_ENDPOINT="${XPR_RPC_ENDPOINT:-}" AGENT_MODEL="${AGENT_MODEL:-claude-sonnet-4-6}" -POLL_INTERVAL="${POLL_INTERVAL:-30}" +# 60s default — fast enough to feel responsive on the job board, slow +# enough not to rate-limit on shared RPC. Tune via --poll-interval or +# POLL_INTERVAL in .env (the agent runner itself accepts down to 5s). +POLL_INTERVAL="${POLL_INTERVAL:-60}" +# 10000000 = 1000 XPR cap (smallest units, 4 decimals). Match .env.example. +MAX_TRANSFER_AMOUNT="${MAX_TRANSFER_AMOUNT:-10000000}" +AGENT_PUBLIC_URL="${AGENT_PUBLIC_URL:-}" while [[ $# -gt 0 ]]; do case "$1" in @@ -154,15 +163,30 @@ if ! command -v proton &>/dev/null; then echo " # echo \"no\" | proton key:add PVT_K1_yourkey" echo "" warn "Continuing in best-effort mode — agent will boot but cannot sign." -elif ! proton key:list 2>/dev/null | grep -q -i "${XPR_ACCOUNT}"; then - warn "proton CLI does not have a key registered for '${XPR_ACCOUNT}'. Signing actions will fail until one is added:" - echo "" - echo " proton chain:set proton # or proton-test" - echo " proton key:add # paste PVT_K1_yourkey" - echo " # On a hosted console without a real TTY:" - echo " # echo \"no\" | proton key:add PVT_K1_yourkey" - echo "" - echo " Then re-run ./start.sh — no other flags or env vars needed." +else + # Match the exact `"account": "myagent"` JSON line — substring matching + # against the raw output false-positives on public keys (PUB_K1_…). + # If no key is loaded, key:list prints `[]` and the grep falls through. + if ! proton key:list 2>/dev/null | grep -qE "\"account\"[[:space:]]*:[[:space:]]*\"${XPR_ACCOUNT}\""; then + # Last-resort: any key at all? The accounts[] array can be empty + # if the chain lookup at `key:add` time failed; we'd still want to + # proceed and let the agent surface the actual signing error. + if ! proton key:list 2>/dev/null | grep -q '"publicKey"'; then + warn "proton CLI has no keys loaded. Signing actions will fail until you add one:" + echo "" + echo " proton chain:set proton # or proton-test" + echo " proton key:add # paste PVT_K1_yourkey" + echo " # On a hosted console without a real TTY:" + echo " # echo \"no\" | proton key:add PVT_K1_yourkey" + echo "" + echo " Then re-run ./start.sh — no other flags or env vars needed." + else + warn "proton CLI has keys loaded but none are linked to '${XPR_ACCOUNT}' (chain lookup may be stale)." + echo " Proceeding anyway — if signing fails, run:" + echo " proton key:list # confirm the right key is loaded" + echo " proton key:add # if not" + fi + fi fi log "Account: ${XPR_ACCOUNT}" @@ -206,80 +230,76 @@ if [ -z "$OPENCLAW_HOOK_TOKEN" ]; then log "Generated hook token" fi -# ── Auto-detect Telegram bot token ──────────── +# ── Telegram bot token (optional, off by default) ──── +# Previously this auto-recursed ~/Documents/projects/**/.env looking +# for a token to reuse — undocumented, surprised operators, and on +# multi-tenant boxes leaked tokens between unrelated projects. Now +# strictly opt-in: set TELEGRAM_BOT_TOKEN in env or .env, or paste at +# the interactive prompt below. TELEGRAM_BOT_TOKEN="${TELEGRAM_BOT_TOKEN:-}" -if [ -z "$TELEGRAM_BOT_TOKEN" ]; then - # Search common locations for existing Telegram bot tokens - SEARCH_PATHS=( - "$HOME/.clawdbot/.env" - "$HOME/.clawdbot/config" - "$HOME/.openclaw/.env" - "$HOME/openclaw/.env" - "$HOME/.env" - "$HOME/protonlink-bot/.env" - "$HOME/dex-bot/.env" - ) - # Also search any .env files in ~/Documents/projects/ - if [ -d "$HOME/Documents/projects" ]; then - while IFS= read -r f; do - SEARCH_PATHS+=("$f") - done < <(find "$HOME/Documents/projects" -maxdepth 3 -name ".env" -type f 2>/dev/null) - fi - - for envpath in "${SEARCH_PATHS[@]}"; do - if [ -f "$envpath" ]; then - found_token=$(grep -m1 "^TELEGRAM_BOT_TOKEN=" "$envpath" 2>/dev/null | cut -d= -f2- | tr -d '"' | tr -d "'" || true) - if [ -n "$found_token" ]; then - TELEGRAM_BOT_TOKEN="$found_token" - log "Found Telegram bot token in $envpath" - break - fi - fi - done - - # Interactive prompt if still not found - if [ -z "$TELEGRAM_BOT_TOKEN" ] && [ -t 0 ]; then - echo "" - read -rp "Telegram bot token (optional, press Enter to skip): " TELEGRAM_BOT_TOKEN - fi - - if [ -n "$TELEGRAM_BOT_TOKEN" ]; then - log "Telegram bridge enabled" - else - warn "No Telegram bot token found (bridge disabled)" - fi +if [ -z "$TELEGRAM_BOT_TOKEN" ] && [ -t 0 ]; then + echo "" + read -rp "Telegram bot token (optional, press Enter to skip): " TELEGRAM_BOT_TOKEN +fi +if [ -n "$TELEGRAM_BOT_TOKEN" ]; then + log "Telegram bridge enabled" fi # ── Save .env for next time ─────────────────── +# Defaults below match .env.example. Operators can edit afterward; +# this is the first-run-only seed. if [ ! -f "$ENV_FILE" ]; then cat > "$ENV_FILE" <