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
29 changes: 28 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ opensea --format table collections stats mfers
| `swaps` | Get swap quotes for token trading |
| `accounts` | Get account details |

Global options: `--api-key`, `--chain` (default: ethereum), `--format` (json/table), `--base-url`
Global options: `--api-key`, `--chain` (default: ethereum), `--format` (json/table/toon), `--base-url`

Full command reference with all options and flags: [docs/cli-reference.md](docs/cli-reference.md)

Expand Down Expand Up @@ -137,6 +137,33 @@ Table - human-readable output:
opensea --format table collections list --limit 5
```

TOON - [Token-Oriented Object Notation](https://github.com/toon-format/toon), a compact format that uses ~40% fewer tokens than JSON. Ideal for piping output into LLM / AI agent context windows:

```bash
opensea --format toon tokens trending --limit 5
```

Example TOON output for a list of tokens:

```
tokens[3]{name,symbol,chain,market_cap,price_usd}:
Ethereum,ETH,ethereum,250000000000,2100.50
Bitcoin,BTC,bitcoin,900000000000,48000.00
Solana,SOL,solana,30000000000,95.25
next: abc123
```

TOON collapses uniform arrays of objects into CSV-like tables with a single header row, while nested objects use YAML-like indentation. The encoder follows the [TOON v3.0 spec](https://github.com/toon-format/spec/blob/main/SPEC.md) and is implemented without external dependencies.

TOON is also available programmatically via the SDK:

```typescript
import { formatToon } from "@opensea/cli"

const data = await client.tokens.trending({ limit: 5 })
console.log(formatToon(data))
```

## Exit Codes

- `0` - Success
Expand Down
9 changes: 6 additions & 3 deletions src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
swapsCommand,
tokensCommand,
} from "./commands/index.js"
import type { OutputFormat } from "./output.js"
import { parseIntOption } from "./parse.js"

const BANNER = `
Expand All @@ -33,7 +34,7 @@ program
.addHelpText("before", BANNER)
.option("--api-key <key>", "OpenSea API key (or set OPENSEA_API_KEY env var)")
.option("--chain <chain>", "Default chain", "ethereum")
.option("--format <format>", "Output format (json or table)", "json")
.option("--format <format>", "Output format (json, table, or toon)", "json")
.option("--base-url <url>", "API base URL")
.option("--timeout <ms>", "Request timeout in milliseconds", "30000")
.option("--verbose", "Log request and response info to stderr")
Expand Down Expand Up @@ -64,9 +65,11 @@ function getClient(): OpenSeaClient {
})
}

