Skip to content

Commit e2af5bf

Browse files
committed
Release v1.2.0
1 parent 1988e25 commit e2af5bf

12 files changed

Lines changed: 243 additions & 3 deletions

.github/workflows/npm-publish-reusable.yml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,11 @@ jobs:
3737
# Use npm install (not npm ci) because the package-lock.json may be
3838
# out of sync after the sync-package workflow resolves workspace: protocols
3939
# and updates dependencies. npm ci requires exact lockfile match.
40-
- run: npm install
40+
#
41+
# --legacy-peer-deps mirrors pnpm's permissive peer-dep behavior used in the
42+
# monorepo. Without it, packages like api-types fail on openapi-typescript@^7
43+
# which declares a strict `typescript@^5.x` peer while we ship typescript@^6.
44+
- run: npm install --legacy-peer-deps
4145

4246
- if: ${{ inputs.build-before-publish }}
4347
run: npm run build

CHANGELOG.md

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

3+
## 1.2.0
4+
5+
### Minor Changes
6+
7+
- bc9c6ce: Add token-groups and instant API key endpoints.
8+
9+
**SDK**:
10+
11+
- `sdk.api.getTokenGroups({ limit?, cursor? })` and `sdk.api.getTokenGroup(slug)` for the new `/api/v2/token-groups` endpoints.
12+
- `OpenSeaSDK.requestInstantApiKey()` and `OpenSeaAPI.requestInstantApiKey()` — static methods that call `POST /api/v2/auth/keys` without authentication and return a free-tier key you can pass into the SDK constructor. Rate limited to 3 keys/hour per IP; keys expire after 30 days.
13+
- `OpenSeaAPI` class is now exported from the package root (`@opensea/sdk` and `@opensea/sdk/viem`).
14+
15+
**CLI**:
16+
17+
- New `opensea token-groups list` and `opensea token-groups get <slug>` commands.
18+
- New `opensea auth request-key` command — works without `--api-key` / `OPENSEA_API_KEY` since the endpoint is unauthenticated.
19+
20+
### Patch Changes
21+
22+
- Updated dependencies [5b6ba13]
23+
- @opensea/api-types@0.2.1
24+
325
## 1.1.0
426

527
### 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.1.0",
3+
"version": "1.2.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.2.0",
26+
"@opensea/api-types": "^0.2.1",
2727
"commander": "^14.0.3",
2828
"zod": "^4.3.6"
2929
},

