Skip to content

Commit 25466ea

Browse files
CodySearsOSryanio
andcommitted
Release v1.10.0
Origin-SHA: b3b4d2b981fd6e907115acf1270962651112affe Co-authored-by: Ryan Ghods <ryan@ryanio.com>
1 parent f46f92f commit 25466ea

13 files changed

Lines changed: 407 additions & 3 deletions

File tree

.agents/rules.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -228,7 +228,6 @@ npm run test -- --coverage # Run with coverage report
228228
| `test/cli-network-error.test.ts` | CLI network error handling |
229229
| `test/cli-rate-limit.test.ts` | CLI rate-limit (HTTP 429) handling |
230230
| `test/parse.test.ts` | `--body` JSON file parsing utility |
231-
| `test/toon.test.ts` | Toon output formatter |
232231
| `test/resolve-quantity.test.ts` | Quantity resolution logic |
233232
| `test/integration.test.ts` | End-to-end SDK flows with mocked `fetch` |
234233
| `test/mocks.ts` | Shared mock factories (`createCommandTestContext`, `mockFetchResponse`, `mockFetchTextResponse`) |

CHANGELOG.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,16 @@
11
# @opensea/cli
22

3+
## 1.10.0
4+
5+
### Minor Changes
6+
7+
- Add the `opensea tools` command for searching, listing, and inspecting registered AI agent tools (ERC-8257). New `search`, `get`, and `list` subcommands wrap the `[Beta]` tool registry API, with matching `OpenSeaCLI` SDK methods and `RegisteredToolResponse` / `ToolSearchPaginatedResponse` / `ToolListPaginatedResponse` type re-exports sourced from `@opensea/api-types`.
8+
9+
### Patch Changes
10+
11+
- Updated dependencies
12+
- @opensea/api-types@0.4.4
13+
314
## 1.9.0
415

