@compose-market/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.
Files changed (3) hide show
  1. package/index.d.ts +244 -0
  2. package/index.js +397 -0
  3. package/package.json +126 -0
package/index.d.ts ADDED
@@ -0,0 +1,244 @@
1
+ type FetchLike = typeof fetch;
2
+ export interface ComposeSDKOptions {
3
+ baseUrl?: string;
4
+ fetch?: FetchLike;
5
+ walletAddress?: string;
6
+ chainId?: number;
7
+ composeKey?: string;
8
+ }
9
+ export interface ComposeRequestOptions {
10
+ walletAddress?: string;
11
+ chainId?: number;
12
+ composeKey?: string | null;
13
+ paymentSignature?: string;
14
+ x402MaxAmountWei?: string;
15
+ headers?: HeadersInit;
16
+ }
17
+ export interface PaymentRequirement {
18
+ scheme: string;
19
+ network: string;
20
+ amount: string;
21
+ asset: string;
22
+ payTo: string;
23
+ maxTimeoutSeconds: number;
24
+ extra?: Record<string, unknown> | null;
25
+ }
26
+ export interface PaymentRequired {
27
+ x402Version: 2;
28
+ error?: string;
29
+ resource: {
30
+ url: string;
31
+ description?: string;
32
+ mimeType?: string;
33
+ };
34
+ accepts: PaymentRequirement[];
35
+ extensions?: Record<string, unknown> | null;
36
+ }
37
+ export interface ModelParamDefinition {
38
+ type: "string" | "integer" | "number" | "boolean" | "array";
39
+ required: boolean;
40
+ default?: string | number | boolean;
41
+ options?: Array<string | number>;
42
+ description?: string;
43
+ }
44
+ export interface ModelParamsResponse {
45
+ modelId: string;
46
+ type: "video" | "image" | null;
47
+ provider: string | null;
48
+ params: Record<string, ModelParamDefinition>;
49
+ defaults: Record<string, unknown>;
50
+ }
51
+ export interface FacilitatorSupportedResponse {
52
+ kinds: Array<{
53
+ x402Version: number;
54
+ scheme: string;
55
+ network: string;
56
+ extra?: Record<string, unknown>;
57
+ }>;
58
+ extensions: string[];
59
+ signers?: Record<string, string[]>;
60
+ }
61
+ export interface ComposeKeyResponse {
62
+ keyId: string;
63
+ token: string;
64
+ purpose: "session" | "api";
65
+ budgetLimit: string;
66
+ budgetUsed: string;
67
+ budgetRemaining: string;
68
+ createdAt: number;
69
+ expiresAt: number;
70
+ chainId: number;
71
+ name?: string;
72
+ }
73
+ export interface ActiveComposeKeyResponse {
74
+ hasSession: boolean;
75
+ reason?: string;
76
+ keyId?: string;
77
+ token?: string;
78
+ budgetLimit?: string;
79
+ budgetUsed?: string;
80
+ budgetLocked?: string;
81
+ budgetRemaining?: string;
82
+ expiresAt?: number;
83
+ chainId?: number;
84
+ name?: string;
85
+ status?: {
86
+ isActive: boolean;
87
+ isExpired: boolean;
88
+ expiresInSeconds: number;
89
+ budgetPercentRemaining: number;
90
+ warnings: {
91
+ budgetDepleted: boolean;
92
+ budgetLow: boolean;
93
+ expiringSoon: boolean;
94
+ expired: boolean;
95
+ };
96
+ };
97
+ }
98
+ export interface ComposeKeyListItem {
99
+ keyId: string;
100
+ purpose: "session" | "api";
101
+ budgetLimit: number;
102
+ budgetUsed: number;
103
+ budgetRemaining: number;
104
+ createdAt: number;
105
+ expiresAt: number;
106
+ revokedAt?: number;
107
+ name?: string;
108
+ lastUsedAt?: number;
109
+ }
110
+ export interface WalletAttachment {
111
+ address: string;
112
+ chainId: number;
113
+ activeKey: ActiveComposeKeyResponse;
114
+ }
115
+ export declare class ComposeApiError extends Error {
116
+ readonly status: number;
117
+ readonly body: unknown;
118
+ readonly headers: Record<string, string>;
119
+ constructor(input: {
120
+ message: string;
121
+ status: number;
122
+ body: unknown;
123
+ headers: Record<string, string>;
124
+ });
125
+ }
126
+ export declare class ComposePaymentRequiredError extends ComposeApiError {
127
+ readonly paymentRequired: PaymentRequired;
128
+ readonly paymentRequiredHeader: string | null;
129
+ constructor(input: {
130
+ message: string;
131
+ status: number;
132
+ body: unknown;
133
+ headers: Record<string, string>;
134
+ paymentRequired: PaymentRequired;
135
+ paymentRequiredHeader: string | null;
136
+ });
137
+ }
138
+ export declare class ComposeSDK {
139
+ private readonly baseUrl;
140
+ private readonly fetchImpl;
141
+ private walletAddress;
142
+ private chainId;
143
+ private composeKey;
144
+ readonly wallets: {
145
+ attach: (input: {
146
+ address: string;
147
+ chainId: number;
148
+ }) => Promise<WalletAttachment>;
149
+ current: () => {
150
+ address: string | null;
151
+ chainId: number | null;
152
+ };
153
+ clear: () => void;
154
+ };
155
+ readonly keys: {
156
+ create: (input: {
157
+ purpose: "session" | "api";
158
+ budgetUsd?: number;
159
+ budgetWei?: number | string;
160
+ durationHours?: number;
161
+ expiresAt?: number;
162
+ chainId?: number;
163
+ name?: string;
164
+ }) => Promise<ComposeKeyResponse>;
165
+ getActive: (input?: {
166
+ chainId?: number;
167
+ }) => Promise<ActiveComposeKeyResponse>;
168
+ list: () => Promise<ComposeKeyListItem[]>;
169
+ revoke: (keyId: string) => Promise<{
170
+ success: boolean;
171
+ keyId: string;
172
+ }>;
173
+ use: (token: string) => void;
174
+ currentToken: () => string | null;
175
+ clearToken: () => void;
176
+ };
177
+ readonly models: {
178
+ list: () => Promise<{
179
+ object: "list";
180
+ data: Array<Record<string, unknown>>;
181
+ }>;
182
+ listAll: () => Promise<{
183
+ object: "list";
184
+ data: Array<Record<string, unknown>>;
185
+ }>;
186
+ get: (modelId: string) => Promise<Record<string, unknown>>;
187
+ getParams: (modelId: string) => Promise<ModelParamsResponse>;
188
+ };
189
+ readonly pricing: {
190
+ list: () => Promise<{
191
+ models: Array<Record<string, unknown>>;
192
+ [key: string]: unknown;
193
+ }>;
194
+ };
195
+ readonly inference: {
196
+ responses: {
197
+ create: (body: Record<string, unknown>, options?: ComposeRequestOptions) => Promise<Record<string, unknown>>;
198
+ get: (responseId: string, options?: ComposeRequestOptions) => Promise<Record<string, unknown>>;
199
+ inputItems: (responseId: string, options?: ComposeRequestOptions) => Promise<Record<string, unknown>>;
200
+ cancel: (responseId: string, options?: ComposeRequestOptions) => Promise<Record<string, unknown>>;
201
+ };
202
+ chat: {
203
+ completions: {
204
+ create: (body: Record<string, unknown>, options?: ComposeRequestOptions) => Promise<Record<string, unknown>>;
205
+ };
206
+ };
207
+ images: {
208
+ generate: (body: Record<string, unknown>, options?: ComposeRequestOptions) => Promise<Record<string, unknown>>;
209
+ edit: (body: Record<string, unknown>, options?: ComposeRequestOptions) => Promise<Record<string, unknown>>;
210
+ };
211
+ audio: {
212
+ speech: (body: Record<string, unknown>, options?: ComposeRequestOptions) => Promise<Record<string, unknown>>;
213
+ transcriptions: (body: Record<string, unknown>, options?: ComposeRequestOptions) => Promise<Record<string, unknown>>;
214
+ };
215
+ embeddings: {
216
+ create: (body: Record<string, unknown>, options?: ComposeRequestOptions) => Promise<Record<string, unknown>>;
217
+ };
218
+ videos: {
219
+ generate: (body: Record<string, unknown>, options?: ComposeRequestOptions) => Promise<Record<string, unknown>>;
220
+ get: (videoId: string, options?: ComposeRequestOptions) => Promise<Record<string, unknown>>;
221
+ };
222
+ };
223
+ readonly x402: {
224
+ facilitator: {
225
+ getSupported: () => Promise<FacilitatorSupportedResponse>;
226
+ verify: (body: Record<string, unknown>) => Promise<Record<string, unknown>>;
227
+ settle: (body: Record<string, unknown>) => Promise<Record<string, unknown>>;
228
+ };
229
+ decodePaymentRequired: (headerValue: string) => PaymentRequired;
230
+ };
231
+ constructor(options?: ComposeSDKOptions);
232
+ private resolveWalletContext;
233
+ private resolveComposeKeyToken;
234
+ private attachWallet;
235
+ private createKey;
236
+ private getActiveKey;
237
+ private listKeys;
238
+ private revokeKey;
239
+ private buildHeaders;
240
+ private requestJson;
241
+ private buildError;
242
+ private extractPaymentRequired;
243
+ }
244
+ export {};
package/index.js ADDED
@@ -0,0 +1,397 @@
1
+ export class ComposeApiError extends Error {
2
+ status;
3
+ body;
4
+ headers;
5
+ constructor(input) {
6
+ super(input.message);
7
+ this.name = "ComposeApiError";
8
+ this.status = input.status;
9
+ this.body = input.body;
10
+ this.headers = input.headers;
11
+ }
12
+ }
13
+ export class ComposePaymentRequiredError extends ComposeApiError {
14
+ paymentRequired;
15
+ paymentRequiredHeader;
16
+ constructor(input) {
17
+ super(input);
18
+ this.name = "ComposePaymentRequiredError";
19
+ this.paymentRequired = input.paymentRequired;
20
+ this.paymentRequiredHeader = input.paymentRequiredHeader;
21
+ }
22
+ }
23
+ function normalizeBaseUrl(baseUrl) {
24
+ return (baseUrl || "https://api.compose.market").replace(/\/+$/, "");
25
+ }
26
+ function normalizeWalletAddress(address) {
27
+ const normalized = address.trim().toLowerCase();
28
+ if (!/^0x[a-f0-9]{40}$/u.test(normalized)) {
29
+ throw new Error("wallet address must be a valid 0x-prefixed EVM address");
30
+ }
31
+ return normalized;
32
+ }
33
+ function resolveChainId(chainId) {
34
+ if (!Number.isInteger(chainId) || Number(chainId) <= 0) {
35
+ throw new Error("chainId must be a positive integer");
36
+ }
37
+ return Number(chainId);
38
+ }
39
+ function toBudgetWei(input) {
40
+ const hasBudgetUsd = typeof input.budgetUsd === "number";
41
+ const hasBudgetWei = typeof input.budgetWei === "number" || typeof input.budgetWei === "string";
42
+ if (Number(hasBudgetUsd) + Number(hasBudgetWei) !== 1) {
43
+ throw new Error("provide exactly one of budgetUsd or budgetWei");
44
+ }
45
+ if (hasBudgetUsd) {
46
+ const usd = input.budgetUsd;
47
+ if (!Number.isFinite(usd) || usd <= 0) {
48
+ throw new Error("budgetUsd must be a positive number");
49
+ }
50
+ return String(Math.round(usd * 1_000_000));
51
+ }
52
+ if (typeof input.budgetWei === "number") {
53
+ if (!Number.isInteger(input.budgetWei) || input.budgetWei <= 0) {
54
+ throw new Error("budgetWei must be a positive integer");
55
+ }
56
+ return String(input.budgetWei);
57
+ }
58
+ const trimmed = input.budgetWei.trim();
59
+ if (!/^\d+$/u.test(trimmed) || BigInt(trimmed) <= 0n) {
60
+ throw new Error("budgetWei must be a positive integer string");
61
+ }
62
+ return trimmed;
63
+ }
64
+ function resolveExpiresAt(input) {
65
+ const hasDurationHours = typeof input.durationHours === "number";
66
+ const hasExpiresAt = typeof input.expiresAt === "number";
67
+ if (Number(hasDurationHours) + Number(hasExpiresAt) !== 1) {
68
+ throw new Error("provide exactly one of durationHours or expiresAt");
69
+ }
70
+ if (hasExpiresAt) {
71
+ if (!Number.isInteger(input.expiresAt) || input.expiresAt <= Date.now()) {
72
+ throw new Error("expiresAt must be a future unix timestamp in milliseconds");
73
+ }
74
+ return input.expiresAt;
75
+ }
76
+ const durationHours = input.durationHours;
77
+ if (!Number.isFinite(durationHours) || durationHours <= 0) {
78
+ throw new Error("durationHours must be a positive number");
79
+ }
80
+ return Date.now() + Math.round(durationHours * 60 * 60 * 1000);
81
+ }
82
+ function headersToRecord(headers) {
83
+ return Object.fromEntries(headers.entries());
84
+ }
85
+ function tryParseJson(value) {
86
+ if (!value) {
87
+ return null;
88
+ }
89
+ try {
90
+ return JSON.parse(value);
91
+ }
92
+ catch {
93
+ return value;
94
+ }
95
+ }
96
+ function parseErrorMessage(body, fallback) {
97
+ if (body && typeof body === "object") {
98
+ const record = body;
99
+ if (typeof record.error === "string" && record.error.trim().length > 0) {
100
+ return record.error;
101
+ }
102
+ if (record.error && typeof record.error === "object") {
103
+ const nested = record.error;
104
+ if (typeof nested.message === "string" && nested.message.trim().length > 0) {
105
+ return nested.message;
106
+ }
107
+ }
108
+ if (typeof record.message === "string" && record.message.trim().length > 0) {
109
+ return record.message;
110
+ }
111
+ }
112
+ return fallback;
113
+ }
114
+ function decodeBase64Json(value) {
115
+ const normalized = value.replace(/-/gu, "+").replace(/_/gu, "/");
116
+ const padding = normalized.length % 4 === 0 ? "" : "=".repeat(4 - (normalized.length % 4));
117
+ const base64 = `${normalized}${padding}`;
118
+ if (typeof globalThis.atob !== "function") {
119
+ throw new Error("Base64 decoding is unavailable in this runtime");
120
+ }
121
+ const binary = globalThis.atob(base64);
122
+ const bytes = Uint8Array.from(binary, (char) => char.charCodeAt(0));
123
+ const text = new TextDecoder().decode(bytes);
124
+ return JSON.parse(text);
125
+ }
126
+ function decodePaymentRequiredHeaderValue(value) {
127
+ return decodeBase64Json(value);
128
+ }
129
+ function stripUndefined(value) {
130
+ return Object.fromEntries(Object.entries(value).filter(([, entry]) => entry !== undefined));
131
+ }
132
+ export class ComposeSDK {
133
+ baseUrl;
134
+ fetchImpl;
135
+ walletAddress;
136
+ chainId;
137
+ composeKey;
138
+ wallets;
139
+ keys;
140
+ models;
141
+ pricing;
142
+ inference;
143
+ x402;
144
+ constructor(options = {}) {
145
+ this.baseUrl = normalizeBaseUrl(options.baseUrl);
146
+ this.fetchImpl = options.fetch || fetch;
147
+ this.walletAddress = options.walletAddress ? normalizeWalletAddress(options.walletAddress) : null;
148
+ this.chainId = options.chainId !== undefined ? resolveChainId(options.chainId) : null;
149
+ this.composeKey = options.composeKey || null;
150
+ this.wallets = {
151
+ attach: async (input) => this.attachWallet(input),
152
+ current: () => ({
153
+ address: this.walletAddress,
154
+ chainId: this.chainId,
155
+ }),
156
+ clear: () => {
157
+ this.walletAddress = null;
158
+ this.chainId = null;
159
+ },
160
+ };
161
+ this.keys = {
162
+ create: async (input) => this.createKey(input),
163
+ getActive: async (input) => this.getActiveKey(input),
164
+ list: async () => this.listKeys(),
165
+ revoke: async (keyId) => this.revokeKey(keyId),
166
+ use: (token) => {
167
+ this.composeKey = token;
168
+ },
169
+ currentToken: () => this.composeKey,
170
+ clearToken: () => {
171
+ this.composeKey = null;
172
+ },
173
+ };
174
+ this.models = {
175
+ list: async () => this.requestJson("/v1/models", { method: "GET" }),
176
+ listAll: async () => this.requestJson("/v1/models/all", { method: "GET" }),
177
+ get: async (modelId) => this.requestJson(`/v1/models/${encodeURIComponent(modelId)}`, { method: "GET" }),
178
+ getParams: async (modelId) => this.requestJson(`/v1/models/${encodeURIComponent(modelId)}/params`, { method: "GET" }),
179
+ };
180
+ this.pricing = {
181
+ list: async () => this.requestJson("/api/pricing", { method: "GET" }),
182
+ };
183
+ this.inference = {
184
+ responses: {
185
+ create: async (body, options) => this.requestJson("/v1/responses", {
186
+ method: "POST",
187
+ body: JSON.stringify(body),
188
+ }, options),
189
+ get: async (responseId, options) => this.requestJson(`/v1/responses/${encodeURIComponent(responseId)}`, { method: "GET" }, options),
190
+ inputItems: async (responseId, options) => this.requestJson(`/v1/responses/${encodeURIComponent(responseId)}/input_items`, { method: "GET" }, options),
191
+ cancel: async (responseId, options) => this.requestJson(`/v1/responses/${encodeURIComponent(responseId)}/cancel`, { method: "POST" }, options),
192
+ },
193
+ chat: {
194
+ completions: {
195
+ create: async (body, options) => this.requestJson("/v1/chat/completions", {
196
+ method: "POST",
197
+ body: JSON.stringify(body),
198
+ }, options),
199
+ },
200
+ },
201
+ images: {
202
+ generate: async (body, options) => this.requestJson("/v1/images/generations", {
203
+ method: "POST",
204
+ body: JSON.stringify(body),
205
+ }, options),
206
+ edit: async (body, options) => this.requestJson("/v1/images/edits", {
207
+ method: "POST",
208
+ body: JSON.stringify(body),
209
+ }, options),
210
+ },
211
+ audio: {
212
+ speech: async (body, options) => this.requestJson("/v1/audio/speech", {
213
+ method: "POST",
214
+ body: JSON.stringify(body),
215
+ }, options),
216
+ transcriptions: async (body, options) => this.requestJson("/v1/audio/transcriptions", {
217
+ method: "POST",
218
+ body: JSON.stringify(body),
219
+ }, options),
220
+ },
221
+ embeddings: {
222
+ create: async (body, options) => this.requestJson("/v1/embeddings", {
223
+ method: "POST",
224
+ body: JSON.stringify(body),
225
+ }, options),
226
+ },
227
+ videos: {
228
+ generate: async (body, options) => this.requestJson("/v1/videos/generations", {
229
+ method: "POST",
230
+ body: JSON.stringify(body),
231
+ }, options),
232
+ get: async (videoId, options) => this.requestJson(`/v1/videos/${encodeURIComponent(videoId)}`, { method: "GET" }, options),
233
+ },
234
+ };
235
+ this.x402 = {
236
+ facilitator: {
237
+ getSupported: async () => this.requestJson("/api/x402/facilitator/supported", { method: "GET" }),
238
+ verify: async (body) => this.requestJson("/api/x402/facilitator/verify", {
239
+ method: "POST",
240
+ body: JSON.stringify(body),
241
+ }),
242
+ settle: async (body) => this.requestJson("/api/x402/facilitator/settle", {
243
+ method: "POST",
244
+ body: JSON.stringify(body),
245
+ }),
246
+ },
247
+ decodePaymentRequired: decodePaymentRequiredHeaderValue,
248
+ };
249
+ }
250
+ resolveWalletContext(input) {
251
+ const address = input?.walletAddress ?? this.walletAddress;
252
+ const chainId = input?.chainId ?? this.chainId;
253
+ if (!address) {
254
+ throw new Error("wallet context is required; call wallets.attach(...) first or pass walletAddress");
255
+ }
256
+ if (chainId === null || chainId === undefined) {
257
+ throw new Error("chainId is required; call wallets.attach(...) first or pass chainId");
258
+ }
259
+ return {
260
+ address: normalizeWalletAddress(address),
261
+ chainId: resolveChainId(chainId),
262
+ };
263
+ }
264
+ resolveComposeKeyToken(input) {
265
+ if (input && "composeKey" in input) {
266
+ return input.composeKey || null;
267
+ }
268
+ return this.composeKey;
269
+ }
270
+ async attachWallet(input) {
271
+ const address = normalizeWalletAddress(input.address);
272
+ const chainId = resolveChainId(input.chainId);
273
+ this.walletAddress = address;
274
+ this.chainId = chainId;
275
+ const activeKey = await this.getActiveKey({ chainId });
276
+ return {
277
+ address,
278
+ chainId,
279
+ activeKey,
280
+ };
281
+ }
282
+ async createKey(input) {
283
+ const wallet = this.resolveWalletContext({ chainId: input.chainId });
284
+ return this.requestJson("/api/keys", {
285
+ method: "POST",
286
+ body: JSON.stringify(stripUndefined({
287
+ budgetLimit: toBudgetWei(input),
288
+ expiresAt: resolveExpiresAt(input),
289
+ chainId: wallet.chainId,
290
+ purpose: input.purpose,
291
+ name: input.name,
292
+ })),
293
+ }, {
294
+ walletAddress: wallet.address,
295
+ chainId: wallet.chainId,
296
+ });
297
+ }
298
+ async getActiveKey(input) {
299
+ const wallet = this.resolveWalletContext({ chainId: input?.chainId });
300
+ return this.requestJson("/api/session", { method: "GET" }, {
301
+ walletAddress: wallet.address,
302
+ chainId: wallet.chainId,
303
+ });
304
+ }
305
+ async listKeys() {
306
+ const wallet = this.resolveWalletContext();
307
+ const result = await this.requestJson("/api/keys", { method: "GET" }, {
308
+ walletAddress: wallet.address,
309
+ chainId: wallet.chainId,
310
+ });
311
+ return result.keys;
312
+ }
313
+ async revokeKey(keyId) {
314
+ const wallet = this.resolveWalletContext();
315
+ return this.requestJson(`/api/keys/${encodeURIComponent(keyId)}`, { method: "DELETE" }, {
316
+ walletAddress: wallet.address,
317
+ chainId: wallet.chainId,
318
+ });
319
+ }
320
+ buildHeaders(options = {}, hasBody) {
321
+ const headers = new Headers(options.headers || {});
322
+ if (hasBody && !headers.has("Content-Type")) {
323
+ headers.set("Content-Type", "application/json");
324
+ }
325
+ const composeKey = this.resolveComposeKeyToken(options);
326
+ if (composeKey) {
327
+ headers.set("Authorization", `Bearer ${composeKey}`);
328
+ }
329
+ const walletAddress = options.walletAddress ?? this.walletAddress;
330
+ if (walletAddress) {
331
+ headers.set("x-session-user-address", normalizeWalletAddress(walletAddress));
332
+ }
333
+ const chainId = options.chainId ?? this.chainId;
334
+ if (chainId !== null && chainId !== undefined) {
335
+ headers.set("x-chain-id", String(resolveChainId(chainId)));
336
+ }
337
+ if (options.paymentSignature) {
338
+ headers.set("PAYMENT-SIGNATURE", options.paymentSignature);
339
+ }
340
+ if (options.x402MaxAmountWei) {
341
+ headers.set("x-x402-max-amount-wei", options.x402MaxAmountWei);
342
+ }
343
+ return headers;
344
+ }
345
+ async requestJson(path, init, options = {}) {
346
+ const response = await this.fetchImpl(`${this.baseUrl}${path}`, {
347
+ ...init,
348
+ headers: this.buildHeaders(options, typeof init.body === "string"),
349
+ });
350
+ const text = await response.text();
351
+ const parsedBody = tryParseJson(text);
352
+ if (!response.ok) {
353
+ throw this.buildError(response, parsedBody);
354
+ }
355
+ return parsedBody;
356
+ }
357
+ buildError(response, body) {
358
+ const headers = headersToRecord(response.headers);
359
+ const paymentRequiredHeader = response.headers.get("payment-required");
360
+ const paymentRequired = this.extractPaymentRequired(body, paymentRequiredHeader);
361
+ const fallbackMessage = `${response.status} ${response.statusText}`.trim();
362
+ const message = parseErrorMessage(body, fallbackMessage);
363
+ if (response.status === 402 && paymentRequired) {
364
+ return new ComposePaymentRequiredError({
365
+ message,
366
+ status: response.status,
367
+ body,
368
+ headers,
369
+ paymentRequired,
370
+ paymentRequiredHeader,
371
+ });
372
+ }
373
+ return new ComposeApiError({
374
+ message,
375
+ status: response.status,
376
+ body,
377
+ headers,
378
+ });
379
+ }
380
+ extractPaymentRequired(body, headerValue) {
381
+ if (headerValue) {
382
+ try {
383
+ return decodePaymentRequiredHeaderValue(headerValue);
384
+ }
385
+ catch {
386
+ return null;
387
+ }
388
+ }
389
+ if (body && typeof body === "object") {
390
+ const record = body;
391
+ if (record.x402Version === 2 && Array.isArray(record.accepts) && record.resource) {
392
+ return record;
393
+ }
394
+ }
395
+ return null;
396
+ }
397
+ }
package/package.json ADDED
@@ -0,0 +1,126 @@
1
+ {
2
+ "name": "@compose-market/sdk",
3
+ "version": "0.1.0",
4
+ "description": "Official Compose.Market SDK for Compose keys, x402 facilitator access, model discovery, and inference APIs.",
5
+ "keywords": [
6
+ "x402",
7
+ "facilitator",
8
+ "a2a",
9
+ "agentic-economy",
10
+ "decentralized-inference"
11
+ ],
12
+ "homepage": "https://github.com/compose-market/sdk#readme",
13
+ "bugs": {
14
+ "url": "https://github.com/compose-market/sdk/issues"
15
+ },
16
+ "repository": {
17
+ "type": "git",
18
+ "url": "git+https://github.com/compose-market/sdk.git"
19
+ },
20
+ "license": "MIT",
21
+ "author": "jabyl",
22
+ "type": "module",
23
+ "exports": {
24
+ ".": {
25
+ "types": "./index.d.ts",
26
+ "import": "./index.js",
27
+ "default": "./index.js"
28
+ }
29
+ },
30
+ "main": "./index.js",
31
+ "types": "./index.d.ts",
32
+ "directories": {
33
+ "test": "tests"
34
+ },
35
+ "files": [
36
+ "index.js",
37
+ "index.d.ts"
38
+ ],
39
+ "scripts": {
40
+ "clean": "rm -f index.js index.d.ts",
41
+ "build": "npm run clean && tsc -p tsconfig.json",
42
+ "test": "npm run build && node --import tsx --test tests/**/*.test.ts",
43
+ "typecheck": "tsc --noEmit -p tsconfig.json"
44
+ },
45
+ "dependencies": {
46
+ "accepts": "^2.0.0",
47
+ "body-parser": "^2.2.2",
48
+ "bytes": "^3.1.2",
49
+ "call-bind-apply-helpers": "^1.0.2",
50
+ "call-bound": "^1.0.4",
51
+ "content-disposition": "^1.1.0",
52
+ "content-type": "^1.0.5",
53
+ "cookie": "^0.7.2",
54
+ "cookie-signature": "^1.2.2",
55
+ "debug": "^4.4.3",
56
+ "depd": "^2.0.0",
57
+ "dunder-proto": "^1.0.1",
58
+ "ee-first": "^1.1.1",
59
+ "encodeurl": "^2.0.0",
60
+ "es-define-property": "^1.0.1",
61
+ "es-errors": "^1.3.0",
62
+ "es-object-atoms": "^1.1.1",
63
+ "esbuild": "^0.27.7",
64
+ "escape-html": "^1.0.3",
65
+ "etag": "^1.8.1",
66
+ "finalhandler": "^2.1.1",
67
+ "forwarded": "^0.2.0",
68
+ "fresh": "^2.0.0",
69
+ "function-bind": "^1.1.2",
70
+ "get-intrinsic": "^1.3.0",
71
+ "get-proto": "^1.0.1",
72
+ "get-tsconfig": "^4.14.0",
73
+ "gopd": "^1.2.0",
74
+ "has-symbols": "^1.1.0",
75
+ "hasown": "^2.0.3",
76
+ "http-errors": "^2.0.1",
77
+ "iconv-lite": "^0.7.2",
78
+ "inherits": "^2.0.4",
79
+ "ipaddr.js": "^1.9.1",
80
+ "is-promise": "^4.0.0",
81
+ "math-intrinsics": "^1.1.0",
82
+ "media-typer": "^1.1.0",
83
+ "merge-descriptors": "^2.0.0",
84
+ "mime-db": "^1.54.0",
85
+ "mime-types": "^3.0.2",
86
+ "ms": "^2.1.3",
87
+ "negotiator": "^1.0.0",
88
+ "object-inspect": "^1.13.4",
89
+ "on-finished": "^2.4.1",
90
+ "once": "^1.4.0",
91
+ "parseurl": "^1.3.3",
92
+ "path-to-regexp": "^8.4.2",
93
+ "proxy-addr": "^2.0.7",
94
+ "qs": "^6.15.1",
95
+ "range-parser": "^1.2.1",
96
+ "raw-body": "^3.0.2",
97
+ "resolve-pkg-maps": "^1.0.0",
98
+ "router": "^2.2.0",
99
+ "safer-buffer": "^2.1.2",
100
+ "send": "^1.2.1",
101
+ "serve-static": "^2.2.1",
102
+ "setprototypeof": "^1.2.0",
103
+ "side-channel": "^1.1.0",
104
+ "side-channel-list": "^1.0.1",
105
+ "side-channel-map": "^1.0.1",
106
+ "side-channel-weakmap": "^1.0.2",
107
+ "statuses": "^2.0.2",
108
+ "toidentifier": "^1.0.1",
109
+ "type-is": "^2.0.1",
110
+ "undici-types": "^7.19.2",
111
+ "unpipe": "^1.0.0",
112
+ "vary": "^1.1.2",
113
+ "wrappy": "^1.0.2"
114
+ },
115
+ "devDependencies": {
116
+ "@types/express": "^5.0.6",
117
+ "@types/node": "^25.6.0",
118
+ "express": "^5.2.1",
119
+ "tsx": "^4.21.0",
120
+ "typescript": "^6.0.3"
121
+ },
122
+ "publishConfig": {
123
+ "access": "public"
124
+ },
125
+ "module": "./index.js"
126
+ }