@devlab.group/agent-commerce 1.0.0 → 1.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/README.md +20 -13
- package/dist/{chunk-BQNE3A5H.js → chunk-64UZVJEO.js} +3 -3
- package/dist/{chunk-BQNE3A5H.js.map → chunk-64UZVJEO.js.map} +1 -1
- package/dist/{chunk-MEPETSP6.js → chunk-M4I7I2UY.js} +3 -3
- package/dist/{chunk-MEPETSP6.js.map → chunk-M4I7I2UY.js.map} +1 -1
- package/dist/cli/index.js +105 -10
- package/dist/cli/index.js.map +1 -1
- package/dist/index.d.ts +8 -4
- package/dist/index.js +102 -12
- package/dist/index.js.map +1 -1
- package/dist/mcp.d.ts +2 -2
- package/dist/mcp.js +1 -1
- package/dist/{protocol-adapter-CZPOb9uG.d.ts → protocol-adapter-DZ8qkkAa.d.ts} +22 -2
- package/dist/{runtime-B-9IjMbO.d.ts → runtime-CC6EFmLP.d.ts} +1 -1
- package/dist/x402.d.ts +1 -1
- package/dist/x402.js +3 -3
- package/package.json +2 -1
package/dist/index.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { I as IsoTimestamp, D as DecimalAmount, P as PaymentMethodName, B as BackendHandler, A as AdapterDescriptor, a as AdapterHealth, C as CommerceResource, R as ResourceRegistry, b as PaymentProvider, L as Logger, c as Clock, d as IdGenerator } from './runtime-
|
|
2
|
-
export { e as BackendMethod, f as DEFAULT_BACKEND_TIMEOUT_MS, J as JsonSchema, N as NOOP_LOGGER, g as PaymentChallenge, h as PaymentContext, i as PaymentRequirement, j as PaymentResult, k as PaymentSettlementContext, l as PaymentSubmission, m as PaymentVerificationContext, n as Pricing, o as ProtocolName, s as systemClock } from './runtime-
|
|
1
|
+
import { I as IsoTimestamp, D as DecimalAmount, P as PaymentMethodName, B as BackendHandler, A as AdapterDescriptor, a as AdapterHealth, C as CommerceResource, R as ResourceRegistry, b as PaymentProvider, L as Logger, c as Clock, d as IdGenerator } from './runtime-CC6EFmLP.js';
|
|
2
|
+
export { e as BackendMethod, f as DEFAULT_BACKEND_TIMEOUT_MS, J as JsonSchema, N as NOOP_LOGGER, g as PaymentChallenge, h as PaymentContext, i as PaymentRequirement, j as PaymentResult, k as PaymentSettlementContext, l as PaymentSubmission, m as PaymentVerificationContext, n as Pricing, o as ProtocolName, s as systemClock } from './runtime-CC6EFmLP.js';
|
|
3
3
|
import { X as X402FacilitatorConfig } from './guardrails-Dmwi63KB.js';
|
|
4
|
-
import { D as DeliveredOutcome, P as PaymentRequiredOutcome, C as CommerceEvent, a as PaymentAttempt, b as CommerceReceipt, E as ExecutionPipeline, c as ProtocolAdapter } from './protocol-adapter-
|
|
5
|
-
export { d as COMMERCE_EVENT_TYPES, e as CanonicalRequest, f as CommerceEventType, g as EventSink, h as ExecutionOutcome, H as HttpProtocolAdapter, i as ProtocolAdapterContext, j as isHttpProtocolAdapter } from './protocol-adapter-
|
|
4
|
+
import { D as DeliveredOutcome, P as PaymentRequiredOutcome, C as CommerceEvent, a as PaymentAttempt, b as CommerceReceipt, E as ExecutionPipeline, c as ProtocolAdapter } from './protocol-adapter-DZ8qkkAa.js';
|
|
5
|
+
export { A as AdapterHttpRoute, d as COMMERCE_EVENT_TYPES, e as CanonicalRequest, f as CommerceEventType, g as EventSink, h as ExecutionOutcome, H as HttpProtocolAdapter, i as ProtocolAdapterContext, j as isHttpProtocolAdapter } from './protocol-adapter-DZ8qkkAa.js';
|
|
6
6
|
import { FastifyInstance } from 'fastify';
|
|
7
7
|
import 'node:http';
|
|
8
8
|
|
|
@@ -362,6 +362,10 @@ interface GatewayConfig {
|
|
|
362
362
|
readonly enabled: boolean;
|
|
363
363
|
readonly mountPath: string;
|
|
364
364
|
};
|
|
365
|
+
readonly a2a: {
|
|
366
|
+
readonly enabled: boolean;
|
|
367
|
+
readonly mountPath: string;
|
|
368
|
+
};
|
|
365
369
|
};
|
|
366
370
|
/** Canonical resources, already normalised. */
|
|
367
371
|
readonly resources: readonly CommerceResource[];
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { resolveX402Deployment, resolveDeploymentMode, requireNetworkProfile } from './chunk-
|
|
2
|
-
import { CommerceError, PAYMENT_INPUT_FIELD, systemClock, NOOP_LOGGER, PACKAGE_VERSION, toCommerceError, DEFAULT_BACKEND_TIMEOUT_MS, toErrorEnvelope, isHttpProtocolAdapter, isCommerceError, PAYMENT_HEADER, toPaymentRequiredEnvelope, PAYMENT_REQUIRED_HEADER, PAYMENT_RESPONSE_HEADER } from './chunk-
|
|
3
|
-
export { COMMERCE_ERROR_CODES, COMMERCE_ERROR_HTTP_STATUS, COMMERCE_EVENT_TYPES, CommerceError, DEFAULT_BACKEND_TIMEOUT_MS, DELIVERY_SUMMARY_META_KEY, NOOP_LOGGER, PAYMENT_HEADER, PAYMENT_INPUT_FIELD, PAYMENT_REQUIRED_HEADER, PAYMENT_RESPONSE_HEADER, RETRYABLE_ERROR_CODES, isCommerceError, isHttpProtocolAdapter, isPaymentRequiredEnvelope, systemClock, toCommerceError, toDeliverySummary, toErrorEnvelope, toPaymentRequiredEnvelope } from './chunk-
|
|
1
|
+
import { resolveX402Deployment, resolveDeploymentMode, requireNetworkProfile } from './chunk-M4I7I2UY.js';
|
|
2
|
+
import { CommerceError, PAYMENT_INPUT_FIELD, systemClock, NOOP_LOGGER, PACKAGE_VERSION, toCommerceError, DEFAULT_BACKEND_TIMEOUT_MS, toErrorEnvelope, isHttpProtocolAdapter, isCommerceError, PAYMENT_HEADER, toPaymentRequiredEnvelope, PAYMENT_REQUIRED_HEADER, PAYMENT_RESPONSE_HEADER } from './chunk-64UZVJEO.js';
|
|
3
|
+
export { COMMERCE_ERROR_CODES, COMMERCE_ERROR_HTTP_STATUS, COMMERCE_EVENT_TYPES, CommerceError, DEFAULT_BACKEND_TIMEOUT_MS, DELIVERY_SUMMARY_META_KEY, NOOP_LOGGER, PAYMENT_HEADER, PAYMENT_INPUT_FIELD, PAYMENT_REQUIRED_HEADER, PAYMENT_RESPONSE_HEADER, RETRYABLE_ERROR_CODES, isCommerceError, isHttpProtocolAdapter, isPaymentRequiredEnvelope, systemClock, toCommerceError, toDeliverySummary, toErrorEnvelope, toPaymentRequiredEnvelope } from './chunk-64UZVJEO.js';
|
|
4
4
|
import { promises, existsSync, mkdirSync, accessSync, constants, closeSync, openSync, chmodSync } from 'fs';
|
|
5
5
|
import * as path from 'path';
|
|
6
6
|
import { dirname } from 'path';
|
|
@@ -1125,13 +1125,16 @@ var RESERVED_GATEWAY_PATHS = [
|
|
|
1125
1125
|
"/health",
|
|
1126
1126
|
"/ready",
|
|
1127
1127
|
"/.well-known/agent-commerce",
|
|
1128
|
+
// Fixed by the A2A specification, so it is the adapter's to serve and never
|
|
1129
|
+
// a configurable mount's to claim.
|
|
1130
|
+
"/.well-known/agent-card.json",
|
|
1128
1131
|
"/api/resources",
|
|
1129
1132
|
"/api/resources/:id/invoke",
|
|
1130
1133
|
"/api/receipts",
|
|
1131
1134
|
"/api/events",
|
|
1132
1135
|
"/api/events/stream"
|
|
1133
1136
|
];
|
|
1134
|
-
var
|
|
1137
|
+
var MountPathSchema = z.string().min(1).refine((value) => value.startsWith("/"), {
|
|
1135
1138
|
message: 'must start with "/" \u2014 it is an absolute gateway path, e.g. "/mcp".'
|
|
1136
1139
|
}).refine((value) => !/[:*?\s]/.test(value), {
|
|
1137
1140
|
message: 'must not contain ":", "*", "?" or whitespace \u2014 the mount is a literal path prefix, not a Fastify route pattern, and registers its own wildcard.'
|
|
@@ -1150,9 +1153,16 @@ var ProtocolsSchema = z.object({
|
|
|
1150
1153
|
http: z.object({ enabled: BooleanOrString }).strict(),
|
|
1151
1154
|
mcp: z.object({
|
|
1152
1155
|
enabled: BooleanOrString,
|
|
1153
|
-
mountPath:
|
|
1154
|
-
}).strict()
|
|
1156
|
+
mountPath: MountPathSchema
|
|
1157
|
+
}).strict(),
|
|
1158
|
+
// Optional block: A2A is off unless an operator asks for it, and an
|
|
1159
|
+
// existing config predating the adapter stays valid.
|
|
1160
|
+
a2a: z.object({
|
|
1161
|
+
enabled: BooleanOrString,
|
|
1162
|
+
mountPath: MountPathSchema.optional()
|
|
1163
|
+
}).strict().optional()
|
|
1155
1164
|
}).strict();
|
|
1165
|
+
var DEFAULT_A2A_MOUNT_PATH = "/a2a";
|
|
1156
1166
|
var BackendMethodSchema = z.enum(["GET", "POST", "PUT", "PATCH", "DELETE"]);
|
|
1157
1167
|
var BackendHandlerSchema = z.object({
|
|
1158
1168
|
type: z.literal("http"),
|
|
@@ -1336,7 +1346,7 @@ function toBoolean(value, path2) {
|
|
|
1336
1346
|
{ details: { path: path2 } }
|
|
1337
1347
|
);
|
|
1338
1348
|
}
|
|
1339
|
-
var SUPPORTED_PROTOCOLS = /* @__PURE__ */ new Set(["http", "mcp"]);
|
|
1349
|
+
var SUPPORTED_PROTOCOLS = /* @__PURE__ */ new Set(["http", "mcp", "a2a"]);
|
|
1340
1350
|
var SUPPORTED_PAYMENT_METHODS = /* @__PURE__ */ new Set(["x402"]);
|
|
1341
1351
|
function normalise(raw) {
|
|
1342
1352
|
const protocols = {
|
|
@@ -1344,8 +1354,13 @@ function normalise(raw) {
|
|
|
1344
1354
|
mcp: {
|
|
1345
1355
|
enabled: toBoolean(raw.protocols.mcp.enabled, "protocols.mcp.enabled"),
|
|
1346
1356
|
mountPath: raw.protocols.mcp.mountPath
|
|
1357
|
+
},
|
|
1358
|
+
a2a: {
|
|
1359
|
+
enabled: toBoolean(raw.protocols.a2a?.enabled ?? false, "protocols.a2a.enabled"),
|
|
1360
|
+
mountPath: raw.protocols.a2a?.mountPath ?? DEFAULT_A2A_MOUNT_PATH
|
|
1347
1361
|
}
|
|
1348
1362
|
};
|
|
1363
|
+
validateMountPaths(protocols);
|
|
1349
1364
|
const x402Raw = raw.payments.x402;
|
|
1350
1365
|
const facilitator = x402Raw === void 0 ? void 0 : x402Raw.facilitator.mode === "local" ? { mode: "local", signerPrivateKey: x402Raw.facilitator.signerPrivateKey } : {
|
|
1351
1366
|
mode: "remote",
|
|
@@ -1420,6 +1435,25 @@ function normalise(raw) {
|
|
|
1420
1435
|
}
|
|
1421
1436
|
};
|
|
1422
1437
|
}
|
|
1438
|
+
function validateMountPaths(protocols) {
|
|
1439
|
+
const mounts = [
|
|
1440
|
+
["mcp", protocols.mcp],
|
|
1441
|
+
["a2a", protocols.a2a]
|
|
1442
|
+
].filter(([, p]) => p.enabled);
|
|
1443
|
+
for (const [index, [nameA, a]] of mounts.entries()) {
|
|
1444
|
+
for (const [nameB, b] of mounts.slice(index + 1)) {
|
|
1445
|
+
const baseA = a.mountPath.replace(/\/+$/, "");
|
|
1446
|
+
const baseB = b.mountPath.replace(/\/+$/, "");
|
|
1447
|
+
if (baseA === baseB || baseA.startsWith(`${baseB}/`) || baseB.startsWith(`${baseA}/`)) {
|
|
1448
|
+
throw new CommerceError(
|
|
1449
|
+
"CONFIG_INVALID",
|
|
1450
|
+
`protocols.${nameA}.mountPath ("${a.mountPath}") collides with protocols.${nameB}.mountPath ("${b.mountPath}") \u2014 each mount registers a wildcard, so overlapping prefixes cannot both be served`,
|
|
1451
|
+
{ details: { path: `protocols.${nameA}.mountPath` } }
|
|
1452
|
+
);
|
|
1453
|
+
}
|
|
1454
|
+
}
|
|
1455
|
+
}
|
|
1456
|
+
}
|
|
1423
1457
|
function normaliseResource(id, entry, protocols, x402) {
|
|
1424
1458
|
if (entry.input !== void 0) validateResourceSchemaKeywords(id, "input", entry.input);
|
|
1425
1459
|
const inputProperties = entry.input?.["properties"];
|
|
@@ -1440,7 +1474,7 @@ function normaliseResource(id, entry, protocols, x402) {
|
|
|
1440
1474
|
const hint = protocol === "ucp" ? " (UCP is planned, not supported in this release)" : "";
|
|
1441
1475
|
throw new CommerceError(
|
|
1442
1476
|
"CONFIG_INVALID",
|
|
1443
|
-
`Resource "${id}" exposes unsupported protocol "${protocol}"${hint}. Supported: http, mcp.`,
|
|
1477
|
+
`Resource "${id}" exposes unsupported protocol "${protocol}"${hint}. Supported: http, mcp, a2a.`,
|
|
1444
1478
|
{ details: { path: `resources.${id}.expose`, resourceId: id, protocol } }
|
|
1445
1479
|
);
|
|
1446
1480
|
}
|
|
@@ -1461,6 +1495,13 @@ function normaliseResource(id, entry, protocols, x402) {
|
|
|
1461
1495
|
);
|
|
1462
1496
|
}
|
|
1463
1497
|
}
|
|
1498
|
+
if (entry.expose.includes("a2a") && !protocols.a2a.enabled) {
|
|
1499
|
+
throw new CommerceError(
|
|
1500
|
+
"CONFIG_INVALID",
|
|
1501
|
+
`Resource "${id}" is exposed via "a2a" but protocols.a2a.enabled is false`,
|
|
1502
|
+
{ details: { path: `resources.${id}.expose`, resourceId: id } }
|
|
1503
|
+
);
|
|
1504
|
+
}
|
|
1464
1505
|
if (entry.expose.includes("http") && !protocols.http.enabled) {
|
|
1465
1506
|
throw new CommerceError(
|
|
1466
1507
|
"CONFIG_INVALID",
|
|
@@ -1977,6 +2018,7 @@ function hashToken(value) {
|
|
|
1977
2018
|
// src/gateway/adapters.ts
|
|
1978
2019
|
var MOUNT_BODY_LIMIT_BYTES = 256 * 1024;
|
|
1979
2020
|
async function startAndMountAdapters(options) {
|
|
2021
|
+
assertNoRouteConflicts(options.adapters);
|
|
1980
2022
|
const runtimes = [];
|
|
1981
2023
|
for (const adapter of options.adapters) {
|
|
1982
2024
|
const adapterLogger = options.logger.child({ adapter: adapter.name });
|
|
@@ -2002,11 +2044,51 @@ async function startAndMountAdapters(options) {
|
|
|
2002
2044
|
}
|
|
2003
2045
|
return runtimes;
|
|
2004
2046
|
}
|
|
2047
|
+
function routeClaims(adapters) {
|
|
2048
|
+
const claims = [];
|
|
2049
|
+
for (const adapter of adapters) {
|
|
2050
|
+
if (!isHttpProtocolAdapter(adapter)) continue;
|
|
2051
|
+
claims.push({
|
|
2052
|
+
adapter: adapter.name,
|
|
2053
|
+
path: adapter.mountPath.replace(/\/+$/, ""),
|
|
2054
|
+
wildcard: true,
|
|
2055
|
+
method: "ALL"
|
|
2056
|
+
});
|
|
2057
|
+
for (const route of adapter.additionalHttpRoutes ?? []) {
|
|
2058
|
+
claims.push({
|
|
2059
|
+
adapter: adapter.name,
|
|
2060
|
+
path: route.path.replace(/\/+$/, ""),
|
|
2061
|
+
wildcard: false,
|
|
2062
|
+
method: route.method
|
|
2063
|
+
});
|
|
2064
|
+
}
|
|
2065
|
+
}
|
|
2066
|
+
return claims;
|
|
2067
|
+
}
|
|
2068
|
+
function claimsCollide(a, b) {
|
|
2069
|
+
if (a.wildcard && (b.path === a.path || b.path.startsWith(`${a.path}/`))) return true;
|
|
2070
|
+
if (b.wildcard && a.path.startsWith(`${b.path}/`)) return true;
|
|
2071
|
+
return a.path === b.path && (a.method === b.method || a.method === "ALL" || b.method === "ALL");
|
|
2072
|
+
}
|
|
2073
|
+
function assertNoRouteConflicts(adapters) {
|
|
2074
|
+
const claims = routeClaims(adapters);
|
|
2075
|
+
for (const [index, a] of claims.entries()) {
|
|
2076
|
+
for (const b of claims.slice(index + 1)) {
|
|
2077
|
+
if (a.adapter === b.adapter || !claimsCollide(a, b)) continue;
|
|
2078
|
+
throw new CommerceError(
|
|
2079
|
+
"CONFIG_INVALID",
|
|
2080
|
+
`Protocol adapters "${a.adapter}" and "${b.adapter}" both claim the HTTP path "${b.path}"; each adapter path must be served by exactly one adapter`,
|
|
2081
|
+
{ details: { adapters: [a.adapter, b.adapter], path: b.path } }
|
|
2082
|
+
);
|
|
2083
|
+
}
|
|
2084
|
+
}
|
|
2085
|
+
}
|
|
2005
2086
|
var MOUNT_MAX_CONCURRENT_REQUESTS = 20;
|
|
2006
2087
|
var MOUNT_BUSY_RETRY_AFTER_SECONDS = 1;
|
|
2007
2088
|
function mountHttpAdapter(server, adapter, logger) {
|
|
2008
2089
|
const mountPath = adapter.mountPath;
|
|
2009
2090
|
const wildcard = mountPath.endsWith("/") ? `${mountPath}*` : `${mountPath}/*`;
|
|
2091
|
+
const additionalRoutes = adapter.additionalHttpRoutes ?? [];
|
|
2010
2092
|
let inFlight = 0;
|
|
2011
2093
|
void server.register(async (instance) => {
|
|
2012
2094
|
instance.addContentTypeParser(
|
|
@@ -2018,7 +2100,7 @@ function mountHttpAdapter(server, adapter, logger) {
|
|
|
2018
2100
|
instance.addContentTypeParser("*", (_request, _payload, done) => {
|
|
2019
2101
|
done(null);
|
|
2020
2102
|
});
|
|
2021
|
-
const
|
|
2103
|
+
const makeHandler = (handleHttp) => async (request, reply) => {
|
|
2022
2104
|
if (inFlight >= MOUNT_MAX_CONCURRENT_REQUESTS) {
|
|
2023
2105
|
const error = new CommerceError(
|
|
2024
2106
|
"GATEWAY_BUSY",
|
|
@@ -2036,7 +2118,7 @@ function mountHttpAdapter(server, adapter, logger) {
|
|
|
2036
2118
|
logger
|
|
2037
2119
|
);
|
|
2038
2120
|
try {
|
|
2039
|
-
await
|
|
2121
|
+
await handleHttp(request.raw, reply.raw);
|
|
2040
2122
|
} catch (error) {
|
|
2041
2123
|
logger.error({ err: describeError3(error) }, "Protocol adapter request handler threw");
|
|
2042
2124
|
if (!reply.raw.headersSent) {
|
|
@@ -2050,8 +2132,16 @@ function mountHttpAdapter(server, adapter, logger) {
|
|
|
2050
2132
|
inFlight -= 1;
|
|
2051
2133
|
}
|
|
2052
2134
|
};
|
|
2053
|
-
|
|
2054
|
-
instance.all(
|
|
2135
|
+
const mountHandler = makeHandler((req, res) => adapter.handleHttp(req, res));
|
|
2136
|
+
instance.all(mountPath, mountHandler);
|
|
2137
|
+
instance.all(wildcard, mountHandler);
|
|
2138
|
+
for (const route of additionalRoutes) {
|
|
2139
|
+
instance.route({
|
|
2140
|
+
method: route.method,
|
|
2141
|
+
url: route.path,
|
|
2142
|
+
handler: makeHandler((req, res) => route.handleHttp(req, res))
|
|
2143
|
+
});
|
|
2144
|
+
}
|
|
2055
2145
|
});
|
|
2056
2146
|
}
|
|
2057
2147
|
function enforceMountBodyLimit(req, res, maxBytes, logger) {
|