Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,23 @@ AELFSCAN_TIMEOUT_MS=10000

# Retry count for transient request failures
AELFSCAN_RETRY=1

# Retry backoff base milliseconds and max milliseconds (exponential + jitter)
AELFSCAN_RETRY_BASE_MS=200
AELFSCAN_RETRY_MAX_MS=3000

# HTTP client concurrency limit
AELFSCAN_MAX_CONCURRENT_REQUESTS=5

# Default in-memory cache TTL for statistics GET requests (milliseconds)
AELFSCAN_CACHE_TTL_MS=60000
# Maximum in-memory cache entries before FIFO eviction
AELFSCAN_CACHE_MAX_ENTRIES=500

# Pagination maxResultCount upper bound
AELFSCAN_MAX_RESULT_COUNT=200

# MCP output controls
AELFSCAN_MCP_MAX_ITEMS=50
AELFSCAN_MCP_MAX_CHARS=60000
AELFSCAN_MCP_INCLUDE_RAW=false
59 changes: 59 additions & 0 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
name: Publish to npm

on:
push:
tags:
- 'v*'
workflow_dispatch:

permissions:
id-token: write
contents: read

jobs:
verify:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- uses: oven-sh/setup-bun@v2
with:
bun-version: latest

- name: Install dependencies
run: bun install --frozen-lockfile

- name: Type check
run: bunx tsc --noEmit

- name: Run tests
run: bun run test

- name: Verify generated openclaw config
run: bun run build:openclaw:check

publish:
if: startsWith(github.ref, 'refs/tags/v')
needs: verify
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- uses: actions/setup-node@v4
with:
node-version: '22'
registry-url: 'https://registry.npmjs.org'

- name: Ensure latest npm (trusted publishing requires >= 11.5.1)
run: npm install -g npm@latest

- name: Verify tag matches package.json version
run: |
PKG_VERSION=$(node -p "require('./package.json').version")
TAG_VERSION="${GITHUB_REF_NAME#v}"
if [ "$PKG_VERSION" != "$TAG_VERSION" ]; then
echo "::error::Tag version ($TAG_VERSION) does not match package.json ($PKG_VERSION)"
exit 1
fi

- run: npm publish --provenance --access public
51 changes: 51 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
name: Test

on:
pull_request:
push:
branches:
- main
- master
- 'codex/**'

permissions:
contents: read

jobs:
test:
runs-on: ubuntu-latest
env:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
steps:
- uses: actions/checkout@v4

- uses: oven-sh/setup-bun@v2
with:
bun-version: latest

- name: Install dependencies
run: bun install --frozen-lockfile

- name: Type check
run: bunx tsc --noEmit

- name: Run tests
run: bun run test

- name: Unit coverage
run: bun run test:unit:coverage

- name: Verify generated openclaw config
run: bun run build:openclaw:check

- name: Upload coverage to Codecov
if: ${{ env.CODECOV_TOKEN != '' }}
uses: codecov/codecov-action@v5
with:
token: ${{ env.CODECOV_TOKEN }}
files: ./coverage/lcov.info
fail_ci_if_error: true

- name: Skip Codecov upload (missing token)
if: ${{ env.CODECOV_TOKEN == '' }}
run: echo "CODECOV_TOKEN is not set; skipping Codecov upload."
18 changes: 17 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ AelfScan explorer skill toolkit for AI agents, with **SDK + MCP + CLI + OpenClaw
- Token: token list/detail/transfers/holders
- NFT: collections/detail/transfers/holders/inventory/item detail/item holders/item activity
- Statistics: daily transactions/addresses/activity, produce metrics, fees/reward/burn, supply/market/staking/TVL, node/ELF supply and date-range summary APIs
- Metadata-driven tool registry: one source of truth for SDK/CLI/MCP/OpenClaw
- MCP output governance: array truncation + max chars + configurable raw payload inclusion
- Unified output shape: `ToolResult<T>` with `traceId`, standardized errors, and `raw` payload

## Architecture
Expand All @@ -20,7 +22,8 @@ aelfscan-skill/
├── aelfscan_skill.ts # CLI adapter
├── src/
│ ├── core/ # Domain logic (search/blockchain/address/token/nft/statistics)
│ └── mcp/server.ts # MCP adapter
│ ├── tooling/ # Single source tool descriptors
│ └── mcp/ # MCP adapter + output policy
├── lib/ # Config/http/errors/trace/types
├── bin/setup.ts # Setup for claude/cursor/openclaw
├── openclaw.json
Expand Down Expand Up @@ -58,6 +61,7 @@ bun run aelfscan_skill.ts blockchain log-events --input '{"chainId":"AELF","cont
bun run aelfscan_skill.ts address detail --input '{"chainId":"AELF","address":"JRmBduh4nXWi1aXgdUsj5gJrzeZb2LxmrAbf7W99faZSvoAaE"}'
bun run aelfscan_skill.ts statistics daily-transactions --input '{"chainId":"AELF"}'
bun run aelfscan_skill.ts statistics daily-transaction-info --input '{"chainId":"AELF","startDate":"2026-02-20","endDate":"2026-02-26"}'
bun run aelfscan_skill.ts statistics metric --input '{"metric":"dailyTransactions","chainId":"AELF"}'
```

## MCP Config Example
Expand All @@ -72,12 +76,15 @@ bun run setup cursor
bun run setup cursor --global
bun run setup openclaw
bun run setup list
bun run build:openclaw
```

