@aibtc/mcp-server 1.0.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 (178) hide show
  1. package/README.md +340 -0
  2. package/dist/api.d.ts +9 -0
  3. package/dist/api.d.ts.map +1 -0
  4. package/dist/api.js +79 -0
  5. package/dist/api.js.map +1 -0
  6. package/dist/config/contracts.d.ts +169 -0
  7. package/dist/config/contracts.d.ts.map +1 -0
  8. package/dist/config/contracts.js +250 -0
  9. package/dist/config/contracts.js.map +1 -0
  10. package/dist/config/index.d.ts +3 -0
  11. package/dist/config/index.d.ts.map +1 -0
  12. package/dist/config/index.js +3 -0
  13. package/dist/config/index.js.map +1 -0
  14. package/dist/config/networks.d.ts +11 -0
  15. package/dist/config/networks.d.ts.map +1 -0
  16. package/dist/config/networks.js +21 -0
  17. package/dist/config/networks.js.map +1 -0
  18. package/dist/endpoints/index.d.ts +2 -0
  19. package/dist/endpoints/index.d.ts.map +1 -0
  20. package/dist/endpoints/index.js +2 -0
  21. package/dist/endpoints/index.js.map +1 -0
  22. package/dist/endpoints/registry.d.ts +38 -0
  23. package/dist/endpoints/registry.d.ts.map +1 -0
  24. package/dist/endpoints/registry.js +935 -0
  25. package/dist/endpoints/registry.js.map +1 -0
  26. package/dist/index.d.ts +3 -0
  27. package/dist/index.d.ts.map +1 -0
  28. package/dist/index.js +79 -0
  29. package/dist/index.js.map +1 -0
  30. package/dist/services/bitflow.service.d.ts +159 -0
  31. package/dist/services/bitflow.service.d.ts.map +1 -0
  32. package/dist/services/bitflow.service.js +325 -0
  33. package/dist/services/bitflow.service.js.map +1 -0
  34. package/dist/services/bns.service.d.ts +87 -0
  35. package/dist/services/bns.service.d.ts.map +1 -0
  36. package/dist/services/bns.service.js +312 -0
  37. package/dist/services/bns.service.js.map +1 -0
  38. package/dist/services/defi.service.d.ts +146 -0
  39. package/dist/services/defi.service.d.ts.map +1 -0
  40. package/dist/services/defi.service.js +461 -0
  41. package/dist/services/defi.service.js.map +1 -0
  42. package/dist/services/hiro-api.d.ts +438 -0
  43. package/dist/services/hiro-api.d.ts.map +1 -0
  44. package/dist/services/hiro-api.js +376 -0
  45. package/dist/services/hiro-api.js.map +1 -0
  46. package/dist/services/nft.service.d.ts +70 -0
  47. package/dist/services/nft.service.d.ts.map +1 -0
  48. package/dist/services/nft.service.js +148 -0
  49. package/dist/services/nft.service.js.map +1 -0
  50. package/dist/services/sbtc.service.d.ts +44 -0
  51. package/dist/services/sbtc.service.d.ts.map +1 -0
  52. package/dist/services/sbtc.service.js +100 -0
  53. package/dist/services/sbtc.service.js.map +1 -0
  54. package/dist/services/scaffold.service.d.ts +41 -0
  55. package/dist/services/scaffold.service.d.ts.map +1 -0
  56. package/dist/services/scaffold.service.js +1055 -0
  57. package/dist/services/scaffold.service.js.map +1 -0
  58. package/dist/services/stacking.service.d.ts +58 -0
  59. package/dist/services/stacking.service.d.ts.map +1 -0
  60. package/dist/services/stacking.service.js +153 -0
  61. package/dist/services/stacking.service.js.map +1 -0
  62. package/dist/services/tokens.service.d.ts +62 -0
  63. package/dist/services/tokens.service.d.ts.map +1 -0
  64. package/dist/services/tokens.service.js +119 -0
  65. package/dist/services/tokens.service.js.map +1 -0
  66. package/dist/services/wallet-manager.d.ts +107 -0
  67. package/dist/services/wallet-manager.d.ts.map +1 -0
  68. package/dist/services/wallet-manager.js +389 -0
  69. package/dist/services/wallet-manager.js.map +1 -0
  70. package/dist/services/x402.service.d.ts +26 -0
  71. package/dist/services/x402.service.d.ts.map +1 -0
  72. package/dist/services/x402.service.js +125 -0
  73. package/dist/services/x402.service.js.map +1 -0
  74. package/dist/tools/bitflow.tools.d.ts +3 -0
  75. package/dist/tools/bitflow.tools.d.ts.map +1 -0
  76. package/dist/tools/bitflow.tools.js +501 -0
  77. package/dist/tools/bitflow.tools.js.map +1 -0
  78. package/dist/tools/bns.tools.d.ts +3 -0
  79. package/dist/tools/bns.tools.d.ts.map +1 -0
  80. package/dist/tools/bns.tools.js +164 -0
  81. package/dist/tools/bns.tools.js.map +1 -0
  82. package/dist/tools/contract.tools.d.ts +3 -0
  83. package/dist/tools/contract.tools.d.ts.map +1 -0
  84. package/dist/tools/contract.tools.js +126 -0
  85. package/dist/tools/contract.tools.js.map +1 -0
  86. package/dist/tools/defi.tools.d.ts +3 -0
  87. package/dist/tools/defi.tools.d.ts.map +1 -0
  88. package/dist/tools/defi.tools.js +425 -0
  89. package/dist/tools/defi.tools.js.map +1 -0
  90. package/dist/tools/endpoint.tools.d.ts +3 -0
  91. package/dist/tools/endpoint.tools.d.ts.map +1 -0
  92. package/dist/tools/endpoint.tools.js +157 -0
  93. package/dist/tools/endpoint.tools.js.map +1 -0
  94. package/dist/tools/index.d.ts +6 -0
  95. package/dist/tools/index.d.ts.map +1 -0
  96. package/dist/tools/index.js +52 -0
  97. package/dist/tools/index.js.map +1 -0
  98. package/dist/tools/nft.tools.d.ts +3 -0
  99. package/dist/tools/nft.tools.d.ts.map +1 -0
  100. package/dist/tools/nft.tools.js +154 -0
  101. package/dist/tools/nft.tools.js.map +1 -0
  102. package/dist/tools/openrouter.tools.d.ts +3 -0
  103. package/dist/tools/openrouter.tools.d.ts.map +1 -0
  104. package/dist/tools/openrouter.tools.js +664 -0
  105. package/dist/tools/openrouter.tools.js.map +1 -0
  106. package/dist/tools/query.tools.d.ts +3 -0
  107. package/dist/tools/query.tools.d.ts.map +1 -0
  108. package/dist/tools/query.tools.js +209 -0
  109. package/dist/tools/query.tools.js.map +1 -0
  110. package/dist/tools/sbtc.tools.d.ts +3 -0
  111. package/dist/tools/sbtc.tools.d.ts.map +1 -0
  112. package/dist/tools/sbtc.tools.js +103 -0
  113. package/dist/tools/sbtc.tools.js.map +1 -0
  114. package/dist/tools/scaffold.tools.d.ts +3 -0
  115. package/dist/tools/scaffold.tools.d.ts.map +1 -0
  116. package/dist/tools/scaffold.tools.js +216 -0
  117. package/dist/tools/scaffold.tools.js.map +1 -0
  118. package/dist/tools/stacking.tools.d.ts +3 -0
  119. package/dist/tools/stacking.tools.d.ts.map +1 -0
  120. package/dist/tools/stacking.tools.js +112 -0
  121. package/dist/tools/stacking.tools.js.map +1 -0
  122. package/dist/tools/tokens.tools.d.ts +3 -0
  123. package/dist/tools/tokens.tools.d.ts.map +1 -0
  124. package/dist/tools/tokens.tools.js +154 -0
  125. package/dist/tools/tokens.tools.js.map +1 -0
  126. package/dist/tools/transfer.tools.d.ts +3 -0
  127. package/dist/tools/transfer.tools.d.ts.map +1 -0
  128. package/dist/tools/transfer.tools.js +62 -0
  129. package/dist/tools/transfer.tools.js.map +1 -0
  130. package/dist/tools/wallet-management.tools.d.ts +6 -0
  131. package/dist/tools/wallet-management.tools.d.ts.map +1 -0
  132. package/dist/tools/wallet-management.tools.js +390 -0
  133. package/dist/tools/wallet-management.tools.js.map +1 -0
  134. package/dist/tools/wallet.tools.d.ts +3 -0
  135. package/dist/tools/wallet.tools.d.ts.map +1 -0
  136. package/dist/tools/wallet.tools.js +105 -0
  137. package/dist/tools/wallet.tools.js.map +1 -0
  138. package/dist/transactions/builder.d.ts +56 -0
  139. package/dist/transactions/builder.d.ts.map +1 -0
  140. package/dist/transactions/builder.js +134 -0
  141. package/dist/transactions/builder.js.map +1 -0
  142. package/dist/transactions/clarity-values.d.ts +67 -0
  143. package/dist/transactions/clarity-values.d.ts.map +1 -0
  144. package/dist/transactions/clarity-values.js +169 -0
  145. package/dist/transactions/clarity-values.js.map +1 -0
  146. package/dist/transactions/post-conditions.d.ts +27 -0
  147. package/dist/transactions/post-conditions.d.ts.map +1 -0
  148. package/dist/transactions/post-conditions.js +101 -0
  149. package/dist/transactions/post-conditions.js.map +1 -0
  150. package/dist/utils/encryption.d.ts +33 -0
  151. package/dist/utils/encryption.d.ts.map +1 -0
  152. package/dist/utils/encryption.js +110 -0
  153. package/dist/utils/encryption.js.map +1 -0
  154. package/dist/utils/errors.d.ts +84 -0
  155. package/dist/utils/errors.d.ts.map +1 -0
  156. package/dist/utils/errors.js +132 -0
  157. package/dist/utils/errors.js.map +1 -0
  158. package/dist/utils/formatting.d.ts +51 -0
  159. package/dist/utils/formatting.d.ts.map +1 -0
  160. package/dist/utils/formatting.js +114 -0
  161. package/dist/utils/formatting.js.map +1 -0
  162. package/dist/utils/index.d.ts +6 -0
  163. package/dist/utils/index.d.ts.map +1 -0
  164. package/dist/utils/index.js +6 -0
  165. package/dist/utils/index.js.map +1 -0
  166. package/dist/utils/storage.d.ts +90 -0
  167. package/dist/utils/storage.d.ts.map +1 -0
  168. package/dist/utils/storage.js +196 -0
  169. package/dist/utils/storage.js.map +1 -0
  170. package/dist/utils/validation.d.ts +67 -0
  171. package/dist/utils/validation.d.ts.map +1 -0
  172. package/dist/utils/validation.js +74 -0
  173. package/dist/utils/validation.js.map +1 -0
  174. package/dist/wallet.d.ts +86 -0
  175. package/dist/wallet.d.ts.map +1 -0
  176. package/dist/wallet.js +279 -0
  177. package/dist/wallet.js.map +1 -0
  178. package/package.json +58 -0
