@haven_ai/signer 0.2.0-alpha.0 → 0.2.1-alpha.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 +41 -1
- package/dist/cli.cjs +85 -10
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +85 -10
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +85 -10
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +10 -2
- package/dist/index.d.ts +10 -2
- package/dist/index.js +85 -10
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.d.cts
CHANGED
|
@@ -420,9 +420,17 @@ interface ToolFailure {
|
|
|
420
420
|
/**
|
|
421
421
|
* #1309: precise recovery guidance as DATA, not just prose inside `message`
|
|
422
422
|
* — the same text the hosted quote's advisory `signer_compatibility.fallback`
|
|
423
|
-
* carries when the refusal is an out-of-date signer.
|
|
423
|
+
* carries when the refusal is an out-of-date signer. #3001 reuses this same
|
|
424
|
+
* field for every sign-context refusal (`'typed_data_b64'`).
|
|
424
425
|
*/
|
|
425
426
|
fallback?: string;
|
|
427
|
+
/**
|
|
428
|
+
* #3001: present on `SIGN_CONTEXT_REFUSED` — the HTTP status the backend
|
|
429
|
+
* answered the `/x402/:id/sign-context` fetch with (404, 410, …).
|
|
430
|
+
*/
|
|
431
|
+
http_status?: number;
|
|
432
|
+
/** #3001: the backend's own `error_code` on `SIGN_CONTEXT_REFUSED` (`expired`, `already_executed`, `not_signable`, `sign_context_unavailable`). */
|
|
433
|
+
backend_error_code?: string;
|
|
426
434
|
}
|
|
427
435
|
type ToolPayload<T = unknown> = ToolSuccess<T> | ToolFailure;
|
|
428
436
|
interface ToolHandlerOptions {
|
|
@@ -468,7 +476,7 @@ declare function renderSignerConsentBlock(input: SignerConsentInput, hash: strin
|
|
|
468
476
|
declare function ensureSignerConsent(input: SignerConsentInput, options?: SignerConsentOptions): Promise<SignerConsentDecision>;
|
|
469
477
|
|
|
470
478
|
declare const SIGNER_NAME = "@haven_ai/signer";
|
|
471
|
-
declare const SIGNER_VERSION = "0.2.
|
|
479
|
+
declare const SIGNER_VERSION = "0.2.1-alpha.0";
|
|
472
480
|
interface SignerOptions {
|
|
473
481
|
/** Path to a Haven credential JSON file (delegate_key is read from it). */
|
|
474
482
|
credentialsPath?: string;
|
package/dist/index.d.ts
CHANGED
|
@@ -420,9 +420,17 @@ interface ToolFailure {
|
|
|
420
420
|
/**
|
|
421
421
|
* #1309: precise recovery guidance as DATA, not just prose inside `message`
|
|
422
422
|
* — the same text the hosted quote's advisory `signer_compatibility.fallback`
|
|
423
|
-
* carries when the refusal is an out-of-date signer.
|
|
423
|
+
* carries when the refusal is an out-of-date signer. #3001 reuses this same
|
|
424
|
+
* field for every sign-context refusal (`'typed_data_b64'`).
|
|
424
425
|
*/
|
|
425
426
|
fallback?: string;
|
|
427
|
+
/**
|
|
428
|
+
* #3001: present on `SIGN_CONTEXT_REFUSED` — the HTTP status the backend
|
|
429
|
+
* answered the `/x402/:id/sign-context` fetch with (404, 410, …).
|
|
430
|
+
*/
|
|
431
|
+
http_status?: number;
|
|
432
|
+
/** #3001: the backend's own `error_code` on `SIGN_CONTEXT_REFUSED` (`expired`, `already_executed`, `not_signable`, `sign_context_unavailable`). */
|
|
433
|
+
backend_error_code?: string;
|
|
426
434
|
}
|
|
427
435
|
type ToolPayload<T = unknown> = ToolSuccess<T> | ToolFailure;
|
|
428
436
|
interface ToolHandlerOptions {
|
|
@@ -468,7 +476,7 @@ declare function renderSignerConsentBlock(input: SignerConsentInput, hash: strin
|
|
|
468
476
|
declare function ensureSignerConsent(input: SignerConsentInput, options?: SignerConsentOptions): Promise<SignerConsentDecision>;
|
|
469
477
|
|
|
470
478
|
declare const SIGNER_NAME = "@haven_ai/signer";
|
|
471
|
-
declare const SIGNER_VERSION = "0.2.
|
|
479
|
+
declare const SIGNER_VERSION = "0.2.1-alpha.0";
|
|
472
480
|
interface SignerOptions {
|
|
473
481
|
/** Path to a Haven credential JSON file (delegate_key is read from it). */
|
|
474
482
|
credentialsPath?: string;
|
package/dist/index.js
CHANGED
|
@@ -627,6 +627,49 @@ function stableStringify(value) {
|
|
|
627
627
|
const object = value;
|
|
628
628
|
return `{${Object.keys(object).sort().map((key) => `${JSON.stringify(key)}:${stableStringify(object[key])}`).join(",")}}`;
|
|
629
629
|
}
|
|
630
|
+
var HavenSignContextError = class extends HavenSigningError {
|
|
631
|
+
/**
|
|
632
|
+
* #3001 / #3010 review: the recovery path, when signing OTHER bytes is
|
|
633
|
+
* actually a remedy — a transport failure (timeout, unreachable) or a
|
|
634
|
+
* body this signer could not read. `typed_data_b64` is NOT in the default
|
|
635
|
+
* quote result since #1272: obtain it by re-running the SAME quote tool
|
|
636
|
+
* with the SAME idempotency_key plus `include_signing_payload: true`.
|
|
637
|
+
* Absent on a backend REFUSAL: an expired, executed or unsignable intent
|
|
638
|
+
* cannot be rescued by re-signing its bytes.
|
|
639
|
+
*/
|
|
640
|
+
fallback;
|
|
641
|
+
/**
|
|
642
|
+
* #3001: `AgentPaymentNextAction` values the signer already emits — the
|
|
643
|
+
* version-mismatch refusal's `stop_and_tell_user` for the classes where
|
|
644
|
+
* retrying the same call cannot help, and `payment_window_expired` (with
|
|
645
|
+
* `retry_with_new_quote`) for the backend's 410 `expired`, exactly as the
|
|
646
|
+
* plain `HavenError` branch already does for that code.
|
|
647
|
+
*/
|
|
648
|
+
next_action;
|
|
649
|
+
retry_with_new_quote;
|
|
650
|
+
/** Present only for `SIGN_CONTEXT_REFUSED` (the backend's HTTP status). */
|
|
651
|
+
http_status;
|
|
652
|
+
/** Present only for `SIGN_CONTEXT_REFUSED`: the backend's own `error_code`. */
|
|
653
|
+
backend_error_code;
|
|
654
|
+
constructor(message, code, refusal) {
|
|
655
|
+
super(message);
|
|
656
|
+
this.code = code;
|
|
657
|
+
this.name = "HavenSignContextError";
|
|
658
|
+
if (code === "SIGN_CONTEXT_REFUSED") {
|
|
659
|
+
this.http_status = refusal?.httpStatus;
|
|
660
|
+
this.backend_error_code = refusal?.errorCode;
|
|
661
|
+
if (refusal?.httpStatus === 410 || refusal?.errorCode === "expired") {
|
|
662
|
+
this.next_action = AgentPaymentNextAction.PaymentWindowExpired;
|
|
663
|
+
this.retry_with_new_quote = true;
|
|
664
|
+
} else {
|
|
665
|
+
this.next_action = AgentPaymentNextAction.StopAndTellUser;
|
|
666
|
+
}
|
|
667
|
+
} else {
|
|
668
|
+
this.fallback = "typed_data_b64";
|
|
669
|
+
this.next_action = AgentPaymentNextAction.StopAndTellUser;
|
|
670
|
+
}
|
|
671
|
+
}
|
|
672
|
+
};
|
|
630
673
|
async function loadHavenIdentity(credentialsPath) {
|
|
631
674
|
if (!credentialsPath) return null;
|
|
632
675
|
try {
|
|
@@ -641,30 +684,50 @@ async function loadHavenIdentity(credentialsPath) {
|
|
|
641
684
|
return null;
|
|
642
685
|
}
|
|
643
686
|
}
|
|
644
|
-
|
|
687
|
+
var SIGN_CONTEXT_TIMEOUT_MS = 15e3;
|
|
688
|
+
async function fetchX402SignContext(identity, paymentId, fetchImpl = fetch, timeoutMs = SIGN_CONTEXT_TIMEOUT_MS) {
|
|
645
689
|
let response;
|
|
646
690
|
try {
|
|
647
691
|
response = await fetchImpl(
|
|
648
692
|
`${identity.apiUrl}/x402/${encodeURIComponent(paymentId)}/sign-context`,
|
|
649
|
-
{
|
|
693
|
+
{
|
|
694
|
+
headers: { Authorization: `Bearer ${identity.apiKey}` },
|
|
695
|
+
signal: AbortSignal.timeout(timeoutMs)
|
|
696
|
+
}
|
|
650
697
|
);
|
|
651
698
|
} catch (err) {
|
|
652
|
-
|
|
653
|
-
|
|
699
|
+
const timedOut = err instanceof Error && (err.name === "TimeoutError" || err.name === "AbortError");
|
|
700
|
+
throw new HavenSignContextError(
|
|
701
|
+
timedOut ? `Haven did not answer the signing-context fetch for ${paymentId} within ${timeoutMs} ms. Retry, or pass typed_data_b64 from the quote result instead.` : `Could not reach Haven to fetch the signing context for ${paymentId}: ${err instanceof Error ? err.message : String(err)}. Retry, or pass typed_data_b64 from the quote result instead.`,
|
|
702
|
+
timedOut ? "SIGN_CONTEXT_TIMEOUT" : "SIGN_CONTEXT_UNREACHABLE"
|
|
654
703
|
);
|
|
655
704
|
}
|
|
656
|
-
|
|
705
|
+
let body;
|
|
706
|
+
try {
|
|
707
|
+
body = await response.json();
|
|
708
|
+
} catch (err) {
|
|
709
|
+
if (err instanceof Error && (err.name === "TimeoutError" || err.name === "AbortError")) {
|
|
710
|
+
throw new HavenSignContextError(
|
|
711
|
+
`Haven did not finish sending the signing context for ${paymentId} within ${timeoutMs} ms. Retry, or pass typed_data_b64 from the quote result instead.`,
|
|
712
|
+
"SIGN_CONTEXT_TIMEOUT"
|
|
713
|
+
);
|
|
714
|
+
}
|
|
715
|
+
body = {};
|
|
716
|
+
}
|
|
657
717
|
if (!response.ok) {
|
|
658
718
|
const detail = typeof body.error === "string" ? body.error : `HTTP ${response.status}`;
|
|
659
|
-
throw new
|
|
660
|
-
`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." : "")
|
|
719
|
+
throw new HavenSignContextError(
|
|
720
|
+
`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." : ""),
|
|
721
|
+
"SIGN_CONTEXT_REFUSED",
|
|
722
|
+
{ httpStatus: response.status, errorCode: typeof body.error_code === "string" ? body.error_code : void 0 }
|
|
661
723
|
);
|
|
662
724
|
}
|
|
663
725
|
const signData = body.sign_data;
|
|
664
726
|
const x402Expected = body.x402_expected;
|
|
665
727
|
if (!signData || typeof signData.hash !== "string" || !signData.typed_data || typeof signData.typed_data !== "object" || !x402Expected) {
|
|
666
|
-
throw new
|
|
667
|
-
"The Haven sign-context response is missing sign_data.typed_data or x402_expected \u2014 the backend may predate #1263. Pass typed_data_b64 from the quote result instead."
|
|
728
|
+
throw new HavenSignContextError(
|
|
729
|
+
"The Haven sign-context response is missing sign_data.typed_data or x402_expected \u2014 the backend may predate #1263. Pass typed_data_b64 from the quote result instead.",
|
|
730
|
+
"SIGN_CONTEXT_MALFORMED"
|
|
668
731
|
);
|
|
669
732
|
}
|
|
670
733
|
const paymentRequired = body.payment_required;
|
|
@@ -1095,6 +1158,18 @@ function normalizeError(err) {
|
|
|
1095
1158
|
next_action: AgentPaymentNextAction.StopAndTellUser
|
|
1096
1159
|
};
|
|
1097
1160
|
}
|
|
1161
|
+
if (err instanceof HavenSignContextError) {
|
|
1162
|
+
return {
|
|
1163
|
+
success: false,
|
|
1164
|
+
code: err.code,
|
|
1165
|
+
message: err.message,
|
|
1166
|
+
next_action: err.next_action,
|
|
1167
|
+
...err.fallback !== void 0 ? { fallback: err.fallback } : {},
|
|
1168
|
+
...err.retry_with_new_quote ? { retry_with_new_quote: true } : {},
|
|
1169
|
+
...err.http_status !== void 0 ? { http_status: err.http_status } : {},
|
|
1170
|
+
...err.backend_error_code !== void 0 ? { backend_error_code: err.backend_error_code } : {}
|
|
1171
|
+
};
|
|
1172
|
+
}
|
|
1098
1173
|
if (err instanceof HavenSigningError) {
|
|
1099
1174
|
return { success: false, code: err.code, message: err.message };
|
|
1100
1175
|
}
|
|
@@ -1241,7 +1316,7 @@ async function writeAckFile(path, hash) {
|
|
|
1241
1316
|
);
|
|
1242
1317
|
}
|
|
1243
1318
|
var SIGNER_NAME = "@haven_ai/signer";
|
|
1244
|
-
var SIGNER_VERSION = "0.2.
|
|
1319
|
+
var SIGNER_VERSION = "0.2.1-alpha.0";
|
|
1245
1320
|
async function resolveEdgeSigner(options = {}) {
|
|
1246
1321
|
const { signer } = await resolveSignerRuntime(options);
|
|
1247
1322
|
return signer;
|