feat: rename sdk package

This commit is contained in:
vimtor
2026-07-06 13:13:11 +02:00
parent 787fb9b325
commit 030205f889
9 changed files with 63 additions and 52 deletions
+1 -1
View File
@@ -30,7 +30,7 @@
},
},
"packages/sdk": {
"name": "models.dev",
"name": "@opencode-ai/models",
"version": "0.0.0",
"devDependencies": {
"@models.dev/core": "workspace:*",
+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2025 models.dev
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+7 -7
View File
@@ -1,9 +1,9 @@
# models.dev
# @opencode-ai/models
Official typed client for the [models.dev](https://models.dev) API — an open-source database of AI model capabilities, pricing, and limits.
```sh
npm install models.dev
npm install @opencode-ai/models
```
- **Zero dependencies.** The root client is a small `fetch` wrapper; works on Node ≥ 18, Bun, Deno, browsers, and edge runtimes.
@@ -13,7 +13,7 @@ npm install models.dev
## Usage
```ts
import { Models } from "models.dev"
import { Models } from "@opencode-ai/models"
const client = Models.make()
@@ -58,7 +58,7 @@ Errors are a single `ModelsDevError` with `reason: "Transport" | "UnexpectedStat
A full copy of the database ships inside the package as a separate, tree-shakable entrypoint — nothing from it is loaded or bundled unless you import it:
```ts
import snapshot, { providers, models, generatedAt } from "models.dev/snapshot"
import snapshot, { providers, models, generatedAt } from "@opencode-ai/models/snapshot"
providers["anthropic"]?.models["claude-opus-4-6"]?.limit.context
```
@@ -66,17 +66,17 @@ providers["anthropic"]?.models["claude-opus-4-6"]?.limit.context
Use it for no-network runtimes, tests, cold-start-sensitive paths, or as an explicit fallback:
```ts
const providers = await client.providers().catch(async () => (await import("models.dev/snapshot")).providers)
const providers = await client.providers().catch(async () => (await import("@opencode-ai/models/snapshot")).providers)
```
Freshness: the published snapshot is at most ~24h behind the live API (data releases are automated). The client is the freshness path; the snapshot is the availability path.
## Effect
An Effect-native client lives at `models.dev/effect` (requires the optional peer dependency `effect`):
An Effect-native client lives at `@opencode-ai/models/effect` (requires the optional peer dependency `effect`):
```ts
import { Models } from "models.dev/effect"
import { Models } from "@opencode-ai/models/effect"
import { FetchHttpClient } from "effect/unstable/http"
import { Effect } from "effect"
+14 -4
View File
@@ -1,6 +1,6 @@
{
"$schema": "https://json.schemastore.org/package.json",
"name": "models.dev",
"name": "@opencode-ai/models",
"version": "0.0.0",
"description": "Official typed client for the models.dev API \u2014 an open database of AI model capabilities, pricing, and limits",
"type": "module",
@@ -26,9 +26,18 @@
"node": ">=18"
},
"exports": {
".": "./src/index.ts",
"./effect": "./src/effect.ts",
"./snapshot": "./src/snapshot.js"
".": {
"types": "./dist/index.d.ts",
"default": "./dist/index.js"
},
"./effect": {
"types": "./dist/effect.d.ts",
"default": "./dist/effect.js"
},
"./snapshot": {
"types": "./dist/snapshot.d.ts",
"default": "./dist/snapshot.js"
}
},
"files": [
"dist"
@@ -36,6 +45,7 @@
"scripts": {
"generate": "bun script/generate.ts",
"build": "bun script/build.ts",
"prepack": "bun run build",
"typecheck": "tsc --noEmit",
"test": "bun run generate && bun run typecheck && bun test"
},
+11 -29
View File
@@ -1,12 +1,11 @@
#!/usr/bin/env bun
// Publishes models.dev to npm, opencode-style:
// - the version is never stored in git: it is `npm view models.dev version`
// Publishes @opencode-ai/models to npm, opencode-style:
// - the version is never stored in git: it is read from npm
// plus a semver bump computed here (patch by default);
// - `--if-changed` (scheduled data releases) skips publishing when the
// freshly generated snapshot payload is byte-identical to the one inside
// the currently published tarball;
// - package.json exports are rewritten src -> dist for the tarball and
// restored afterwards.
// - package.json and src/version.ts are restored after publishing.
//
// Auth: npm Trusted Publishing (OIDC) in CI — no token needed once the
// package is linked to this repo+workflow on npmjs.com. `--provenance` is
@@ -17,9 +16,9 @@ import { appendFile, mkdtemp, rm } from "node:fs/promises"
import { tmpdir } from "node:os"
import { $ } from "bun"
import { loadCatalog, snapshotPayload } from "./generate.ts"
import { build } from "./build.ts"
const pkg = path.join(import.meta.dirname, "..")
const packageName = "@opencode-ai/models"
const packageJsonPath = path.join(pkg, "package.json")
const versionTsPath = path.join(pkg, "src", "version.ts")
@@ -32,11 +31,7 @@ if (!["patch", "minor", "major"].includes(bumpArg)) {
}
async function currentVersion(): Promise<string> {
try {
return (await $`npm view models.dev version`.text()).trim()
} catch {
return "0.0.0"
}
return (await $`npm view ${packageName} version`.text()).trim()
}
function bump(version: string, kind: string): string {
@@ -50,7 +45,7 @@ function bump(version: string, kind: string): string {
async function publishedSnapshotLine(): Promise<string | undefined> {
const directory = await mkdtemp(path.join(tmpdir(), "models-dev-publish-"))
try {
const tarball = (await $`npm pack models.dev@latest --pack-destination ${directory}`.cwd(directory).text())
const tarball = (await $`npm pack ${packageName}@latest --pack-destination ${directory}`.cwd(directory).text())
.trim()
.split("\n")
.at(-1)!
@@ -59,8 +54,6 @@ async function publishedSnapshotLine(): Promise<string | undefined> {
if (!(await file.exists())) return undefined
const text = await file.text()
return text.split("\n").find((line) => line.startsWith("const data = "))
} catch {
return undefined
} finally {
await rm(directory, { recursive: true, force: true })
}
@@ -80,27 +73,16 @@ if (ifChanged) {
const current = await currentVersion()
const next = bump(current, bumpArg)
const alreadyPublished = await $`npm view models.dev@${next} version`.quiet().nothrow().text()
if (alreadyPublished.trim() === next) {
console.log(`models.dev@${next} already published; skipping`)
process.exit(0)
}
console.log(`Publishing ${packageName}@${next} (${bumpArg} bump from ${current})`)
console.log(`Publishing models.dev@${next} (${bumpArg} bump from ${current})`)
const packageJson = await Bun.file(packageJsonPath).json()
const packageJsonText = await Bun.file(packageJsonPath).text()
const packageJson = JSON.parse(packageJsonText)
const versionTs = await Bun.file(versionTsPath).text()
try {
await Bun.write(versionTsPath, versionTs.replace('"0.0.0"', JSON.stringify(next)))
await build()
packageJson.version = next
packageJson.exports = {
".": { types: "./dist/index.d.ts", default: "./dist/index.js" },
"./effect": { types: "./dist/effect.d.ts", default: "./dist/effect.js" },
"./snapshot": { types: "./dist/snapshot.d.ts", default: "./dist/snapshot.js" },
}
await Bun.write(packageJsonPath, JSON.stringify(packageJson, null, 2) + "\n")
const provenance = process.env["GITHUB_ACTIONS"] === "true" ? ["--provenance"] : []
@@ -108,7 +90,7 @@ try {
const output = process.env["GITHUB_OUTPUT"]
if (output !== undefined) await appendFile(output, `version=${next}\n`)
console.log(`Published models.dev@${next}`)
console.log(`Published ${packageName}@${next}`)
} finally {
await $`git checkout -- ${packageJsonPath} ${versionTsPath}`.nothrow()
await Promise.all([Bun.write(packageJsonPath, packageJsonText), Bun.write(versionTsPath, versionTs)])
}
+2 -3
View File
@@ -1,6 +1,5 @@
import { ModelsDevError } from "./error.js"
import type { Catalog, ModelMetadataMap, ProviderMap } from "./types.js"
import { VERSION } from "./version.js"
/** Accepted anywhere headers can be passed. Same shapes as the standard `HeadersInit`. */
export type HeadersInput = Headers | Record<string, string> | Array<[string, string]>
@@ -28,7 +27,7 @@ export interface RequestOptions {
* Creates a stateless models.dev client. Every method performs exactly one
* `GET` and nothing is ever cached — callers who want caching should wrap
* calls with their own policy. For a no-network alternative, see the
* `models.dev/snapshot` entrypoint.
* `@opencode-ai/models/snapshot` entrypoint.
*/
export function make(options: ClientOptions = {}) {
const baseUrl = options.baseUrl ?? "https://models.dev"
@@ -36,7 +35,7 @@ export function make(options: ClientOptions = {}) {
const request = async <A>(path: string, requestOptions?: RequestOptions): Promise<A> => {
const fetch = options.fetch ?? globalThis.fetch
const headers = new Headers({ "user-agent": `models.dev/${VERSION}` })
const headers = new Headers()
for (const [key, value] of new Headers(options.headers)) headers.set(key, value)
for (const [key, value] of new Headers(requestOptions?.headers)) headers.set(key, value)
+2 -3
View File
@@ -1,7 +1,6 @@
import { Context, Effect, Layer, Schema } from "effect"
import { HttpClient, HttpClientResponse } from "effect/unstable/http"
import type { Catalog, ModelMetadataMap, ProviderMap } from "../types.js"
import { VERSION } from "../version.js"
/** The only error in the failure channel of client methods. Wraps the underlying `HttpClientError` as `cause`. */
export class ModelsDevError extends Schema.TaggedErrorClass<ModelsDevError>()("ModelsDevError", {
@@ -30,7 +29,7 @@ export const make = (options?: ClientOptions) =>
const get = <A>(path: string): Effect.Effect<A, ModelsDevError> =>
http
.get(new URL(path, base), {
headers: { "user-agent": `models.dev/${VERSION}`, ...options?.headers },
headers: options?.headers,
})
.pipe(
Effect.flatMap(HttpClientResponse.filterStatusOk),
@@ -52,7 +51,7 @@ export const make = (options?: ClientOptions) =>
export type ModelsClient = Effect.Success<ReturnType<typeof make>>
/** Service key for dependency-injecting a shared client: `yield* Models.Service`. */
export class Service extends Context.Service<Service, ModelsClient>()("models.dev/Models") {}
export class Service extends Context.Service<Service, ModelsClient>()("@opencode-ai/models/Models") {}
/** Layer providing `Models.Service`; requires an `HttpClient` in the environment. */
export const layer = (options?: ClientOptions) => Layer.effect(Service)(make(options))
+4 -4
View File
@@ -1,5 +1,5 @@
import { expect, test } from "bun:test"
import { Models, ModelsDevError, VERSION } from "../src/index.js"
import { Models, ModelsDevError } from "../src/index.js"
interface Call {
url: URL
@@ -50,13 +50,13 @@ test("baseUrl with subpath is preserved, with or without trailing slash", async
])
})
test("identifies itself with a versioned user-agent", async () => {
test("does not add headers by default", async () => {
const { calls, fetch } = stub({})
await Models.make({ fetch }).providers()
expect(headers(calls[0]!).get("user-agent")).toBe(`models.dev/${VERSION}`)
expect([...headers(calls[0]!).entries()]).toEqual([])
})
test("client headers override defaults, request headers override client headers", async () => {
test("request headers override client headers", async () => {
const { calls, fetch } = stub({})
const client = Models.make({ fetch, headers: { "user-agent": "custom", "x-one": "client", "x-two": "client" } })
await client.providers({ headers: { "x-two": "request" } })
+1 -1
View File
@@ -25,7 +25,7 @@ test("providers() succeeds through an injected transport", async () => {
const result = await program.pipe(Effect.provide(layer), Effect.runPromise)
expect(result["anthropic"]?.id).toBe("anthropic")
expect(requests[0]?.url).toBe("https://models.dev/api.json")
expect(requests[0]?.headers.get("user-agent")).toMatch(/^models\.dev\//)
expect(requests[0]?.headers.get("user-agent")).toBeNull()
})
test("models() and catalog() hit their endpoints, baseUrl subpath preserved", async () => {