src/cli.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { Command } from "commander"
22
import { OpenSeaAPIError, OpenSeaClient } from "./client.js"
33
import {
44
accountsCommand,
5+
authCommand,
56
chainsCommand,
67
collectionsCommand,
78
dropsCommand,
@@ -12,6 +13,7 @@ import {
1213
offersCommand,
1314
searchCommand,
1415
swapsCommand,
16+
tokenGroupsCommand,
1517
tokensCommand,
1618
} from "./commands/index.js"
1719
import { type OutputFormat, setOutputOptions } from "./output.js"
@@ -123,6 +125,10 @@ program.addCommand(offersCommand(getClient, getFormat))
123125
program.addCommand(eventsCommand(getClient, getFormat))
124126
program.addCommand(accountsCommand(getClient, getFormat))
125127
program.addCommand(tokensCommand(getClient, getFormat))
128+
program.addCommand(tokenGroupsCommand(getClient, getFormat))
129+
program.addCommand(
130+
authCommand(() => program.opts<{ baseUrl?: string }>().baseUrl, getFormat),
131+
)
126132
program.addCommand(searchCommand(getClient, getFormat))
127133
program.addCommand(swapsCommand(getClient, getFormat))
128134
program.addCommand(healthCommand(getClient, getFormat))

src/commands/auth.ts

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import { Command } from "commander"
2+
import type { OutputFormat } from "../output.js"
3+
import { formatOutput } from "../output.js"
4+
5+
const DEFAULT_BASE_URL = "https://api.opensea.io"
6+
7+
export function authCommand(
8+
getBaseUrl: () => string | undefined,
9+
getFormat: () => OutputFormat,
10+
): Command {
11+
const cmd = new Command("auth").description(
12+
"Authentication helpers — bootstrap an API key without existing credentials",
13+
)
14+
15+
cmd
16+
.command("request-key")
17+
.description(
18+
"Request a free-tier API key (rate limited to 3/hour per IP, keys expire after 30 days)",
19+
)
20+
.action(async () => {
21+
const baseUrl = getBaseUrl() ?? DEFAULT_BASE_URL
22+
const response = await fetch(`${baseUrl}/api/v2/auth/keys`, {
23+
method: "POST",
24+
headers: { "Content-Type": "application/json" },
25+
body: "{}",
26+
})
27+
if (!response.ok) {
28+
const body = await response.text().catch(() => "")
29+
console.error(
30+
JSON.stringify(
31+
{ error: "API Error", status: response.status, body },
32+
null,
33+
2,
34+
),
35+
)
36+
process.exit(1)
37+
}
38+
const result = (await response.json()) as Record<string, unknown>
39+
console.log(formatOutput(result, getFormat()))
40+
})
41+
42+
return cmd
43+
}

src/commands/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
export { accountsCommand } from "./accounts.js"
2+
export { authCommand } from "./auth.js"
23
export { chainsCommand } from "./chains.js"
34
export { collectionsCommand } from "./collections.js"
45
export { dropsCommand } from "./drops.js"
@@ -9,4 +10,5 @@ export { nftsCommand } from "./nfts.js"
910
export { offersCommand } from "./offers.js"
1011
export { searchCommand } from "./search.js"
1112
export { swapsCommand } from "./swaps.js"
13+
export { tokenGroupsCommand } from "./token-groups.js"
1214
export { tokensCommand } from "./tokens.js"

src/commands/token-groups.ts

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import { Command } from "commander"
2+
import type { OpenSeaClient } from "../client.js"
3+
import type { OutputFormat } from "../output.js"
4+
import { formatOutput } from "../output.js"
5+
import { parseIntOption } from "../parse.js"
6+
7+
type TokenGroup = Record<string, unknown>
8+
9+
export function tokenGroupsCommand(
10+
getClient: () => OpenSeaClient,
11+
getFormat: () => OutputFormat,
12+
): Command {
13+
const cmd = new Command("token-groups").description(
14+
"Query token groups (equivalent currencies across chains, e.g. 'eth')",
15+
)
16+
17+
cmd
18+
.command("list")
19+
.description("List token groups sorted by market cap descending")
20+
.option("--limit <limit>", "Number of results (max 100, default 50)", "50")
21+
.option("--next <cursor>", "Pagination cursor")
22+
.action(async (options: { limit: string; next?: string }) => {
23+
const client = getClient()
24+
const result = await client.get<{
25+
token_groups: TokenGroup[]
26+
next?: string
27+
}>("/api/v2/token-groups", {
28+
limit: parseIntOption(options.limit, "--limit"),
29+
// Token Groups API uses "cursor" instead of "next" as the query param
30+
cursor: options.next,
31+
})
32+
console.log(formatOutput(result, getFormat()))
33+
})
34+
35+
cmd
36+
.command("get")
37+
.description("Get a single token group by slug")
38+
.argument("<slug>", "Token group slug (e.g. 'eth')")
39+
.action(async (slug: string) => {
40+
const client = getClient()
41+
const result = await client.get<TokenGroup>(
42+
`/api/v2/token-groups/${slug}`,
43+
)
44+
console.log(formatOutput(result, getFormat()))
45+
})
46+
47+
return cmd
48+
}

test/cli-api-error.test.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { OpenSeaAPIError } from "../src/client.js"
44

55
vi.mock("../src/commands/index.js", () => ({
66
accountsCommand: () => new Command("accounts"),
7+
authCommand: () => new Command("auth"),
78
chainsCommand: () => new Command("chains"),
89
collectionsCommand: () => new Command("collections"),
910
dropsCommand: () => new Command("drops"),
@@ -13,6 +14,7 @@ vi.mock("../src/commands/index.js", () => ({
1314
offersCommand: () => new Command("offers"),
1415
searchCommand: () => new Command("search"),
1516
swapsCommand: () => new Command("swaps"),
17+
tokenGroupsCommand: () => new Command("token-groups"),
1618
tokensCommand: () => new Command("tokens"),
1719
healthCommand: () => new Command("health"),
1820
}))

test/cli-network-error.test.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { afterAll, expect, it, vi } from "vitest"
33

44
vi.mock("../src/commands/index.js", () => ({
55
accountsCommand: () => new Command("accounts"),
6+
authCommand: () => new Command("auth"),
67
chainsCommand: () => new Command("chains"),
78
collectionsCommand: () => new Command("collections"),
89
dropsCommand: () => new Command("drops"),
@@ -12,6 +13,7 @@ vi.mock("../src/commands/index.js", () => ({
1213
offersCommand: () => new Command("offers"),
1314
searchCommand: () => new Command("search"),
1415
swapsCommand: () => new Command("swaps"),
16+
tokenGroupsCommand: () => new Command("token-groups"),
1517
tokensCommand: () => new Command("tokens"),
1618
healthCommand: () => new Command("health"),
1719
}))

test/cli-rate-limit.test.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { OpenSeaAPIError } from "../src/client.js"
44

55
vi.mock("../src/commands/index.js", () => ({
66
accountsCommand: () => new Command("accounts"),
7+
authCommand: () => new Command("auth"),
78
chainsCommand: () => new Command("chains"),
89
collectionsCommand: () => new Command("collections"),
910
dropsCommand: () => new Command("drops"),
@@ -13,6 +14,7 @@ vi.mock("../src/commands/index.js", () => ({
1314
offersCommand: () => new Command("offers"),
1415
searchCommand: () => new Command("search"),
1516
swapsCommand: () => new Command("swaps"),
17+
tokenGroupsCommand: () => new Command("token-groups"),
1618
tokensCommand: () => new Command("tokens"),
1719
healthCommand: () => new Command("health"),
1820
}))

0 commit comments

Comments
 (0)