@dtelecom/x402-client 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/dist/index.mjs ADDED
@@ -0,0 +1,369 @@
1
+ // src/client.ts
2
+ import { createPublicClient, http } from "viem";
3
+ import { base } from "viem/chains";
4
+ import { x402Client, wrapFetchWithPayment } from "@x402/fetch";
5
+ import { registerExactEvmScheme } from "@x402/evm/exact/client";
6
+ import { toClientEvmSigner } from "@x402/evm";
7
+
8
+ // src/auth.ts
9
+ async function createAuthHeaders(account, method, path) {
10
+ const timestamp = Math.floor(Date.now() / 1e3).toString();
11
+ const pathname = path.split("?")[0];
12
+ const message = `${method}
13
+ ${pathname}
14
+ ${timestamp}`;
15
+ const signature = await account.signMessage({ message });
16
+ return {
17
+ Authorization: `evm:${signature}`,
18
+ "X-Wallet-Address": account.address,
19
+ "X-Wallet-Chain": "evm",
20
+ "X-Timestamp": timestamp
21
+ };
22
+ }
23
+
24
+ // src/errors.ts
25
+ var GatewayError = class extends Error {
26
+ status;
27
+ constructor(message, status) {
28
+ super(message);
29
+ this.name = "GatewayError";
30
+ this.status = status;
31
+ }
32
+ };
33
+ var InsufficientCreditsError = class extends GatewayError {
34
+ constructor(message) {
35
+ super(message, 402);
36
+ this.name = "InsufficientCreditsError";
37
+ }
38
+ };
39
+ var ConcurrencyLimitError = class extends GatewayError {
40
+ constructor(message) {
41
+ super(message, 429);
42
+ this.name = "ConcurrencyLimitError";
43
+ }
44
+ };
45
+ var RateLimitError = class extends GatewayError {
46
+ constructor(message) {
47
+ super(message, 429);
48
+ this.name = "RateLimitError";
49
+ }
50
+ };
51
+ var NoCapacityError = class extends GatewayError {
52
+ service;
53
+ constructor(message, service) {
54
+ super(message, 503);
55
+ this.name = "NoCapacityError";
56
+ this.service = service;
57
+ }
58
+ };
59
+ var PaymentError = class extends GatewayError {
60
+ constructor(message) {
61
+ super(message, 402);
62
+ this.name = "PaymentError";
63
+ }
64
+ };
65
+
66
+ // src/client.ts
67
+ var DtelecomGateway = class {
68
+ baseUrl;
69
+ account;
70
+ fetchWithPayment;
71
+ constructor(config) {
72
+ this.baseUrl = config.gatewayUrl.replace(/\/+$/, "");
73
+ this.account = config.account;
74
+ const publicClient = createPublicClient({ chain: base, transport: http() });
75
+ const signer = toClientEvmSigner(config.account, publicClient);
76
+ const x402 = new x402Client();
77
+ registerExactEvmScheme(x402, { signer });
78
+ this.fetchWithPayment = wrapFetchWithPayment(fetch, x402);
79
+ }
80
+ // --- Credits ---
81
+ async buyCredits(options) {
82
+ const r = await this.requestWithPayment("/v1/credits/purchase", {
83
+ wallet_address: this.account.address,
84
+ wallet_chain: "evm",
85
+ amount_usd: options.amountUsd
86
+ });
87
+ return {
88
+ accountId: r.account_id,
89
+ creditedMicrocredits: r.credited_microcredits,
90
+ amountUsd: r.amount_usd
91
+ };
92
+ }
93
+ // --- Account ---
94
+ async getAccount() {
95
+ const r = await this.request("GET", "/v1/account");
96
+ return {
97
+ id: r.id,
98
+ walletAddress: r.wallet_address,
99
+ walletChain: r.wallet_chain,
100
+ creditBalance: r.credit_balance,
101
+ availableBalance: r.available_balance,
102
+ maxConcurrentSessions: r.max_concurrent_sessions,
103
+ maxApiRate: r.max_api_rate,
104
+ createdAt: r.created_at
105
+ };
106
+ }
107
+ async getTransactions(options) {
108
+ const params = new URLSearchParams();
109
+ if (options?.limit !== void 0) params.set("limit", String(options.limit));
110
+ if (options?.offset !== void 0)
111
+ params.set("offset", String(options.offset));
112
+ const qs = params.toString();
113
+ const r = await this.request(
114
+ "GET",
115
+ `/v1/account/transactions${qs ? `?${qs}` : ""}`
116
+ );
117
+ return {
118
+ transactions: r.transactions.map(mapTransaction),
119
+ limit: r.limit,
120
+ offset: r.offset
121
+ };
122
+ }
123
+ async getSessions(options) {
124
+ const params = new URLSearchParams();
125
+ if (options?.limit !== void 0) params.set("limit", String(options.limit));
126
+ if (options?.offset !== void 0)
127
+ params.set("offset", String(options.offset));
128
+ if (options?.status) params.set("status", options.status);
129
+ const qs = params.toString();
130
+ const r = await this.request(
131
+ "GET",
132
+ `/v1/account/sessions${qs ? `?${qs}` : ""}`
133
+ );
134
+ return {
135
+ sessions: r.sessions.map(mapSession),
136
+ limit: r.limit,
137
+ offset: r.offset
138
+ };
139
+ }
140
+ // --- Bundled Agent Session ---
141
+ async createAgentSession(options) {
142
+ const r = await this.request("POST", "/v1/agent-session", {
143
+ room_name: options.roomName,
144
+ participant_identity: options.participantIdentity,
145
+ duration_minutes: options.durationMinutes,
146
+ language: options.language,
147
+ tts_max_characters: options.ttsMaxCharacters,
148
+ metadata: options.metadata
149
+ });
150
+ return {
151
+ bundleId: r.bundle_id,
152
+ webrtc: {
153
+ sessionId: r.webrtc.session_id,
154
+ token: r.webrtc.token,
155
+ wsUrl: r.webrtc.ws_url
156
+ },
157
+ stt: {
158
+ sessionId: r.stt.session_id,
159
+ token: r.stt.token,
160
+ serverUrl: r.stt.server_url
161
+ },
162
+ tts: {
163
+ sessionId: r.tts.session_id,
164
+ token: r.tts.token,
165
+ serverUrl: r.tts.server_url
166
+ },
167
+ expiresAt: r.expires_at
168
+ };
169
+ }
170
+ async extendAgentSession(options) {
171
+ const r = await this.request("POST", "/v1/agent-session/extend", {
172
+ bundle_id: options.bundleId,
173
+ additional_minutes: options.additionalMinutes,
174
+ additional_tts_characters: options.additionalTtsCharacters
175
+ });
176
+ const result = {};
177
+ if (r.webrtc) {
178
+ result.webrtc = {
179
+ token: r.webrtc.token,
180
+ newExpiresAt: r.webrtc.new_expires_at
181
+ };
182
+ }
183
+ if (r.stt) {
184
+ result.stt = {
185
+ token: r.stt.token,
186
+ newExpiresAt: r.stt.new_expires_at
187
+ };
188
+ }
189
+ if (r.tts) {
190
+ result.tts = {
191
+ token: r.tts.token,
192
+ newExpiresAt: r.tts.new_expires_at
193
+ };
194
+ }
195
+ return result;
196
+ }
197
+ // --- Standalone WebRTC ---
198
+ async createWebRTCToken(options) {
199
+ const r = await this.request("POST", "/v1/webrtc/token", {
200
+ room_name: options.roomName,
201
+ participant_identity: options.participantIdentity,
202
+ duration_minutes: options.durationMinutes,
203
+ metadata: options.metadata
204
+ });
205
+ return {
206
+ sessionId: r.session_id,
207
+ token: r.token,
208
+ wsUrl: r.ws_url,
209
+ expiresAt: r.expires_at
210
+ };
211
+ }
212
+ async extendWebRTCToken(options) {
213
+ const r = await this.request("POST", "/v1/webrtc/token/extend", {
214
+ session_id: options.sessionId,
215
+ additional_minutes: options.additionalMinutes
216
+ });
217
+ return {
218
+ token: r.token,
219
+ wsUrl: r.ws_url,
220
+ newExpiresAt: r.new_expires_at
221
+ };
222
+ }
223
+ // --- Standalone STT ---
224
+ async createSTTSession(options) {
225
+ const r = await this.request("POST", "/v1/stt/session", {
226
+ duration_minutes: options.durationMinutes,
227
+ language: options.language
228
+ });
229
+ return {
230
+ sessionId: r.session_id,
231
+ token: r.token,
232
+ serverUrl: r.server_url,
233
+ expiresAt: r.expires_at
234
+ };
235
+ }
236
+ async extendSTTSession(options) {
237
+ const r = await this.request("POST", "/v1/stt/session/extend", {
238
+ session_id: options.sessionId,
239
+ additional_minutes: options.additionalMinutes
240
+ });
241
+ return {
242
+ token: r.token,
243
+ newExpiresAt: r.new_expires_at
244
+ };
245
+ }
246
+ // --- Standalone TTS ---
247
+ async createTTSSession(options) {
248
+ const r = await this.request("POST", "/v1/tts/session", {
249
+ max_characters: options.maxCharacters,
250
+ language: options.language
251
+ });
252
+ return {
253
+ sessionId: r.session_id,
254
+ token: r.token,
255
+ serverUrl: r.server_url,
256
+ expiresAt: r.expires_at
257
+ };
258
+ }
259
+ async extendTTSSession(options) {
260
+ const r = await this.request("POST", "/v1/tts/session/extend", {
261
+ session_id: options.sessionId,
262
+ additional_characters: options.additionalCharacters
263
+ });
264
+ return {
265
+ token: r.token,
266
+ maxCharacters: r.max_characters,
267
+ newExpiresAt: r.new_expires_at
268
+ };
269
+ }
270
+ // --- Internal: authenticated request ---
271
+ async request(method, path, body) {
272
+ const headers = await createAuthHeaders(this.account, method, path);
273
+ const init = {
274
+ method,
275
+ headers: {
276
+ ...headers,
277
+ ...body !== void 0 ? { "Content-Type": "application/json" } : {}
278
+ },
279
+ ...body !== void 0 ? { body: JSON.stringify(body) } : {}
280
+ };
281
+ const resp = await fetch(`${this.baseUrl}${path}`, init);
282
+ return this.handleResponse(resp, path);
283
+ }
284
+ // --- Internal: x402 payment request (for buyCredits) ---
285
+ async requestWithPayment(path, body) {
286
+ let resp;
287
+ try {
288
+ resp = await this.fetchWithPayment(`${this.baseUrl}${path}`, {
289
+ method: "POST",
290
+ headers: { "Content-Type": "application/json" },
291
+ body: JSON.stringify(body)
292
+ });
293
+ } catch (err) {
294
+ throw new PaymentError(
295
+ `Payment failed: ${err instanceof Error ? err.message : String(err)}`
296
+ );
297
+ }
298
+ return this.handleResponse(resp, path);
299
+ }
300
+ // --- Internal: response handling ---
301
+ async handleResponse(resp, path) {
302
+ if (resp.ok) {
303
+ return await resp.json();
304
+ }
305
+ const text = await resp.text().catch(() => "");
306
+ let errorMessage;
307
+ try {
308
+ const parsed = JSON.parse(text);
309
+ errorMessage = parsed.error ?? text;
310
+ } catch {
311
+ errorMessage = text || resp.statusText;
312
+ }
313
+ switch (resp.status) {
314
+ case 402:
315
+ throw new InsufficientCreditsError(errorMessage);
316
+ case 429: {
317
+ if (errorMessage.toLowerCase().includes("concurrent")) {
318
+ throw new ConcurrencyLimitError(errorMessage);
319
+ }
320
+ throw new RateLimitError(errorMessage);
321
+ }
322
+ case 503: {
323
+ const service = path.split("/")[2] ?? "unknown";
324
+ throw new NoCapacityError(errorMessage, service);
325
+ }
326
+ default:
327
+ throw new GatewayError(errorMessage, resp.status);
328
+ }
329
+ }
330
+ };
331
+ function mapTransaction(r) {
332
+ return {
333
+ id: r.id,
334
+ amount: r.amount,
335
+ balanceAfter: r.balance_after,
336
+ type: r.type,
337
+ referenceId: r.reference_id,
338
+ service: r.service,
339
+ description: r.description,
340
+ createdAt: r.created_at
341
+ };
342
+ }
343
+ function mapSession(r) {
344
+ return {
345
+ id: r.id,
346
+ service: r.service,
347
+ bundleId: r.bundle_id,
348
+ status: r.status,
349
+ roomName: r.room_name,
350
+ serverUrl: r.server_url,
351
+ reservedMicrocredits: r.reserved_microcredits,
352
+ chargedMicrocredits: r.charged_microcredits,
353
+ tokenExpiresAt: r.token_expires_at,
354
+ startedAt: r.started_at,
355
+ endedAt: r.ended_at,
356
+ settlementMethod: r.settlement_method,
357
+ createdAt: r.created_at
358
+ };
359
+ }
360
+ export {
361
+ ConcurrencyLimitError,
362
+ DtelecomGateway,
363
+ GatewayError,
364
+ InsufficientCreditsError,
365
+ NoCapacityError,
366
+ PaymentError,
367
+ RateLimitError,
368
+ createAuthHeaders
369
+ };
package/package.json ADDED
@@ -0,0 +1,54 @@
1
+ {
2
+ "name": "@dtelecom/x402-client",
3
+ "version": "0.1.0",
4
+ "description": "TypeScript SDK for dTelecom x402 gateway — buy credits and create WebRTC/STT/TTS sessions",
5
+ "main": "dist/index.js",
6
+ "module": "dist/index.mjs",
7
+ "types": "dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "import": {
11
+ "types": "./dist/index.d.mts",
12
+ "default": "./dist/index.mjs"
13
+ },
14
+ "require": {
15
+ "types": "./dist/index.d.ts",
16
+ "default": "./dist/index.js"
17
+ }
18
+ }
19
+ },
20
+ "files": [
21
+ "dist"
22
+ ],
23
+ "scripts": {
24
+ "build": "tsup",
25
+ "typecheck": "tsc --noEmit",
26
+ "test": "vitest run"
27
+ },
28
+ "keywords": [
29
+ "x402",
30
+ "webrtc",
31
+ "stt",
32
+ "tts",
33
+ "micropayments",
34
+ "dtelecom"
35
+ ],
36
+ "author": "dTelecom <dev@dtelecom.org>",
37
+ "license": "MIT",
38
+ "repository": {
39
+ "type": "git",
40
+ "url": "https://github.com/dTelecom/x402-client.git"
41
+ },
42
+ "homepage": "https://github.com/dTelecom/x402-client#readme",
43
+ "dependencies": {
44
+ "@x402/evm": "^2.5.0",
45
+ "@x402/fetch": "^2.5.0",
46
+ "viem": "^2.0.0"
47
+ },
48
+ "devDependencies": {
49
+ "@types/node": "^22.0.0",
50
+ "tsup": "^8.0.0",
51
+ "typescript": "^5.7.0",
52
+ "vitest": "^3.0.0"
53
+ }
54
+ }