@@ -0,0 +1,1055 @@
1
+ import fs from "fs/promises";
2
+ import path from "path";
3
+ // Token decimals for conversion
4
+ const TOKEN_DECIMALS = {
5
+ STX: 6,
6
+ sBTC: 8,
7
+ USDCx: 6,
8
+ };
9
+ /**
10
+ * Convert human-readable amount to smallest unit (microSTX, sats, etc.)
11
+ */
12
+ function toSmallestUnit(amount, tokenType) {
13
+ const decimals = TOKEN_DECIMALS[tokenType];
14
+ const [whole, fraction = ""] = amount.split(".");
15
+ const paddedFraction = fraction.padEnd(decimals, "0").slice(0, decimals);
16
+ return BigInt(whole + paddedFraction).toString();
17
+ }
18
+ /**
19
+ * Generate Hono route code for each endpoint
20
+ */
21
+ function generateEndpointCode(endpoints) {
22
+ return endpoints
23
+ .map((ep) => {
24
+ const amountSmallest = toSmallestUnit(ep.amount, ep.tokenType);
25
+ // Generate real example logic based on endpoint characteristics
26
+ const exampleLogic = generateExampleLogic(ep);
27
+ return `
28
+ // ${ep.description}
29
+ app.${ep.method.toLowerCase()}('${ep.path}',
30
+ x402Middleware({
31
+ amount: '${amountSmallest}',
32
+ address: env.RECIPIENT_ADDRESS,
33
+ network: env.NETWORK as 'mainnet' | 'testnet',
34
+ tokenType: '${ep.tokenType}',
35
+ facilitatorUrl: env.FACILITATOR_URL,
36
+ }),
37
+ async (c) => {
38
+ const payment = c.get('payment');
39
+ ${exampleLogic}
40
+ }
41
+ );`;
42
+ })
43
+ .join("\n");
44
+ }
45
+ /**
46
+ * Generate real example logic for endpoints based on their configuration.
47
+ * This replaces placeholder/TODO code with working examples.
48
+ */
49
+ function generateExampleLogic(ep) {
50
+ if (ep.method === "POST") {
51
+ return `
52
+ // Parse request body
53
+ const body = await c.req.json<Record<string, unknown>>().catch(() => ({}));
54
+
55
+ // Your business logic here - this example echoes the request
56
+ const result = {
57
+ received: body,
58
+ processedAt: new Date().toISOString(),
59
+ };
60
+
61
+ return c.json({
62
+ success: true,
63
+ data: result,
64
+ payment: {
65
+ txId: payment?.txId,
66
+ sender: payment?.sender,
67
+ amount: payment?.amount?.toString(),
68
+ },
69
+ });`;
70
+ }
71
+ // GET endpoint - return example data
72
+ return `
73
+ // Your business logic here - this example returns sample data
74
+ const data = {
75
+ id: crypto.randomUUID(),
76
+ description: '${ep.description}',
77
+ generatedAt: new Date().toISOString(),
78
+ };
79
+
80
+ return c.json({
81
+ success: true,
82
+ data,
83
+ payment: {
84
+ txId: payment?.txId,
85
+ sender: payment?.sender,
86
+ amount: payment?.amount?.toString(),
87
+ },
88
+ });`;
89
+ }
90
+ /**
91
+ * Generate endpoint documentation for README
92
+ */
93
+ function generateEndpointDocs(endpoints) {
94
+ return endpoints
95
+ .map((ep) => {
96
+ return `### ${ep.method} ${ep.path}
97
+ - **Description:** ${ep.description}
98
+ - **Cost:** ${ep.amount} ${ep.tokenType}
99
+ - **Payment Required:** Yes`;
100
+ })
101
+ .join("\n\n");
102
+ }
103
+ /**
104
+ * Generate token list for README
105
+ */
106
+ function generateTokenList(endpoints) {
107
+ const tokens = [...new Set(endpoints.map((ep) => ep.tokenType))];
108
+ return tokens.map((t) => `- ${t}`).join("\n");
109
+ }
110
+ // =============================================================================
111
+ // FILE TEMPLATES
112
+ // =============================================================================
113
+ function getIndexTemplate(endpoints) {
114
+ const endpointCode = generateEndpointCode(endpoints);
115
+ return `import { Hono } from 'hono';
116
+ import { cors } from 'hono/cors';
117
+ import { x402Middleware } from './x402-middleware';
118
+
119
+ type Bindings = {
120
+ RECIPIENT_ADDRESS: string;
121
+ NETWORK: string;
122
+ FACILITATOR_URL: string;
123
+ };
124
+
125
+ type Variables = {
126
+ payment?: {
127
+ txId: string;
128
+ status: string;
129
+ sender: string;
130
+ recipient: string;
131
+ amount: bigint;
132
+ };
133
+ };
134
+
135
+ const app = new Hono<{ Bindings: Bindings; Variables: Variables }>();
136
+
137
+ app.use('*', cors());
138
+
139
+ // Startup validation - fail fast if required secrets are missing
140
+ app.use('*', async (c, next) => {
141
+ const missingSecrets: string[] = [];
142
+
143
+ if (!c.env.RECIPIENT_ADDRESS) {
144
+ missingSecrets.push('RECIPIENT_ADDRESS');
145
+ }
146
+ if (!c.env.FACILITATOR_URL) {
147
+ missingSecrets.push('FACILITATOR_URL');
148
+ }
149
+
150
+ if (missingSecrets.length > 0) {
151
+ return c.json({
152
+ error: 'Server configuration error',
153
+ message: \`Missing required secrets: \${missingSecrets.join(', ')}\`,
154
+ hint: missingSecrets.map(s => \`Run: npm run wrangler -- secret put \${s}\`).join(' && '),
155
+ }, 503);
156
+ }
157
+
158
+ await next();
159
+ });
160
+
161
+ // Health check (free)
162
+ app.get('/health', (c) => {
163
+ return c.json({
164
+ status: 'ok',
165
+ timestamp: new Date().toISOString(),
166
+ });
167
+ });
168
+
169
+ // x402-protected endpoints
170
+ app.use('*', async (c, next) => {
171
+ // Make env available to middleware
172
+ const env = c.env;
173
+ (globalThis as Record<string, unknown>).__env = env;
174
+ await next();
175
+ });
176
+
177
+ const env = {
178
+ get RECIPIENT_ADDRESS() {
179
+ return ((globalThis as Record<string, unknown>).__env as Bindings)?.RECIPIENT_ADDRESS || '';
180
+ },
181
+ get NETWORK() {
182
+ return ((globalThis as Record<string, unknown>).__env as Bindings)?.NETWORK || 'testnet';
183
+ },
184
+ get FACILITATOR_URL() {
185
+ return ((globalThis as Record<string, unknown>).__env as Bindings)?.FACILITATOR_URL || '';
186
+ },
187
+ };
188
+ ${endpointCode}
189
+
190
+ export default app;
191
+ `;
192
+ }
193
+ function getMiddlewareTemplate() {
194
+ return `import type { Context, Next } from 'hono';
195
+
196
+ export interface X402Config {
197
+ amount: string;
198
+ address: string;
199
+ network: 'mainnet' | 'testnet';
200
+ tokenType: 'STX' | 'sBTC' | 'USDCx';
201
+ facilitatorUrl?: string;
202
+ resource?: string;
203
+ }
204
+
205
+ interface TokenContract {
206
+ address: string;
207
+ name: string;
208
+ }
209
+
210
+ // Token contract addresses for payment verification
211
+ const TOKEN_CONTRACTS: Record<string, Record<string, TokenContract | null>> = {
212
+ mainnet: {
213
+ STX: null,
214
+ sBTC: { address: 'SM3VDXK3WZZSA84XXFKAFAF15NNZX32CTSG82JFQ4', name: 'sbtc-token' },
215
+ USDCx: { address: 'SP2XD7417HGPRTREMKF748VNEQPDRR0RMANB7X1NK', name: 'token-usdcx' },
216
+ },
217
+ testnet: {
218
+ STX: null,
219
+ sBTC: { address: 'ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM', name: 'sbtc-token' },
220
+ USDCx: null,
221
+ },
222
+ };
223
+
224
+ interface PaymentRequirement {
225
+ maxAmountRequired: string;
226
+ resource: string;
227
+ payTo: string;
228
+ network: string;
229
+ nonce: string;
230
+ expiresAt: string;
231
+ tokenType: string;
232
+ tokenContract?: TokenContract;
233
+ }
234
+
235
+ interface SettleRequest {
236
+ signed_transaction: string;
237
+ expected_recipient: string;
238
+ min_amount: string;
239
+ network: string;
240
+ token_type: string;
241
+ resource: string;
242
+ method: string;
243
+ }
244
+
245
+ interface SettleResponse {
246
+ success: boolean;
247
+ tx_id?: string;
248
+ status?: string;
249
+ sender_address?: string;
250
+ recipient_address?: string;
251
+ amount?: number;
252
+ error?: string;
253
+ }
254
+
255
+ /**
256
+ * x402 Payment Middleware for Hono
257
+ *
258
+ * Handles the x402 payment flow:
259
+ * 1. If no X-PAYMENT header, return 402 with payment requirements
260
+ * 2. If X-PAYMENT header present, verify payment via facilitator
261
+ * 3. On success, attach payment info and continue to handler
262
+ */
263
+ export function x402Middleware(config: X402Config) {
264
+ const facilitatorUrl = config.facilitatorUrl || 'https://facilitator.x402stacks.xyz';
265
+ const tokenContract = TOKEN_CONTRACTS[config.network]?.[config.tokenType] || null;
266
+
267
+ return async (c: Context, next: Next) => {
268
+ const signedPayment = c.req.header('x-payment');
269
+
270
+ if (!signedPayment) {
271
+ // Return 402 Payment Required with payment details
272
+ const paymentReq: PaymentRequirement = {
273
+ maxAmountRequired: config.amount,
274
+ resource: config.resource || c.req.path,
275
+ payTo: config.address,
276
+ network: config.network,
277
+ nonce: crypto.randomUUID(),
278
+ expiresAt: new Date(Date.now() + 300000).toISOString(),
279
+ tokenType: config.tokenType,
280
+ };
281
+
282
+ if (tokenContract) {
283
+ paymentReq.tokenContract = tokenContract;
284
+ }
285
+
286
+ return c.json(paymentReq, 402);
287
+ }
288
+
289
+ // Verify and settle payment via facilitator
290
+ try {
291
+ const settleRequest: SettleRequest = {
292
+ signed_transaction: signedPayment,
293
+ expected_recipient: config.address,
294
+ min_amount: config.amount,
295
+ network: config.network,
296
+ token_type: config.tokenType.toUpperCase(),
297
+ resource: config.resource || c.req.path,
298
+ method: c.req.method,
299
+ };
300
+
301
+ const response = await fetch(\`\${facilitatorUrl}/api/v1/settle\`, {
302
+ method: 'POST',
303
+ headers: { 'Content-Type': 'application/json' },
304
+ body: JSON.stringify(settleRequest),
305
+ });
306
+
307
+ const result = (await response.json()) as SettleResponse;
308
+
309
+ if (!result.success) {
310
+ return c.json(
311
+ {
312
+ error: 'Payment verification failed',
313
+ reason: result.error || 'Unknown error',
314
+ },
315
+ 402
316
+ );
317
+ }
318
+
319
+ // Store payment info in context for handler to use
320
+ c.set('payment', {
321
+ txId: result.tx_id,
322
+ status: result.status,
323
+ sender: result.sender_address,
324
+ recipient: result.recipient_address,
325
+ amount: BigInt(result.amount || 0),
326
+ });
327
+
328
+ await next();
329
+ } catch (error) {
330
+ return c.json(
331
+ {
332
+ error: 'Payment processing error',
333
+ message: error instanceof Error ? error.message : 'Unknown error',
334
+ },
335
+ 500
336
+ );
337
+ }
338
+ };
339
+ }
340
+ `;
341
+ }
342
+ function getWranglerTemplate(projectName, network, facilitatorUrl) {
343
+ return `{
344
+ "name": "${projectName}",
345
+ "main": "src/index.ts",
346
+ "compatibility_date": "2026-01-14",
347
+ "compatibility_flags": ["nodejs_compat"],
348
+ "vars": {
349
+ "NETWORK": "${network}",
350
+ "FACILITATOR_URL": "${facilitatorUrl}"
351
+ },
352
+ "env": {
353
+ "production": {
354
+ "vars": {
355
+ "NETWORK": "mainnet"
356
+ }
357
+ }
358
+ }
359
+ }
360
+ `;
361
+ }
362
+ function getPackageJsonTemplate(projectName) {
363
+ return `{
364
+ "name": "${projectName}",
365
+ "version": "1.0.0",
366
+ "private": true,
367
+ "type": "module",
368
+ "scripts": {
369
+ "wrangler": "set -a && . ./.env && set +a && wrangler",
370
+ "dev": "npm run wrangler -- dev",
371
+ "deploy": "npm run wrangler -- deploy",
372
+ "deploy:dry": "npm run wrangler -- deploy --dry-run",
373
+ "deploy:production": "npm run wrangler -- deploy --env production",
374
+ "tail": "npm run wrangler -- tail"
375
+ },
376
+ "dependencies": {
377
+ "hono": "^4.7.0"
378
+ },
379
+ "devDependencies": {
380
+ "@cloudflare/workers-types": "^4.20250109.0",
381
+ "typescript": "^5.7.0",
382
+ "wrangler": "^4.5.0"
383
+ }
384
+ }
385
+ `;
386
+ }
387
+ function getTsconfigTemplate() {
388
+ return `{
389
+ "compilerOptions": {
390
+ "target": "ES2022",
391
+ "module": "ESNext",
392
+ "moduleResolution": "bundler",
393
+ "strict": true,
394
+ "lib": ["ES2022"],
395
+ "types": ["@cloudflare/workers-types"],
396
+ "esModuleInterop": true,
397
+ "skipLibCheck": true,
398
+ "forceConsistentCasingInFileNames": true,
399
+ "outDir": "dist",
400
+ "rootDir": "src"
401
+ },
402
+ "include": ["src/**/*"],
403
+ "exclude": ["node_modules"]
404
+ }
405
+ `;
406
+ }
407
+ function getEnvExampleTemplate(recipientAddress) {
408
+ return `# Cloudflare credentials
409
+ CLOUDFLARE_API_TOKEN=your-api-token-here
410
+ CLOUDFLARE_ACCOUNT_ID=your-account-id-here
411
+
412
+ # x402 recipient address (set via wrangler secret)
413
+ # wrangler secret put RECIPIENT_ADDRESS
414
+ # Value: ${recipientAddress}
415
+ `;
416
+ }
417
+ function getGitignoreTemplate() {
418
+ return `node_modules/
419
+ dist/
420
+ .env
421
+ .dev.vars
422
+ .wrangler/
423
+ `;
424
+ }
425
+ function getReadmeTemplate(projectName, endpoints, recipientAddress) {
426
+ const tokenList = generateTokenList(endpoints);
427
+ const endpointDocs = generateEndpointDocs(endpoints);
428
+ return `# ${projectName}
429
+
430
+ x402-enabled API endpoints on Cloudflare Workers.
431
+
432
+ ## Payment Tokens
433
+
434
+ This API accepts payments in:
435
+ ${tokenList}
436
+
437
+ ## Recipient Address
438
+
439
+ Payments are sent to: \`${recipientAddress}\`
440
+
441
+ ## Endpoints
442
+
443
+ ### GET /health
444
+ - **Description:** Health check endpoint
445
+ - **Cost:** Free
446
+ - **Payment Required:** No
447
+
448
+ ${endpointDocs}
449
+
450
+ ## Setup
451
+
452
+ 1. Install dependencies:
453
+ \`\`\`bash
454
+ npm install
455
+ \`\`\`
456
+
457
+ 2. Create \`.env\` file from \`.env.example\`:
458
+ \`\`\`bash
459
+ cp .env.example .env
460
+ \`\`\`
461
+
462
+ 3. Add your Cloudflare credentials to \`.env\`
463
+
464
+ 4. Set the recipient address as a secret:
465
+ \`\`\`bash
466
+ npm run wrangler -- secret put RECIPIENT_ADDRESS
467
+ # Enter: ${recipientAddress}
468
+ \`\`\`
469
+
470
+ ## Local Development
471
+
472
+ \`\`\`bash
473
+ npm run dev
474
+ \`\`\`
475
+
476
+ The server will start at http://localhost:8787
477
+
478
+ ## Deploy
479
+
480
+ \`\`\`bash
481
+ # Dry run first
482
+ npm run deploy:dry
483
+
484
+ # Deploy to staging
485
+ npm run deploy
486
+
487
+ # Deploy to production
488
+ npm run deploy:production
489
+ \`\`\`
490
+
491
+ ## x402 Payment Flow
492
+
493
+ 1. Client makes request without payment header
494
+ 2. Server returns HTTP 402 with payment requirements:
495
+ \`\`\`json
496
+ {
497
+ "maxAmountRequired": "1000",
498
+ "resource": "/api/endpoint",
499
+ "payTo": "${recipientAddress}",
500
+ "network": "testnet",
501
+ "tokenType": "STX"
502
+ }
503
+ \`\`\`
504
+ 3. Client signs payment transaction (does NOT broadcast)
505
+ 4. Client retries request with \`X-PAYMENT\` header containing signed tx
506
+ 5. Server verifies and settles payment via facilitator
507
+ 6. Server returns actual response
508
+
509
+ ## Testing with curl
510
+
511
+ \`\`\`bash
512
+ # Health check (free)
513
+ curl http://localhost:8787/health
514
+
515
+ # Protected endpoint (returns 402)
516
+ curl http://localhost:8787${endpoints[0]?.path || "/api/endpoint"}
517
+ \`\`\`
518
+
519
+ ---
520
+
521
+ Generated with @aibtc/mcp-server scaffold tool.
522
+ `;
523
+ }
524
+ // =============================================================================
525
+ // MAIN SCAFFOLD FUNCTION
526
+ // =============================================================================
527
+ export async function scaffoldProject(config) {
528
+ const { outputDir, projectName, endpoints, recipientAddress, network, facilitatorUrl } = config;
529
+ // Validate output directory exists
530
+ try {
531
+ const stat = await fs.stat(outputDir);
532
+ if (!stat.isDirectory()) {
533
+ throw new Error(`Output path is not a directory: ${outputDir}`);
534
+ }
535
+ }
536
+ catch (error) {
537
+ if (error.code === "ENOENT") {
538
+ throw new Error(`Output directory does not exist: ${outputDir}`);
539
+ }
540
+ throw error;
541
+ }
542
+ const projectPath = path.join(outputDir, projectName);
543
+ const srcPath = path.join(projectPath, "src");
544
+ // Create project directory structure
545
+ await fs.mkdir(projectPath, { recursive: true });
546
+ await fs.mkdir(srcPath, { recursive: true });
547
+ const filesCreated = [];
548
+ // Generate and write files
549
+ const files = [
550
+ { name: "src/index.ts", content: getIndexTemplate(endpoints) },
551
+ { name: "src/x402-middleware.ts", content: getMiddlewareTemplate() },
552
+ { name: "wrangler.jsonc", content: getWranglerTemplate(projectName, network, facilitatorUrl) },
553
+ { name: "package.json", content: getPackageJsonTemplate(projectName) },
554
+ { name: "tsconfig.json", content: getTsconfigTemplate() },
555
+ { name: ".env.example", content: getEnvExampleTemplate(recipientAddress) },
556
+ { name: ".gitignore", content: getGitignoreTemplate() },
557
+ { name: "README.md", content: getReadmeTemplate(projectName, endpoints, recipientAddress) },
558
+ ];
559
+ for (const file of files) {
560
+ const filePath = path.join(projectPath, file.name);
561
+ await fs.writeFile(filePath, file.content, "utf-8");
562
+ filesCreated.push(file.name);
563
+ }
564
+ return {
565
+ projectPath,
566
+ filesCreated,
567
+ nextSteps: [
568
+ `cd ${projectPath}`,
569
+ "npm install",
570
+ "cp .env.example .env",
571
+ "# Add your Cloudflare credentials to .env",
572
+ `npm run wrangler -- secret put RECIPIENT_ADDRESS (enter: ${recipientAddress})`,
573
+ "npm run dev",
574
+ ],
575
+ };
576
+ }
577
+ // =============================================================================
578
+ // AI ENDPOINT TEMPLATES (OpenRouter)
579
+ // =============================================================================
580
+ const AI_TYPE_CONFIGS = {
581
+ chat: {
582
+ systemPrompt: "You are a helpful AI assistant.",
583
+ description: "Chat with an AI assistant",
584
+ },
585
+ completion: {
586
+ systemPrompt: "You are a creative writing assistant. Complete the given text naturally.",
587
+ description: "AI text completion",
588
+ },
589
+ summarize: {
590
+ systemPrompt: "You are a summarization expert. Provide concise summaries of the given text, capturing the key points.",
591
+ description: "Summarize text using AI",
592
+ },
593
+ translate: {
594
+ systemPrompt: "You are a professional translator. Translate the given text accurately while preserving meaning and tone.",
595
+ description: "Translate text using AI",
596
+ },
597
+ custom: {
598
+ systemPrompt: "You are a helpful AI assistant.",
599
+ description: "Custom AI endpoint",
600
+ },
601
+ };
602
+ function generateAIEndpointCode(endpoints, defaultModel) {
603
+ return endpoints
604
+ .map((ep) => {
605
+ const amountSmallest = toSmallestUnit(ep.amount, ep.tokenType);
606
+ const config = AI_TYPE_CONFIGS[ep.aiType];
607
+ const systemPrompt = ep.systemPrompt || config.systemPrompt;
608
+ const model = ep.model || defaultModel;
609
+ return `
610
+ // ${ep.description}
611
+ app.post('${ep.path}',
612
+ x402Middleware({
613
+ amount: '${amountSmallest}',
614
+ address: env.RECIPIENT_ADDRESS,
615
+ network: env.NETWORK as 'mainnet' | 'testnet',
616
+ tokenType: '${ep.tokenType}',
617
+ facilitatorUrl: env.FACILITATOR_URL,
618
+ }),
619
+ async (c) => {
620
+ const payment = c.get('payment');
621
+ const body = await c.req.json<{ prompt?: string; message?: string; text?: string; targetLanguage?: string }>();
622
+ const userInput = body.prompt || body.message || body.text || '';
623
+
624
+ if (!userInput) {
625
+ return c.json({ error: 'Missing required field: prompt, message, or text' }, 400);
626
+ }
627
+
628
+ const result = await callOpenRouter({
629
+ apiKey: env.OPENROUTER_API_KEY,
630
+ model: '${model}',
631
+ systemPrompt: \`${systemPrompt.replace(/`/g, "\\`")}\`,
632
+ userMessage: ${ep.aiType === "translate" ? "`Translate to ${body.targetLanguage || 'English'}: ${userInput}`" : "userInput"},
633
+ });
634
+
635
+ return c.json({
636
+ result: result.content,
637
+ model: result.model,
638
+ usage: result.usage,
639
+ txId: payment?.txId,
640
+ });
641
+ }
642
+ );`;
643
+ })
644
+ .join("\n");
645
+ }
646
+ function generateAIEndpointDocs(endpoints) {
647
+ return endpoints
648
+ .map((ep) => {
649
+ const inputField = ep.aiType === "translate" ? "text, targetLanguage (optional)" : "prompt or message or text";
650
+ return `### POST ${ep.path}
651
+ - **Description:** ${ep.description}
652
+ - **Cost:** ${ep.amount} ${ep.tokenType}
653
+ - **AI Type:** ${ep.aiType}
654
+ - **Input:** \`{ ${inputField} }\`
655
+ - **Payment Required:** Yes`;
656
+ })
657
+ .join("\n\n");
658
+ }
659
+ function getAIIndexTemplate(endpoints, defaultModel) {
660
+ const endpointCode = generateAIEndpointCode(endpoints, defaultModel);
661
+ return `import { Hono } from 'hono';
662
+ import { cors } from 'hono/cors';
663
+ import { x402Middleware } from './x402-middleware';
664
+ import { callOpenRouter } from './openrouter';
665
+
666
+ type Bindings = {
667
+ RECIPIENT_ADDRESS: string;
668
+ NETWORK: string;
669
+ FACILITATOR_URL: string;
670
+ OPENROUTER_API_KEY: string;
671
+ };
672
+
673
+ type Variables = {
674
+ payment?: {
675
+ txId: string;
676
+ status: string;
677
+ sender: string;
678
+ recipient: string;
679
+ amount: bigint;
680
+ };
681
+ };
682
+
683
+ const app = new Hono<{ Bindings: Bindings; Variables: Variables }>();
684
+
685
+ app.use('*', cors());
686
+
687
+ // Startup validation - fail fast if required secrets are missing
688
+ app.use('*', async (c, next) => {
689
+ const missingSecrets: string[] = [];
690
+
691
+ if (!c.env.RECIPIENT_ADDRESS) {
692
+ missingSecrets.push('RECIPIENT_ADDRESS');
693
+ }
694
+ if (!c.env.FACILITATOR_URL) {
695
+ missingSecrets.push('FACILITATOR_URL');
696
+ }
697
+ if (!c.env.OPENROUTER_API_KEY) {
698
+ missingSecrets.push('OPENROUTER_API_KEY');
699
+ }
700
+
701
+ if (missingSecrets.length > 0) {
702
+ return c.json({
703
+ error: 'Server configuration error',
704
+ message: \`Missing required secrets: \${missingSecrets.join(', ')}\`,
705
+ hint: missingSecrets.map(s => \`Run: npm run wrangler -- secret put \${s}\`).join(' && '),
706
+ }, 503);
707
+ }
708
+
709
+ await next();
710
+ });
711
+
712
+ // Health check (free)
713
+ app.get('/health', (c) => {
714
+ return c.json({
715
+ status: 'ok',
716
+ timestamp: new Date().toISOString(),
717
+ });
718
+ });
719
+
720
+ // x402-protected AI endpoints
721
+ app.use('*', async (c, next) => {
722
+ // Make env available to middleware
723
+ const env = c.env;
724
+ (globalThis as Record<string, unknown>).__env = env;
725
+ await next();
726
+ });
727
+
728
+ const env = {
729
+ get RECIPIENT_ADDRESS() {
730
+ return ((globalThis as Record<string, unknown>).__env as Bindings)?.RECIPIENT_ADDRESS || '';
731
+ },
732
+ get NETWORK() {
733
+ return ((globalThis as Record<string, unknown>).__env as Bindings)?.NETWORK || 'testnet';
734
+ },
735
+ get FACILITATOR_URL() {
736
+ return ((globalThis as Record<string, unknown>).__env as Bindings)?.FACILITATOR_URL || '';
737
+ },
738
+ get OPENROUTER_API_KEY() {
739
+ return ((globalThis as Record<string, unknown>).__env as Bindings)?.OPENROUTER_API_KEY || '';
740
+ },
741
+ };
742
+ ${endpointCode}
743
+
744
+ export default app;
745
+ `;
746
+ }
747
+ function getOpenRouterTemplate() {
748
+ return `/**
749
+ * OpenRouter API Client
750
+ * https://openrouter.ai/docs
751
+ */
752
+
753
+ export interface OpenRouterRequest {
754
+ apiKey: string;
755
+ model: string;
756
+ systemPrompt: string;
757
+ userMessage: string;
758
+ maxTokens?: number;
759
+ temperature?: number;
760
+ }
761
+
762
+ export interface OpenRouterResponse {
763
+ content: string;
764
+ model: string;
765
+ usage: {
766
+ promptTokens: number;
767
+ completionTokens: number;
768
+ totalTokens: number;
769
+ };
770
+ }
771
+
772
+ interface OpenRouterAPIResponse {
773
+ id: string;
774
+ model: string;
775
+ choices: Array<{
776
+ message: {
777
+ role: string;
778
+ content: string;
779
+ };
780
+ finish_reason: string;
781
+ }>;
782
+ usage: {
783
+ prompt_tokens: number;
784
+ completion_tokens: number;
785
+ total_tokens: number;
786
+ };
787
+ }
788
+
789
+ export async function callOpenRouter(request: OpenRouterRequest): Promise<OpenRouterResponse> {
790
+ const {
791
+ apiKey,
792
+ model,
793
+ systemPrompt,
794
+ userMessage,
795
+ maxTokens = 1024,
796
+ temperature = 0.7,
797
+ } = request;
798
+
799
+ const response = await fetch('https://openrouter.ai/api/v1/chat/completions', {
800
+ method: 'POST',
801
+ headers: {
802
+ 'Authorization': \`Bearer \${apiKey}\`,
803
+ 'Content-Type': 'application/json',
804
+ 'HTTP-Referer': 'https://x402stacks.xyz',
805
+ 'X-Title': 'x402 AI Endpoint',
806
+ },
807
+ body: JSON.stringify({
808
+ model,
809
+ messages: [
810
+ { role: 'system', content: systemPrompt },
811
+ { role: 'user', content: userMessage },
812
+ ],
813
+ max_tokens: maxTokens,
814
+ temperature,
815
+ }),
816
+ });
817
+
818
+ if (!response.ok) {
819
+ const error = await response.text();
820
+ throw new Error(\`OpenRouter API error: \${response.status} - \${error}\`);
821
+ }
822
+
823
+ const data = (await response.json()) as OpenRouterAPIResponse;
824
+
825
+ return {
826
+ content: data.choices[0]?.message?.content || '',
827
+ model: data.model,
828
+ usage: {
829
+ promptTokens: data.usage.prompt_tokens,
830
+ completionTokens: data.usage.completion_tokens,
831
+ totalTokens: data.usage.total_tokens,
832
+ },
833
+ };
834
+ }
835
+ `;
836
+ }
837
+ function getAIPackageJsonTemplate(projectName) {
838
+ return `{
839
+ "name": "${projectName}",
840
+ "version": "1.0.0",
841
+ "private": true,
842
+ "type": "module",
843
+ "scripts": {
844
+ "wrangler": "set -a && . ./.env && set +a && wrangler",
845
+ "dev": "npm run wrangler -- dev",
846
+ "deploy": "npm run wrangler -- deploy",
847
+ "deploy:dry": "npm run wrangler -- deploy --dry-run",
848
+ "deploy:production": "npm run wrangler -- deploy --env production",
849
+ "tail": "npm run wrangler -- tail"
850
+ },
851
+ "dependencies": {
852
+ "hono": "^4.7.0"
853
+ },
854
+ "devDependencies": {
855
+ "@cloudflare/workers-types": "^4.20250109.0",
856
+ "typescript": "^5.7.0",
857
+ "wrangler": "^4.5.0"
858
+ }
859
+ }
860
+ `;
861
+ }
862
+ function getAIEnvExampleTemplate(recipientAddress) {
863
+ return `# Cloudflare credentials
864
+ CLOUDFLARE_API_TOKEN=your-api-token-here
865
+ CLOUDFLARE_ACCOUNT_ID=your-account-id-here
866
+
867
+ # x402 recipient address (set via wrangler secret)
868
+ # wrangler secret put RECIPIENT_ADDRESS
869
+ # Value: ${recipientAddress}
870
+
871
+ # OpenRouter API key (set via wrangler secret)
872
+ # Get your key at https://openrouter.ai/keys
873
+ # wrangler secret put OPENROUTER_API_KEY
874
+ `;
875
+ }
876
+ function getAIReadmeTemplate(projectName, endpoints, recipientAddress, defaultModel) {
877
+ const tokenList = [...new Set(endpoints.map((ep) => `- ${ep.tokenType}`))].join("\n");
878
+ const endpointDocs = generateAIEndpointDocs(endpoints);
879
+ return `# ${projectName}
880
+
881
+ x402-enabled AI API endpoints on Cloudflare Workers, powered by OpenRouter.
882
+
883
+ ## AI Provider
884
+
885
+ This API uses [OpenRouter](https://openrouter.ai) to access AI models.
886
+ Default model: \`${defaultModel}\`
887
+
888
+ ## Payment Tokens
889
+
890
+ This API accepts payments in:
891
+ ${tokenList}
892
+
893
+ ## Recipient Address
894
+
895
+ Payments are sent to: \`${recipientAddress}\`
896
+
897
+ ## Endpoints
898
+
899
+ ### GET /health
900
+ - **Description:** Health check endpoint
901
+ - **Cost:** Free
902
+ - **Payment Required:** No
903
+
904
+ ${endpointDocs}
905
+
906
+ ## Setup
907
+
908
+ 1. Install dependencies:
909
+ \`\`\`bash
910
+ npm install
911
+ \`\`\`
912
+
913
+ 2. Create \`.env\` file from \`.env.example\`:
914
+ \`\`\`bash
915
+ cp .env.example .env
916
+ \`\`\`
917
+
918
+ 3. Add your Cloudflare credentials to \`.env\`
919
+
920
+ 4. Set secrets:
921
+ \`\`\`bash
922
+ # Recipient address for payments
923
+ npm run wrangler -- secret put RECIPIENT_ADDRESS
924
+ # Enter: ${recipientAddress}
925
+
926
+ # OpenRouter API key (get from https://openrouter.ai/keys)
927
+ npm run wrangler -- secret put OPENROUTER_API_KEY
928
+ \`\`\`
929
+
930
+ ## Local Development
931
+
932
+ For local development, create a \`.dev.vars\` file:
933
+ \`\`\`
934
+ RECIPIENT_ADDRESS=${recipientAddress}
935
+ OPENROUTER_API_KEY=your-openrouter-key
936
+ \`\`\`
937
+
938
+ Then run:
939
+ \`\`\`bash
940
+ npm run dev
941
+ \`\`\`
942
+
943
+ The server will start at http://localhost:8787
944
+
945
+ ## Deploy
946
+
947
+ \`\`\`bash
948
+ # Dry run first
949
+ npm run deploy:dry
950
+
951
+ # Deploy to staging
952
+ npm run deploy
953
+
954
+ # Deploy to production
955
+ npm run deploy:production
956
+ \`\`\`
957
+
958
+ ## Example Usage
959
+
960
+ \`\`\`bash
961
+ # Health check (free)
962
+ curl http://localhost:8787/health
963
+
964
+ # AI endpoint (returns 402 without payment)
965
+ curl -X POST http://localhost:8787${endpoints[0]?.path || "/api/chat"} \\
966
+ -H "Content-Type: application/json" \\
967
+ -d '{"prompt": "Hello, how are you?"}'
968
+ \`\`\`
969
+
970
+ ## x402 Payment Flow
971
+
972
+ 1. Client makes request without payment header
973
+ 2. Server returns HTTP 402 with payment requirements
974
+ 3. Client signs payment transaction (does NOT broadcast)
975
+ 4. Client retries request with \`X-PAYMENT\` header containing signed tx
976
+ 5. Server verifies and settles payment via facilitator
977
+ 6. Server calls OpenRouter API and returns AI response
978
+
979
+ ## OpenRouter Models
980
+
981
+ You can use any model available on OpenRouter. Popular options:
982
+ - \`anthropic/claude-3.5-sonnet\` - Best for complex tasks
983
+ - \`anthropic/claude-3-haiku\` - Fast and affordable
984
+ - \`openai/gpt-4o\` - OpenAI's latest
985
+ - \`openai/gpt-4o-mini\` - Fast and cheap
986
+ - \`meta-llama/llama-3.1-70b-instruct\` - Open source
987
+ - \`google/gemini-pro-1.5\` - Google's model
988
+
989
+ See all models: https://openrouter.ai/models
990
+
991
+ ---
992
+
993
+ Generated with @aibtc/mcp-server scaffold tool.
994
+ `;
995
+ }
996
+ // =============================================================================
997
+ // AI SCAFFOLD FUNCTION
998
+ // =============================================================================
999
+ export async function scaffoldAIProject(config) {
1000
+ const { outputDir, projectName, endpoints, recipientAddress, network, facilitatorUrl, defaultModel, } = config;
1001
+ // Validate output directory exists
1002
+ try {
1003
+ const stat = await fs.stat(outputDir);
1004
+ if (!stat.isDirectory()) {
1005
+ throw new Error(`Output path is not a directory: ${outputDir}`);
1006
+ }
1007
+ }
1008
+ catch (error) {
1009
+ if (error.code === "ENOENT") {
1010
+ throw new Error(`Output directory does not exist: ${outputDir}`);
1011
+ }
1012
+ throw error;
1013
+ }
1014
+ const projectPath = path.join(outputDir, projectName);
1015
+ const srcPath = path.join(projectPath, "src");
1016
+ // Create project directory structure
1017
+ await fs.mkdir(projectPath, { recursive: true });
1018
+ await fs.mkdir(srcPath, { recursive: true });
1019
+ const filesCreated = [];
1020
+ // Generate and write files
1021
+ const files = [
1022
+ { name: "src/index.ts", content: getAIIndexTemplate(endpoints, defaultModel) },
1023
+ { name: "src/x402-middleware.ts", content: getMiddlewareTemplate() },
1024
+ { name: "src/openrouter.ts", content: getOpenRouterTemplate() },
1025
+ { name: "wrangler.jsonc", content: getWranglerTemplate(projectName, network, facilitatorUrl) },
1026
+ { name: "package.json", content: getAIPackageJsonTemplate(projectName) },
1027
+ { name: "tsconfig.json", content: getTsconfigTemplate() },
1028
+ { name: ".env.example", content: getAIEnvExampleTemplate(recipientAddress) },
1029
+ { name: ".gitignore", content: getGitignoreTemplate() },
1030
+ {
1031
+ name: "README.md",
1032
+ content: getAIReadmeTemplate(projectName, endpoints, recipientAddress, defaultModel),
1033
+ },
1034
+ ];
1035
+ for (const file of files) {
1036
+ const filePath = path.join(projectPath, file.name);
1037
+ await fs.writeFile(filePath, file.content, "utf-8");
1038
+ filesCreated.push(file.name);
1039
+ }
1040
+ return {
1041
+ projectPath,
1042
+ filesCreated,
1043
+ nextSteps: [
1044
+ `cd ${projectPath}`,
1045
+ "npm install",
1046
+ "cp .env.example .env",
1047
+ "# Add your Cloudflare credentials to .env",
1048
+ `npm run wrangler -- secret put RECIPIENT_ADDRESS (enter: ${recipientAddress})`,
1049
+ "npm run wrangler -- secret put OPENROUTER_API_KEY (get from https://openrouter.ai/keys)",
1050
+ "# For local dev, create .dev.vars with RECIPIENT_ADDRESS and OPENROUTER_API_KEY",
1051
+ "npm run dev",
1052
+ ],
1053
+ };
1054
+ }
1055
+ //# sourceMappingURL=scaffold.service.js.map