skilld-sdk 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +179 -0
- package/dist/contract.d.mts +6816 -0
- package/dist/contract.mjs +2440 -0
- package/dist/index.d.mts +97 -0
- package/dist/index.mjs +253 -0
- package/generated/openapi.v1.json +10244 -0
- package/package.json +48 -0
package/README.md
ADDED
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
# skilld-sdk
|
|
2
|
+
|
|
3
|
+
The typed TypeScript SDK and the OpenAPI document for the skilld API at `https://skilld.dev/api/v1`.
|
|
4
|
+
|
|
5
|
+
The skilld API reads the skilld registry and manages your account: likes, watches, collections, and the digest.
|
|
6
|
+
Every Skill in an answer carries its provenance: the Owner, the Repository, and the exact `SKILL.md`.
|
|
7
|
+
|
|
8
|
+
## Install
|
|
9
|
+
|
|
10
|
+
```sh
|
|
11
|
+
npm install skilld-sdk
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
The package is ESM only. It uses the runtime's global `fetch`, or a `fetch` you pass in.
|
|
15
|
+
|
|
16
|
+
## Quick start
|
|
17
|
+
|
|
18
|
+
```ts
|
|
19
|
+
import { createSkilldClient } from 'skilld-sdk'
|
|
20
|
+
|
|
21
|
+
// Public operations work without a token.
|
|
22
|
+
const skilld = createSkilldClient({ token: process.env.SKILLD_TOKEN })
|
|
23
|
+
|
|
24
|
+
const found = await skilld.skills.search({ query: { q: 'tailwind' } })
|
|
25
|
+
if (found._tag === 'Err')
|
|
26
|
+
throw new Error(found.error._tag)
|
|
27
|
+
|
|
28
|
+
for (const item of found.value.items)
|
|
29
|
+
console.log(`${item.source.owner}/${item.source.repository}/${item.name}`)
|
|
30
|
+
|
|
31
|
+
const skill = await skilld.skills.get({
|
|
32
|
+
params: { owner: 'vercel-labs', repository: 'agent-skills', name: 'web-design-guidelines' },
|
|
33
|
+
})
|
|
34
|
+
if (skill._tag === 'Ok')
|
|
35
|
+
console.log(skill.value.runCommand, skill.value.sourceUrl)
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Each operation has a place on the SDK: `skilld.<namespace>.<key>(input)`.
|
|
39
|
+
The input holds up to three locations: `params` for the path, `query` for the query string, and `body` for the JSON body.
|
|
40
|
+
The SDK checks the input against the operation's schema before it sends anything.
|
|
41
|
+
|
|
42
|
+
## Results and failures
|
|
43
|
+
|
|
44
|
+
Every call returns a result. Check `_tag` before you read `value`.
|
|
45
|
+
|
|
46
|
+
```ts
|
|
47
|
+
type Result<TValue, TError>
|
|
48
|
+
= | { _tag: 'Ok', value: TValue, requestId?: string }
|
|
49
|
+
| { _tag: 'Err', error: TError }
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Nothing throws for an expected failure.
|
|
53
|
+
`createSkilldClient` throws a `TypeError` only for bad options, such as a retry count out of range or a runtime with no `fetch`.
|
|
54
|
+
|
|
55
|
+
An `Err` holds one of four failures. Each has a `_tag` and the `operationId` of the call.
|
|
56
|
+
|
|
57
|
+
| `_tag` | When | What to do |
|
|
58
|
+
| --- | --- | --- |
|
|
59
|
+
| `RequestFailure` | The input failed the operation's schema, so the SDK sent nothing. `location` and `issues` say which input. | Fix the input. |
|
|
60
|
+
| `ApiFailure` | skilld.dev answered with a problem the operation declares. It carries `code`, `status`, `title`, `detail`, and `retryable`. | Read `code`. For `AUTH_REQUIRED`, send a skilld token. |
|
|
61
|
+
| `ContractFailure` | skilld.dev answered with something the contract does not allow. | Report it with `requestId`. |
|
|
62
|
+
| `TransportFailure` | No answer arrived. `reason` is `network`, `aborted`, or `credential`. `credential` means the token function threw. | Retry later if `retryable` is true. |
|
|
63
|
+
|
|
64
|
+
An `ApiFailure.code` is one of these:
|
|
65
|
+
|
|
66
|
+
| Code | Status | Retryable |
|
|
67
|
+
| --- | --- | --- |
|
|
68
|
+
| `INVALID_REQUEST` | 400 | no |
|
|
69
|
+
| `AUTH_REQUIRED` | 401 | no |
|
|
70
|
+
| `FORBIDDEN` | 403 | no |
|
|
71
|
+
| `NOT_FOUND` | 404 | no |
|
|
72
|
+
| `CONFLICT` | 409 | no |
|
|
73
|
+
| `RATE_LIMITED` | 429 | yes |
|
|
74
|
+
| `INTERNAL_ERROR` | 500 | no |
|
|
75
|
+
| `SERVICE_UNAVAILABLE` | 503 | yes |
|
|
76
|
+
|
|
77
|
+
Every operation can answer `INTERNAL_ERROR` and `SERVICE_UNAVAILABLE`. The OpenAPI document lists the other codes for each operation.
|
|
78
|
+
|
|
79
|
+
On the wire, every failure is RFC 9457 `application/problem+json` with exactly six fields: `type`, `title`, `status`, `detail`, `instance`, and `code`.
|
|
80
|
+
Every answer carries an `X-Request-Id` header. The SDK puts it on the result as `requestId`. Quote it when you report a problem.
|
|
81
|
+
|
|
82
|
+
## Authentication
|
|
83
|
+
|
|
84
|
+
Public operations need no credential. Account operations need a skilld token, or they answer `AUTH_REQUIRED`.
|
|
85
|
+
|
|
86
|
+
- To create a skilld token for a script, open [skilld.dev/me/cli-tokens/new](https://skilld.dev/me/cli-tokens/new).
|
|
87
|
+
- `skilld auth login` stores a skilld token for the skilld CLI in the operating system keychain. The SDK does not read it.
|
|
88
|
+
- To revoke a skilld token, open [skilld.dev/me/devices](https://skilld.dev/me/devices).
|
|
89
|
+
|
|
90
|
+
Pass the skilld token as a string, or as a function that returns one:
|
|
91
|
+
|
|
92
|
+
```ts
|
|
93
|
+
const skilld = createSkilldClient({
|
|
94
|
+
token: async () => readTokenFromYourVault(),
|
|
95
|
+
})
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
The SDK calls a token function before each attempt, so a rotated token takes effect on the next call.
|
|
99
|
+
It sends the skilld token as `Authorization: Bearer <token>`.
|
|
100
|
+
|
|
101
|
+
A page served from skilld.dev can use the sign-in cookie instead of a skilld token.
|
|
102
|
+
Account operations send no CORS header, so the cookie works only on skilld.dev itself.
|
|
103
|
+
|
|
104
|
+
## Retries
|
|
105
|
+
|
|
106
|
+
The SDK retries only a call that is safe to send again:
|
|
107
|
+
|
|
108
|
+
- a query (every `GET`)
|
|
109
|
+
- a `PUT` or `DELETE` mutation, because each one names the end state
|
|
110
|
+
|
|
111
|
+
A `POST` or `PATCH` is never sent twice.
|
|
112
|
+
|
|
113
|
+
A safe call retries after a network failure, `RATE_LIMITED`, or `SERVICE_UNAVAILABLE`.
|
|
114
|
+
The delay doubles from 200 ms up to 5 s. If skilld.dev sends `Retry-After`, the SDK waits at least that long, up to 60 s.
|
|
115
|
+
The default is 3 attempts, the first one included.
|
|
116
|
+
|
|
117
|
+
```ts
|
|
118
|
+
const skilld = createSkilldClient({
|
|
119
|
+
retry: { maxAttempts: 5, baseDelayMs: 500, maxDelayMs: 10_000 },
|
|
120
|
+
})
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
To turn retries off, set `retry: { maxAttempts: 1 }`.
|
|
124
|
+
To cancel a call, pass an `AbortSignal`: `skilld.skills.search({ query: { q: 'vue' } }, { signal })`.
|
|
125
|
+
|
|
126
|
+
## Other options
|
|
127
|
+
|
|
128
|
+
| Option | Default | Use |
|
|
129
|
+
| --- | --- | --- |
|
|
130
|
+
| `baseUrl` | `https://skilld.dev` | Point the SDK at a local or preview deployment. |
|
|
131
|
+
| `fetch` | `globalThis.fetch` | Supply your own `fetch`, for example in a test. |
|
|
132
|
+
| `headers` | none | Add headers to every request. |
|
|
133
|
+
| `credentials` | the `fetch` default | Passed to `fetch` as is. |
|
|
134
|
+
|
|
135
|
+
## The OpenAPI document
|
|
136
|
+
|
|
137
|
+
The OpenAPI 3.1 document describes every operation: its path, its input, its answer, its errors, and an example.
|
|
138
|
+
|
|
139
|
+
- skilld.dev serves it at [`https://skilld.dev/api/v1/openapi.json`](https://skilld.dev/api/v1/openapi.json).
|
|
140
|
+
- The package ships it as `skilld-sdk/openapi.json`.
|
|
141
|
+
|
|
142
|
+
```ts
|
|
143
|
+
import document from 'skilld-sdk/openapi.json' with { type: 'json' }
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
`skilld-sdk/contract` exports the operation descriptors and their [Zod](https://zod.dev) schemas.
|
|
147
|
+
`skilld.execute(operation, input)` calls an operation by its descriptor.
|
|
148
|
+
|
|
149
|
+
## Versioning
|
|
150
|
+
|
|
151
|
+
The API version is the URL major: `/api/v1`.
|
|
152
|
+
|
|
153
|
+
- Within v1, answers only gain fields. A field never changes its meaning, and a field never disappears.
|
|
154
|
+
- The SDK keeps unknown fields when it reads an answer, so a newer server never breaks an older SDK.
|
|
155
|
+
- A deprecated operation sends a `Deprecation` header. The OpenAPI document marks it, and names its replacement when one exists.
|
|
156
|
+
- A change that would break a caller ships under a new URL major, such as `/api/v2`.
|
|
157
|
+
|
|
158
|
+
One exception: the `skills.search` answer is frozen.
|
|
159
|
+
skilld 3.2.0 parses it with strict rules, so it never gains a field. Read new fields from `skills.get`.
|
|
160
|
+
|
|
161
|
+
The package version follows semver on its own, apart from the API version.
|
|
162
|
+
|
|
163
|
+
## Development
|
|
164
|
+
|
|
165
|
+
This package lives in the skilld.dev repository at `packages/sdk`, beside the server that serves the API.
|
|
166
|
+
One descriptor per operation in `src/contract` drives the OpenAPI document, the server's route checks, this SDK, and a route parity test.
|
|
167
|
+
ADR-0006 in that repository records the decisions.
|
|
168
|
+
|
|
169
|
+
```sh
|
|
170
|
+
pnpm --filter skilld-sdk generate # rewrite generated/openapi.v1.json from the contract
|
|
171
|
+
pnpm --filter skilld-sdk test # fails when the committed document drifts from the contract
|
|
172
|
+
pnpm --filter skilld-sdk typecheck
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
If you change a descriptor, run `generate` and commit the document with the change.
|
|
176
|
+
|
|
177
|
+
## License
|
|
178
|
+
|
|
179
|
+
MIT
|