@astrasyncai/verification-gateway 5.19.0 → 5.20.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/CHANGELOG.md +83 -0
- package/README.md +51 -2
- package/dist/adapter-interface/interface.d.mts +2 -2
- package/dist/adapter-interface/interface.d.ts +2 -2
- package/dist/adapters/express.d.mts +1 -1
- package/dist/adapters/express.d.ts +1 -1
- package/dist/adapters/express.js +65 -16
- package/dist/adapters/express.js.map +1 -1
- package/dist/adapters/express.mjs +65 -16
- package/dist/adapters/express.mjs.map +1 -1
- package/dist/adapters/mcp.d.mts +23 -1
- package/dist/adapters/mcp.d.ts +23 -1
- package/dist/adapters/mcp.js +91 -16
- package/dist/adapters/mcp.js.map +1 -1
- package/dist/adapters/mcp.mjs +91 -16
- package/dist/adapters/mcp.mjs.map +1 -1
- package/dist/adapters/nextjs.d.mts +1 -1
- package/dist/adapters/nextjs.d.ts +1 -1
- package/dist/adapters/nextjs.js +57 -11
- package/dist/adapters/nextjs.js.map +1 -1
- package/dist/adapters/nextjs.mjs +57 -11
- package/dist/adapters/nextjs.mjs.map +1 -1
- package/dist/adapters/sdk.d.mts +58 -2
- package/dist/adapters/sdk.d.ts +58 -2
- package/dist/adapters/sdk.js +124 -11
- package/dist/adapters/sdk.js.map +1 -1
- package/dist/adapters/sdk.mjs +124 -11
- package/dist/adapters/sdk.mjs.map +1 -1
- package/dist/agent/index.d.mts +1 -1
- package/dist/agent/index.d.ts +1 -1
- package/dist/agent/index.js +1 -1
- package/dist/agent/index.js.map +1 -1
- package/dist/agent/index.mjs +1 -1
- package/dist/agent/index.mjs.map +1 -1
- package/dist/bin/astrasync-claude-hook.js +49 -11
- package/dist/bin/astrasync-codex-hook.js +49 -11
- package/dist/bin/astrasync-guard.js +49 -11
- package/dist/bin/astrasync.js +51 -12
- package/dist/browser/background.js +49 -11
- package/dist/browser/background.js.map +1 -1
- package/dist/browser/background.mjs +49 -11
- package/dist/browser/background.mjs.map +1 -1
- package/dist/cli/index.js +1 -1
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/index.mjs +1 -1
- package/dist/cli/index.mjs.map +1 -1
- package/dist/codex/index.js +49 -11
- package/dist/codex/index.js.map +1 -1
- package/dist/codex/index.mjs +49 -11
- package/dist/codex/index.mjs.map +1 -1
- package/dist/cursor/extension.js +49 -11
- package/dist/cursor/extension.js.map +1 -1
- package/dist/cursor/extension.mjs +49 -11
- package/dist/cursor/extension.mjs.map +1 -1
- package/dist/edge-config.d.mts +1 -1
- package/dist/edge-config.d.ts +1 -1
- package/dist/edge-config.js +1 -1
- package/dist/edge-config.js.map +1 -1
- package/dist/edge-config.mjs +1 -1
- package/dist/edge-config.mjs.map +1 -1
- package/dist/edge-core/index.d.mts +1 -1
- package/dist/edge-core/index.d.ts +1 -1
- package/dist/edge-core/index.js +49 -11
- package/dist/edge-core/index.js.map +1 -1
- package/dist/edge-core/index.mjs +49 -11
- package/dist/edge-core/index.mjs.map +1 -1
- package/dist/gateway/gateway.d.mts +2 -2
- package/dist/gateway/gateway.d.ts +2 -2
- package/dist/gateway/gateway.js +49 -11
- package/dist/gateway/gateway.js.map +1 -1
- package/dist/gateway/gateway.mjs +49 -11
- package/dist/gateway/gateway.mjs.map +1 -1
- package/dist/git-trigger/git-hooks.d.mts +1 -1
- package/dist/git-trigger/git-hooks.d.ts +1 -1
- package/dist/index.d.mts +168 -8
- package/dist/index.d.ts +168 -8
- package/dist/index.js +229 -31
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +226 -31
- package/dist/index.mjs.map +1 -1
- package/dist/local-evaluator/evaluator.d.mts +2 -2
- package/dist/local-evaluator/evaluator.d.ts +2 -2
- package/dist/registration/index.d.mts +11 -2
- package/dist/registration/index.d.ts +11 -2
- package/dist/registration/index.js +1 -1
- package/dist/registration/index.js.map +1 -1
- package/dist/registration/index.mjs +1 -1
- package/dist/registration/index.mjs.map +1 -1
- package/dist/transport/index.d.mts +1 -1
- package/dist/transport/index.d.ts +1 -1
- package/dist/transport/index.js +1 -1
- package/dist/transport/index.js.map +1 -1
- package/dist/transport/index.mjs +1 -1
- package/dist/transport/index.mjs.map +1 -1
- package/dist/{types-BRz2U0Pn.d.ts → types-B_HLhZ9n.d.ts} +1 -1
- package/dist/{types-BfILnheI.d.mts → types-CU17R4Nr.d.mts} +11 -0
- package/dist/{types-BfILnheI.d.ts → types-CU17R4Nr.d.ts} +11 -0
- package/dist/{types-DqWPkiYl.d.mts → types-CxjqmPl-.d.mts} +25 -1
- package/dist/{types-DqWPkiYl.d.ts → types-CxjqmPl-.d.ts} +25 -1
- package/dist/{types-BoGjyon0.d.mts → types-D37aSQ9c.d.mts} +25 -1
- package/dist/{types-CfBpm3w5.d.mts → types-Djpz-L0w.d.mts} +1 -1
- package/dist/{types-eWyZuz-3.d.ts → types-Dn8tXnei.d.ts} +25 -1
- package/dist/ui/index.d.mts +1 -1
- package/dist/ui/index.d.ts +1 -1
- package/dist/verify.d.mts +28 -3
- package/dist/verify.d.ts +28 -3
- package/dist/verify.js +71 -11
- package/dist/verify.js.map +1 -1
- package/dist/verify.mjs +69 -11
- package/dist/verify.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -133,6 +133,7 @@ __export(src_exports, {
|
|
|
133
133
|
isEdgeConfig: () => isEdgeConfig,
|
|
134
134
|
isRejectedIdentityClaim: () => isRejectedIdentityClaim,
|
|
135
135
|
isRetryableSettlementError: () => isRetryableSettlementError,
|
|
136
|
+
isSharedVerdict: () => isSharedVerdict,
|
|
136
137
|
isSpecialUseIp: () => isSpecialUseIp,
|
|
137
138
|
isVerificationUnavailable: () => isVerificationUnavailable,
|
|
138
139
|
isVerifiedHopValidFor: () => isVerifiedHopValidFor,
|
|
@@ -144,6 +145,7 @@ __export(src_exports, {
|
|
|
144
145
|
negotiateUcp: () => negotiateUcp,
|
|
145
146
|
nextjs: () => nextjs_exports,
|
|
146
147
|
normalizeDeclaredSource: () => normalizeDeclaredSource,
|
|
148
|
+
onVerificationResult: () => onVerificationResult,
|
|
147
149
|
openCheckoutMandate: () => openCheckoutMandate,
|
|
148
150
|
openPaymentMandate: () => openPaymentMandate,
|
|
149
151
|
parseLocalPolicyYaml: () => parseYaml,
|
|
@@ -177,6 +179,7 @@ __export(src_exports, {
|
|
|
177
179
|
ucpEscalation: () => ucpEscalation,
|
|
178
180
|
ucpFailure: () => ucpFailure,
|
|
179
181
|
ucpProfileHandler: () => ucpProfileHandler,
|
|
182
|
+
ucpProfileKeys: () => ucpProfileKeys,
|
|
180
183
|
ucpRequiredComponents: () => ucpRequiredComponents,
|
|
181
184
|
unavailableRetryAfterSeconds: () => unavailableRetryAfterSeconds,
|
|
182
185
|
validateLocalPolicy: () => validatePolicy,
|
|
@@ -218,7 +221,7 @@ function getTrustLevel(score) {
|
|
|
218
221
|
}
|
|
219
222
|
|
|
220
223
|
// src/version.ts
|
|
221
|
-
var SDK_VERSION = "5.
|
|
224
|
+
var SDK_VERSION = "5.20.0";
|
|
222
225
|
|
|
223
226
|
// src/http.ts
|
|
224
227
|
var SDK_USER_AGENT = `astrasync-sdk/${SDK_VERSION}`;
|
|
@@ -500,6 +503,7 @@ function cacheResult(request, result, configuredTtl, counterpartyId) {
|
|
|
500
503
|
}
|
|
501
504
|
function clearCache() {
|
|
502
505
|
verificationCache.clear();
|
|
506
|
+
inflightVerifies.clear();
|
|
503
507
|
}
|
|
504
508
|
function extractCredentials(headers, query) {
|
|
505
509
|
const credentials = {};
|
|
@@ -673,6 +677,9 @@ async function callVerifyAccessAPI(config, request) {
|
|
|
673
677
|
if (requestData.enableRuntimeChallenge)
|
|
674
678
|
body.enableRuntimeChallenge = requestData.enableRuntimeChallenge;
|
|
675
679
|
if (requestData.routePolicy) body.routePolicy = requestData.routePolicy;
|
|
680
|
+
if (config.actingProof?.connectorToken) {
|
|
681
|
+
body.actingProof = { connectorToken: config.actingProof.connectorToken };
|
|
682
|
+
}
|
|
676
683
|
if (requestData.approvalHeldByRoute) body.approvalHeldByRoute = true;
|
|
677
684
|
if (requestData.createSession) body.createSession = requestData.createSession;
|
|
678
685
|
if (requestData.durationRequired) body.durationRequired = requestData.durationRequired;
|
|
@@ -840,22 +847,46 @@ async function verify(config, request, options) {
|
|
|
840
847
|
"[VerificationGateway] minTrustScore / minTrustScoreForFull are deprecated in v2.3.0 and have no effect. Server is now the single source of truth for access decisions (verified ID + runtime challenge + PDLSS + trust score). To gate access to an endpoint, configure the endpoint's trust_score_requirement server-side."
|
|
841
848
|
);
|
|
842
849
|
}
|
|
843
|
-
const uncacheable = Boolean(request.commerceArtifacts);
|
|
850
|
+
const uncacheable = Boolean(request.commerceArtifacts) || Boolean(mergedConfig.actingProof);
|
|
844
851
|
if (mergedConfig.cacheTtl !== 0 && !options?.bypassCache && !uncacheable) {
|
|
845
852
|
const cached = getCachedResult(request, mergedConfig.counterpartyId);
|
|
846
853
|
if (cached) {
|
|
847
854
|
if (mergedConfig.debug) {
|
|
848
855
|
console.log("[VerificationGateway] Returning cached result");
|
|
849
856
|
}
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
+
return sharedVerdict(cached, request);
|
|
858
|
+
}
|
|
859
|
+
}
|
|
860
|
+
if (mergedConfig.cacheTtl !== 0 && !options?.bypassCache && !uncacheable) {
|
|
861
|
+
const key = getCacheKey(request, mergedConfig.counterpartyId);
|
|
862
|
+
const pending = inflightVerifies.get(key);
|
|
863
|
+
if (pending) return sharedVerdict(await pending, request);
|
|
864
|
+
const flight = verifyViaApi(mergedConfig, request, urls, uncacheable);
|
|
865
|
+
inflightVerifies.set(key, flight);
|
|
866
|
+
try {
|
|
867
|
+
return await flight;
|
|
868
|
+
} finally {
|
|
869
|
+
inflightVerifies.delete(key);
|
|
857
870
|
}
|
|
858
871
|
}
|
|
872
|
+
return verifyViaApi(mergedConfig, request, urls, uncacheable);
|
|
873
|
+
}
|
|
874
|
+
var inflightVerifies = /* @__PURE__ */ new Map();
|
|
875
|
+
var sharedVerdicts = /* @__PURE__ */ new WeakSet();
|
|
876
|
+
function isSharedVerdict(result) {
|
|
877
|
+
return sharedVerdicts.has(result);
|
|
878
|
+
}
|
|
879
|
+
function sharedVerdict(source, request) {
|
|
880
|
+
const hit = { ...source };
|
|
881
|
+
if (request.attemptId) {
|
|
882
|
+
hit.attemptId = request.attemptId;
|
|
883
|
+
} else {
|
|
884
|
+
delete hit.attemptId;
|
|
885
|
+
}
|
|
886
|
+
sharedVerdicts.add(hit);
|
|
887
|
+
return hit;
|
|
888
|
+
}
|
|
889
|
+
async function verifyViaApi(mergedConfig, request, urls, uncacheable) {
|
|
859
890
|
const enrichedRequest = { ...request };
|
|
860
891
|
if (!enrichedRequest.counterpartyUrl && mergedConfig.counterpartyUrl) {
|
|
861
892
|
enrichedRequest.counterpartyUrl = mergedConfig.counterpartyUrl;
|
|
@@ -934,7 +965,9 @@ async function verify(config, request, options) {
|
|
|
934
965
|
fulfilment: apiResponse.fulfilment,
|
|
935
966
|
mandates: apiResponse.mandates,
|
|
936
967
|
kyaToken: apiResponse.kyaToken,
|
|
937
|
-
aep: apiResponse.aep
|
|
968
|
+
aep: apiResponse.aep,
|
|
969
|
+
// 5.20.0 (round 29.1): non-fatal integration warnings (e.g. events_url_missing).
|
|
970
|
+
warnings: apiResponse.warnings
|
|
938
971
|
};
|
|
939
972
|
return result2;
|
|
940
973
|
}
|
|
@@ -1002,7 +1035,9 @@ async function verify(config, request, options) {
|
|
|
1002
1035
|
// 5.15.0: merchant-lane AP2 mandates + KYAPay identity token.
|
|
1003
1036
|
mandates: apiResponse.mandates,
|
|
1004
1037
|
kyaToken: apiResponse.kyaToken,
|
|
1005
|
-
aep: apiResponse.aep
|
|
1038
|
+
aep: apiResponse.aep,
|
|
1039
|
+
// 5.20.0 (round 29.1): non-fatal integration warnings (e.g. events_url_missing).
|
|
1040
|
+
warnings: apiResponse.warnings
|
|
1006
1041
|
};
|
|
1007
1042
|
if (result.recommendation === "deny") {
|
|
1008
1043
|
result.policyAllowed = false;
|
|
@@ -1028,8 +1063,24 @@ async function verify(config, request, options) {
|
|
|
1028
1063
|
cacheResult(request, result, mergedConfig.cacheTtl, mergedConfig.counterpartyId);
|
|
1029
1064
|
}
|
|
1030
1065
|
}
|
|
1066
|
+
notifyVerificationListeners(request, result);
|
|
1031
1067
|
return result;
|
|
1032
1068
|
}
|
|
1069
|
+
var verificationListeners = /* @__PURE__ */ new Set();
|
|
1070
|
+
function onVerificationResult(listener) {
|
|
1071
|
+
verificationListeners.add(listener);
|
|
1072
|
+
return () => {
|
|
1073
|
+
verificationListeners.delete(listener);
|
|
1074
|
+
};
|
|
1075
|
+
}
|
|
1076
|
+
function notifyVerificationListeners(request, result) {
|
|
1077
|
+
for (const listener of verificationListeners) {
|
|
1078
|
+
try {
|
|
1079
|
+
listener(result, request);
|
|
1080
|
+
} catch {
|
|
1081
|
+
}
|
|
1082
|
+
}
|
|
1083
|
+
}
|
|
1033
1084
|
async function recordDecision(config, sessionId, decision, reason) {
|
|
1034
1085
|
const headers = { "Content-Type": "application/json" };
|
|
1035
1086
|
if (config.apiKey) {
|
|
@@ -1094,6 +1145,7 @@ async function fetchRoutes(config, counterpartyId, options = {}) {
|
|
|
1094
1145
|
headers["X-API-Key"] = config.apiKey;
|
|
1095
1146
|
}
|
|
1096
1147
|
try {
|
|
1148
|
+
headers["X-AstraSync-SDK-Version"] = SDK_VERSION;
|
|
1097
1149
|
const response = await sdkFetch(
|
|
1098
1150
|
`${config.apiBaseUrl}/endpoints/${encodeURIComponent(counterpartyId)}/routes` + (options.codeGates?.length ? `?codeGates=${options.codeGates.map(encodeURIComponent).join(",")}` : ""),
|
|
1099
1151
|
{ method: "GET", headers }
|
|
@@ -1108,6 +1160,13 @@ async function fetchRoutes(config, counterpartyId, options = {}) {
|
|
|
1108
1160
|
return null;
|
|
1109
1161
|
}
|
|
1110
1162
|
const body = await response.json();
|
|
1163
|
+
const type2 = body.data?.counterpartyType;
|
|
1164
|
+
if (typeof type2 === "string" && options.onEndpointType) {
|
|
1165
|
+
try {
|
|
1166
|
+
options.onEndpointType(type2);
|
|
1167
|
+
} catch {
|
|
1168
|
+
}
|
|
1169
|
+
}
|
|
1111
1170
|
return body.data?.routes ?? [];
|
|
1112
1171
|
} catch {
|
|
1113
1172
|
return null;
|
|
@@ -3031,14 +3090,14 @@ function createMiddleware(options) {
|
|
|
3031
3090
|
res.setHeader("X-Astra-Gateway-Mode", "enforced");
|
|
3032
3091
|
res.setHeader("X-Astra-Gateway-Reason", "evaluated-not-enforced");
|
|
3033
3092
|
}
|
|
3034
|
-
if (shouldRecordDecisions && sessionId) {
|
|
3093
|
+
if (shouldRecordDecisions && sessionId && !isSharedVerdict(result)) {
|
|
3035
3094
|
recordDecision(config, sessionId, "granted").catch(() => {
|
|
3036
3095
|
});
|
|
3037
3096
|
}
|
|
3038
3097
|
return next();
|
|
3039
3098
|
}
|
|
3040
3099
|
if (!result.policyAllowed) {
|
|
3041
|
-
if (shouldRecordDecisions && sessionId) {
|
|
3100
|
+
if (shouldRecordDecisions && sessionId && !isSharedVerdict(result)) {
|
|
3042
3101
|
recordDecision(config, sessionId, "denied", result.denialReasons?.[0]).catch(() => {
|
|
3043
3102
|
});
|
|
3044
3103
|
}
|
|
@@ -3051,7 +3110,7 @@ function createMiddleware(options) {
|
|
|
3051
3110
|
}
|
|
3052
3111
|
if (requiresHumanApproval(result)) {
|
|
3053
3112
|
annotateApprovalRequired(result);
|
|
3054
|
-
if (shouldRecordDecisions && sessionId) {
|
|
3113
|
+
if (shouldRecordDecisions && sessionId && !isSharedVerdict(result)) {
|
|
3055
3114
|
recordDecision(config, sessionId, "denied", result.denialReasons?.[0]).catch(() => {
|
|
3056
3115
|
});
|
|
3057
3116
|
}
|
|
@@ -3076,7 +3135,7 @@ function createMiddleware(options) {
|
|
|
3076
3135
|
documentationUrl: wellKnownUrls.documentationUrl
|
|
3077
3136
|
};
|
|
3078
3137
|
}
|
|
3079
|
-
if (shouldRecordDecisions && sessionId) {
|
|
3138
|
+
if (shouldRecordDecisions && sessionId && !isSharedVerdict(result)) {
|
|
3080
3139
|
recordDecision(config, sessionId, "denied", trustFailure.message).catch(() => {
|
|
3081
3140
|
});
|
|
3082
3141
|
}
|
|
@@ -3085,7 +3144,7 @@ function createMiddleware(options) {
|
|
|
3085
3144
|
return;
|
|
3086
3145
|
}
|
|
3087
3146
|
}
|
|
3088
|
-
if (shouldRecordDecisions && sessionId) {
|
|
3147
|
+
if (shouldRecordDecisions && sessionId && !isSharedVerdict(result)) {
|
|
3089
3148
|
recordDecision(config, sessionId, "granted").catch(() => {
|
|
3090
3149
|
});
|
|
3091
3150
|
}
|
|
@@ -3933,6 +3992,80 @@ var VerificationGatewayClient = class {
|
|
|
3933
3992
|
}
|
|
3934
3993
|
return json4.data;
|
|
3935
3994
|
}
|
|
3995
|
+
/**
|
|
3996
|
+
* 5.20.0 (round 29.1): change an endpoint's OPERATIONAL URLs with this
|
|
3997
|
+
* client's API key — `PATCH /api/endpoints/:id`. Accepts the key of the
|
|
3998
|
+
* endpoint's owner, or a personal key of an individual with an active seat
|
|
3999
|
+
* in the owning organisation. Policy (rules, trust floor, challenge, edge
|
|
4000
|
+
* config) stays dashboard-only. `null` clears a field. The owner is alerted
|
|
4001
|
+
* when `eventsUrl` changes.
|
|
4002
|
+
*
|
|
4003
|
+
* @param endpointId the ASTRAE id (defaults to the client's `counterpartyId`)
|
|
4004
|
+
* @returns the endpoint as stored, plus `changed` (the fields that changed)
|
|
4005
|
+
* @throws AstraSyncError — 400 `UNKNOWN_FIELDS` / validation, 401/403, 404
|
|
4006
|
+
*/
|
|
4007
|
+
async updateEndpoint(endpointId, patch) {
|
|
4008
|
+
return this.endpointCall("PATCH", endpointId, "", patch);
|
|
4009
|
+
}
|
|
4010
|
+
/**
|
|
4011
|
+
* 5.20.0 (round 29.1): ask AstraSync to send ONE signed step-up event to the
|
|
4012
|
+
* endpoint's Events URL now — `POST /api/endpoints/:id/events/test`.
|
|
4013
|
+
* Delivered synchronously, outside the event outbox (random `eventId`, no
|
|
4014
|
+
* retries), signed with the endpoint's webhook secret like a real event.
|
|
4015
|
+
* Without `approvalId` the payload is synthetic (a kit acknowledges and
|
|
4016
|
+
* ignores it). With the id of a held kit approval at this endpoint the
|
|
4017
|
+
* payload is the real one, so your resume path runs: `approved` makes the
|
|
4018
|
+
* kit re-confirm (held again — nothing is charged); `denied` / `expired` /
|
|
4019
|
+
* `lapsed` release the session.
|
|
4020
|
+
*
|
|
4021
|
+
* Resolves even when your endpoint refused the event (`delivered: false`,
|
|
4022
|
+
* with `responseCode` / `error`).
|
|
4023
|
+
*/
|
|
4024
|
+
async sendTestEvent(endpointId, options) {
|
|
4025
|
+
return this.endpointCall("POST", endpointId, "/events/test", options);
|
|
4026
|
+
}
|
|
4027
|
+
async endpointCall(method, endpointId, suffix, body) {
|
|
4028
|
+
const id = endpointId ?? this.config.counterpartyId;
|
|
4029
|
+
if (!id) {
|
|
4030
|
+
throw new AstraSyncError(
|
|
4031
|
+
"An endpoint id (ASTRAE-\u2026) is required: pass it, or set counterpartyId on the client",
|
|
4032
|
+
400,
|
|
4033
|
+
"VALIDATION"
|
|
4034
|
+
);
|
|
4035
|
+
}
|
|
4036
|
+
if (!this.config.apiKey) {
|
|
4037
|
+
throw new AstraSyncError("This call needs the client apiKey", 401, "AUTH_REQUIRED");
|
|
4038
|
+
}
|
|
4039
|
+
const base = this.config.apiBaseUrl.replace(/\/+$/, "");
|
|
4040
|
+
const url = `${base}/endpoints/${encodeURIComponent(id)}${suffix}`;
|
|
4041
|
+
let res;
|
|
4042
|
+
try {
|
|
4043
|
+
res = await sdkFetch(url, {
|
|
4044
|
+
method,
|
|
4045
|
+
headers: {
|
|
4046
|
+
"Content-Type": "application/json",
|
|
4047
|
+
Authorization: `Bearer ${this.config.apiKey}`,
|
|
4048
|
+
"X-API-Key": this.config.apiKey
|
|
4049
|
+
},
|
|
4050
|
+
body: JSON.stringify(body),
|
|
4051
|
+
signal: AbortSignal.timeout(Math.max(this.timeout, 15e3))
|
|
4052
|
+
});
|
|
4053
|
+
} catch (err) {
|
|
4054
|
+
throw new AstraSyncError(
|
|
4055
|
+
`${method} ${suffix || "/endpoints/:id"} failed: ${err instanceof Error ? err.message : String(err)}`,
|
|
4056
|
+
0,
|
|
4057
|
+
"network_error"
|
|
4058
|
+
);
|
|
4059
|
+
}
|
|
4060
|
+
const json4 = await res.json().catch(() => ({}));
|
|
4061
|
+
if (!res.ok || json4.success === false || json4.data === void 0) {
|
|
4062
|
+
const errObj = typeof json4.error === "object" ? json4.error : void 0;
|
|
4063
|
+
const code = json4.code ?? errObj?.code ?? (typeof json4.error === "string" ? json4.error : "unknown_error");
|
|
4064
|
+
const message = json4.message ?? errObj?.message ?? `HTTP ${res.status}`;
|
|
4065
|
+
throw new AstraSyncError(`${message} (${code})`, res.status, code);
|
|
4066
|
+
}
|
|
4067
|
+
return json4.data;
|
|
4068
|
+
}
|
|
3936
4069
|
/**
|
|
3937
4070
|
* Quick verification — checks credentials and policy in one call.
|
|
3938
4071
|
*
|
|
@@ -6894,16 +7027,30 @@ function createMcpMiddleware(options) {
|
|
|
6894
7027
|
failOnError = "closed",
|
|
6895
7028
|
toolGatePrecedence = "code",
|
|
6896
7029
|
routesRefreshMs,
|
|
7030
|
+
skipEvaluation,
|
|
6897
7031
|
...config
|
|
6898
7032
|
} = options;
|
|
7033
|
+
const skipTools = new Set(skipEvaluation?.tools ?? []);
|
|
7034
|
+
const skipMethods = new Set(skipEvaluation?.methods ?? []);
|
|
6899
7035
|
if (config.apiBaseUrl) {
|
|
6900
7036
|
prefetchWellKnown(config.apiBaseUrl).catch(() => {
|
|
6901
7037
|
});
|
|
6902
7038
|
}
|
|
6903
7039
|
const codeGatedTools = Object.entries(toolGates ?? {}).filter(([, g]) => g !== "observe").map(([name]) => name);
|
|
7040
|
+
let warnedEndpointType = false;
|
|
7041
|
+
const onEndpointType = (counterpartyType) => {
|
|
7042
|
+
if (counterpartyType === "mcp_server" || warnedEndpointType) return;
|
|
7043
|
+
warnedEndpointType = true;
|
|
7044
|
+
console.warn(
|
|
7045
|
+
`[VerificationGateway/MCP] counterpartyId ${config.counterpartyId} is a '${counterpartyType}' endpoint, not an 'mcp_server' endpoint. Dashboard tool rules are read from an mcp_server endpoint only: register a separate MCP endpoint for this server (same owner) and use its id here. See https://astrasync.ai/docs/mcp-integration#tool-rules.`
|
|
7046
|
+
);
|
|
7047
|
+
};
|
|
6904
7048
|
const rulesCache = config.apiKey && config.counterpartyId ? createRoutesCache(config, {
|
|
6905
7049
|
...routesRefreshMs !== void 0 && { refreshMs: routesRefreshMs },
|
|
6906
|
-
|
|
7050
|
+
fetchOptions: {
|
|
7051
|
+
...codeGatedTools.length && { codeGates: codeGatedTools },
|
|
7052
|
+
onEndpointType
|
|
7053
|
+
}
|
|
6907
7054
|
}) : void 0;
|
|
6908
7055
|
return async (req, res, next) => {
|
|
6909
7056
|
try {
|
|
@@ -6953,6 +7100,9 @@ function createMcpMiddleware(options) {
|
|
|
6953
7100
|
return next();
|
|
6954
7101
|
}
|
|
6955
7102
|
}
|
|
7103
|
+
if (!parsed.toolName && skipMethods.has(parsed.method) && methodGates?.[parsed.method] === void 0) {
|
|
7104
|
+
return next();
|
|
7105
|
+
}
|
|
6956
7106
|
const dashboardRules = rulesCache ? dashboardToolRules(await rulesCache.current()) : void 0;
|
|
6957
7107
|
const gateDecision = resolveGate(parsed, {
|
|
6958
7108
|
toolGates,
|
|
@@ -6960,6 +7110,9 @@ function createMcpMiddleware(options) {
|
|
|
6960
7110
|
dashboardRules,
|
|
6961
7111
|
toolGatePrecedence
|
|
6962
7112
|
});
|
|
7113
|
+
if (!gateDecision.enforce && (parsed.toolName && skipTools.has(parsed.toolName) || !parsed.toolName && skipMethods.has(parsed.method))) {
|
|
7114
|
+
return next();
|
|
7115
|
+
}
|
|
6963
7116
|
const credentials = extractCredentials(
|
|
6964
7117
|
req.headers,
|
|
6965
7118
|
req.query
|
|
@@ -7016,8 +7169,14 @@ function createMcpMiddleware(options) {
|
|
|
7016
7169
|
const forwardedFor = req.headers["x-forwarded-for"];
|
|
7017
7170
|
const forwardedForStr = Array.isArray(forwardedFor) ? forwardedFor.join(", ") : forwardedFor;
|
|
7018
7171
|
const sourceIp = req.ip ?? (forwardedForStr ? forwardedForStr.split(",")[0].trim() : void 0);
|
|
7172
|
+
const attemptRaw = readSingleHeader(req.headers["x-astra-attempt-id"]);
|
|
7173
|
+
const metaAttempt = req.body?.params?._meta?.astrasync?.attemptId;
|
|
7174
|
+
const attemptId = [attemptRaw, metaAttempt].find(
|
|
7175
|
+
(v) => typeof v === "string" && /^att_[0-9a-f]{32}$/.test(v)
|
|
7176
|
+
);
|
|
7019
7177
|
const verificationRequest = {
|
|
7020
7178
|
credentials,
|
|
7179
|
+
...attemptId && { attemptId },
|
|
7021
7180
|
purpose: pdlss.purpose,
|
|
7022
7181
|
action: pdlss.action,
|
|
7023
7182
|
resource: pdlss.resource,
|
|
@@ -7079,14 +7238,14 @@ function createMcpMiddleware(options) {
|
|
|
7079
7238
|
res.setHeader("X-Astra-Gateway-Mode", "enforced");
|
|
7080
7239
|
res.setHeader("X-Astra-Gateway-Reason", "evaluated-not-enforced");
|
|
7081
7240
|
}
|
|
7082
|
-
if (shouldRecordDecisions && sessionId) {
|
|
7241
|
+
if (shouldRecordDecisions && sessionId && !isSharedVerdict(result)) {
|
|
7083
7242
|
recordDecision(config, sessionId, "granted").catch(() => {
|
|
7084
7243
|
});
|
|
7085
7244
|
}
|
|
7086
7245
|
return next();
|
|
7087
7246
|
}
|
|
7088
7247
|
if (!result.policyAllowed) {
|
|
7089
|
-
if (shouldRecordDecisions && sessionId) {
|
|
7248
|
+
if (shouldRecordDecisions && sessionId && !isSharedVerdict(result)) {
|
|
7090
7249
|
recordDecision(config, sessionId, "denied", result.denialReasons?.[0]).catch(() => {
|
|
7091
7250
|
});
|
|
7092
7251
|
}
|
|
@@ -7096,7 +7255,7 @@ function createMcpMiddleware(options) {
|
|
|
7096
7255
|
}
|
|
7097
7256
|
if (requiresHumanApproval(result)) {
|
|
7098
7257
|
annotateApprovalRequired(result);
|
|
7099
|
-
if (shouldRecordDecisions && sessionId) {
|
|
7258
|
+
if (shouldRecordDecisions && sessionId && !isSharedVerdict(result)) {
|
|
7100
7259
|
recordDecision(config, sessionId, "denied", result.denialReasons?.[0]).catch(() => {
|
|
7101
7260
|
});
|
|
7102
7261
|
}
|
|
@@ -7114,7 +7273,7 @@ function createMcpMiddleware(options) {
|
|
|
7114
7273
|
})
|
|
7115
7274
|
);
|
|
7116
7275
|
}
|
|
7117
|
-
if (shouldRecordDecisions && sessionId) {
|
|
7276
|
+
if (shouldRecordDecisions && sessionId && !isSharedVerdict(result)) {
|
|
7118
7277
|
recordDecision(config, sessionId, "granted").catch(() => {
|
|
7119
7278
|
});
|
|
7120
7279
|
}
|
|
@@ -11123,6 +11282,16 @@ function checkUcpAgentBinding(header, identities) {
|
|
|
11123
11282
|
|
|
11124
11283
|
// src/ucp/profile.ts
|
|
11125
11284
|
var UCP_VERSION = "2026-08-25";
|
|
11285
|
+
function ucpProfileKeys(profile) {
|
|
11286
|
+
const out = [];
|
|
11287
|
+
for (const list of [profile?.keys, profile?.signing_keys]) {
|
|
11288
|
+
if (!Array.isArray(list)) continue;
|
|
11289
|
+
for (const k of list) {
|
|
11290
|
+
if (k && typeof k === "object" && k.use !== "enc") out.push(k);
|
|
11291
|
+
}
|
|
11292
|
+
}
|
|
11293
|
+
return out;
|
|
11294
|
+
}
|
|
11126
11295
|
var SPEC = `https://ucp.dev/${UCP_VERSION}/specification`;
|
|
11127
11296
|
var SCHEMAS = `https://ucp.dev/${UCP_VERSION}/schemas`;
|
|
11128
11297
|
function astrasyncPaymentHandlers(handlers, specBase = "https://astrasync.ai/ucp/handlers") {
|
|
@@ -12282,6 +12451,19 @@ var resumeResult = (outcome, session) => ({
|
|
|
12282
12451
|
});
|
|
12283
12452
|
|
|
12284
12453
|
// src/ucp/checkout-kit.ts
|
|
12454
|
+
function withValidImageUrl(item) {
|
|
12455
|
+
if (item.image_url === void 0) return item;
|
|
12456
|
+
let ok = false;
|
|
12457
|
+
try {
|
|
12458
|
+
const u = new URL(item.image_url);
|
|
12459
|
+
ok = u.protocol === "https:" || u.protocol === "http:";
|
|
12460
|
+
} catch {
|
|
12461
|
+
ok = false;
|
|
12462
|
+
}
|
|
12463
|
+
if (ok) return item;
|
|
12464
|
+
const { image_url: _drop, ...rest } = item;
|
|
12465
|
+
return rest;
|
|
12466
|
+
}
|
|
12285
12467
|
var MemoryCheckoutStore = class {
|
|
12286
12468
|
constructor() {
|
|
12287
12469
|
this.m = /* @__PURE__ */ new Map();
|
|
@@ -12565,7 +12747,8 @@ function createUcpCheckoutKit(opts) {
|
|
|
12565
12747
|
const messages = [];
|
|
12566
12748
|
for (const [i, l] of lines.entries()) {
|
|
12567
12749
|
const q = Math.max(1, Math.floor(l.quantity ?? 1));
|
|
12568
|
-
const
|
|
12750
|
+
const found = l.item?.id ? await opts.catalog(l.item.id, q) : null;
|
|
12751
|
+
const item = found && withValidImageUrl(found);
|
|
12569
12752
|
if (!item) {
|
|
12570
12753
|
messages.push(
|
|
12571
12754
|
ucpError(
|
|
@@ -12654,7 +12837,7 @@ function createUcpCheckoutKit(opts) {
|
|
|
12654
12837
|
{ method: req.method, url: req.url, headers, body: body || void 0 },
|
|
12655
12838
|
async (fresh) => {
|
|
12656
12839
|
const pp = fresh ? await fetchProfile(profileUrl, true) : plat;
|
|
12657
|
-
return "
|
|
12840
|
+
return "ucp" in pp ? ucpProfileKeys(pp) : [];
|
|
12658
12841
|
}
|
|
12659
12842
|
);
|
|
12660
12843
|
if (!sig.ok) return { error: json2(sig.status, { code: sig.code, content: sig.content }) };
|
|
@@ -12950,7 +13133,7 @@ function createUcpCheckoutKit(opts) {
|
|
|
12950
13133
|
ucpError(
|
|
12951
13134
|
`approval_${reason}`,
|
|
12952
13135
|
reason === "denied" ? "The buyer declined this purchase." : "The approval for this purchase is no longer valid. Complete the checkout again to request a new one.",
|
|
12953
|
-
"
|
|
13136
|
+
"recoverable"
|
|
12954
13137
|
)
|
|
12955
13138
|
];
|
|
12956
13139
|
await store.put(s);
|
|
@@ -12985,7 +13168,7 @@ function createUcpCheckoutKit(opts) {
|
|
|
12985
13168
|
});
|
|
12986
13169
|
if (status === "approved" || status === "consumed") {
|
|
12987
13170
|
out.push({ sessionId: s.id, outcome: (await resumeApproved(s.id)).outcome });
|
|
12988
|
-
} else if (status === "denied" || status === "expired") {
|
|
13171
|
+
} else if (status === "denied" || status === "expired" || status === "lapsed") {
|
|
12989
13172
|
out.push({ sessionId: s.id, outcome: (await resumeDeclined(s.id, status)).outcome });
|
|
12990
13173
|
} else {
|
|
12991
13174
|
out.push({ sessionId: s.id, outcome: "pending" });
|
|
@@ -13028,7 +13211,7 @@ function createUcpCheckoutKit(opts) {
|
|
|
13028
13211
|
_caller: caller,
|
|
13029
13212
|
_platformProfile: ctx.profileUrl,
|
|
13030
13213
|
_ap2: ctx.ap2,
|
|
13031
|
-
_platformKeys: ctx.plat
|
|
13214
|
+
_platformKeys: ucpProfileKeys(ctx.plat),
|
|
13032
13215
|
...typeof webhookUrl === "string" && /^https:\/\//.test(webhookUrl) && { _webhookUrl: webhookUrl },
|
|
13033
13216
|
...req.headers.get("x-astra-checkout-session") && {
|
|
13034
13217
|
_astraCheckoutSessionId: req.headers.get("x-astra-checkout-session").slice(0, 64)
|
|
@@ -13709,7 +13892,12 @@ var ACP_CODES = /* @__PURE__ */ new Set([
|
|
|
13709
13892
|
"out_of_stock",
|
|
13710
13893
|
"payment_declined",
|
|
13711
13894
|
"requires_sign_in",
|
|
13712
|
-
"requires_3ds"
|
|
13895
|
+
"requires_3ds",
|
|
13896
|
+
// 5.20.0: the owner-approval codes the kit itself emits.
|
|
13897
|
+
"approval_required",
|
|
13898
|
+
"approval_denied",
|
|
13899
|
+
"approval_expired",
|
|
13900
|
+
"approval_lapsed"
|
|
13713
13901
|
]);
|
|
13714
13902
|
function heldBody2(text) {
|
|
13715
13903
|
try {
|
|
@@ -13776,7 +13964,8 @@ function createAcpCheckoutKit(opts) {
|
|
|
13776
13964
|
const messages = [];
|
|
13777
13965
|
for (const [i, it] of items.entries()) {
|
|
13778
13966
|
const q = Math.max(1, Math.floor(it.quantity ?? 1));
|
|
13779
|
-
const
|
|
13967
|
+
const found = it.id ? await opts.catalog(it.id, q) : null;
|
|
13968
|
+
const c = found && withValidImageUrl(found);
|
|
13780
13969
|
if (!c) {
|
|
13781
13970
|
messages.push({
|
|
13782
13971
|
type: "error",
|
|
@@ -13790,7 +13979,13 @@ function createAcpCheckoutKit(opts) {
|
|
|
13790
13979
|
const amount = c.price * q;
|
|
13791
13980
|
lines.push({
|
|
13792
13981
|
id: `li_${i + 1}`,
|
|
13793
|
-
item: {
|
|
13982
|
+
item: {
|
|
13983
|
+
id: c.id,
|
|
13984
|
+
name: c.title,
|
|
13985
|
+
unit_amount: c.price,
|
|
13986
|
+
...c.image_url && { image_url: c.image_url },
|
|
13987
|
+
...typeof c.description === "string" && { description: c.description }
|
|
13988
|
+
},
|
|
13794
13989
|
quantity: q,
|
|
13795
13990
|
name: c.title,
|
|
13796
13991
|
unit_amount: c.price,
|
|
@@ -14137,7 +14332,7 @@ function createAcpCheckoutKit(opts) {
|
|
|
14137
14332
|
code: `approval_${reason}`,
|
|
14138
14333
|
content: reason === "denied" ? "The buyer declined this purchase." : "The approval for this purchase is no longer valid. Complete the session again to request a new one.",
|
|
14139
14334
|
severity: "medium",
|
|
14140
|
-
resolution: "
|
|
14335
|
+
resolution: "recoverable"
|
|
14141
14336
|
}
|
|
14142
14337
|
];
|
|
14143
14338
|
await store.put(s);
|
|
@@ -14172,7 +14367,7 @@ function createAcpCheckoutKit(opts) {
|
|
|
14172
14367
|
});
|
|
14173
14368
|
if (status === "approved" || status === "consumed") {
|
|
14174
14369
|
out.push({ sessionId: s.id, outcome: (await resumeApproved(s.id)).outcome });
|
|
14175
|
-
} else if (status === "denied" || status === "expired") {
|
|
14370
|
+
} else if (status === "denied" || status === "expired" || status === "lapsed") {
|
|
14176
14371
|
out.push({ sessionId: s.id, outcome: (await resumeDeclined(s.id, status)).outcome });
|
|
14177
14372
|
} else {
|
|
14178
14373
|
out.push({ sessionId: s.id, outcome: "pending" });
|
|
@@ -14490,6 +14685,7 @@ function expressHandler(kit, options = {}) {
|
|
|
14490
14685
|
isEdgeConfig,
|
|
14491
14686
|
isRejectedIdentityClaim,
|
|
14492
14687
|
isRetryableSettlementError,
|
|
14688
|
+
isSharedVerdict,
|
|
14493
14689
|
isSpecialUseIp,
|
|
14494
14690
|
isVerificationUnavailable,
|
|
14495
14691
|
isVerifiedHopValidFor,
|
|
@@ -14501,6 +14697,7 @@ function expressHandler(kit, options = {}) {
|
|
|
14501
14697
|
negotiateUcp,
|
|
14502
14698
|
nextjs,
|
|
14503
14699
|
normalizeDeclaredSource,
|
|
14700
|
+
onVerificationResult,
|
|
14504
14701
|
openCheckoutMandate,
|
|
14505
14702
|
openPaymentMandate,
|
|
14506
14703
|
parseLocalPolicyYaml,
|
|
@@ -14534,6 +14731,7 @@ function expressHandler(kit, options = {}) {
|
|
|
14534
14731
|
ucpEscalation,
|
|
14535
14732
|
ucpFailure,
|
|
14536
14733
|
ucpProfileHandler,
|
|
14734
|
+
ucpProfileKeys,
|
|
14537
14735
|
ucpRequiredComponents,
|
|
14538
14736
|
unavailableRetryAfterSeconds,
|
|
14539
14737
|
validateLocalPolicy,
|