function getFormat(): "json" | "table" {
function getFormat(): OutputFormat {
const opts = program.opts<{ format: string }>()
return opts.format === "table" ? "table" : "json"
if (opts.format === "table") return "table"
if (opts.format === "toon") return "toon"
return "json"
}

program.addCommand(collectionsCommand(getClient, getFormat))
Expand Down
3 changes: 2 additions & 1 deletion src/commands/accounts.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import { Command } from "commander"
import type { OpenSeaClient } from "../client.js"
import type { OutputFormat } from "../output.js"
import { formatOutput } from "../output.js"
import type { Account } from "../types/index.js"

export function accountsCommand(
getClient: () => OpenSeaClient,
getFormat: () => "json" | "table",
getFormat: () => OutputFormat,
): Command {
const cmd = new Command("accounts").description("Query accounts")

Expand Down
3 changes: 2 additions & 1 deletion src/commands/collections.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Command } from "commander"
import type { OpenSeaClient } from "../client.js"
import type { OutputFormat } from "../output.js"
import { formatOutput } from "../output.js"
import { parseIntOption } from "../parse.js"
import type {
Expand All @@ -12,7 +13,7 @@ import type {

export function collectionsCommand(
getClient: () => OpenSeaClient,
getFormat: () => "json" | "table",
getFormat: () => OutputFormat,
): Command {
const cmd = new Command("collections").description(
"Manage and query NFT collections",
Expand Down
3 changes: 2 additions & 1 deletion src/commands/events.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import { Command } from "commander"
import type { OpenSeaClient } from "../client.js"
import type { OutputFormat } from "../output.js"
import { formatOutput } from "../output.js"
import { parseIntOption } from "../parse.js"
import type { AssetEvent } from "../types/index.js"

export function eventsCommand(
getClient: () => OpenSeaClient,
getFormat: () => "json" | "table",
getFormat: () => OutputFormat,
): Command {
const cmd = new Command("events").description("Query marketplace events")

Expand Down
3 changes: 2 additions & 1 deletion src/commands/listings.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import { Command } from "commander"
import type { OpenSeaClient } from "../client.js"
import type { OutputFormat } from "../output.js"
import { formatOutput } from "../output.js"
import { parseIntOption } from "../parse.js"
import type { Listing } from "../types/index.js"

export function listingsCommand(
getClient: () => OpenSeaClient,
getFormat: () => "json" | "table",
getFormat: () => OutputFormat,
): Command {
const cmd = new Command("listings").description("Query NFT listings")

Expand Down
3 changes: 2 additions & 1 deletion src/commands/nfts.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import { Command } from "commander"
import type { OpenSeaClient } from "../client.js"
import type { OutputFormat } from "../output.js"
import { formatOutput } from "../output.js"
import { parseIntOption } from "../parse.js"
import type { Contract, NFT } from "../types/index.js"

export function nftsCommand(
getClient: () => OpenSeaClient,
getFormat: () => "json" | "table",
getFormat: () => OutputFormat,
): Command {
const cmd = new Command("nfts").description("Query NFTs")

Expand Down
3 changes: 2 additions & 1 deletion src/commands/offers.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import { Command } from "commander"
import type { OpenSeaClient } from "../client.js"
import type { OutputFormat } from "../output.js"
import { formatOutput } from "../output.js"
import { parseIntOption } from "../parse.js"
import type { Offer } from "../types/index.js"

export function offersCommand(
getClient: () => OpenSeaClient,
getFormat: () => "json" | "table",
getFormat: () => OutputFormat,
): Command {
const cmd = new Command("offers").description("Query NFT offers")

Expand Down
3 changes: 2 additions & 1 deletion src/commands/search.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Command } from "commander"
import type { OpenSeaClient } from "../client.js"
import type { OutputFormat } from "../output.js"
import { formatOutput } from "../output.js"
import { parseIntOption } from "../parse.js"
import {
Expand All @@ -17,7 +18,7 @@ import type {

export function searchCommand(
getClient: () => OpenSeaClient,
getFormat: () => "json" | "table",
getFormat: () => OutputFormat,
): Command {
const cmd = new Command("search").description(
"Search for collections, NFTs, tokens, and accounts",
Expand Down
3 changes: 2 additions & 1 deletion src/commands/swaps.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import { Command } from "commander"
import type { OpenSeaClient } from "../client.js"
import type { OutputFormat } from "../output.js"
import { formatOutput } from "../output.js"
import { parseFloatOption } from "../parse.js"
import type { SwapQuoteResponse } from "../types/index.js"

export function swapsCommand(
getClient: () => OpenSeaClient,
getFormat: () => "json" | "table",
getFormat: () => OutputFormat,
): Command {
const cmd = new Command("swaps").description(
"Get swap quotes for token trading",
Expand Down
3 changes: 2 additions & 1 deletion src/commands/tokens.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import { Command } from "commander"
import type { OpenSeaClient } from "../client.js"
import type { OutputFormat } from "../output.js"
import { formatOutput } from "../output.js"
import { parseIntOption } from "../parse.js"
import type { Chain, Token, TokenDetails } from "../types/index.js"

export function tokensCommand(
getClient: () => OpenSeaClient,
getFormat: () => "json" | "table",
getFormat: () => OutputFormat,
): Command {
const cmd = new Command("tokens").description(
"Query trending tokens, top tokens, and token details",
Expand Down
3 changes: 3 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
export { OpenSeaAPIError, OpenSeaClient } from "./client.js"
export type { OutputFormat } from "./output.js"
export { formatOutput } from "./output.js"
export { OpenSeaCLI } from "./sdk.js"
export { formatToon } from "./toon.js"
export type * from "./types/index.js"
9 changes: 8 additions & 1 deletion src/output.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,14 @@
export function formatOutput(data: unknown, format: "json" | "table"): string {
import { formatToon } from "./toon.js"

export type OutputFormat = "json" | "table" | "toon"

export function formatOutput(data: unknown, format: OutputFormat): string {
if (format === "table") {
return formatTable(data)
}
if (format === "toon") {
return formatToon(data)
}
return JSON.stringify(data, null, 2)
}

Expand Down
Loading