@aixyz/erc-8004 0.20.0 → 0.22.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/package.json +1 -1
- package/schemas/registration.test.ts +5 -150
- package/schemas/registration.ts +6 -41
package/package.json
CHANGED
|
@@ -2,18 +2,17 @@ import { describe, expect, test } from "bun:test";
|
|
|
2
2
|
import {
|
|
3
3
|
ERC8004_REGISTRATION_TYPE,
|
|
4
4
|
AgentRegistrationFileSchema,
|
|
5
|
-
StrictAgentRegistrationFileSchema,
|
|
6
5
|
TrustMechanismSchema,
|
|
7
6
|
ServiceSchema,
|
|
8
7
|
RegistrationEntrySchema,
|
|
9
|
-
getServices,
|
|
10
|
-
hasX402Support,
|
|
11
8
|
} from "./registration";
|
|
12
9
|
|
|
13
10
|
const minimalValid = {
|
|
11
|
+
type: ERC8004_REGISTRATION_TYPE,
|
|
14
12
|
name: "Agently Price Feed",
|
|
15
13
|
description: "Real-time cryptocurrency price feed agent with x402 payment support",
|
|
16
14
|
image: "ipfs://QmYwAPJzv5CZsnA625s3Xf2nemtYgPpHdWEz79ojWnPbdG",
|
|
15
|
+
services: [{ name: "mcp-server", endpoint: "https://mcp.acme-agents.com/v1/price-feed" }],
|
|
17
16
|
};
|
|
18
17
|
|
|
19
18
|
const fullValid = {
|
|
@@ -170,24 +169,9 @@ describe("RawAgentRegistrationFileSchema", () => {
|
|
|
170
169
|
expect(result.did).toBe("did:pkh:eip155:11155111:0x1234567890abcdef1234567890abcdef12345678");
|
|
171
170
|
});
|
|
172
171
|
|
|
173
|
-
test("
|
|
174
|
-
const result = AgentRegistrationFileSchema.parse(minimalValid);
|
|
175
|
-
expect(result.
|
|
176
|
-
});
|
|
177
|
-
|
|
178
|
-
test("accepts endpoints as legacy field", () => {
|
|
179
|
-
const result = AgentRegistrationFileSchema.parse({
|
|
180
|
-
...minimalValid,
|
|
181
|
-
endpoints: [{ name: "legacy-mcp", endpoint: "https://mcp.acme-agents.com/v0/translate" }],
|
|
182
|
-
});
|
|
183
|
-
expect(result.endpoints).toHaveLength(1);
|
|
184
|
-
});
|
|
185
|
-
|
|
186
|
-
test("accepts both x402support casings", () => {
|
|
187
|
-
const r1 = AgentRegistrationFileSchema.parse({ ...minimalValid, x402support: true });
|
|
188
|
-
const r2 = AgentRegistrationFileSchema.parse({ ...minimalValid, x402Support: true });
|
|
189
|
-
expect(r1.x402support).toBe(true);
|
|
190
|
-
expect(r2.x402Support).toBe(true);
|
|
172
|
+
test("accepts x402support", () => {
|
|
173
|
+
const result = AgentRegistrationFileSchema.parse({ ...minimalValid, x402support: true });
|
|
174
|
+
expect(result.x402support).toBe(true);
|
|
191
175
|
});
|
|
192
176
|
|
|
193
177
|
test("rejects missing name", () => {
|
|
@@ -209,58 +193,6 @@ describe("RawAgentRegistrationFileSchema", () => {
|
|
|
209
193
|
});
|
|
210
194
|
});
|
|
211
195
|
|
|
212
|
-
describe("StrictAgentRegistrationFileSchema", () => {
|
|
213
|
-
const strictValid = {
|
|
214
|
-
...minimalValid,
|
|
215
|
-
type: ERC8004_REGISTRATION_TYPE,
|
|
216
|
-
services: [{ name: "mcp-server", endpoint: "https://mcp.acme-agents.com/v1/price-feed" }],
|
|
217
|
-
};
|
|
218
|
-
|
|
219
|
-
test("accepts valid strict file", () => {
|
|
220
|
-
const result = StrictAgentRegistrationFileSchema.parse(strictValid);
|
|
221
|
-
expect(result.type).toBe(ERC8004_REGISTRATION_TYPE);
|
|
222
|
-
expect(result.services).toHaveLength(1);
|
|
223
|
-
});
|
|
224
|
-
|
|
225
|
-
test("rejects missing type", () => {
|
|
226
|
-
expect(() =>
|
|
227
|
-
StrictAgentRegistrationFileSchema.parse({
|
|
228
|
-
...minimalValid,
|
|
229
|
-
services: [{ name: "mcp-server", endpoint: "https://mcp.acme-agents.com/v1/price-feed" }],
|
|
230
|
-
}),
|
|
231
|
-
).toThrow();
|
|
232
|
-
});
|
|
233
|
-
|
|
234
|
-
test("rejects wrong type literal", () => {
|
|
235
|
-
expect(() =>
|
|
236
|
-
StrictAgentRegistrationFileSchema.parse({
|
|
237
|
-
...minimalValid,
|
|
238
|
-
type: "wrong-type",
|
|
239
|
-
services: [{ name: "mcp-server", endpoint: "https://mcp.acme-agents.com/v1/price-feed" }],
|
|
240
|
-
}),
|
|
241
|
-
).toThrow();
|
|
242
|
-
});
|
|
243
|
-
|
|
244
|
-
test("rejects empty services array", () => {
|
|
245
|
-
expect(() =>
|
|
246
|
-
StrictAgentRegistrationFileSchema.parse({
|
|
247
|
-
...minimalValid,
|
|
248
|
-
type: ERC8004_REGISTRATION_TYPE,
|
|
249
|
-
services: [],
|
|
250
|
-
}),
|
|
251
|
-
).toThrow();
|
|
252
|
-
});
|
|
253
|
-
|
|
254
|
-
test("rejects missing services", () => {
|
|
255
|
-
expect(() =>
|
|
256
|
-
StrictAgentRegistrationFileSchema.parse({
|
|
257
|
-
...minimalValid,
|
|
258
|
-
type: ERC8004_REGISTRATION_TYPE,
|
|
259
|
-
}),
|
|
260
|
-
).toThrow();
|
|
261
|
-
});
|
|
262
|
-
});
|
|
263
|
-
|
|
264
196
|
describe("AgentRegistrationFileSchema.safeParse", () => {
|
|
265
197
|
test("returns success for valid data", () => {
|
|
266
198
|
const result = AgentRegistrationFileSchema.safeParse(minimalValid);
|
|
@@ -277,80 +209,3 @@ describe("AgentRegistrationFileSchema.safeParse", () => {
|
|
|
277
209
|
expect(result.success).toBe(false);
|
|
278
210
|
});
|
|
279
211
|
});
|
|
280
|
-
|
|
281
|
-
describe("StrictAgentRegistrationFileSchema.safeParse", () => {
|
|
282
|
-
test("returns success for strict-valid data", () => {
|
|
283
|
-
const result = StrictAgentRegistrationFileSchema.safeParse({
|
|
284
|
-
...minimalValid,
|
|
285
|
-
type: ERC8004_REGISTRATION_TYPE,
|
|
286
|
-
services: [{ name: "mcp-server", endpoint: "https://mcp.acme-agents.com/v1/price-feed" }],
|
|
287
|
-
});
|
|
288
|
-
expect(result.success).toBe(true);
|
|
289
|
-
});
|
|
290
|
-
|
|
291
|
-
test("returns failure when type is missing", () => {
|
|
292
|
-
const result = StrictAgentRegistrationFileSchema.safeParse({
|
|
293
|
-
...minimalValid,
|
|
294
|
-
services: [{ name: "mcp-server", endpoint: "https://mcp.acme-agents.com/v1/price-feed" }],
|
|
295
|
-
});
|
|
296
|
-
expect(result.success).toBe(false);
|
|
297
|
-
});
|
|
298
|
-
|
|
299
|
-
test("returns failure when services is empty", () => {
|
|
300
|
-
const result = StrictAgentRegistrationFileSchema.safeParse({
|
|
301
|
-
...minimalValid,
|
|
302
|
-
type: ERC8004_REGISTRATION_TYPE,
|
|
303
|
-
services: [],
|
|
304
|
-
});
|
|
305
|
-
expect(result.success).toBe(false);
|
|
306
|
-
});
|
|
307
|
-
});
|
|
308
|
-
|
|
309
|
-
describe("getServices", () => {
|
|
310
|
-
test("returns services when present", () => {
|
|
311
|
-
const file = {
|
|
312
|
-
...minimalValid,
|
|
313
|
-
services: [{ name: "mcp-server", endpoint: "https://mcp.acme-agents.com/v1/translate" }],
|
|
314
|
-
};
|
|
315
|
-
expect(getServices(file)).toEqual([{ name: "mcp-server", endpoint: "https://mcp.acme-agents.com/v1/translate" }]);
|
|
316
|
-
});
|
|
317
|
-
|
|
318
|
-
test("falls back to endpoints", () => {
|
|
319
|
-
const file = {
|
|
320
|
-
...minimalValid,
|
|
321
|
-
endpoints: [{ name: "legacy-mcp", endpoint: "https://mcp.acme-agents.com/v0/translate" }],
|
|
322
|
-
};
|
|
323
|
-
expect(getServices(file)).toEqual([{ name: "legacy-mcp", endpoint: "https://mcp.acme-agents.com/v0/translate" }]);
|
|
324
|
-
});
|
|
325
|
-
|
|
326
|
-
test("prefers services over endpoints", () => {
|
|
327
|
-
const file = {
|
|
328
|
-
...minimalValid,
|
|
329
|
-
services: [{ name: "mcp-server", endpoint: "https://mcp.acme-agents.com/v1/translate" }],
|
|
330
|
-
endpoints: [{ name: "legacy-mcp", endpoint: "https://mcp.acme-agents.com/v0/translate" }],
|
|
331
|
-
};
|
|
332
|
-
expect(getServices(file)).toEqual([{ name: "mcp-server", endpoint: "https://mcp.acme-agents.com/v1/translate" }]);
|
|
333
|
-
});
|
|
334
|
-
|
|
335
|
-
test("returns empty array when neither present", () => {
|
|
336
|
-
expect(getServices(minimalValid as any)).toEqual([]);
|
|
337
|
-
});
|
|
338
|
-
});
|
|
339
|
-
|
|
340
|
-
describe("hasX402Support", () => {
|
|
341
|
-
test("returns true for x402support: true", () => {
|
|
342
|
-
expect(hasX402Support({ ...minimalValid, x402support: true })).toBe(true);
|
|
343
|
-
});
|
|
344
|
-
|
|
345
|
-
test("returns true for x402Support: true", () => {
|
|
346
|
-
expect(hasX402Support({ ...minimalValid, x402Support: true })).toBe(true);
|
|
347
|
-
});
|
|
348
|
-
|
|
349
|
-
test("returns false when both are false", () => {
|
|
350
|
-
expect(hasX402Support({ ...minimalValid, x402support: false, x402Support: false })).toBe(false);
|
|
351
|
-
});
|
|
352
|
-
|
|
353
|
-
test("returns false when neither is set", () => {
|
|
354
|
-
expect(hasX402Support(minimalValid as any)).toBe(false);
|
|
355
|
-
});
|
|
356
|
-
});
|
package/schemas/registration.ts
CHANGED
|
@@ -1,12 +1,5 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
|
|
3
|
-
/**
|
|
4
|
-
* ERC-8004 Raw Agent Registration File Schema
|
|
5
|
-
* For parsing raw registration files fetched from IPFS/URIs
|
|
6
|
-
*
|
|
7
|
-
* Based on: https://github.com/erc-8004/erc-8004-contracts/blob/093d7b91eb9c22048d411896ed397d695742a5f8/ERC8004SPEC.md#agent-uri-and-agent-registration-file
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
3
|
export const ERC8004_REGISTRATION_TYPE = "https://eips.ethereum.org/EIPS/eip-8004#registration-v1";
|
|
11
4
|
|
|
12
5
|
/**
|
|
@@ -50,12 +43,14 @@ export const RegistrationEntrySchema = z.object({
|
|
|
50
43
|
});
|
|
51
44
|
|
|
52
45
|
/**
|
|
53
|
-
* Raw Agent Registration File Schema
|
|
54
|
-
* For parsing registration files fetched from IPFS/URIs
|
|
46
|
+
* ERC-8004 Raw Agent Registration File Schema
|
|
47
|
+
* For parsing raw registration files fetched from IPFS/URIs
|
|
48
|
+
*
|
|
49
|
+
* Based on: https://github.com/erc-8004/erc-8004-contracts/blob/093d7b91eb9c22048d411896ed397d695742a5f8/ERC8004SPEC.md#agent-uri-and-agent-registration-file
|
|
55
50
|
*/
|
|
56
51
|
export const AgentRegistrationFileSchema = z.object({
|
|
57
52
|
// Schema identifiers
|
|
58
|
-
type: z.
|
|
53
|
+
type: z.literal(ERC8004_REGISTRATION_TYPE),
|
|
59
54
|
$schema: z.string().optional(),
|
|
60
55
|
|
|
61
56
|
// ERC-721 metadata compatibility (required)
|
|
@@ -64,13 +59,11 @@ export const AgentRegistrationFileSchema = z.object({
|
|
|
64
59
|
image: z.string(),
|
|
65
60
|
|
|
66
61
|
// Service endpoints
|
|
67
|
-
services: z.array(ServiceSchema).
|
|
68
|
-
endpoints: z.array(ServiceSchema).optional(), // Legacy field name
|
|
62
|
+
services: z.array(ServiceSchema).min(1, "at least one service endpoint is required"),
|
|
69
63
|
|
|
70
64
|
// Agent configuration
|
|
71
65
|
active: z.boolean().optional(),
|
|
72
66
|
x402support: z.boolean().optional(),
|
|
73
|
-
x402Support: z.boolean().optional(), // Alternative casing
|
|
74
67
|
|
|
75
68
|
// Cross-chain & identity
|
|
76
69
|
registrations: z.array(RegistrationEntrySchema).optional(),
|
|
@@ -79,35 +72,7 @@ export const AgentRegistrationFileSchema = z.object({
|
|
|
79
72
|
did: z.string().optional(),
|
|
80
73
|
});
|
|
81
74
|
|
|
82
|
-
/**
|
|
83
|
-
* Strict Schema - for creating new registration files
|
|
84
|
-
* Requires correct type literal and at least one service
|
|
85
|
-
*/
|
|
86
|
-
export const StrictAgentRegistrationFileSchema = AgentRegistrationFileSchema.extend({
|
|
87
|
-
type: z.literal(ERC8004_REGISTRATION_TYPE),
|
|
88
|
-
services: z.array(ServiceSchema).min(1, "At least one service endpoint is required"),
|
|
89
|
-
});
|
|
90
|
-
|
|
91
75
|
export type TrustMechanism = z.infer<typeof TrustMechanismSchema>;
|
|
92
76
|
export type Service = z.infer<typeof ServiceSchema>;
|
|
93
77
|
export type RegistrationEntry = z.infer<typeof RegistrationEntrySchema>;
|
|
94
78
|
export type AgentRegistrationFile = z.infer<typeof AgentRegistrationFileSchema>;
|
|
95
|
-
export type StrictAgentRegistrationFile = z.infer<typeof StrictAgentRegistrationFileSchema>;
|
|
96
|
-
|
|
97
|
-
/**
|
|
98
|
-
* Get services from a registration file
|
|
99
|
-
* Handles both `services` (new) and `endpoints` (legacy) field names
|
|
100
|
-
* @default TODO: parse and merge both fields
|
|
101
|
-
*/
|
|
102
|
-
export function getServices(file: AgentRegistrationFile | StrictAgentRegistrationFile): Service[] {
|
|
103
|
-
return file.services ?? file.endpoints ?? [];
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
/**
|
|
107
|
-
* Check if agent supports x402 payment protocol
|
|
108
|
-
* Handles both `x402support` and `x402Support` casing variants
|
|
109
|
-
* @deprecated TODO: parse and merge both fields
|
|
110
|
-
*/
|
|
111
|
-
export function hasX402Support(file: AgentRegistrationFile | StrictAgentRegistrationFile): boolean {
|
|
112
|
-
return file.x402support === true || file.x402Support === true;
|
|
113
|
-
}
|