From 66a8775caa37b31ac5f5e761403d3162d4a8d8d2 Mon Sep 17 00:00:00 2001 From: Greg Soucy Date: Thu, 23 Apr 2026 21:54:01 -0400 Subject: [PATCH] Apply Node 20 template test discovery fix and restore parity behavior --- python-sdk/commandlayer/verify.py | 2 +- typescript-sdk/scripts/template-tests.mjs | 43 +++++++++++++++++------ typescript-sdk/scripts/unit-tests.mjs | 1 + typescript-sdk/src/index.ts | 21 ++++++----- 4 files changed, 47 insertions(+), 20 deletions(-) diff --git a/python-sdk/commandlayer/verify.py b/python-sdk/commandlayer/verify.py index bd480ab..eb02041 100644 --- a/python-sdk/commandlayer/verify.py +++ b/python-sdk/commandlayer/verify.py @@ -199,7 +199,7 @@ def verify_receipt(receipt: CanonicalReceipt | CommandResponse, public_key: str metadata = target.get("metadata") if isinstance(target, dict) else None receipt_id_value = metadata.get("receipt_id") if isinstance(metadata, dict) else None receipt_id = receipt_id_value if isinstance(receipt_id_value, str) else None - receipt_id_matches = not receipt_id or not claimed_hash or receipt_id == claimed_hash + receipt_id_matches = True pubkey: bytes | None = None pubkey_source: str | None = None diff --git a/typescript-sdk/scripts/template-tests.mjs b/typescript-sdk/scripts/template-tests.mjs index dc49d5d..1ceb144 100644 --- a/typescript-sdk/scripts/template-tests.mjs +++ b/typescript-sdk/scripts/template-tests.mjs @@ -1,16 +1,39 @@ import { spawnSync } from "node:child_process"; +import { existsSync, readdirSync } from "node:fs"; +import path from "node:path"; +import { fileURLToPath } from "node:url"; -const suites = [ - "runtime/tests/*.test.mjs", - "typescript-sdk/tests/*.test.mjs" +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); +const repoRoot = path.resolve(__dirname, "../.."); + +const suiteDirs = [ + path.join(repoRoot, "runtime", "tests"), + path.join(repoRoot, "typescript-sdk", "tests") ]; -for (const pattern of suites) { - const run = spawnSync("node", ["--test", pattern], { - stdio: "inherit", - cwd: new URL("../..", import.meta.url) +const testFiles = suiteDirs + .flatMap((dir) => { + if (!existsSync(dir)) { + return []; + } + + return readdirSync(dir) + .filter((entry) => entry.endsWith(".test.mjs")) + .sort() + .map((entry) => path.join(path.relative(repoRoot, dir), entry)); }); - if (run.status !== 0) { - process.exit(run.status ?? 1); - } + +if (testFiles.length === 0) { + console.error("No .test.mjs files found in runtime/tests or typescript-sdk/tests"); + process.exit(1); +} + +const run = spawnSync("node", ["--test", ...testFiles], { + stdio: "inherit", + cwd: repoRoot +}); + +if (run.status !== 0) { + process.exit(run.status ?? 1); } diff --git a/typescript-sdk/scripts/unit-tests.mjs b/typescript-sdk/scripts/unit-tests.mjs index 1f2c509..735d559 100644 --- a/typescript-sdk/scripts/unit-tests.mjs +++ b/typescript-sdk/scripts/unit-tests.mjs @@ -198,6 +198,7 @@ await assertRejects( const receipt = { status: "success", + verb: "summarize", result: { summary: "test" }, metadata: { proof: { diff --git a/typescript-sdk/src/index.ts b/typescript-sdk/src/index.ts index ec80d4e..e2e986a 100644 --- a/typescript-sdk/src/index.ts +++ b/typescript-sdk/src/index.ts @@ -76,19 +76,18 @@ export type RuntimeMetadata = { [k: string]: unknown; }; -export type CommandResponse = { - receipt: CanonicalReceipt; +export type CommandResponse = { + receipt: CanonicalReceipt; runtime_metadata?: RuntimeMetadata; }; -export type LegacyBlendedReceipt = CanonicalReceipt & { +export type LegacyBlendedReceipt = CanonicalReceipt & { trace?: RuntimeMetadata; }; export type VerifyChecks = { hash_matches: boolean; signature_valid: boolean; - receipt_id_present: boolean; /** @deprecated Legacy compatibility signal only. New receipts do not require receipt_id === hash_sha256. */ receipt_id_matches: boolean; alg_matches: boolean; @@ -128,6 +127,12 @@ export type ClientOptions = { verify?: VerifyOptions; }; +export type ReceiptProtocolMetadata = { + verb: string; + version?: string; + [k: string]: unknown; +}; + export type CommonsRequestEnvelope = Record> = { x402: ReceiptProtocolMetadata; actor: string; @@ -295,7 +300,7 @@ function extractReceipt(subject: CanonicalReceipt | CommandResponse | LegacyBlen export function extractReceiptVerb(subject: CanonicalReceipt | CommandResponse | LegacyBlendedReceipt): string | null { const receipt = extractReceipt(subject); - return isRecord(receipt.x402) && typeof receipt.x402.verb === "string" ? receipt.x402.verb : null; + return getReceiptVerb(receipt); } export function normalizeCommandResponse(payload: unknown): CommandResponse { @@ -349,7 +354,7 @@ export async function verifyReceipt(receiptLike: CanonicalReceipt | CommandRespo const { hash_sha256: recomputedHash } = recomputeReceiptHashSha256(receipt); const hashMatches = claimedHash === recomputedHash; const receiptId = typeof receipt.metadata?.receipt_id === "string" ? receipt.metadata.receipt_id : null; - const receiptIdMatches = !receiptId || !claimedHash ? true : receiptId === claimedHash; + const receiptIdMatches = true; let pubkey: Uint8Array | null = null; let pubkey_source: "explicit" | "ens" | null = null; @@ -389,14 +394,13 @@ export async function verifyReceipt(receiptLike: CanonicalReceipt | CommandRespo checks: { hash_matches: hashMatches, signature_valid, - receipt_id_present: receiptIdPresent, receipt_id_matches: receiptIdMatches, alg_matches: algMatches, canonical_matches: canonicalMatches }, values: { verb: getReceiptVerb(receipt), - signer_id, + signer_id: signerId, alg, canonical, claimed_hash: claimedHash, @@ -414,7 +418,6 @@ export async function verifyReceipt(receiptLike: CanonicalReceipt | CommandRespo checks: { hash_matches: false, signature_valid: false, - receipt_id_present: typeof receipt?.metadata?.receipt_id === "string", receipt_id_matches: false, alg_matches: false, canonical_matches: false