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
1 change: 1 addition & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ jobs:
bun-version: latest

- run: bun install
- run: bun run deps:check
- run: bun run test:coverage:ci
- run: bun run build:openclaw:check

Expand Down
7 changes: 7 additions & 0 deletions deps-baseline.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"dependencies": {
"@modelcontextprotocol/sdk": "^1.26.0",
"zod": "^3.24.0",
"aelf-sdk": "^3.5.1-beta.0"
}
}
28 changes: 20 additions & 8 deletions lib/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,18 @@ import { homedir } from 'node:os';
import { join } from 'node:path';
import type { NodeProfile } from './types.js';

export const CHAIN_IDS = {
AELF: 'AELF',
TDVV: 'tDVV',
} as const;

export const DEFAULT_CHAIN_ID = CHAIN_IDS.AELF;

export const DEFAULT_RPC_URLS: Record<(typeof CHAIN_IDS)[keyof typeof CHAIN_IDS], string> = {
[CHAIN_IDS.AELF]: 'https://aelf-public-node.aelf.io',
[CHAIN_IDS.TDVV]: 'https://tdvv-public-node.aelf.io',
};

const DEFAULT_TIMEOUT_MS = 10_000;
const DEFAULT_RETRY = 1;
const DEFAULT_SDK_INSTANCE_CACHE_MAX = 32;
Expand All @@ -11,15 +23,15 @@ const DEFAULT_REST_CLIENT_CACHE_MAX = 64;
export const DEFAULT_NODES: NodeProfile[] = [
{
id: 'default-aelf',
chainId: 'AELF',
rpcUrl: 'https://aelf-public-node.aelf.io',
chainId: CHAIN_IDS.AELF,
rpcUrl: DEFAULT_RPC_URLS[CHAIN_IDS.AELF],
enabled: true,
source: 'default',
},
{
id: 'default-tdvv',
chainId: 'tDVV',
rpcUrl: 'https://tdvv-public-node.aelf.io',
chainId: CHAIN_IDS.TDVV,
rpcUrl: DEFAULT_RPC_URLS[CHAIN_IDS.TDVV],
enabled: true,
source: 'default',
},
Expand Down Expand Up @@ -52,19 +64,19 @@ export function getRestClientCacheMax(): number {
}

export function getRegistryPath(): string {
return process.env.AELF_NODE_REGISTRY_PATH || join(homedir(), '.aelf-node-skill', 'nodes.json');
return process.env.AELF_NODE_REGISTRY_PATH ?? join(homedir(), '.aelf-node-skill', 'nodes.json');
}

export function getEoaPrivateKey(override?: string): string | undefined {
return override || process.env.AELF_PRIVATE_KEY;
return override ?? process.env.AELF_PRIVATE_KEY;
}

export function getEnvOverrideNodes(): NodeProfile[] {
const result: NodeProfile[] = [];
if (process.env.AELF_NODE_AELF_RPC_URL) {
result.push({
id: 'env-aelf',
chainId: 'AELF',
chainId: CHAIN_IDS.AELF,
rpcUrl: process.env.AELF_NODE_AELF_RPC_URL,
enabled: true,
source: 'env',
Expand All @@ -73,7 +85,7 @@ export function getEnvOverrideNodes(): NodeProfile[] {
if (process.env.AELF_NODE_TDVV_RPC_URL) {
result.push({
id: 'env-tdvv',
chainId: 'tDVV',
chainId: CHAIN_IDS.TDVV,
rpcUrl: process.env.AELF_NODE_TDVV_RPC_URL,
enabled: true,
source: 'env',
Expand Down
6 changes: 3 additions & 3 deletions lib/node-router.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { DEFAULT_NODES, getEnvOverrideNodes } from './config.js';
import { DEFAULT_CHAIN_ID, DEFAULT_NODES, getEnvOverrideNodes } from './config.js';
import { listImportedNodes } from './node-registry.js';
import { validateChainTargetInput } from './validators.js';
import type { NodeProfile, ResolveNodeInput, ResolveNodeResult } from './types.js';
Expand All @@ -21,7 +21,7 @@ export async function resolveNode(input: ResolveNodeInput): Promise<ResolveNodeR
return {
node: {
id: 'direct-rpc',
chainId: input.chainId || 'AELF',
chainId: input.chainId ?? DEFAULT_CHAIN_ID,
rpcUrl: input.rpcUrl,
enabled: true,
source: 'direct',
Expand All @@ -40,7 +40,7 @@ export async function resolveNode(input: ResolveNodeInput): Promise<ResolveNodeR
return { node, candidates };
}

const chainId = input.chainId || 'AELF';
const chainId = input.chainId ?? DEFAULT_CHAIN_ID;
const node = candidates.find(item => item.chainId === chainId);
if (!node) {
throw new Error(`No available node for chainId: ${chainId}`);
Expand Down
7 changes: 4 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@blockchain-forever/aelf-node-skill",
"version": "0.1.0",
"version": "0.1.1",
"description": "AElf Node Skill for AI agents: REST for reads, SDK for contract execution, and selective fallback for fee estimate.",
"type": "module",
"main": "index.ts",
Expand Down Expand Up @@ -40,7 +40,8 @@
"test:coverage:ci": "COVERAGE_MIN_LINES=85 COVERAGE_MIN_FUNCS=80 bun run test:unit:coverage && bun run coverage:gate",
"build:openclaw": "bun run bin/generate-openclaw.ts",
"build:openclaw:check": "bun run bin/generate-openclaw.ts --check",
"coverage:badge": "bun run bin/generate-coverage-badge.ts"
"coverage:badge": "bun run bin/generate-coverage-badge.ts",
"deps:check": "bun run scripts/check-deps-baseline.ts"
},
"keywords": [
"aelf",
Expand All @@ -64,7 +65,7 @@
"license": "MIT",
"dependencies": {
"@modelcontextprotocol/sdk": "^1.26.0",
"aelf-sdk": "3.5.1-beta.0",
"aelf-sdk": "^3.5.1-beta.0",
"commander": "^12.1.0",
"zod": "^3.24.0"
},
Expand Down
58 changes: 58 additions & 0 deletions scripts/check-deps-baseline.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
#!/usr/bin/env bun
import { existsSync, readFileSync } from 'node:fs';
import { resolve } from 'node:path';

type Baseline = {
dependencies: Record<string, string>;
};

function readJson<T>(filePath: string): T {
return JSON.parse(readFileSync(filePath, 'utf8')) as T;
}

function main() {
const cwd = process.cwd();
const baselinePath = resolve(cwd, 'deps-baseline.json');
const packagePath = resolve(cwd, 'package.json');

if (!existsSync(baselinePath)) {
console.error(`[deps:check] missing deps-baseline.json at ${baselinePath}`);
process.exit(1);
}

if (!existsSync(packagePath)) {
console.error(`[deps:check] missing package.json at ${packagePath}`);
process.exit(1);
}

const baseline = readJson<Baseline>(baselinePath);
const pkg = readJson<any>(packagePath);
const declaredDeps = {
...(pkg.dependencies || {}),
...(pkg.devDependencies || {}),
} as Record<string, string>;

const failures: string[] = [];
for (const [name, expected] of Object.entries(baseline.dependencies || {})) {
const actual = declaredDeps[name];
if (!actual) {
failures.push(`${name}: missing (expected ${expected})`);
continue;
}
if (actual !== expected) {
failures.push(`${name}: expected ${expected}, got ${actual}`);
}
}

if (failures.length > 0) {
console.error('[deps:check] dependency baseline mismatch:');
for (const failure of failures) {
console.error(`- ${failure}`);
}
process.exit(1);
}

console.log('[deps:check] passed');
}

main();
9 changes: 7 additions & 2 deletions tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,12 @@
"forceConsistentCasingInFileNames": true,
"esModuleInterop": true,
"resolveJsonModule": true,
"types": ["bun-types"]
"types": [
"bun-types"
],
"noEmit": true
},
"include": ["**/*.ts"]
"include": [
"**/*.ts"
]
}