@framers/agentos-ext-domain-porkbun 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. package/LICENSE +98 -0
  2. package/dist/PorkbunService.d.ts +119 -0
  3. package/dist/PorkbunService.d.ts.map +1 -0
  4. package/dist/PorkbunService.js +224 -0
  5. package/dist/PorkbunService.js.map +1 -0
  6. package/dist/index.d.ts +43 -0
  7. package/dist/index.d.ts.map +1 -0
  8. package/dist/index.js +76 -0
  9. package/dist/index.js.map +1 -0
  10. package/dist/tools/configureDns.d.ts +75 -0
  11. package/dist/tools/configureDns.d.ts.map +1 -0
  12. package/dist/tools/configureDns.js +91 -0
  13. package/dist/tools/configureDns.js.map +1 -0
  14. package/dist/tools/listDomains.d.ts +26 -0
  15. package/dist/tools/listDomains.d.ts.map +1 -0
  16. package/dist/tools/listDomains.js +32 -0
  17. package/dist/tools/listDomains.js.map +1 -0
  18. package/dist/tools/registerDomain.d.ts +89 -0
  19. package/dist/tools/registerDomain.d.ts.map +1 -0
  20. package/dist/tools/registerDomain.js +58 -0
  21. package/dist/tools/registerDomain.js.map +1 -0
  22. package/dist/tools/searchDomain.d.ts +34 -0
  23. package/dist/tools/searchDomain.d.ts.map +1 -0
  24. package/dist/tools/searchDomain.js +34 -0
  25. package/dist/tools/searchDomain.js.map +1 -0
  26. package/dist/tools/transferDomain.d.ts +39 -0
  27. package/dist/tools/transferDomain.d.ts.map +1 -0
  28. package/dist/tools/transferDomain.js +35 -0
  29. package/dist/tools/transferDomain.js.map +1 -0
  30. package/manifest.json +33 -0
  31. package/package.json +65 -0
  32. package/src/PorkbunService.ts +327 -0
  33. package/src/index.ts +133 -0
  34. package/src/tools/configureDns.ts +114 -0
  35. package/src/tools/listDomains.ts +33 -0
  36. package/src/tools/registerDomain.ts +74 -0
  37. package/src/tools/searchDomain.ts +39 -0
  38. package/src/tools/transferDomain.ts +41 -0
  39. package/test/PorkbunService.test.ts +434 -0
  40. package/test/index.test.ts +50 -0
  41. package/test/tools.test.ts +399 -0
  42. package/tsconfig.json +22 -0