## Tests

```bash
bun run test:unit
bun run test:unit:coverage
bun run coverage:badge
bun run test:integration
bun run test:e2e

Expand All @@ -91,6 +98,15 @@ RUN_LIVE_TESTS=1 bun run test:e2e
- `AELFSCAN_DEFAULT_CHAIN_ID` (default: empty for multi-chain)
- `AELFSCAN_TIMEOUT_MS` (default: `10000`)
- `AELFSCAN_RETRY` (default: `1`)
- `AELFSCAN_RETRY_BASE_MS` (default: `200`)
- `AELFSCAN_RETRY_MAX_MS` (default: `3000`)
- `AELFSCAN_MAX_CONCURRENT_REQUESTS` (default: `5`)
- `AELFSCAN_CACHE_TTL_MS` (default: `60000`)
- `AELFSCAN_CACHE_MAX_ENTRIES` (default: `500`)
- `AELFSCAN_MAX_RESULT_COUNT` (default: `200`)
- `AELFSCAN_MCP_MAX_ITEMS` (default: `50`)
- `AELFSCAN_MCP_MAX_CHARS` (default: `60000`)
- `AELFSCAN_MCP_INCLUDE_RAW` (default: `false`)

## License

Expand Down
18 changes: 17 additions & 1 deletion README.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
- Token:列表、详情、转账、持有人
- NFT:合集列表/详情、转账、持有人、库存、Item 详情/持有人/活动
- Statistics:交易/地址活跃度、产块指标、手续费/奖励/销毁、供给/市值/质押/TVL、节点与 ELF 供给、按日期区间汇总
- 单一元数据源:SDK/CLI/MCP/OpenClaw 共用 tool descriptor
- MCP 输出治理:数组截断 + 文本长度上限 + `raw` 可配置
- 统一返回模型:`ToolResult<T>`,包含 `traceId`、标准化错误和 `raw` 原始响应

## 架构
Expand All @@ -20,7 +22,8 @@ aelfscan-skill/
├── aelfscan_skill.ts # CLI 适配层
├── src/
│ ├── core/ # 域逻辑(search/blockchain/address/token/nft/statistics)
│ └── mcp/server.ts # MCP 适配层
│ ├── tooling/ # Tool descriptor 单一真源
│ └── mcp/ # MCP 适配层与输出治理
├── lib/ # config/http/errors/trace/types
├── bin/setup.ts # claude/cursor/openclaw 一键配置
├── openclaw.json
Expand Down Expand Up @@ -58,6 +61,7 @@ bun run aelfscan_skill.ts blockchain log-events --input '{"chainId":"AELF","cont
bun run aelfscan_skill.ts address detail --input '{"chainId":"AELF","address":"JRmBduh4nXWi1aXgdUsj5gJrzeZb2LxmrAbf7W99faZSvoAaE"}'
bun run aelfscan_skill.ts statistics daily-transactions --input '{"chainId":"AELF"}'
bun run aelfscan_skill.ts statistics daily-transaction-info --input '{"chainId":"AELF","startDate":"2026-02-20","endDate":"2026-02-26"}'
bun run aelfscan_skill.ts statistics metric --input '{"metric":"dailyTransactions","chainId":"AELF"}'
```

## MCP 配置模板
Expand All @@ -72,12 +76,15 @@ bun run setup cursor
bun run setup cursor --global
bun run setup openclaw
bun run setup list
bun run build:openclaw
```

## 测试

```bash
bun run test:unit
bun run test:unit:coverage
bun run coverage:badge
bun run test:integration
bun run test:e2e

Expand All @@ -91,6 +98,15 @@ RUN_LIVE_TESTS=1 bun run test:e2e
- `AELFSCAN_DEFAULT_CHAIN_ID`(默认空字符串,表示 multi-chain)
- `AELFSCAN_TIMEOUT_MS`(默认 `10000`)
- `AELFSCAN_RETRY`(默认 `1`)
- `AELFSCAN_RETRY_BASE_MS`(默认 `200`)
- `AELFSCAN_RETRY_MAX_MS`(默认 `3000`)
- `AELFSCAN_MAX_CONCURRENT_REQUESTS`(默认 `5`)
- `AELFSCAN_CACHE_TTL_MS`(默认 `60000`)
- `AELFSCAN_CACHE_MAX_ENTRIES`(默认 `500`)
- `AELFSCAN_MAX_RESULT_COUNT`(默认 `200`)
- `AELFSCAN_MCP_MAX_ITEMS`(默认 `50`)
- `AELFSCAN_MCP_MAX_CHARS`(默认 `60000`)
- `AELFSCAN_MCP_INCLUDE_RAW`(默认 `false`)

## License

Expand Down
Loading