@omnixhq/ucp-client 0.1.1
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/LICENSE +21 -0
- package/README.md +193 -0
- package/dist/index.cjs +1053 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +8061 -0
- package/dist/index.d.cts.map +1 -0
- package/dist/index.d.ts +8061 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +996 -0
- package/dist/index.js.map +1 -0
- package/package.json +92 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,996 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { randomUUID } from "node:crypto";
|
|
3
|
+
import { BuyerSchema, CheckoutResponseStatusSchema, ExtendedCheckoutCreateRequestSchema, ExtendedCheckoutResponseSchema, ExtendedCheckoutUpdateRequestSchema, FulfillmentMethodResponseSchema, FulfillmentResponseSchema, ItemResponseSchema, LineItemResponseSchema, MessageErrorSchema, MessageSchema, OrderSchema, PaymentHandlerResponseSchema, PaymentInstrumentSchema, PaymentResponseSchema, PostalAddressSchema, TotalResponseSchema, UcpDiscoveryProfileSchema } from "@ucp-js/sdk";
|
|
4
|
+
|
|
5
|
+
//#region src/errors.ts
|
|
6
|
+
/** Thrown when the gateway returns an error response with `messages[]`. */
|
|
7
|
+
var UCPError = class extends Error {
|
|
8
|
+
code;
|
|
9
|
+
type;
|
|
10
|
+
statusCode;
|
|
11
|
+
/** JSONPath to the field that caused the error, from the first message. */
|
|
12
|
+
path;
|
|
13
|
+
contentType;
|
|
14
|
+
/** All messages from the gateway response. */
|
|
15
|
+
messages;
|
|
16
|
+
constructor(code, message, type = "error", statusCode = 400, options = {}) {
|
|
17
|
+
super(message);
|
|
18
|
+
this.name = "UCPError";
|
|
19
|
+
this.code = code;
|
|
20
|
+
this.type = type;
|
|
21
|
+
this.statusCode = statusCode;
|
|
22
|
+
this.path = options.path;
|
|
23
|
+
this.contentType = options.contentType;
|
|
24
|
+
this.messages = options.messages ?? [];
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
/** Thrown on HTTP 409 when no `messages[]` body is present (idempotency key collision). */
|
|
28
|
+
var UCPIdempotencyConflictError = class extends UCPError {
|
|
29
|
+
constructor(message = "Idempotency key reused with different request body") {
|
|
30
|
+
super("IDEMPOTENCY_CONFLICT", message, "error", 409);
|
|
31
|
+
this.name = "UCPIdempotencyConflictError";
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
/** Thrown when a checkout response has `status: 'requires_escalation'` with a `continue_url`. */
|
|
35
|
+
var UCPEscalationError = class extends Error {
|
|
36
|
+
/** The URL to redirect the buyer to for merchant-hosted checkout UI. */
|
|
37
|
+
continue_url;
|
|
38
|
+
constructor(continue_url, message = "Payment requires escalation") {
|
|
39
|
+
super(message);
|
|
40
|
+
this.name = "UCPEscalationError";
|
|
41
|
+
this.continue_url = continue_url;
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
/** Thrown when an OAuth token exchange, refresh, or revocation fails. */
|
|
45
|
+
var UCPOAuthError = class extends Error {
|
|
46
|
+
statusCode;
|
|
47
|
+
constructor(message, statusCode) {
|
|
48
|
+
super(message);
|
|
49
|
+
this.name = "UCPOAuthError";
|
|
50
|
+
this.statusCode = statusCode;
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
//#endregion
|
|
55
|
+
//#region src/http.ts
|
|
56
|
+
var HttpClient = class HttpClient {
|
|
57
|
+
gatewayUrl;
|
|
58
|
+
agentProfileUrl;
|
|
59
|
+
ucpVersion;
|
|
60
|
+
requestSignature;
|
|
61
|
+
accessToken;
|
|
62
|
+
onValidationWarning;
|
|
63
|
+
constructor(config) {
|
|
64
|
+
this.gatewayUrl = config.gatewayUrl;
|
|
65
|
+
this.agentProfileUrl = config.agentProfileUrl;
|
|
66
|
+
this.ucpVersion = config.ucpVersion;
|
|
67
|
+
this.requestSignature = config.requestSignature;
|
|
68
|
+
this.accessToken = config.accessToken;
|
|
69
|
+
this.onValidationWarning = config.onValidationWarning ?? ((msg, detail) => console.warn(msg, detail));
|
|
70
|
+
}
|
|
71
|
+
withAccessToken(token) {
|
|
72
|
+
return new HttpClient({
|
|
73
|
+
gatewayUrl: this.gatewayUrl,
|
|
74
|
+
agentProfileUrl: this.agentProfileUrl,
|
|
75
|
+
ucpVersion: this.ucpVersion,
|
|
76
|
+
...this.requestSignature !== void 0 ? { requestSignature: this.requestSignature } : {},
|
|
77
|
+
accessToken: token,
|
|
78
|
+
onValidationWarning: this.onValidationWarning
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
async request(method, path, body) {
|
|
82
|
+
const url = `${this.gatewayUrl}${path}`;
|
|
83
|
+
const requestId = randomUUID();
|
|
84
|
+
const headers = {
|
|
85
|
+
"UCP-Agent": `profile="${this.agentProfileUrl}", version="${this.ucpVersion}"`,
|
|
86
|
+
"request-id": requestId
|
|
87
|
+
};
|
|
88
|
+
if (body !== void 0) headers["Content-Type"] = "application/json";
|
|
89
|
+
if (this.requestSignature !== void 0) headers["request-signature"] = this.requestSignature;
|
|
90
|
+
if (this.accessToken !== void 0) headers["Authorization"] = `Bearer ${this.accessToken}`;
|
|
91
|
+
if (method === "POST" || method === "PUT") headers["idempotency-key"] = randomUUID();
|
|
92
|
+
const res = await fetch(url, {
|
|
93
|
+
method,
|
|
94
|
+
headers,
|
|
95
|
+
...body !== void 0 ? { body: JSON.stringify(body) } : {}
|
|
96
|
+
});
|
|
97
|
+
const data = await res.json().catch(() => ({}));
|
|
98
|
+
if (!res.ok) this.throwFromResponse(data, res.status);
|
|
99
|
+
return data;
|
|
100
|
+
}
|
|
101
|
+
validate(data, schema) {
|
|
102
|
+
const result = schema.safeParse(data);
|
|
103
|
+
if (!result.success) {
|
|
104
|
+
this.onValidationWarning("[UCPClient] Response validation failed:", result.error.message);
|
|
105
|
+
return data;
|
|
106
|
+
}
|
|
107
|
+
return result.data;
|
|
108
|
+
}
|
|
109
|
+
throwFromResponse(data, statusCode) {
|
|
110
|
+
if (typeof data !== "object" || data === null) {
|
|
111
|
+
if (statusCode === 409) throw new UCPIdempotencyConflictError();
|
|
112
|
+
throw new UCPError("HTTP_ERROR", `Gateway returned ${statusCode}`, "error", statusCode);
|
|
113
|
+
}
|
|
114
|
+
const body = data;
|
|
115
|
+
const rawMessages = body["messages"];
|
|
116
|
+
if (Array.isArray(rawMessages) && rawMessages.length > 0) {
|
|
117
|
+
const allMessages = parseMessages(rawMessages);
|
|
118
|
+
const first = allMessages[0];
|
|
119
|
+
const code = first.code ?? "UNKNOWN";
|
|
120
|
+
throw new UCPError(code, first.content, first.type, statusCode, {
|
|
121
|
+
...first.path !== void 0 ? { path: first.path } : {},
|
|
122
|
+
...first.content_type !== void 0 ? { contentType: first.content_type } : {},
|
|
123
|
+
messages: allMessages
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
if (statusCode === 409) throw new UCPIdempotencyConflictError();
|
|
127
|
+
throw new UCPError("HTTP_ERROR", `Gateway returned ${statusCode}`, "error", statusCode);
|
|
128
|
+
}
|
|
129
|
+
};
|
|
130
|
+
function parseMessages(rawMessages) {
|
|
131
|
+
return rawMessages.map((m) => {
|
|
132
|
+
const record = m;
|
|
133
|
+
const rawType = String(record["type"] ?? "error");
|
|
134
|
+
const validTypes = [
|
|
135
|
+
"error",
|
|
136
|
+
"warning",
|
|
137
|
+
"info"
|
|
138
|
+
];
|
|
139
|
+
const type = validTypes.includes(rawType) ? rawType : "error";
|
|
140
|
+
return {
|
|
141
|
+
type,
|
|
142
|
+
content: String(record["content"] ?? "Unknown error"),
|
|
143
|
+
...record["code"] !== void 0 ? { code: String(record["code"]) } : {},
|
|
144
|
+
...record["severity"] !== void 0 ? { severity: String(record["severity"]) } : {},
|
|
145
|
+
...record["path"] !== void 0 ? { path: String(record["path"]) } : {},
|
|
146
|
+
...record["content_type"] !== void 0 ? { content_type: String(record["content_type"]) } : {}
|
|
147
|
+
};
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
//#endregion
|
|
152
|
+
//#region src/schemas.ts
|
|
153
|
+
const CheckoutSessionSchema = ExtendedCheckoutResponseSchema.passthrough();
|
|
154
|
+
const UCPProfileSchema = UcpDiscoveryProfileSchema.passthrough();
|
|
155
|
+
const UCPProductSchema = z.object({
|
|
156
|
+
id: z.string(),
|
|
157
|
+
title: z.string(),
|
|
158
|
+
description: z.string().nullable(),
|
|
159
|
+
price_cents: z.number().int(),
|
|
160
|
+
currency: z.string().min(3).max(3),
|
|
161
|
+
in_stock: z.boolean(),
|
|
162
|
+
stock_quantity: z.number().int().min(0),
|
|
163
|
+
images: z.array(z.string().url()),
|
|
164
|
+
variants: z.array(z.object({
|
|
165
|
+
id: z.string(),
|
|
166
|
+
title: z.string(),
|
|
167
|
+
price_cents: z.number().int(),
|
|
168
|
+
in_stock: z.boolean(),
|
|
169
|
+
attributes: z.record(z.string())
|
|
170
|
+
}).passthrough())
|
|
171
|
+
}).passthrough();
|
|
172
|
+
const UCPOrderSchema = z.object({
|
|
173
|
+
id: z.string(),
|
|
174
|
+
status: z.enum([
|
|
175
|
+
"pending",
|
|
176
|
+
"processing",
|
|
177
|
+
"shipped",
|
|
178
|
+
"delivered",
|
|
179
|
+
"canceled"
|
|
180
|
+
]),
|
|
181
|
+
total_cents: z.number().int(),
|
|
182
|
+
currency: z.string().min(3).max(3),
|
|
183
|
+
created_at_iso: z.string().datetime({ offset: true })
|
|
184
|
+
}).passthrough();
|
|
185
|
+
const CreateCheckoutRequestSchema = ExtendedCheckoutCreateRequestSchema.passthrough();
|
|
186
|
+
const UpdateCheckoutRequestSchema = ExtendedCheckoutUpdateRequestSchema.passthrough();
|
|
187
|
+
const CompleteCheckoutRequestSchema = z.object({ payment: z.object({ instruments: z.array(z.object({
|
|
188
|
+
id: z.string(),
|
|
189
|
+
handler_id: z.string(),
|
|
190
|
+
type: z.string(),
|
|
191
|
+
selected: z.boolean().optional(),
|
|
192
|
+
credential: z.object({
|
|
193
|
+
type: z.string(),
|
|
194
|
+
token: z.string().optional()
|
|
195
|
+
}).optional(),
|
|
196
|
+
billing_address: z.unknown().optional()
|
|
197
|
+
}).passthrough()) }) });
|
|
198
|
+
|
|
199
|
+
//#endregion
|
|
200
|
+
//#region src/capabilities/checkout.ts
|
|
201
|
+
const DEFAULT_METHOD_ID = "default";
|
|
202
|
+
const DEFAULT_GROUP_ID = "default";
|
|
203
|
+
/**
|
|
204
|
+
* Checkout session operations. Available when the server declares `dev.ucp.shopping.checkout`.
|
|
205
|
+
* Check `extensions` to see which optional features (fulfillment, discount, etc.) are supported.
|
|
206
|
+
*/
|
|
207
|
+
var CheckoutCapability = class {
|
|
208
|
+
/** Which checkout extensions the server supports. */
|
|
209
|
+
extensions;
|
|
210
|
+
constructor(http, extensions) {
|
|
211
|
+
this.http = http;
|
|
212
|
+
this.extensions = extensions;
|
|
213
|
+
}
|
|
214
|
+
async create(payload) {
|
|
215
|
+
const data = await this.http.request("POST", "/checkout-sessions", payload);
|
|
216
|
+
return this.validateSession(data);
|
|
217
|
+
}
|
|
218
|
+
async get(id) {
|
|
219
|
+
const data = await this.http.request("GET", `/checkout-sessions/${encodeURIComponent(id)}`);
|
|
220
|
+
return this.validateSession(data);
|
|
221
|
+
}
|
|
222
|
+
async update(id, patch) {
|
|
223
|
+
const data = await this.http.request("PUT", `/checkout-sessions/${encodeURIComponent(id)}`, patch);
|
|
224
|
+
return this.validateSession(data);
|
|
225
|
+
}
|
|
226
|
+
async complete(id, payload) {
|
|
227
|
+
const data = await this.http.request("POST", `/checkout-sessions/${encodeURIComponent(id)}/complete`, payload);
|
|
228
|
+
return this.validateSession(data);
|
|
229
|
+
}
|
|
230
|
+
async cancel(id) {
|
|
231
|
+
const data = await this.http.request("POST", `/checkout-sessions/${encodeURIComponent(id)}/cancel`);
|
|
232
|
+
return this.validateSession(data);
|
|
233
|
+
}
|
|
234
|
+
async setFulfillment(id, type, patch) {
|
|
235
|
+
return this.update(id, {
|
|
236
|
+
...patch,
|
|
237
|
+
fulfillment: { methods: [{
|
|
238
|
+
id: DEFAULT_METHOD_ID,
|
|
239
|
+
type
|
|
240
|
+
}] }
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
async selectDestination(id, destinationId, fulfillmentType = "shipping", patch) {
|
|
244
|
+
return this.update(id, {
|
|
245
|
+
...patch,
|
|
246
|
+
fulfillment: { methods: [{
|
|
247
|
+
id: DEFAULT_METHOD_ID,
|
|
248
|
+
type: fulfillmentType,
|
|
249
|
+
selected_destination_id: destinationId
|
|
250
|
+
}] }
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
async selectFulfillmentOption(id, optionId, destinationId, fulfillmentType = "shipping", patch) {
|
|
254
|
+
return this.update(id, {
|
|
255
|
+
...patch,
|
|
256
|
+
fulfillment: { methods: [{
|
|
257
|
+
id: DEFAULT_METHOD_ID,
|
|
258
|
+
type: fulfillmentType,
|
|
259
|
+
...destinationId !== void 0 ? { selected_destination_id: destinationId } : {},
|
|
260
|
+
groups: [{
|
|
261
|
+
id: DEFAULT_GROUP_ID,
|
|
262
|
+
selected_option_id: optionId
|
|
263
|
+
}]
|
|
264
|
+
}] }
|
|
265
|
+
});
|
|
266
|
+
}
|
|
267
|
+
async applyDiscountCodes(id, codes, patch) {
|
|
268
|
+
return this.update(id, {
|
|
269
|
+
...patch,
|
|
270
|
+
discounts: { codes: [...codes] }
|
|
271
|
+
});
|
|
272
|
+
}
|
|
273
|
+
validateSession(data) {
|
|
274
|
+
const session = this.http.validate(data, CheckoutSessionSchema);
|
|
275
|
+
if (session.status === "requires_escalation" && session.continue_url) throw new UCPEscalationError(session.continue_url);
|
|
276
|
+
return session;
|
|
277
|
+
}
|
|
278
|
+
};
|
|
279
|
+
|
|
280
|
+
//#endregion
|
|
281
|
+
//#region src/capabilities/order.ts
|
|
282
|
+
/** Order operations. Available when the server declares `dev.ucp.shopping.order`. */
|
|
283
|
+
var OrderCapability = class {
|
|
284
|
+
constructor(http) {
|
|
285
|
+
this.http = http;
|
|
286
|
+
}
|
|
287
|
+
/** Retrieve an order by ID. Returns the UCP spec-compliant Order object. */
|
|
288
|
+
async get(id) {
|
|
289
|
+
const data = await this.http.request("GET", `/orders/${encodeURIComponent(id)}`);
|
|
290
|
+
return this.http.validate(data, OrderSchema);
|
|
291
|
+
}
|
|
292
|
+
};
|
|
293
|
+
|
|
294
|
+
//#endregion
|
|
295
|
+
//#region src/capabilities/identity-linking.ts
|
|
296
|
+
const TokenResponseSchema = z.object({
|
|
297
|
+
access_token: z.string(),
|
|
298
|
+
token_type: z.string(),
|
|
299
|
+
expires_in: z.number().optional(),
|
|
300
|
+
refresh_token: z.string().optional(),
|
|
301
|
+
scope: z.string().optional()
|
|
302
|
+
}).passthrough();
|
|
303
|
+
/**
|
|
304
|
+
* OAuth 2.0 identity linking for account linking between platforms and merchants.
|
|
305
|
+
* Available when the server declares `dev.ucp.common.identity_linking`.
|
|
306
|
+
*/
|
|
307
|
+
var IdentityLinkingCapability = class {
|
|
308
|
+
constructor(metadata) {
|
|
309
|
+
this.metadata = metadata;
|
|
310
|
+
}
|
|
311
|
+
/** Build the OAuth authorization URL to redirect the buyer to. */
|
|
312
|
+
getAuthorizationUrl(params) {
|
|
313
|
+
const url = new URL(this.metadata.authorization_endpoint);
|
|
314
|
+
url.searchParams.set("response_type", "code");
|
|
315
|
+
url.searchParams.set("client_id", params.client_id);
|
|
316
|
+
url.searchParams.set("redirect_uri", params.redirect_uri);
|
|
317
|
+
url.searchParams.set("scope", params.scope ?? "ucp:scopes:checkout_session");
|
|
318
|
+
if (params.state !== void 0) url.searchParams.set("state", params.state);
|
|
319
|
+
return url.toString();
|
|
320
|
+
}
|
|
321
|
+
async exchangeCode(params) {
|
|
322
|
+
const body = new URLSearchParams({
|
|
323
|
+
grant_type: "authorization_code",
|
|
324
|
+
code: params.code,
|
|
325
|
+
redirect_uri: params.redirect_uri
|
|
326
|
+
});
|
|
327
|
+
return this.tokenRequest(params.client_id, params.client_secret, body);
|
|
328
|
+
}
|
|
329
|
+
async refreshToken(params) {
|
|
330
|
+
const body = new URLSearchParams({
|
|
331
|
+
grant_type: "refresh_token",
|
|
332
|
+
refresh_token: params.refresh_token
|
|
333
|
+
});
|
|
334
|
+
return this.tokenRequest(params.client_id, params.client_secret, body);
|
|
335
|
+
}
|
|
336
|
+
async revokeToken(params) {
|
|
337
|
+
const body = new URLSearchParams({ token: params.token });
|
|
338
|
+
if (params.token_type_hint !== void 0) body.set("token_type_hint", params.token_type_hint);
|
|
339
|
+
const res = await fetch(this.metadata.revocation_endpoint, {
|
|
340
|
+
method: "POST",
|
|
341
|
+
headers: {
|
|
342
|
+
"Content-Type": "application/x-www-form-urlencoded",
|
|
343
|
+
Authorization: encodeBasicAuth(params.client_id, params.client_secret)
|
|
344
|
+
},
|
|
345
|
+
body: body.toString()
|
|
346
|
+
});
|
|
347
|
+
if (!res.ok) throw new UCPOAuthError(`Token revocation failed: ${res.status}`, res.status);
|
|
348
|
+
}
|
|
349
|
+
getMetadata() {
|
|
350
|
+
return this.metadata;
|
|
351
|
+
}
|
|
352
|
+
async tokenRequest(clientId, clientSecret, body) {
|
|
353
|
+
const res = await fetch(this.metadata.token_endpoint, {
|
|
354
|
+
method: "POST",
|
|
355
|
+
headers: {
|
|
356
|
+
"Content-Type": "application/x-www-form-urlencoded",
|
|
357
|
+
Authorization: encodeBasicAuth(clientId, clientSecret)
|
|
358
|
+
},
|
|
359
|
+
body: body.toString()
|
|
360
|
+
});
|
|
361
|
+
if (!res.ok) throw new UCPOAuthError(`Token exchange failed with status ${res.status}`, res.status);
|
|
362
|
+
const raw = await res.json();
|
|
363
|
+
const parsed = TokenResponseSchema.safeParse(raw);
|
|
364
|
+
if (!parsed.success) throw new UCPOAuthError(`Invalid token response: ${parsed.error.message}`, res.status);
|
|
365
|
+
return parsed.data;
|
|
366
|
+
}
|
|
367
|
+
};
|
|
368
|
+
function encodeBasicAuth(username, password) {
|
|
369
|
+
return `Basic ${Buffer.from(`${username}:${password}`).toString("base64")}`;
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
//#endregion
|
|
373
|
+
//#region src/capabilities/products.ts
|
|
374
|
+
/** Product catalog search and retrieval. Always available (gateway-specific, not part of UCP spec). */
|
|
375
|
+
var ProductsCapability = class {
|
|
376
|
+
constructor(http) {
|
|
377
|
+
this.http = http;
|
|
378
|
+
}
|
|
379
|
+
/** Search products by query string with optional filters. */
|
|
380
|
+
async search(query, filters = {}) {
|
|
381
|
+
const params = new URLSearchParams({ q: query });
|
|
382
|
+
const filterEntries = [
|
|
383
|
+
["max_price_cents", filters.max_price_cents],
|
|
384
|
+
["min_price_cents", filters.min_price_cents],
|
|
385
|
+
["in_stock", filters.in_stock],
|
|
386
|
+
["category", filters.category],
|
|
387
|
+
["limit", filters.limit],
|
|
388
|
+
["page", filters.page]
|
|
389
|
+
];
|
|
390
|
+
for (const [key, value] of filterEntries) if (value != null) params.set(key, String(value));
|
|
391
|
+
const res = await this.http.request("GET", `/ucp/products?${params.toString()}`);
|
|
392
|
+
const data = res;
|
|
393
|
+
const products = Array.isArray(data) ? data : data.products ?? [];
|
|
394
|
+
return products.map((p) => this.http.validate(p, UCPProductSchema));
|
|
395
|
+
}
|
|
396
|
+
async get(id) {
|
|
397
|
+
const data = await this.http.request("GET", `/ucp/products/${encodeURIComponent(id)}`);
|
|
398
|
+
return this.http.validate(data, UCPProductSchema);
|
|
399
|
+
}
|
|
400
|
+
};
|
|
401
|
+
|
|
402
|
+
//#endregion
|
|
403
|
+
//#region src/types/config.ts
|
|
404
|
+
const DEFAULT_UCP_VERSION = "2026-01-23";
|
|
405
|
+
const UCP_CAPABILITIES = {
|
|
406
|
+
CHECKOUT: "dev.ucp.shopping.checkout",
|
|
407
|
+
FULFILLMENT: "dev.ucp.shopping.fulfillment",
|
|
408
|
+
DISCOUNT: "dev.ucp.shopping.discount",
|
|
409
|
+
BUYER_CONSENT: "dev.ucp.shopping.buyer_consent",
|
|
410
|
+
ORDER: "dev.ucp.shopping.order",
|
|
411
|
+
IDENTITY_LINKING: "dev.ucp.common.identity_linking",
|
|
412
|
+
AP2_MANDATE: "dev.ucp.shopping.ap2_mandate"
|
|
413
|
+
};
|
|
414
|
+
|
|
415
|
+
//#endregion
|
|
416
|
+
//#region src/agent-tools.ts
|
|
417
|
+
/**
|
|
418
|
+
* Returns ready-to-use tool definitions for agent registration.
|
|
419
|
+
* Only tools supported by the connected server are included.
|
|
420
|
+
*
|
|
421
|
+
* Each tool has:
|
|
422
|
+
* - `name` — unique tool identifier
|
|
423
|
+
* - `description` — what the tool does (for the LLM)
|
|
424
|
+
* - `parameters` — JSON Schema describing the expected input
|
|
425
|
+
* - `execute(params)` — function that calls the right capability method
|
|
426
|
+
*
|
|
427
|
+
* @example
|
|
428
|
+
* ```typescript
|
|
429
|
+
* const client = await UCPClient.connect(config);
|
|
430
|
+
* const tools = getAgentTools(client);
|
|
431
|
+
*
|
|
432
|
+
* // Register with Anthropic Claude API
|
|
433
|
+
* const response = await anthropic.messages.create({
|
|
434
|
+
* tools: tools.map(t => ({
|
|
435
|
+
* name: t.name,
|
|
436
|
+
* description: t.description,
|
|
437
|
+
* input_schema: t.parameters,
|
|
438
|
+
* })),
|
|
439
|
+
* // ...
|
|
440
|
+
* });
|
|
441
|
+
*
|
|
442
|
+
* // Execute tool calls
|
|
443
|
+
* for (const block of response.content) {
|
|
444
|
+
* if (block.type === 'tool_use') {
|
|
445
|
+
* const tool = tools.find(t => t.name === block.name);
|
|
446
|
+
* const result = await tool.execute(block.input);
|
|
447
|
+
* }
|
|
448
|
+
* }
|
|
449
|
+
* ```
|
|
450
|
+
*/
|
|
451
|
+
function getAgentTools(client) {
|
|
452
|
+
const tools = [...productTools(client)];
|
|
453
|
+
if (client.checkout) {
|
|
454
|
+
tools.push(...checkoutTools(client));
|
|
455
|
+
if (client.checkout.extensions.fulfillment) tools.push(...fulfillmentTools(client));
|
|
456
|
+
if (client.checkout.extensions.discount) tools.push(...discountTools(client));
|
|
457
|
+
}
|
|
458
|
+
if (client.order) tools.push(...orderTools(client));
|
|
459
|
+
return tools;
|
|
460
|
+
}
|
|
461
|
+
function productTools(client) {
|
|
462
|
+
return [{
|
|
463
|
+
name: "search_products",
|
|
464
|
+
description: "Search the product catalog by query string. Returns matching products with prices, availability, and images.",
|
|
465
|
+
parameters: {
|
|
466
|
+
type: "object",
|
|
467
|
+
properties: {
|
|
468
|
+
query: {
|
|
469
|
+
type: "string",
|
|
470
|
+
description: "Search query (e.g., \"running shoes\")"
|
|
471
|
+
},
|
|
472
|
+
max_price_cents: {
|
|
473
|
+
type: "number",
|
|
474
|
+
description: "Maximum price in cents"
|
|
475
|
+
},
|
|
476
|
+
min_price_cents: {
|
|
477
|
+
type: "number",
|
|
478
|
+
description: "Minimum price in cents"
|
|
479
|
+
},
|
|
480
|
+
in_stock: {
|
|
481
|
+
type: "boolean",
|
|
482
|
+
description: "Filter to in-stock items only"
|
|
483
|
+
},
|
|
484
|
+
category: {
|
|
485
|
+
type: "string",
|
|
486
|
+
description: "Product category"
|
|
487
|
+
},
|
|
488
|
+
limit: {
|
|
489
|
+
type: "number",
|
|
490
|
+
description: "Max results to return"
|
|
491
|
+
}
|
|
492
|
+
},
|
|
493
|
+
required: ["query"]
|
|
494
|
+
},
|
|
495
|
+
execute: async (params) => {
|
|
496
|
+
const { query,...filters } = params;
|
|
497
|
+
return client.products.search(query, filters);
|
|
498
|
+
}
|
|
499
|
+
}, {
|
|
500
|
+
name: "get_product",
|
|
501
|
+
description: "Get detailed product information by ID, including variants, images, and stock.",
|
|
502
|
+
parameters: {
|
|
503
|
+
type: "object",
|
|
504
|
+
properties: { id: {
|
|
505
|
+
type: "string",
|
|
506
|
+
description: "Product ID"
|
|
507
|
+
} },
|
|
508
|
+
required: ["id"]
|
|
509
|
+
},
|
|
510
|
+
execute: async (params) => client.products.get(params["id"])
|
|
511
|
+
}];
|
|
512
|
+
}
|
|
513
|
+
function checkoutTools(client) {
|
|
514
|
+
return [
|
|
515
|
+
{
|
|
516
|
+
name: "create_checkout",
|
|
517
|
+
description: "Create a new checkout session with line items. Returns a session with an ID to use in subsequent calls.",
|
|
518
|
+
parameters: {
|
|
519
|
+
type: "object",
|
|
520
|
+
properties: {
|
|
521
|
+
line_items: {
|
|
522
|
+
type: "array",
|
|
523
|
+
description: "Products to purchase",
|
|
524
|
+
items: {
|
|
525
|
+
type: "object",
|
|
526
|
+
properties: {
|
|
527
|
+
item: {
|
|
528
|
+
type: "object",
|
|
529
|
+
properties: { id: {
|
|
530
|
+
type: "string",
|
|
531
|
+
description: "Product ID"
|
|
532
|
+
} },
|
|
533
|
+
required: ["id"]
|
|
534
|
+
},
|
|
535
|
+
quantity: {
|
|
536
|
+
type: "number",
|
|
537
|
+
description: "Quantity to purchase"
|
|
538
|
+
}
|
|
539
|
+
},
|
|
540
|
+
required: ["item", "quantity"]
|
|
541
|
+
}
|
|
542
|
+
},
|
|
543
|
+
currency: {
|
|
544
|
+
type: "string",
|
|
545
|
+
description: "ISO 4217 currency code (e.g., \"USD\")"
|
|
546
|
+
}
|
|
547
|
+
},
|
|
548
|
+
required: ["line_items"]
|
|
549
|
+
},
|
|
550
|
+
execute: async (params) => client.checkout.create(params)
|
|
551
|
+
},
|
|
552
|
+
{
|
|
553
|
+
name: "get_checkout",
|
|
554
|
+
description: "Get the current state of a checkout session, including status, totals, and available options.",
|
|
555
|
+
parameters: {
|
|
556
|
+
type: "object",
|
|
557
|
+
properties: { id: {
|
|
558
|
+
type: "string",
|
|
559
|
+
description: "Checkout session ID"
|
|
560
|
+
} },
|
|
561
|
+
required: ["id"]
|
|
562
|
+
},
|
|
563
|
+
execute: async (params) => client.checkout.get(params["id"])
|
|
564
|
+
},
|
|
565
|
+
{
|
|
566
|
+
name: "update_checkout",
|
|
567
|
+
description: "Update a checkout session with buyer information, shipping address, or payment details.",
|
|
568
|
+
parameters: {
|
|
569
|
+
type: "object",
|
|
570
|
+
properties: {
|
|
571
|
+
id: {
|
|
572
|
+
type: "string",
|
|
573
|
+
description: "Checkout session ID"
|
|
574
|
+
},
|
|
575
|
+
buyer: {
|
|
576
|
+
type: "object",
|
|
577
|
+
description: "Buyer contact information",
|
|
578
|
+
properties: {
|
|
579
|
+
first_name: { type: "string" },
|
|
580
|
+
last_name: { type: "string" },
|
|
581
|
+
email: { type: "string" },
|
|
582
|
+
phone_number: { type: "string" }
|
|
583
|
+
}
|
|
584
|
+
},
|
|
585
|
+
context: {
|
|
586
|
+
type: "object",
|
|
587
|
+
description: "Localization context for pricing and availability",
|
|
588
|
+
properties: {
|
|
589
|
+
address_country: {
|
|
590
|
+
type: "string",
|
|
591
|
+
description: "ISO 3166-1 alpha-2 country code"
|
|
592
|
+
},
|
|
593
|
+
address_region: {
|
|
594
|
+
type: "string",
|
|
595
|
+
description: "State or province"
|
|
596
|
+
},
|
|
597
|
+
postal_code: { type: "string" }
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
},
|
|
601
|
+
required: ["id"]
|
|
602
|
+
},
|
|
603
|
+
execute: async (params) => {
|
|
604
|
+
const { id,...patch } = params;
|
|
605
|
+
return client.checkout.update(id, patch);
|
|
606
|
+
}
|
|
607
|
+
},
|
|
608
|
+
{
|
|
609
|
+
name: "complete_checkout",
|
|
610
|
+
description: "Complete a checkout session with payment. Places the order. Returns the completed session with order ID.",
|
|
611
|
+
parameters: {
|
|
612
|
+
type: "object",
|
|
613
|
+
properties: {
|
|
614
|
+
id: {
|
|
615
|
+
type: "string",
|
|
616
|
+
description: "Checkout session ID"
|
|
617
|
+
},
|
|
618
|
+
payment: {
|
|
619
|
+
type: "object",
|
|
620
|
+
description: "Payment information",
|
|
621
|
+
properties: { instruments: {
|
|
622
|
+
type: "array",
|
|
623
|
+
items: {
|
|
624
|
+
type: "object",
|
|
625
|
+
properties: {
|
|
626
|
+
id: {
|
|
627
|
+
type: "string",
|
|
628
|
+
description: "Instrument ID"
|
|
629
|
+
},
|
|
630
|
+
handler_id: {
|
|
631
|
+
type: "string",
|
|
632
|
+
description: "Payment handler ID from the server profile"
|
|
633
|
+
},
|
|
634
|
+
type: {
|
|
635
|
+
type: "string",
|
|
636
|
+
description: "Payment type (e.g., \"card\", \"offline\")"
|
|
637
|
+
},
|
|
638
|
+
credential: {
|
|
639
|
+
type: "object",
|
|
640
|
+
properties: {
|
|
641
|
+
type: {
|
|
642
|
+
type: "string",
|
|
643
|
+
description: "Credential type (e.g., \"token\")"
|
|
644
|
+
},
|
|
645
|
+
token: {
|
|
646
|
+
type: "string",
|
|
647
|
+
description: "Payment token"
|
|
648
|
+
}
|
|
649
|
+
},
|
|
650
|
+
required: ["type"]
|
|
651
|
+
}
|
|
652
|
+
},
|
|
653
|
+
required: [
|
|
654
|
+
"id",
|
|
655
|
+
"handler_id",
|
|
656
|
+
"type"
|
|
657
|
+
]
|
|
658
|
+
}
|
|
659
|
+
} },
|
|
660
|
+
required: ["instruments"]
|
|
661
|
+
}
|
|
662
|
+
},
|
|
663
|
+
required: ["id", "payment"]
|
|
664
|
+
},
|
|
665
|
+
execute: async (params) => {
|
|
666
|
+
const { id,...payload } = params;
|
|
667
|
+
return client.checkout.complete(String(id), payload);
|
|
668
|
+
}
|
|
669
|
+
},
|
|
670
|
+
{
|
|
671
|
+
name: "cancel_checkout",
|
|
672
|
+
description: "Cancel a checkout session. The session cannot be used after cancellation.",
|
|
673
|
+
parameters: {
|
|
674
|
+
type: "object",
|
|
675
|
+
properties: { id: {
|
|
676
|
+
type: "string",
|
|
677
|
+
description: "Checkout session ID"
|
|
678
|
+
} },
|
|
679
|
+
required: ["id"]
|
|
680
|
+
},
|
|
681
|
+
execute: async (params) => client.checkout.cancel(params["id"])
|
|
682
|
+
}
|
|
683
|
+
];
|
|
684
|
+
}
|
|
685
|
+
function fulfillmentTools(client) {
|
|
686
|
+
return [
|
|
687
|
+
{
|
|
688
|
+
name: "set_fulfillment",
|
|
689
|
+
description: "Set the fulfillment method for a checkout (e.g., \"shipping\" or \"pickup\").",
|
|
690
|
+
parameters: {
|
|
691
|
+
type: "object",
|
|
692
|
+
properties: {
|
|
693
|
+
id: {
|
|
694
|
+
type: "string",
|
|
695
|
+
description: "Checkout session ID"
|
|
696
|
+
},
|
|
697
|
+
type: {
|
|
698
|
+
type: "string",
|
|
699
|
+
enum: ["shipping", "pickup"],
|
|
700
|
+
description: "Fulfillment method"
|
|
701
|
+
}
|
|
702
|
+
},
|
|
703
|
+
required: ["id", "type"]
|
|
704
|
+
},
|
|
705
|
+
execute: async (params) => client.checkout.setFulfillment(params["id"], params["type"])
|
|
706
|
+
},
|
|
707
|
+
{
|
|
708
|
+
name: "select_destination",
|
|
709
|
+
description: "Select a shipping destination for a checkout session.",
|
|
710
|
+
parameters: {
|
|
711
|
+
type: "object",
|
|
712
|
+
properties: {
|
|
713
|
+
id: {
|
|
714
|
+
type: "string",
|
|
715
|
+
description: "Checkout session ID"
|
|
716
|
+
},
|
|
717
|
+
destination_id: {
|
|
718
|
+
type: "string",
|
|
719
|
+
description: "Destination ID to select"
|
|
720
|
+
},
|
|
721
|
+
fulfillment_type: {
|
|
722
|
+
type: "string",
|
|
723
|
+
default: "shipping",
|
|
724
|
+
description: "Fulfillment type"
|
|
725
|
+
}
|
|
726
|
+
},
|
|
727
|
+
required: ["id", "destination_id"]
|
|
728
|
+
},
|
|
729
|
+
execute: async (params) => client.checkout.selectDestination(params["id"], params["destination_id"], params["fulfillment_type"] ?? "shipping")
|
|
730
|
+
},
|
|
731
|
+
{
|
|
732
|
+
name: "select_fulfillment_option",
|
|
733
|
+
description: "Select a fulfillment option (e.g., standard shipping, express shipping) for a checkout session.",
|
|
734
|
+
parameters: {
|
|
735
|
+
type: "object",
|
|
736
|
+
properties: {
|
|
737
|
+
id: {
|
|
738
|
+
type: "string",
|
|
739
|
+
description: "Checkout session ID"
|
|
740
|
+
},
|
|
741
|
+
option_id: {
|
|
742
|
+
type: "string",
|
|
743
|
+
description: "Fulfillment option ID to select"
|
|
744
|
+
},
|
|
745
|
+
destination_id: {
|
|
746
|
+
type: "string",
|
|
747
|
+
description: "Destination ID (if applicable)"
|
|
748
|
+
},
|
|
749
|
+
fulfillment_type: {
|
|
750
|
+
type: "string",
|
|
751
|
+
default: "shipping",
|
|
752
|
+
description: "Fulfillment type"
|
|
753
|
+
}
|
|
754
|
+
},
|
|
755
|
+
required: ["id", "option_id"]
|
|
756
|
+
},
|
|
757
|
+
execute: async (params) => client.checkout.selectFulfillmentOption(params["id"], params["option_id"], params["destination_id"], params["fulfillment_type"] ?? "shipping")
|
|
758
|
+
}
|
|
759
|
+
];
|
|
760
|
+
}
|
|
761
|
+
function discountTools(client) {
|
|
762
|
+
return [{
|
|
763
|
+
name: "apply_discount_codes",
|
|
764
|
+
description: "Apply one or more discount codes to a checkout session.",
|
|
765
|
+
parameters: {
|
|
766
|
+
type: "object",
|
|
767
|
+
properties: {
|
|
768
|
+
id: {
|
|
769
|
+
type: "string",
|
|
770
|
+
description: "Checkout session ID"
|
|
771
|
+
},
|
|
772
|
+
codes: {
|
|
773
|
+
type: "array",
|
|
774
|
+
items: { type: "string" },
|
|
775
|
+
description: "Discount codes to apply"
|
|
776
|
+
}
|
|
777
|
+
},
|
|
778
|
+
required: ["id", "codes"]
|
|
779
|
+
},
|
|
780
|
+
execute: async (params) => client.checkout.applyDiscountCodes(params["id"], params["codes"])
|
|
781
|
+
}];
|
|
782
|
+
}
|
|
783
|
+
function orderTools(client) {
|
|
784
|
+
return [{
|
|
785
|
+
name: "get_order",
|
|
786
|
+
description: "Get order details by ID, including line items, fulfillment status, and tracking information.",
|
|
787
|
+
parameters: {
|
|
788
|
+
type: "object",
|
|
789
|
+
properties: { id: {
|
|
790
|
+
type: "string",
|
|
791
|
+
description: "Order ID"
|
|
792
|
+
} },
|
|
793
|
+
required: ["id"]
|
|
794
|
+
},
|
|
795
|
+
execute: async (params) => client.order.get(params["id"])
|
|
796
|
+
}];
|
|
797
|
+
}
|
|
798
|
+
|
|
799
|
+
//#endregion
|
|
800
|
+
//#region src/UCPClient.ts
|
|
801
|
+
/**
|
|
802
|
+
* Connect to a UCP server, discover its capabilities, and return a {@link ConnectedClient}.
|
|
803
|
+
*
|
|
804
|
+
* @example
|
|
805
|
+
* ```typescript
|
|
806
|
+
* const client = await connect({
|
|
807
|
+
* gatewayUrl: 'https://store.example.com/ucp',
|
|
808
|
+
* agentProfileUrl: 'https://platform.example.com/.well-known/ucp',
|
|
809
|
+
* });
|
|
810
|
+
*
|
|
811
|
+
* if (client.checkout) {
|
|
812
|
+
* const session = await client.checkout.create({ line_items: [...] });
|
|
813
|
+
* }
|
|
814
|
+
* ```
|
|
815
|
+
*/
|
|
816
|
+
async function connect(config, options) {
|
|
817
|
+
validateConfig(config);
|
|
818
|
+
const http = new HttpClient({
|
|
819
|
+
gatewayUrl: config.gatewayUrl.replace(/\/+$/, ""),
|
|
820
|
+
agentProfileUrl: config.agentProfileUrl,
|
|
821
|
+
ucpVersion: config.ucpVersion ?? DEFAULT_UCP_VERSION,
|
|
822
|
+
...config.requestSignature !== void 0 ? { requestSignature: config.requestSignature } : {},
|
|
823
|
+
...options?.onValidationWarning !== void 0 ? { onValidationWarning: options.onValidationWarning } : {}
|
|
824
|
+
});
|
|
825
|
+
const rawProfile = await http.request("GET", "/.well-known/ucp");
|
|
826
|
+
const profile = http.validate(rawProfile, UCPProfileSchema);
|
|
827
|
+
const capabilityNames = extractCapabilityNames(profile);
|
|
828
|
+
const checkout = buildCheckoutCapability(http, capabilityNames);
|
|
829
|
+
const order = capabilityNames.has(UCP_CAPABILITIES.ORDER) ? new OrderCapability(http) : null;
|
|
830
|
+
const identityLinking = await buildIdentityLinking(config, capabilityNames);
|
|
831
|
+
const products = new ProductsCapability(http);
|
|
832
|
+
const paymentHandlers = extractPaymentHandlers(profile);
|
|
833
|
+
const client = {
|
|
834
|
+
profile,
|
|
835
|
+
checkout,
|
|
836
|
+
order,
|
|
837
|
+
identityLinking,
|
|
838
|
+
products,
|
|
839
|
+
paymentHandlers,
|
|
840
|
+
describeTools: () => buildToolDescriptors(checkout, order, identityLinking),
|
|
841
|
+
getAgentTools: () => getAgentTools(client)
|
|
842
|
+
};
|
|
843
|
+
return Object.freeze(client);
|
|
844
|
+
}
|
|
845
|
+
/**
|
|
846
|
+
* UCP client entry point. Use `UCPClient.connect()` to discover server capabilities
|
|
847
|
+
* and get a {@link ConnectedClient}.
|
|
848
|
+
*
|
|
849
|
+
* @example
|
|
850
|
+
* ```typescript
|
|
851
|
+
* const client = await UCPClient.connect({
|
|
852
|
+
* gatewayUrl: 'https://store.example.com/ucp',
|
|
853
|
+
* agentProfileUrl: 'https://platform.example.com/.well-known/ucp',
|
|
854
|
+
* });
|
|
855
|
+
* ```
|
|
856
|
+
*/
|
|
857
|
+
var UCPClient = class {
|
|
858
|
+
constructor() {}
|
|
859
|
+
static connect = connect;
|
|
860
|
+
};
|
|
861
|
+
function validateConfig(config) {
|
|
862
|
+
new URL(config.gatewayUrl);
|
|
863
|
+
if (config.agentProfileUrl.includes("\"") || config.agentProfileUrl.includes("\n")) throw new Error("agentProfileUrl must not contain double quotes or newlines");
|
|
864
|
+
new URL(config.agentProfileUrl);
|
|
865
|
+
}
|
|
866
|
+
function extractCapabilityNames(profile) {
|
|
867
|
+
const capabilities = profile.ucp?.capabilities;
|
|
868
|
+
if (!Array.isArray(capabilities)) return new Set();
|
|
869
|
+
return new Set(capabilities.map((c) => c.name).filter((n) => n !== void 0));
|
|
870
|
+
}
|
|
871
|
+
const PaymentHandlerInstanceSchema = z.object({
|
|
872
|
+
id: z.string(),
|
|
873
|
+
version: z.string(),
|
|
874
|
+
spec: z.string(),
|
|
875
|
+
schema: z.string(),
|
|
876
|
+
config: z.record(z.unknown()).optional()
|
|
877
|
+
}).passthrough();
|
|
878
|
+
const PaymentHandlerMapSchema = z.record(z.array(PaymentHandlerInstanceSchema));
|
|
879
|
+
function extractPaymentHandlers(profile) {
|
|
880
|
+
const raw = profile["payment_handlers"];
|
|
881
|
+
if (typeof raw !== "object" || raw === null) return {};
|
|
882
|
+
const result = PaymentHandlerMapSchema.safeParse(raw);
|
|
883
|
+
if (!result.success) return {};
|
|
884
|
+
return result.data;
|
|
885
|
+
}
|
|
886
|
+
function buildCheckoutCapability(http, capabilityNames) {
|
|
887
|
+
if (!capabilityNames.has(UCP_CAPABILITIES.CHECKOUT)) return null;
|
|
888
|
+
const extensions = {
|
|
889
|
+
fulfillment: capabilityNames.has(UCP_CAPABILITIES.FULFILLMENT),
|
|
890
|
+
discount: capabilityNames.has(UCP_CAPABILITIES.DISCOUNT),
|
|
891
|
+
buyerConsent: capabilityNames.has(UCP_CAPABILITIES.BUYER_CONSENT),
|
|
892
|
+
ap2Mandate: capabilityNames.has(UCP_CAPABILITIES.AP2_MANDATE)
|
|
893
|
+
};
|
|
894
|
+
return new CheckoutCapability(http, extensions);
|
|
895
|
+
}
|
|
896
|
+
const OAuthServerMetadataSchema = z.object({
|
|
897
|
+
issuer: z.string(),
|
|
898
|
+
authorization_endpoint: z.string().url(),
|
|
899
|
+
token_endpoint: z.string().url(),
|
|
900
|
+
revocation_endpoint: z.string().url(),
|
|
901
|
+
scopes_supported: z.array(z.string()),
|
|
902
|
+
response_types_supported: z.array(z.string()),
|
|
903
|
+
grant_types_supported: z.array(z.string()),
|
|
904
|
+
token_endpoint_auth_methods_supported: z.array(z.string()),
|
|
905
|
+
service_documentation: z.string().url().optional()
|
|
906
|
+
}).passthrough();
|
|
907
|
+
async function buildIdentityLinking(config, capabilityNames) {
|
|
908
|
+
if (!capabilityNames.has(UCP_CAPABILITIES.IDENTITY_LINKING)) return null;
|
|
909
|
+
const gatewayUrl = config.gatewayUrl.replace(/\/+$/, "");
|
|
910
|
+
const metadataUrl = `${gatewayUrl}/.well-known/oauth-authorization-server`;
|
|
911
|
+
const res = await fetch(metadataUrl);
|
|
912
|
+
if (!res.ok) throw new Error(`Identity linking capability declared but OAuth metadata fetch failed: ${res.status} from ${metadataUrl}`);
|
|
913
|
+
const raw = await res.json();
|
|
914
|
+
const parsed = OAuthServerMetadataSchema.safeParse(raw);
|
|
915
|
+
if (!parsed.success) throw new Error(`Identity linking OAuth metadata validation failed: ${parsed.error.message}`);
|
|
916
|
+
return new IdentityLinkingCapability(parsed.data);
|
|
917
|
+
}
|
|
918
|
+
function buildToolDescriptors(checkout, order, identityLinking) {
|
|
919
|
+
const tools = [{
|
|
920
|
+
name: "search_products",
|
|
921
|
+
capability: "products",
|
|
922
|
+
description: "Search product catalog"
|
|
923
|
+
}, {
|
|
924
|
+
name: "get_product",
|
|
925
|
+
capability: "products",
|
|
926
|
+
description: "Get product by ID"
|
|
927
|
+
}];
|
|
928
|
+
if (checkout) {
|
|
929
|
+
tools.push({
|
|
930
|
+
name: "create_checkout",
|
|
931
|
+
capability: "checkout",
|
|
932
|
+
description: "Create a checkout session"
|
|
933
|
+
}, {
|
|
934
|
+
name: "get_checkout",
|
|
935
|
+
capability: "checkout",
|
|
936
|
+
description: "Get checkout session by ID"
|
|
937
|
+
}, {
|
|
938
|
+
name: "update_checkout",
|
|
939
|
+
capability: "checkout",
|
|
940
|
+
description: "Update a checkout session"
|
|
941
|
+
}, {
|
|
942
|
+
name: "complete_checkout",
|
|
943
|
+
capability: "checkout",
|
|
944
|
+
description: "Complete checkout with payment"
|
|
945
|
+
}, {
|
|
946
|
+
name: "cancel_checkout",
|
|
947
|
+
capability: "checkout",
|
|
948
|
+
description: "Cancel a checkout session"
|
|
949
|
+
});
|
|
950
|
+
if (checkout.extensions.fulfillment) tools.push({
|
|
951
|
+
name: "set_fulfillment",
|
|
952
|
+
capability: "checkout.fulfillment",
|
|
953
|
+
description: "Set fulfillment method (shipping/pickup)"
|
|
954
|
+
}, {
|
|
955
|
+
name: "select_destination",
|
|
956
|
+
capability: "checkout.fulfillment",
|
|
957
|
+
description: "Select shipping destination"
|
|
958
|
+
}, {
|
|
959
|
+
name: "select_fulfillment_option",
|
|
960
|
+
capability: "checkout.fulfillment",
|
|
961
|
+
description: "Select fulfillment option (e.g., express shipping)"
|
|
962
|
+
});
|
|
963
|
+
if (checkout.extensions.discount) tools.push({
|
|
964
|
+
name: "apply_discount_codes",
|
|
965
|
+
capability: "checkout.discount",
|
|
966
|
+
description: "Apply discount codes to checkout"
|
|
967
|
+
});
|
|
968
|
+
}
|
|
969
|
+
if (order) tools.push({
|
|
970
|
+
name: "get_order",
|
|
971
|
+
capability: "order",
|
|
972
|
+
description: "Get order by ID"
|
|
973
|
+
});
|
|
974
|
+
if (identityLinking) tools.push({
|
|
975
|
+
name: "get_authorization_url",
|
|
976
|
+
capability: "identity_linking",
|
|
977
|
+
description: "Get OAuth authorization URL for account linking"
|
|
978
|
+
}, {
|
|
979
|
+
name: "exchange_auth_code",
|
|
980
|
+
capability: "identity_linking",
|
|
981
|
+
description: "Exchange authorization code for access token"
|
|
982
|
+
}, {
|
|
983
|
+
name: "refresh_access_token",
|
|
984
|
+
capability: "identity_linking",
|
|
985
|
+
description: "Refresh an expired access token"
|
|
986
|
+
}, {
|
|
987
|
+
name: "revoke_token",
|
|
988
|
+
capability: "identity_linking",
|
|
989
|
+
description: "Revoke an access or refresh token"
|
|
990
|
+
});
|
|
991
|
+
return tools;
|
|
992
|
+
}
|
|
993
|
+
|
|
994
|
+
//#endregion
|
|
995
|
+
export { BuyerSchema, CheckoutCapability, CheckoutResponseStatusSchema, CheckoutSessionSchema, CompleteCheckoutRequestSchema, CreateCheckoutRequestSchema, DEFAULT_UCP_VERSION, FulfillmentMethodResponseSchema, FulfillmentResponseSchema, IdentityLinkingCapability, ItemResponseSchema, LineItemResponseSchema, MessageErrorSchema, MessageSchema, OrderCapability, PaymentHandlerResponseSchema, PaymentInstrumentSchema, PaymentResponseSchema, PostalAddressSchema, ProductsCapability, TotalResponseSchema, UCPClient, UCPError, UCPEscalationError, UCPIdempotencyConflictError, UCPOAuthError, UCPOrderSchema, UCPProductSchema, UCPProfileSchema, OrderSchema as UCPSpecOrderSchema, UCP_CAPABILITIES, UpdateCheckoutRequestSchema, connect, getAgentTools };
|
|
996
|
+
//# sourceMappingURL=index.js.map
|