@@ -0,0 +1,399 @@
1
+ // @ts-nocheck
2
+ import { describe, it, expect, vi, beforeEach } from 'vitest';
3
+ import { PorkbunConfigureDnsTool } from '../src/tools/configureDns';
4
+ import { PorkbunListDomainsTool } from '../src/tools/listDomains';
5
+ import { PorkbunRegisterDomainTool } from '../src/tools/registerDomain';
6
+ import { PorkbunSearchDomainTool } from '../src/tools/searchDomain';
7
+ import { PorkbunTransferDomainTool } from '../src/tools/transferDomain';
8
+
9
+ // ---------------------------------------------------------------------------
10
+ // PorkbunConfigureDnsTool
11
+ // ---------------------------------------------------------------------------
12
+
13
+ describe('PorkbunConfigureDnsTool', () => {
14
+ let service: any;
15
+ let tool: PorkbunConfigureDnsTool;
16
+
17
+ beforeEach(() => {
18
+ service = {
19
+ listDnsRecords: vi.fn(),
20
+ createDnsRecord: vi.fn(),
21
+ deleteDnsRecord: vi.fn(),
22
+ editDnsRecord: vi.fn(),
23
+ };
24
+ tool = new PorkbunConfigureDnsTool(service);
25
+ });
26
+
27
+ it('should have correct metadata', () => {
28
+ expect(tool.id).toBe('porkbunConfigureDns');
29
+ expect(tool.name).toBe('porkbunConfigureDns');
30
+ expect(tool.displayName).toBe('Configure DNS Records');
31
+ expect(tool.inputSchema.type).toBe('object');
32
+ expect(tool.inputSchema.required).toContain('domain');
33
+ expect(tool.category).toBe('domain');
34
+ });
35
+
36
+ it('should list DNS records by default', async () => {
37
+ service.listDnsRecords.mockResolvedValue([
38
+ { id: '1', type: 'A', name: 'www', content: '1.2.3.4', ttl: '300' },
39
+ { id: '2', type: 'MX', name: '', content: 'mx.example.com', ttl: '600', prio: '10' },
40
+ ]);
41
+
42
+ const result = await tool.execute({ domain: 'example.com' });
43
+
44
+ expect(result.success).toBe(true);
45
+ expect(Array.isArray(result.data)).toBe(true);
46
+ expect(service.listDnsRecords).toHaveBeenCalledWith('example.com');
47
+ });
48
+
49
+ it('should add a DNS record', async () => {
50
+ service.createDnsRecord.mockResolvedValue({ id: '123' });
51
+
52
+ const result = await tool.execute({
53
+ domain: 'example.com',
54
+ action: 'add',
55
+ type: 'A',
56
+ name: 'www',
57
+ content: '1.2.3.4',
58
+ ttl: '300',
59
+ });
60
+
61
+ expect(result.success).toBe(true);
62
+ expect(service.createDnsRecord).toHaveBeenCalledWith('example.com', {
63
+ name: 'www',
64
+ type: 'A',
65
+ content: '1.2.3.4',
66
+ ttl: '300',
67
+ prio: undefined,
68
+ });
69
+ });
70
+
71
+ it('should fail add without type', async () => {
72
+ const result = await tool.execute({
73
+ domain: 'example.com',
74
+ action: 'add',
75
+ content: '1.2.3.4',
76
+ });
77
+
78
+ expect(result.success).toBe(false);
79
+ expect(result.error).toContain('Record type is required');
80
+ });
81
+
82
+ it('should fail add without content', async () => {
83
+ const result = await tool.execute({
84
+ domain: 'example.com',
85
+ action: 'add',
86
+ type: 'A',
87
+ });
88
+
89
+ expect(result.success).toBe(false);
90
+ expect(result.error).toContain('Record content is required');
91
+ });
92
+
93
+ it('should remove a DNS record', async () => {
94
+ service.deleteDnsRecord.mockResolvedValue(undefined);
95
+
96
+ const result = await tool.execute({
97
+ domain: 'example.com',
98
+ action: 'remove',
99
+ recordId: '123',
100
+ });
101
+
102
+ expect(result.success).toBe(true);
103
+ expect(result.data).toEqual({ deleted: true });
104
+ expect(service.deleteDnsRecord).toHaveBeenCalledWith('example.com', '123');
105
+ });
106
+
107
+ it('should fail remove without recordId', async () => {
108
+ const result = await tool.execute({
109
+ domain: 'example.com',
110
+ action: 'remove',
111
+ });
112
+
113
+ expect(result.success).toBe(false);
114
+ expect(result.error).toContain('Record ID is required');
115
+ });
116
+
117
+ it('should update a DNS record', async () => {
118
+ service.editDnsRecord.mockResolvedValue(undefined);
119
+ service.listDnsRecords.mockResolvedValue([
120
+ { id: '123', type: 'A', content: '5.6.7.8' },
121
+ ]);
122
+
123
+ const result = await tool.execute({
124
+ domain: 'example.com',
125
+ action: 'update',
126
+ recordId: '123',
127
+ content: '5.6.7.8',
128
+ });
129
+
130
+ expect(result.success).toBe(true);
131
+ expect(service.editDnsRecord).toHaveBeenCalledWith('example.com', '123', expect.objectContaining({
132
+ content: '5.6.7.8',
133
+ }));
134
+ });
135
+
136
+ it('should fail update without recordId', async () => {
137
+ const result = await tool.execute({
138
+ domain: 'example.com',
139
+ action: 'update',
140
+ content: '5.6.7.8',
141
+ });
142
+
143
+ expect(result.success).toBe(false);
144
+ expect(result.error).toContain('Record ID is required');
145
+ });
146
+
147
+ it('should handle errors gracefully', async () => {
148
+ service.listDnsRecords.mockRejectedValue(new Error('Domain not found'));
149
+
150
+ const result = await tool.execute({ domain: 'nonexistent.com' });
151
+
152
+ expect(result.success).toBe(false);
153
+ expect(result.error).toContain('Domain not found');
154
+ });
155
+ });
156
+
157
+ // ---------------------------------------------------------------------------
158
+ // PorkbunListDomainsTool
159
+ // ---------------------------------------------------------------------------
160
+
161
+ describe('PorkbunListDomainsTool', () => {
162
+ let service: any;
163
+ let tool: PorkbunListDomainsTool;
164
+
165
+ beforeEach(() => {
166
+ service = {
167
+ listDomains: vi.fn(),
168
+ };
169
+ tool = new PorkbunListDomainsTool(service);
170
+ });
171
+
172
+ it('should have correct metadata', () => {
173
+ expect(tool.id).toBe('porkbunListDomains');
174
+ expect(tool.name).toBe('porkbunListDomains');
175
+ expect(tool.inputSchema.type).toBe('object');
176
+ expect(tool.inputSchema.required).toEqual([]);
177
+ expect(tool.hasSideEffects).toBe(false);
178
+ });
179
+
180
+ it('should list all domains', async () => {
181
+ service.listDomains.mockResolvedValue([
182
+ { domain: 'example.com', status: 'ACTIVE', autoRenew: true },
183
+ { domain: 'test.dev', status: 'ACTIVE', autoRenew: false },
184
+ ]);
185
+
186
+ const result = await tool.execute({} as Record<string, never>);
187
+
188
+ expect(result.success).toBe(true);
189
+ expect(result.data).toHaveLength(2);
190
+ expect(service.listDomains).toHaveBeenCalled();
191
+ });
192
+
193
+ it('should return empty array when no domains', async () => {
194
+ service.listDomains.mockResolvedValue([]);
195
+
196
+ const result = await tool.execute({} as Record<string, never>);
197
+
198
+ expect(result.success).toBe(true);
199
+ expect(result.data).toEqual([]);
200
+ });
201
+
202
+ it('should handle errors gracefully', async () => {
203
+ service.listDomains.mockRejectedValue(new Error('Unauthorized'));
204
+
205
+ const result = await tool.execute({} as Record<string, never>);
206
+
207
+ expect(result.success).toBe(false);
208
+ expect(result.error).toContain('Unauthorized');
209
+ });
210
+ });
211
+
212
+ // ---------------------------------------------------------------------------
213
+ // PorkbunRegisterDomainTool
214
+ // ---------------------------------------------------------------------------
215
+
216
+ describe('PorkbunRegisterDomainTool', () => {
217
+ let service: any;
218
+ let tool: PorkbunRegisterDomainTool;
219
+
220
+ beforeEach(() => {
221
+ service = {
222
+ registerDomain: vi.fn(),
223
+ };
224
+ tool = new PorkbunRegisterDomainTool(service);
225
+ });
226
+
227
+ it('should have correct metadata', () => {
228
+ expect(tool.id).toBe('porkbunRegisterDomain');
229
+ expect(tool.name).toBe('porkbunRegisterDomain');
230
+ expect(tool.inputSchema.type).toBe('object');
231
+ expect(tool.inputSchema.required).toContain('domain');
232
+ expect(tool.hasSideEffects).toBe(true);
233
+ });
234
+
235
+ it('should register a domain successfully', async () => {
236
+ service.registerDomain.mockResolvedValue({
237
+ domain: 'newsite.dev',
238
+ status: 'SUCCESS',
239
+ });
240
+
241
+ const result = await tool.execute({ domain: 'newsite.dev' });
242
+
243
+ expect(result.success).toBe(true);
244
+ expect(result.data).toHaveProperty('domain', 'newsite.dev');
245
+ expect(service.registerDomain).toHaveBeenCalledWith(
246
+ expect.objectContaining({ domain: 'newsite.dev' }),
247
+ );
248
+ });
249
+
250
+ it('should pass all contact fields', async () => {
251
+ service.registerDomain.mockResolvedValue({ domain: 'site.com', status: 'SUCCESS' });
252
+
253
+ await tool.execute({
254
+ domain: 'site.com',
255
+ years: 2,
256
+ firstName: 'John',
257
+ lastName: 'Doe',
258
+ email: 'john@example.com',
259
+ phone: '+1.5551234567',
260
+ city: 'New York',
261
+ state: 'NY',
262
+ zip: '10001',
263
+ country: 'US',
264
+ organization: 'Acme',
265
+ address: '123 Main St',
266
+ });
267
+
268
+ expect(service.registerDomain).toHaveBeenCalledWith(
269
+ expect.objectContaining({
270
+ years: 2,
271
+ firstName: 'John',
272
+ lastName: 'Doe',
273
+ country: 'US',
274
+ }),
275
+ );
276
+ });
277
+
278
+ it('should handle errors gracefully', async () => {
279
+ service.registerDomain.mockRejectedValue(new Error('Insufficient funds'));
280
+
281
+ const result = await tool.execute({ domain: 'expensive.io' });
282
+
283
+ expect(result.success).toBe(false);
284
+ expect(result.error).toContain('Insufficient funds');
285
+ });
286
+ });
287
+
288
+ // ---------------------------------------------------------------------------
289
+ // PorkbunSearchDomainTool
290
+ // ---------------------------------------------------------------------------
291
+
292
+ describe('PorkbunSearchDomainTool', () => {
293
+ let service: any;
294
+ let tool: PorkbunSearchDomainTool;
295
+
296
+ beforeEach(() => {
297
+ service = {
298
+ checkAvailability: vi.fn(),
299
+ };
300
+ tool = new PorkbunSearchDomainTool(service);
301
+ });
302
+
303
+ it('should have correct metadata', () => {
304
+ expect(tool.id).toBe('porkbunSearchDomain');
305
+ expect(tool.name).toBe('porkbunSearchDomain');
306
+ expect(tool.inputSchema.type).toBe('object');
307
+ expect(tool.inputSchema.required).toContain('domain');
308
+ expect(tool.hasSideEffects).toBe(false);
309
+ });
310
+
311
+ it('should check domain availability', async () => {
312
+ service.checkAvailability.mockResolvedValue({
313
+ domain: 'example.dev',
314
+ available: true,
315
+ pricing: { registration: '9.73', renewal: '9.73' },
316
+ });
317
+
318
+ const result = await tool.execute({ domain: 'example.dev' });
319
+
320
+ expect(result.success).toBe(true);
321
+ expect(result.data).toHaveProperty('available', true);
322
+ expect(result.data).toHaveProperty('domain', 'example.dev');
323
+ expect(service.checkAvailability).toHaveBeenCalledWith('example.dev');
324
+ });
325
+
326
+ it('should return unavailable for taken domains', async () => {
327
+ service.checkAvailability.mockResolvedValue({
328
+ domain: 'google.com',
329
+ available: false,
330
+ });
331
+
332
+ const result = await tool.execute({ domain: 'google.com' });
333
+
334
+ expect(result.success).toBe(true);
335
+ expect(result.data?.available).toBe(false);
336
+ });
337
+
338
+ it('should handle errors gracefully', async () => {
339
+ service.checkAvailability.mockRejectedValue(new Error('API error'));
340
+
341
+ const result = await tool.execute({ domain: 'test.xyz' });
342
+
343
+ expect(result.success).toBe(false);
344
+ expect(result.error).toContain('API error');
345
+ });
346
+ });
347
+
348
+ // ---------------------------------------------------------------------------
349
+ // PorkbunTransferDomainTool
350
+ // ---------------------------------------------------------------------------
351
+
352
+ describe('PorkbunTransferDomainTool', () => {
353
+ let service: any;
354
+ let tool: PorkbunTransferDomainTool;
355
+
356
+ beforeEach(() => {
357
+ service = {
358
+ transferDomain: vi.fn(),
359
+ };
360
+ tool = new PorkbunTransferDomainTool(service);
361
+ });
362
+
363
+ it('should have correct metadata', () => {
364
+ expect(tool.id).toBe('porkbunTransferDomain');
365
+ expect(tool.name).toBe('porkbunTransferDomain');
366
+ expect(tool.inputSchema.type).toBe('object');
367
+ expect(tool.inputSchema.required).toContain('domain');
368
+ expect(tool.inputSchema.required).toContain('authCode');
369
+ expect(tool.hasSideEffects).toBe(true);
370
+ });
371
+
372
+ it('should initiate a domain transfer', async () => {
373
+ service.transferDomain.mockResolvedValue({
374
+ domain: 'example.com',
375
+ status: 'SUCCESS',
376
+ });
377
+
378
+ const result = await tool.execute({
379
+ domain: 'example.com',
380
+ authCode: 'EPP-CODE-123',
381
+ });
382
+
383
+ expect(result.success).toBe(true);
384
+ expect(result.data).toHaveProperty('domain', 'example.com');
385
+ expect(service.transferDomain).toHaveBeenCalledWith('example.com', 'EPP-CODE-123');
386
+ });
387
+
388
+ it('should handle errors gracefully', async () => {
389
+ service.transferDomain.mockRejectedValue(new Error('Domain is locked'));
390
+
391
+ const result = await tool.execute({
392
+ domain: 'locked.com',
393
+ authCode: 'invalid',
394
+ });
395
+
396
+ expect(result.success).toBe(false);
397
+ expect(result.error).toContain('Domain is locked');
398
+ });
399
+ });
package/tsconfig.json ADDED
@@ -0,0 +1,22 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "module": "ESNext",
5
+ "lib": ["ES2022"],
6
+ "outDir": "./dist",
7
+ "rootDir": "./src",
8
+ "strict": true,
9
+ "esModuleInterop": true,
10
+ "skipLibCheck": true,
11
+ "noEmitOnError": false,
12
+ "forceConsistentCasingInFileNames": true,
13
+ "declaration": true,
14
+ "declarationMap": true,
15
+ "sourceMap": true,
16
+ "resolveJsonModule": true,
17
+ "moduleResolution": "Bundler",
18
+ "types": ["node"]
19
+ },
20
+ "include": ["src/**/*"],
21
+ "exclude": ["node_modules", "dist", "test"]
22
+ }