@haven_ai/signer 0.0.0-dev.202609250737.3bd5a51 → 0.0.0-dev.202609251409.98ed67a
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 +1 -0
- package/dist/cli.cjs +65 -16
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +66 -17
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +65 -16
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +7 -3
- package/dist/index.d.ts +7 -3
- package/dist/index.js +66 -17
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/cli.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { randomUUID, createHash } from 'crypto';
|
|
3
3
|
import { readFile, mkdir, writeFile, stat, lstat, rename, appendFile, chmod } from 'fs/promises';
|
|
4
4
|
import { resolve, dirname, join } from 'path';
|
|
5
|
-
import { createNextStepBuilder, isSupportedNodeVersion, unsupportedNodeVersionMessage, addressFromKey, HavenSigningError, buildSweepTypedData, selectStandardPaymentOption, HavenApiError, toStandardPaymentRequirements, decodeBase64Json, encodeBase64Json, x402V2PaymentEnvelope, isSettlementChildTypedData, chainIdForNetwork, assertOwnSettlementChild, isPackedUserOperationTypedData, assertUserOpTypedDataBinding, assertBoundDirectPaymentUserOp as assertBoundDirectPaymentUserOp$1, assertFundingLegPaysDelegate, HavenTypedDataRefusedError, connectorRerunCommand, buildSweepAuthorizationMessage, verifySignature, x402AuthorizationAmount, SignerRefusalCode, SIGNER_UPDATE_FALLBACK, HavenUnsupportedSignerVersionError, buildX402ExpectedMessage, DIRECT_SIGN_CONTEXT_VERSION, HavenError, AgentPaymentFailureCode, AgentPaymentNextAction, HavenUserOpBindingError } from '@haven_ai/sdk/edge';
|
|
5
|
+
import { createNextStepBuilder, havenClientIdentity, isSupportedNodeVersion, unsupportedNodeVersionMessage, addressFromKey, HavenSigningError, buildSweepTypedData, selectStandardPaymentOption, HavenApiError, toStandardPaymentRequirements, decodeBase64Json, encodeBase64Json, x402V2PaymentEnvelope, isSettlementChildTypedData, chainIdForNetwork, assertOwnSettlementChild, isPackedUserOperationTypedData, assertUserOpTypedDataBinding, assertBoundDirectPaymentUserOp as assertBoundDirectPaymentUserOp$1, assertFundingLegPaysDelegate, HavenTypedDataRefusedError, connectorRerunCommand, buildSweepAuthorizationMessage, verifySignature, x402AuthorizationAmount, SignerRefusalCode, SIGNER_UPDATE_FALLBACK, HavenUnsupportedSignerVersionError, buildX402ExpectedMessage, DIRECT_SIGN_CONTEXT_VERSION, HavenError, AgentPaymentFailureCode, AgentPaymentNextAction, HavenUserOpBindingError, readClientUpdate, HAVEN_CLIENT_HEADER } from '@haven_ai/sdk/edge';
|
|
6
6
|
import { recoverTypedDataAddress, hashTypedData, hashMessage } from 'viem';
|
|
7
7
|
import { z as z$1 } from 'zod/v3';
|
|
8
8
|
import { z } from 'zod';
|
|
@@ -209,6 +209,12 @@ var HavenSignContextError = class extends HavenSigningError {
|
|
|
209
209
|
http_status;
|
|
210
210
|
/** Present only for `SIGN_CONTEXT_REFUSED`: the backend's own `error_code`. */
|
|
211
211
|
backend_error_code;
|
|
212
|
+
/**
|
|
213
|
+
* #3303: on a 426 `client_outdated` refusal, the backend's update hint —
|
|
214
|
+
* this signer is below the minimum the deployment set, and
|
|
215
|
+
* `client_update.upgrade_command` is what updates it.
|
|
216
|
+
*/
|
|
217
|
+
client_update;
|
|
212
218
|
constructor(message, code, refusal, paymentId, flow = "x402") {
|
|
213
219
|
super(message);
|
|
214
220
|
this.code = code;
|
|
@@ -217,7 +223,15 @@ var HavenSignContextError = class extends HavenSigningError {
|
|
|
217
223
|
if (code === "SIGN_CONTEXT_REFUSED") {
|
|
218
224
|
this.http_status = refusal?.httpStatus;
|
|
219
225
|
this.backend_error_code = refusal?.errorCode;
|
|
220
|
-
if (
|
|
226
|
+
if (refusal?.errorCode === "client_outdated") {
|
|
227
|
+
this.client_update = refusal.clientUpdate;
|
|
228
|
+
this.next_action = AgentPaymentNextAction.StopAndTellUser;
|
|
229
|
+
step = signerRefusalStep({
|
|
230
|
+
nextAction: AgentPaymentNextAction.StopAndTellUser,
|
|
231
|
+
nextTool: null,
|
|
232
|
+
nextToolOmittedReason: `this signer is below the minimum version Haven accepts \u2014 tell the user to run ${refusal.clientUpdate?.upgrade_command ?? "the connector again"}, restart the agent runtime, then retry the same haven_sign / haven_sign_x402 call`
|
|
233
|
+
});
|
|
234
|
+
} else if (flow === "direct" && refusal?.httpStatus === 404) {
|
|
221
235
|
this.fallback = "typed_data_b64";
|
|
222
236
|
this.next_action = AgentPaymentNextAction.StopAndTellUser;
|
|
223
237
|
step = signerRefusalStep({
|
|
@@ -275,13 +289,26 @@ async function loadHavenIdentity(credentialsPath) {
|
|
|
275
289
|
}
|
|
276
290
|
}
|
|
277
291
|
var SIGN_CONTEXT_TIMEOUT_MS = 15e3;
|
|
278
|
-
|
|
292
|
+
function signContextHeaders(identity, clientIdentity) {
|
|
293
|
+
return {
|
|
294
|
+
Authorization: `Bearer ${identity.apiKey}`,
|
|
295
|
+
...clientIdentity ? { [HAVEN_CLIENT_HEADER]: clientIdentity } : {}
|
|
296
|
+
};
|
|
297
|
+
}
|
|
298
|
+
function refusalOf(status, body) {
|
|
299
|
+
return {
|
|
300
|
+
httpStatus: status,
|
|
301
|
+
errorCode: typeof body.error_code === "string" ? body.error_code : void 0,
|
|
302
|
+
clientUpdate: readClientUpdate(body.client_update)
|
|
303
|
+
};
|
|
304
|
+
}
|
|
305
|
+
async function fetchX402SignContext(identity, paymentId, fetchImpl = fetch, timeoutMs = SIGN_CONTEXT_TIMEOUT_MS, clientIdentity) {
|
|
279
306
|
let response;
|
|
280
307
|
try {
|
|
281
308
|
response = await fetchImpl(
|
|
282
309
|
`${identity.apiUrl}/x402/${encodeURIComponent(paymentId)}/sign-context`,
|
|
283
310
|
{
|
|
284
|
-
headers:
|
|
311
|
+
headers: signContextHeaders(identity, clientIdentity),
|
|
285
312
|
signal: AbortSignal.timeout(timeoutMs)
|
|
286
313
|
}
|
|
287
314
|
);
|
|
@@ -313,7 +340,7 @@ async function fetchX402SignContext(identity, paymentId, fetchImpl = fetch, time
|
|
|
313
340
|
throw new HavenSignContextError(
|
|
314
341
|
`Haven refused the signing-context fetch for ${paymentId}: ${detail}` + (response.status === 404 ? " \u2014 check the payment_id came from this agent\u2019s own quote." : response.status === 410 ? " Re-run the quote with the same idempotency key, then sign the fresh payment_id." : ""),
|
|
315
342
|
"SIGN_CONTEXT_REFUSED",
|
|
316
|
-
|
|
343
|
+
refusalOf(response.status, body),
|
|
317
344
|
paymentId
|
|
318
345
|
);
|
|
319
346
|
}
|
|
@@ -333,17 +360,18 @@ async function fetchX402SignContext(identity, paymentId, fetchImpl = fetch, time
|
|
|
333
360
|
payloadHash: signData.hash,
|
|
334
361
|
typedData: signData.typed_data,
|
|
335
362
|
x402Expected,
|
|
336
|
-
paymentRequired: paymentRequired && typeof paymentRequired === "object" && !Array.isArray(paymentRequired) ? paymentRequired : null
|
|
363
|
+
paymentRequired: paymentRequired && typeof paymentRequired === "object" && !Array.isArray(paymentRequired) ? paymentRequired : null,
|
|
364
|
+
clientUpdate: readClientUpdate(body.client_update)
|
|
337
365
|
};
|
|
338
366
|
}
|
|
339
367
|
var SUPPORTED_DIRECT_SIGN_CONTEXT_VERSIONS = [DIRECT_SIGN_CONTEXT_VERSION];
|
|
340
|
-
async function fetchDirectSignContext(identity, paymentId, fetchImpl = fetch, timeoutMs = SIGN_CONTEXT_TIMEOUT_MS) {
|
|
368
|
+
async function fetchDirectSignContext(identity, paymentId, fetchImpl = fetch, timeoutMs = SIGN_CONTEXT_TIMEOUT_MS, clientIdentity) {
|
|
341
369
|
let response;
|
|
342
370
|
try {
|
|
343
371
|
response = await fetchImpl(
|
|
344
372
|
`${identity.apiUrl}/payments/${encodeURIComponent(paymentId)}/sign-context`,
|
|
345
373
|
{
|
|
346
|
-
headers:
|
|
374
|
+
headers: signContextHeaders(identity, clientIdentity),
|
|
347
375
|
signal: AbortSignal.timeout(timeoutMs)
|
|
348
376
|
}
|
|
349
377
|
);
|
|
@@ -377,7 +405,7 @@ async function fetchDirectSignContext(identity, paymentId, fetchImpl = fetch, ti
|
|
|
377
405
|
throw new HavenSignContextError(
|
|
378
406
|
`Haven refused the direct-payment signing-context fetch for ${paymentId}: ${detail}` + (response.status === 404 ? " \u2014 either the payment_id is not from this agent\u2019s own POST /payments call, or this Haven backend predates #3271. Pass payload_hash and typed_data_b64 from the haven_send / haven_pay result, unchanged, instead." : body.error_code === "expired" ? " The payment window has expired; call haven_send / haven_pay again with the same idempotency_key." : ""),
|
|
379
407
|
"SIGN_CONTEXT_REFUSED",
|
|
380
|
-
|
|
408
|
+
refusalOf(response.status, body),
|
|
381
409
|
paymentId,
|
|
382
410
|
"direct"
|
|
383
411
|
);
|
|
@@ -398,7 +426,8 @@ async function fetchDirectSignContext(identity, paymentId, fetchImpl = fetch, ti
|
|
|
398
426
|
payloadHash: signData.hash,
|
|
399
427
|
typedData: signData.typed_data,
|
|
400
428
|
status: typeof body.status === "string" ? body.status : "pending_signature",
|
|
401
|
-
expiresAt: typeof body.expires_at === "string" ? body.expires_at : void 0
|
|
429
|
+
expiresAt: typeof body.expires_at === "string" ? body.expires_at : void 0,
|
|
430
|
+
clientUpdate: readClientUpdate(body.client_update)
|
|
402
431
|
};
|
|
403
432
|
}
|
|
404
433
|
|
|
@@ -669,6 +698,9 @@ function parseFetchedExpected(fetched) {
|
|
|
669
698
|
}
|
|
670
699
|
return parsed.data;
|
|
671
700
|
}
|
|
701
|
+
function clientUpdateField(resolved) {
|
|
702
|
+
return resolved?.ctx.clientUpdate ? { client_update: resolved.ctx.clientUpdate } : {};
|
|
703
|
+
}
|
|
672
704
|
function resolveTypedData(args) {
|
|
673
705
|
if (!args.typed_data_b64) return args.typed_data;
|
|
674
706
|
try {
|
|
@@ -702,14 +734,26 @@ function createToolHandlers(signer, options = {}) {
|
|
|
702
734
|
);
|
|
703
735
|
}
|
|
704
736
|
try {
|
|
705
|
-
const ctx = await fetchX402SignContext(
|
|
737
|
+
const ctx = await fetchX402SignContext(
|
|
738
|
+
identity,
|
|
739
|
+
args.payment_id,
|
|
740
|
+
options.signContext?.fetchImpl,
|
|
741
|
+
void 0,
|
|
742
|
+
options.signContext?.clientIdentity
|
|
743
|
+
);
|
|
706
744
|
checkPayloadHashMatch(args.payload_hash, ctx.payloadHash, args.payment_id);
|
|
707
745
|
return { kind: "x402", ctx };
|
|
708
746
|
} catch (err) {
|
|
709
747
|
if (opts.allowDirectFallback && err instanceof HavenSignContextError && err.http_status === 409 && err.backend_error_code === "sign_context_unavailable") {
|
|
710
748
|
let ctx;
|
|
711
749
|
try {
|
|
712
|
-
ctx = await fetchDirectSignContext(
|
|
750
|
+
ctx = await fetchDirectSignContext(
|
|
751
|
+
identity,
|
|
752
|
+
args.payment_id,
|
|
753
|
+
options.signContext?.fetchImpl,
|
|
754
|
+
void 0,
|
|
755
|
+
options.signContext?.clientIdentity
|
|
756
|
+
);
|
|
713
757
|
} catch (directErr) {
|
|
714
758
|
if (directErr instanceof HavenSignContextError && directErr.http_status === 409 && directErr.backend_error_code === "sign_context_unavailable") {
|
|
715
759
|
throw err;
|
|
@@ -759,12 +803,12 @@ function createToolHandlers(signer, options = {}) {
|
|
|
759
803
|
assertBoundDirectPaymentUserOp(typedData, signer.delegateAddress);
|
|
760
804
|
const signature = await signer.signDelegationTypedData(typedData);
|
|
761
805
|
await auditSigning("haven_sign", hashTypedData(typedData));
|
|
762
|
-
return { signature };
|
|
806
|
+
return { signature, ...clientUpdateField(resolved) };
|
|
763
807
|
}
|
|
764
808
|
throw new HavenBareHashRefusedError();
|
|
765
809
|
}
|
|
766
810
|
await auditSigning("haven_sign", hashTypedData(typedData));
|
|
767
|
-
return { signature: result.signature, x402_binding: result.x402Binding };
|
|
811
|
+
return { signature: result.signature, x402_binding: result.x402Binding, ...clientUpdateField(resolved) };
|
|
768
812
|
}),
|
|
769
813
|
haven_x402_sign_header: async (input) => runTool(async () => {
|
|
770
814
|
const args = parse("haven_x402_sign_header", coercePaymentRequired(input));
|
|
@@ -810,7 +854,8 @@ function createToolHandlers(signer, options = {}) {
|
|
|
810
854
|
signature: funding.signature,
|
|
811
855
|
x402_binding: funding.x402Binding,
|
|
812
856
|
payment_header: header.paymentHeader,
|
|
813
|
-
accepted: header.accepted
|
|
857
|
+
accepted: header.accepted,
|
|
858
|
+
...clientUpdateField(resolved)
|
|
814
859
|
};
|
|
815
860
|
}),
|
|
816
861
|
haven_sign_sweep_delegate: async (input) => runTool(async () => {
|
|
@@ -949,6 +994,7 @@ function normalizeError(err) {
|
|
|
949
994
|
...err.retry_with_new_quote ? { retry_with_new_quote: true } : {},
|
|
950
995
|
...err.http_status !== void 0 ? { http_status: err.http_status } : {},
|
|
951
996
|
...err.backend_error_code !== void 0 ? { backend_error_code: err.backend_error_code } : {},
|
|
997
|
+
...err.client_update ? { client_update: err.client_update } : {},
|
|
952
998
|
// #3103: the typed step the error decided beside its action.
|
|
953
999
|
...err.next_tool ? { next_tool: err.next_tool } : {},
|
|
954
1000
|
...err.next_tool_server ? { next_tool_server: err.next_tool_server } : {},
|
|
@@ -1661,7 +1707,7 @@ async function warnIfCredentialFilePermissive(path, log = (message) => process.s
|
|
|
1661
1707
|
|
|
1662
1708
|
// src/server.ts
|
|
1663
1709
|
var SIGNER_NAME = "@haven_ai/signer";
|
|
1664
|
-
var SIGNER_VERSION = "0.0.0-dev.
|
|
1710
|
+
var SIGNER_VERSION = "0.0.0-dev.202609251409.98ed67a";
|
|
1665
1711
|
async function resolveSignerRuntime(options = {}) {
|
|
1666
1712
|
assertSupportedNodeVersion(options.nodeVersion);
|
|
1667
1713
|
if (options.delegateKey) {
|
|
@@ -1706,7 +1752,10 @@ function buildSignerMcpServer(signer, options = {}) {
|
|
|
1706
1752
|
// The signer CORE stays network-free; fetched bytes still pass the same
|
|
1707
1753
|
// binding verification + digest re-derivation as tool-argument bytes.
|
|
1708
1754
|
signContext: {
|
|
1709
|
-
loadIdentity: () => loadHavenIdentity(credentialsPath)
|
|
1755
|
+
loadIdentity: () => loadHavenIdentity(credentialsPath),
|
|
1756
|
+
// #3303: the backend refuses a signer below a minimum it has set, at
|
|
1757
|
+
// sign-context — the one place this signer meets it.
|
|
1758
|
+
clientIdentity: havenClientIdentity(SIGNER_NAME, SIGNER_VERSION)
|
|
1710
1759
|
}
|
|
1711
1760
|
});
|
|
1712
1761
|
const registerTool = server.tool.bind(server);
|