516
### Minor Changes

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@opensea/cli",
3-
"version": "1.9.0",
3+
"version": "1.10.0",
44
"type": "module",
55
"description": "OpenSea CLI - Query the OpenSea API from the command line or programmatically",
66
"main": "dist/index.js",
@@ -23,7 +23,7 @@
2323
"prepublishOnly": "npm run build"
2424
},
2525
"dependencies": {
26-
"@opensea/api-types": "^0.4.3",
26+
"@opensea/api-types": "^0.4.4",
2727
"@opensea/wallet-adapters": "^0.3.0",
2828
"commander": "^14.0.3",
2929
"zod": "^4.3.6"

src/cli.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import {
1616
swapsCommand,
1717
tokenGroupsCommand,
1818
tokensCommand,
19+
toolsCommand,
1920
transactionsCommand,
2021
walletCommand,
2122
} from "./commands/index.js"
@@ -135,6 +136,7 @@ program.addCommand(
135136
)
136137
program.addCommand(searchCommand(getClient, getFormat))
137138
program.addCommand(swapsCommand(getClient, getFormat))
139+
program.addCommand(toolsCommand(getClient, getFormat))
138140
program.addCommand(transactionsCommand(getClient, getFormat))
139141
program.addCommand(healthCommand(getClient, getFormat))
140142
program.addCommand(walletCommand(getFormat))

src/commands/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,5 +13,6 @@ export { searchCommand } from "./search.js"
1313
export { swapsCommand } from "./swaps.js"
1414
export { tokenGroupsCommand } from "./token-groups.js"
1515
export { tokensCommand } from "./tokens.js"
16+
export { toolsCommand } from "./tools.js"
1617
export { transactionsCommand } from "./transactions.js"
1718
export { walletCommand } from "./wallet.js"

src/commands/tools.ts

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
import { Command } from "commander"
2+
import type { OpenSeaClient } from "../client.js"
3+
import type { OutputFormat } from "../output.js"
4+
import { outputGet } from "../output.js"
5+
import { parseIntOption } from "../parse.js"
6+
7+
export function toolsCommand(
8+
getClient: () => OpenSeaClient,
9+
getFormat: () => OutputFormat,
10+
): Command {
11+
const cmd = new Command("tools").description(
12+
"Search, list, and inspect registered AI agent tools (ERC-8257)",
13+
)
14+
15+
cmd
16+
.command("search")
17+
.description("Search registered tools by name, tags, creator, or criteria")
18+
.option("--query <text>", "Search query text")
19+
.option("--registry-chain <chain>", "Filter by registry chain ID")
20+
.option("--tags <tags>", "Comma-separated tags to filter by")
21+
.option(
22+
"--access-type <type>",
23+
"Filter by access type (open, nft_gated, subscription)",
24+
)
25+
.option("--creator <address>", "Filter by creator address")
26+
.option(
27+
"--sort-by <sort>",
28+
"Sort order (relevance, newest, most_used)",
29+
"relevance",
30+
)
31+
.option("--limit <limit>", "Number of results", "20")
32+
.option("--next <cursor>", "Pagination cursor")
33+
.action(
34+
async (options: {
35+
query?: string
36+
registryChain?: string
37+
tags?: string
38+
accessType?: string
39+
creator?: string
40+
sortBy: string
41+
limit: string
42+
next?: string
43+
}) => {
44+
const client = getClient()
45+
await outputGet(client, getFormat(), "/api/v2/tools/search", {
46+
query: options.query,
47+
registry_chain: options.registryChain,
48+
tags: options.tags,
49+
access_type: options.accessType,
50+
creator: options.creator,
51+
sort_by: options.sortBy,
52+
limit: parseIntOption(options.limit, "--limit"),
53+
"cursor.value": options.next,
54+
})
55+
},
56+
)
57+
58+
cmd
59+
.command("get")
60+
.description(
61+
"Get detailed info about a registered tool by its composite key",
62+
)
63+
.argument("<registry_chain>", "Registry chain ID (e.g., 8453 for Base)")
64+
.argument("<registry_addr>", "Registry contract address")
65+
.argument("<tool_id>", "Numeric tool ID")
66+
.action(
67+
async (registryChain: string, registryAddr: string, toolId: string) => {
68+
const client = getClient()
69+
await outputGet(
70+
client,
71+
getFormat(),
72+
`/api/v2/tools/${registryChain}/${registryAddr}/${toolId}`,
73+
)
74+
},
75+
)
76+
77+
cmd
78+
.command("list")
79+
.description("List registered tools with optional sorting and filtering")
80+
.option("--sort-by <sort>", "Sort order (newest, oldest)", "newest")
81+
.option(
82+
"--type <type>",
83+
"Filter by access type (open, nft_gated, token_gated, subscription, gated)",
84+
)
85+
.option("--limit <limit>", "Number of results", "20")
86+
.option("--next <cursor>", "Pagination cursor")
87+
.action(
88+
async (options: {
89+
sortBy: string
90+
type?: string
91+
limit: string
92+
next?: string
93+
}) => {
94+
const client = getClient()
95+
await outputGet(client, getFormat(), "/api/v2/tools", {
96+
sort_by: options.sortBy,
97+
type: options.type,
98+
limit: parseIntOption(options.limit, "--limit"),
99+
"cursor.value": options.next,
100+
})
101+
},
102+
)
103+
104+
return cmd
105+
}

src/sdk.ts

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ import type {
4444
PortfolioStatsResponse,
4545
PriceHistoryResponse,
4646
ProfileCollectionsResponse,
47+
RegisteredToolResponse,
4748
SearchAssetType,
4849
SearchResponse,
4950
SwapExecuteRequest,
@@ -59,6 +60,8 @@ import type {
5960
TokenHoldersResponse,
6061
TokenLiquidityPoolsResponse,
6162
TokenSwapActivityPaginatedResponse,
63+
ToolListPaginatedResponse,
64+
ToolSearchPaginatedResponse,
6265
TraitFilter,
6366
TransactionReceiptRequest,
6467
TransactionReceiptResponse,
@@ -127,6 +130,7 @@ export class OpenSeaCLI {
127130
readonly swaps: SwapsAPI
128131
readonly transactions: TransactionsAPI
129132
readonly assets: AssetsAPI
133+
readonly tools: ToolsAPI
130134
readonly health: HealthAPI
131135

132136
constructor(config: OpenSeaClientConfig) {
@@ -144,6 +148,7 @@ export class OpenSeaCLI {
144148
this.swaps = new SwapsAPI(this.client)
145149
this.transactions = new TransactionsAPI(this.client)
146150
this.assets = new AssetsAPI(this.client)
151+
this.tools = new ToolsAPI(this.client)
147152
this.health = new HealthAPI(this.client)
148153
}
149154
}
@@ -1069,6 +1074,59 @@ export class AssetsAPI {
10691074
}
10701075
}
10711076

1077+
class ToolsAPI {
1078+
constructor(private client: OpenSeaClient) {}
1079+
1080+
async search(options?: {
1081+
query?: string
1082+
registryChain?: string
1083+
tags?: string[]
1084+
accessType?: string
1085+
creator?: string
1086+
sortBy?: string
1087+
limit?: number
1088+
next?: string
1089+
}): Promise<ToolSearchPaginatedResponse> {
1090+
return this.client.get<ToolSearchPaginatedResponse>(
1091+
"/api/v2/tools/search",
1092+
{
1093+
query: options?.query,
1094+
registry_chain: options?.registryChain,
1095+
tags: options?.tags?.join(","),
1096+
access_type: options?.accessType,
1097+
creator: options?.creator,
1098+
sort_by: options?.sortBy,
1099+
limit: options?.limit,
1100+
"cursor.value": options?.next,
1101+
},
1102+
)
1103+
}
1104+
1105+
async get(
1106+
registryChain: string,
1107+
registryAddr: string,
1108+
toolId: string,
1109+
): Promise<RegisteredToolResponse> {
1110+
return this.client.get<RegisteredToolResponse>(
1111+
`/api/v2/tools/${registryChain}/${registryAddr}/${toolId}`,
1112+
)
1113+
}
1114+
1115+
async list(options?: {
1116+
sortBy?: string
1117+
type?: string
1118+
limit?: number
1119+
next?: string
1120+
}): Promise<ToolListPaginatedResponse> {
1121+
return this.client.get<ToolListPaginatedResponse>("/api/v2/tools", {
1122+
sort_by: options?.sortBy,
1123+
type: options?.type,
1124+
limit: options?.limit,
1125+
"cursor.value": options?.next,
1126+
})
1127+
}
1128+
}
1129+
10721130
class HealthAPI {
10731131
constructor(private client: OpenSeaClient) {}
10741132

src/types/api.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,7 @@ export type CrossChainFulfillmentResponse =
222222
export type SwapTransactionResponse = Schemas["SwapTransactionResponse"]
223223
export type SwapExecuteRequest = Schemas["SwapExecuteRequest"]
224224
export type SwapExecuteResponse = Schemas["SwapExecuteResponse"]
225+
225226
export type SwapQuoteInput = Schemas["SwapQuoteInput"]
226227
export type TransactionReceiptRequest = Schemas["TransactionReceiptRequest"]
227228
export type TransactionReceiptResponse = Schemas["TransactionReceiptResponse"]
@@ -257,3 +258,9 @@ export interface ValidateMetadataResponse {
257258
statusCode?: number
258259
}
259260
}
261+
262+
// ── Tool Registry types ─────────────────────────────────────────────
263+
264+
export type RegisteredToolResponse = Schemas["RegisteredToolResponse"]
265+
export type ToolSearchPaginatedResponse = Schemas["ToolSearchPaginatedResponse"]
266+
export type ToolListPaginatedResponse = Schemas["ToolListPaginatedResponse"]

test/cli-api-error.test.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ vi.mock("../src/commands/index.js", () => ({
1717
swapsCommand: () => new Command("swaps"),
1818
tokenGroupsCommand: () => new Command("token-groups"),
1919
tokensCommand: () => new Command("tokens"),
20+
toolsCommand: () => new Command("tools"),
2021
transactionsCommand: () => new Command("transactions"),
2122
healthCommand: () => new Command("health"),
2223
walletCommand: () => new Command("wallet"),

test/cli-network-error.test.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ vi.mock("../src/commands/index.js", () => ({
1616
swapsCommand: () => new Command("swaps"),
1717
tokenGroupsCommand: () => new Command("token-groups"),
1818
tokensCommand: () => new Command("tokens"),
19+
toolsCommand: () => new Command("tools"),
1920
transactionsCommand: () => new Command("transactions"),
2021
healthCommand: () => new Command("health"),
2122
walletCommand: () => new Command("wallet"),

0 commit comments

Comments
 (0)