blun-king-cli 9.1.599 → 9.1.600
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 +7 -0
- package/README.md +13 -3
- package/agent-spine-plugin/CHANGELOG.md +1 -1
- package/agent-spine-plugin/docs/host-integration.md +1 -15
- package/agent-spine-plugin/docs/preflight-recall.md +1 -1
- package/agent-spine-plugin/scripts/check-install.js +1 -6
- package/agent-spine-plugin/scripts/hermetic-process.js +8 -0
- package/agent-spine-plugin/scripts/release-check.js +2 -3
- package/agent-spine-plugin/scripts/run-tests-hermetic.js +8 -11
- package/agent-spine-plugin/src/hook.js +40 -40
- package/agent-spine-plugin/src/lib/briefing.js +6 -3
- package/agent-spine-plugin/src/lib/hook-context.js +63 -64
- package/agent-spine-plugin/src/lib/hook-output.js +115 -94
- package/agent-spine-plugin/src/lib/hook-timeline.js +20 -11
- package/agent-spine-plugin/src/lib/host-instruction-budget.js +8 -14
- package/agent-spine-plugin/src/lib/mcp-timeline-tools.js +0 -7
- package/agent-spine-plugin/src/lib/pre-answer-timeline-recall.js +104 -0
- package/agent-spine-plugin/src/lib/session-timeline-codex.js +0 -4
- package/agent-spine-plugin/src/lib/session-timeline-enrollment-source.js +0 -3
- package/agent-spine-plugin/src/lib/session-timeline-enrollment-storage.js +0 -4
- package/agent-spine-plugin/src/lib/session-timeline-enrollment-transport.js +0 -2
- package/agent-spine-plugin/src/lib/session-timeline-enrollment.js +0 -22
- package/agent-spine-plugin/src/lib/session-timeline-host-origin.js +0 -4
- package/agent-spine-plugin/src/lib/session-timeline-prior.js +42 -50
- package/agent-spine-plugin/src/lib/session-timeline-provider.js +0 -3
- package/agent-spine-plugin/src/lib/session-timeline-query.js +0 -1
- package/agent-spine-plugin/src/lib/session-timeline-sid-acl.js +0 -4
- package/agent-spine-plugin/src/lib/session-timeline-source.js +0 -3
- package/agent-spine-plugin/src/lib/session-timeline-transport.js +0 -8
- package/agent-spine-plugin/src/lib/session-timeline-windows-acl.js +0 -3
- package/agent-spine-plugin/src/lib/session-timeline.js +370 -408
- package/agent-spine-plugin/src/lib/source-roots.js +26 -16
- package/agent-spine-plugin/src/lib/timeline-user-feedback.js +0 -1
- package/bin/core-bootstrap.js +18 -10
- package/bin/launcher-mode.js +5 -0
- package/bin/launcher-runtime.js +27 -0
- package/bin/verify-bundled-agent-sources.cjs +28 -2
- package/blun.mjs +127 -29
- package/bundled-agent-sources.json +192 -155
- package/package.json +1 -1
- package/standard-skills/translate-native/README.md +1334 -1
- package/standard-skills/translate-native/SKILL.md +1 -1
- package/standard-skills/translate-native/VERSION +1 -1
- package/standard-skills/translate-native/docs/PREMORTEM.md +901 -0
- package/standard-skills/translate-native/docs/WEBSITE_LOCALIZATION.md +208 -41
- package/standard-skills/translate-native/docs/WEBSITE_LOCALIZATION_API.md +1319 -77
- package/standard-skills/translate-native/docs/WEBSITE_LOCALIZATION_EVIDENCE_HTTP.md +66 -11
- package/standard-skills/translate-native/docs/WEBSITE_LOCALIZATION_HTTP_PROVIDER.md +19 -1
- package/standard-skills/translate-native/docs/WEBSITE_LOCALIZATION_RECEIPT_VERIFIER_HTTP.md +24 -6
- package/standard-skills/translate-native/integrations/commercial_localization_profile.py +23 -0
- package/standard-skills/translate-native/integrations/website_localization.py +234 -10
- package/standard-skills/translate-native/integrations/website_localization_benchmark.py +39 -3
- package/standard-skills/translate-native/integrations/website_localization_cms.py +512 -10
- package/standard-skills/translate-native/integrations/website_localization_cms_client.py +116 -0
- package/standard-skills/translate-native/integrations/website_localization_cms_receiver.py +101 -11
- package/standard-skills/translate-native/integrations/website_localization_cms_receiver_runtime.py +12 -1
- package/standard-skills/translate-native/integrations/website_localization_cms_receiver_store.py +186 -24
- package/standard-skills/translate-native/integrations/website_localization_cms_source_client.py +64 -1
- package/standard-skills/translate-native/integrations/website_localization_cms_source_delivery.py +528 -5
- package/standard-skills/translate-native/integrations/website_localization_cms_source_delivery_auth.py +918 -0
- package/standard-skills/translate-native/integrations/website_localization_cms_source_delivery_auth_runtime.py +609 -0
- package/standard-skills/translate-native/integrations/website_localization_cms_source_delivery_client.py +885 -0
- package/standard-skills/translate-native/integrations/website_localization_cms_source_delivery_http.py +1244 -0
- package/standard-skills/translate-native/integrations/website_localization_cms_source_delivery_runtime.py +220 -0
- package/standard-skills/translate-native/integrations/website_localization_cms_source_delivery_sidecar_adapter.py +331 -0
- package/standard-skills/translate-native/integrations/website_localization_cms_source_delivery_submission_capabilities_http.py +444 -0
- package/standard-skills/translate-native/integrations/website_localization_cms_source_delivery_submission_client.py +673 -0
- package/standard-skills/translate-native/integrations/website_localization_cms_source_delivery_submission_dispatch.py +1317 -0
- package/standard-skills/translate-native/integrations/website_localization_cms_source_delivery_submission_dispatch_client.py +826 -0
- package/standard-skills/translate-native/integrations/website_localization_cms_source_delivery_submission_dispatch_http.py +1152 -0
- package/standard-skills/translate-native/integrations/website_localization_cms_source_delivery_submission_dispatch_openapi.py +896 -0
- package/standard-skills/translate-native/integrations/website_localization_cms_source_delivery_submission_dispatch_runtime.py +621 -0
- package/standard-skills/translate-native/integrations/website_localization_cms_source_delivery_submission_http.py +1180 -0
- package/standard-skills/translate-native/integrations/website_localization_cms_source_delivery_submission_runtime.py +1479 -0
- package/standard-skills/translate-native/integrations/website_localization_cms_source_http.py +133 -17
- package/standard-skills/translate-native/integrations/website_localization_cms_source_runtime.py +319 -4
- package/standard-skills/translate-native/integrations/website_localization_cms_source_service.py +72 -2
- package/standard-skills/translate-native/integrations/website_localization_cms_terminal_notification_http.py +13 -0
- package/standard-skills/translate-native/integrations/website_localization_cms_terminal_notification_receiver.py +56 -13
- package/standard-skills/translate-native/integrations/website_localization_cms_terminal_notification_receiver_openapi.py +335 -0
- package/standard-skills/translate-native/integrations/website_localization_cms_terminal_notification_receiver_runtime.py +66 -1
- package/standard-skills/translate-native/integrations/website_localization_cms_terminal_processing_monitor.py +73 -7
- package/standard-skills/translate-native/integrations/website_localization_cms_terminal_receiver_client.py +93 -9
- package/standard-skills/translate-native/integrations/website_localization_evidence_http.py +221 -11
- package/standard-skills/translate-native/integrations/website_localization_health.py +6 -0
- package/standard-skills/translate-native/integrations/website_localization_http_provider.py +1 -1
- package/standard-skills/translate-native/integrations/website_localization_quality_profiles.py +350 -0
- package/standard-skills/translate-native/integrations/website_localization_queue.py +126 -56
- package/standard-skills/translate-native/integrations/website_localization_receipt_verifier_http.py +166 -13
- package/standard-skills/translate-native/integrations/website_localization_release.py +423 -17
- package/standard-skills/translate-native/integrations/website_localization_release_coordinator.py +168 -13
- package/standard-skills/translate-native/integrations/website_localization_runner.py +10 -0
- package/standard-skills/translate-native/integrations/website_localization_worker.py +213 -9
- package/standard-skills/translate-native/provenance.json +2 -2
- package/standard-skills/translate-native/references/commercial-localization.md +96 -17
- package/standard-skills/translate-native/scripts/blun_language_guard.py +3 -1
- package/standard-skills/translate-native/scripts/check_commercial_review.py +69 -9
- package/standard-skills/translate-native/scripts/commercial_localization_profile.py +829 -31
- package/standard-tools/language-guard/VERSION +1 -1
- package/standard-tools/language-guard/blun_language_guard.py +3 -1
- package/standard-tools/language-guard/check_commercial_review.py +69 -9
- package/standard-tools/language-guard/commercial_localization_profile.py +829 -31
- package/standard-tools/language-guard/provenance.json +2 -2
- package/standard-tools/manifest.json +1 -1
- package/worker-host.mjs +115 -22
- package/agent-spine-plugin/scripts/check-install-king.js +0 -37
package/standard-skills/translate-native/integrations/website_localization_receipt_verifier_http.py
CHANGED
|
@@ -4,20 +4,23 @@
|
|
|
4
4
|
from __future__ import annotations
|
|
5
5
|
|
|
6
6
|
import hashlib
|
|
7
|
+
import importlib.util
|
|
7
8
|
import json
|
|
8
9
|
import re
|
|
9
10
|
import socket
|
|
11
|
+
import sys
|
|
10
12
|
import unicodedata
|
|
11
13
|
import urllib.error
|
|
12
14
|
import urllib.parse
|
|
13
15
|
import urllib.request
|
|
14
16
|
from dataclasses import dataclass
|
|
17
|
+
from pathlib import Path
|
|
15
18
|
from typing import Any, Callable, Mapping, Protocol
|
|
16
19
|
|
|
17
20
|
|
|
18
21
|
REQUEST_SCHEMA = "blun.localization-receipt-verification-http-request.v1"
|
|
19
22
|
RESPONSE_SCHEMA = "blun.localization-receipt-verification-http-response.v1"
|
|
20
|
-
RECEIPT_BINDING_SCHEMA = "blun.localization-quality-receipt-binding.
|
|
23
|
+
RECEIPT_BINDING_SCHEMA = "blun.localization-quality-receipt-binding.v9"
|
|
21
24
|
MAX_ENDPOINT_LENGTH = 2048
|
|
22
25
|
MAX_HEADER_VALUE_LENGTH = 4096
|
|
23
26
|
MAX_TEXT_BYTES = 2_000_000
|
|
@@ -28,19 +31,23 @@ HEADER_NAME = re.compile(r"^[!#$%&'*+.^_`|~0-9A-Za-z-]{1,128}$")
|
|
|
28
31
|
SHA256 = re.compile(r"^[0-9a-f]{64}$")
|
|
29
32
|
TOKEN = re.compile(r"^[A-Za-z0-9_.:-]{1,256}$")
|
|
30
33
|
ERROR_CODE = re.compile(r"^[a-z][a-z0-9_.-]{0,127}$")
|
|
31
|
-
|
|
34
|
+
EVIDENCE_REQUEST_ID = re.compile(r"^blun-l10n-evidence-[0-9a-f]{64}$")
|
|
35
|
+
COMMERCIAL_REVIEW_SUMMARY_SCHEMA = "translate-native.commercial-review-summary.v6"
|
|
32
36
|
COMMERCIAL_DIMENSIONS = (
|
|
33
37
|
"amount_currency", "discount_basis", "qualifiers", "tax_status",
|
|
34
38
|
"billing_interval", "commitment", "renewal", "cancellation",
|
|
35
39
|
"conditions", "offer_assignment",
|
|
36
40
|
)
|
|
37
41
|
BINDING_FIELDS = {
|
|
38
|
-
"schema", "review_kind", "
|
|
42
|
+
"schema", "review_kind", "evidence_request_id", "evidence_revision",
|
|
43
|
+
"job_id", "result_sha256", "source_text",
|
|
39
44
|
"target_text", "source_sha256", "target_sha256", "source_locale",
|
|
40
45
|
"target_locale", "content_type", "glossary_version", "policy_version",
|
|
41
46
|
"primary_provider", "review_provider", "software_version",
|
|
42
47
|
"review_confidence", "quality_profile", "commercial_profile",
|
|
43
|
-
"commercial_review",
|
|
48
|
+
"commercial_review", "commercial_review_routing",
|
|
49
|
+
"commercial_review_routing_contract_sha256",
|
|
50
|
+
"commercial_review_resolution_contract_sha256",
|
|
44
51
|
"human_review_required", "independent_review_required",
|
|
45
52
|
}
|
|
46
53
|
RESERVED_HEADERS = {
|
|
@@ -51,6 +58,40 @@ RESERVED_HEADERS = {
|
|
|
51
58
|
}
|
|
52
59
|
|
|
53
60
|
|
|
61
|
+
def _commercial_offer_scope(
|
|
62
|
+
value: Any,
|
|
63
|
+
dimensions: list[str],
|
|
64
|
+
offer_count: Any,
|
|
65
|
+
) -> bool:
|
|
66
|
+
if type(offer_count) is not int or not 0 <= offer_count <= 1000:
|
|
67
|
+
return False
|
|
68
|
+
if not isinstance(value, list) or len(value) > len(COMMERCIAL_DIMENSIONS):
|
|
69
|
+
return False
|
|
70
|
+
previous = -1
|
|
71
|
+
seen: set[str] = set()
|
|
72
|
+
for item in value:
|
|
73
|
+
if (
|
|
74
|
+
not isinstance(item, dict)
|
|
75
|
+
or set(item) != {"dimension", "offer_indexes"}
|
|
76
|
+
or item.get("dimension") not in dimensions
|
|
77
|
+
or item["dimension"] in seen
|
|
78
|
+
or not isinstance(item.get("offer_indexes"), list)
|
|
79
|
+
or not item["offer_indexes"]
|
|
80
|
+
or any(
|
|
81
|
+
type(index) is not int or not 0 <= index < offer_count
|
|
82
|
+
for index in item["offer_indexes"]
|
|
83
|
+
)
|
|
84
|
+
or item["offer_indexes"] != sorted(set(item["offer_indexes"]))
|
|
85
|
+
):
|
|
86
|
+
return False
|
|
87
|
+
position = COMMERCIAL_DIMENSIONS.index(item["dimension"])
|
|
88
|
+
if position <= previous:
|
|
89
|
+
return False
|
|
90
|
+
previous = position
|
|
91
|
+
seen.add(item["dimension"])
|
|
92
|
+
return True
|
|
93
|
+
|
|
94
|
+
|
|
54
95
|
class HTTPReceiptVerifierFailed(RuntimeError):
|
|
55
96
|
"""Content-free verifier failure understood by release coordination."""
|
|
56
97
|
|
|
@@ -66,6 +107,23 @@ class HTTPReceiptVerifierFailed(RuntimeError):
|
|
|
66
107
|
self.retryable = retryable
|
|
67
108
|
|
|
68
109
|
|
|
110
|
+
def _load_module(name: str, path: Path):
|
|
111
|
+
spec = importlib.util.spec_from_file_location(name, path)
|
|
112
|
+
if spec is None or spec.loader is None:
|
|
113
|
+
raise RuntimeError(f"cannot load receipt dependency: {path.name}")
|
|
114
|
+
module = importlib.util.module_from_spec(spec)
|
|
115
|
+
sys.modules[spec.name] = module
|
|
116
|
+
spec.loader.exec_module(module)
|
|
117
|
+
return module
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
_ROOT = Path(__file__).resolve().parents[1]
|
|
121
|
+
_COMMERCIAL = _load_module(
|
|
122
|
+
"blun_website_localization_receipt_http_commercial",
|
|
123
|
+
_ROOT / "integrations" / "commercial_localization_profile.py",
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
|
|
69
127
|
@dataclass(frozen=True)
|
|
70
128
|
class HTTPResult:
|
|
71
129
|
status: int
|
|
@@ -253,6 +311,40 @@ def _provider(value: Any) -> bool:
|
|
|
253
311
|
)
|
|
254
312
|
|
|
255
313
|
|
|
314
|
+
def _quality_profile(
|
|
315
|
+
value: Any,
|
|
316
|
+
*,
|
|
317
|
+
target_locale: Any,
|
|
318
|
+
content_type: Any,
|
|
319
|
+
commercial_profile: Any,
|
|
320
|
+
) -> bool:
|
|
321
|
+
commercial = commercial_profile is not None
|
|
322
|
+
expected_fields = {"locale", "version", "sha256"}
|
|
323
|
+
if commercial:
|
|
324
|
+
expected_fields.add("commercial")
|
|
325
|
+
if (
|
|
326
|
+
not isinstance(value, dict)
|
|
327
|
+
or set(value) != expected_fields
|
|
328
|
+
or value.get("locale") != target_locale
|
|
329
|
+
or not _token(value.get("version"))
|
|
330
|
+
or not isinstance(value.get("sha256"), str)
|
|
331
|
+
or SHA256.fullmatch(value["sha256"]) is None
|
|
332
|
+
or (content_type == "commercial") != commercial
|
|
333
|
+
):
|
|
334
|
+
return False
|
|
335
|
+
if not commercial:
|
|
336
|
+
return True
|
|
337
|
+
nested = value.get("commercial")
|
|
338
|
+
return (
|
|
339
|
+
isinstance(nested, dict)
|
|
340
|
+
and set(nested) == {"profile", "version", "sha256"}
|
|
341
|
+
and nested.get("profile") == commercial_profile
|
|
342
|
+
and _token(nested.get("version"))
|
|
343
|
+
and isinstance(nested.get("sha256"), str)
|
|
344
|
+
and SHA256.fullmatch(nested["sha256"]) is not None
|
|
345
|
+
)
|
|
346
|
+
|
|
347
|
+
|
|
256
348
|
def _binding(value: Any) -> tuple[dict[str, Any], bytes]:
|
|
257
349
|
if (
|
|
258
350
|
not isinstance(value, dict)
|
|
@@ -267,11 +359,18 @@ def _binding(value: Any) -> tuple[dict[str, Any], bytes]:
|
|
|
267
359
|
value, code="binding_invalid", maximum=MAX_REQUEST_BYTES,
|
|
268
360
|
))
|
|
269
361
|
for field in (
|
|
270
|
-
"job_id", "source_locale", "target_locale", "content_type",
|
|
362
|
+
"evidence_revision", "job_id", "source_locale", "target_locale", "content_type",
|
|
271
363
|
"glossary_version", "policy_version", "software_version",
|
|
272
364
|
):
|
|
273
365
|
if not _token(binding[field]):
|
|
274
366
|
_fail("binding_invalid")
|
|
367
|
+
if (
|
|
368
|
+
not isinstance(binding["evidence_request_id"], str)
|
|
369
|
+
or EVIDENCE_REQUEST_ID.fullmatch(
|
|
370
|
+
binding["evidence_request_id"]
|
|
371
|
+
) is None
|
|
372
|
+
):
|
|
373
|
+
_fail("binding_invalid")
|
|
275
374
|
for field in ("result_sha256", "source_sha256", "target_sha256"):
|
|
276
375
|
if not isinstance(binding[field], str) or SHA256.fullmatch(binding[field]) is None:
|
|
277
376
|
_fail("binding_invalid")
|
|
@@ -299,16 +398,17 @@ def _binding(value: Any) -> tuple[dict[str, Any], bytes]:
|
|
|
299
398
|
confidence = binding["review_confidence"]
|
|
300
399
|
profile = binding["quality_profile"]
|
|
301
400
|
commercial_review = binding["commercial_review"]
|
|
401
|
+
commercial_review_routing = binding["commercial_review_routing"]
|
|
302
402
|
if (
|
|
303
403
|
not isinstance(confidence, dict)
|
|
304
404
|
or set(confidence) != {"target_native", "source_fidelity"}
|
|
305
405
|
or any(result not in {"high", "low"} for result in confidence.values())
|
|
306
|
-
or not
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
406
|
+
or not _quality_profile(
|
|
407
|
+
profile,
|
|
408
|
+
target_locale=binding["target_locale"],
|
|
409
|
+
content_type=binding["content_type"],
|
|
410
|
+
commercial_profile=binding["commercial_profile"],
|
|
411
|
+
)
|
|
312
412
|
or (
|
|
313
413
|
binding["commercial_profile"] is not None
|
|
314
414
|
and not _token(binding["commercial_profile"])
|
|
@@ -320,10 +420,16 @@ def _binding(value: Any) -> tuple[dict[str, Any], bytes]:
|
|
|
320
420
|
not isinstance(commercial_review, dict)
|
|
321
421
|
or set(commercial_review) != {
|
|
322
422
|
"schema", "profile", "status",
|
|
323
|
-
"review_required_dimensions",
|
|
423
|
+
"review_required_dimensions",
|
|
424
|
+
"offer_count", "review_required_offers",
|
|
425
|
+
"review_evidence_contract_sha256", "evidence_sha256",
|
|
324
426
|
}
|
|
325
427
|
or commercial_review["schema"] != COMMERCIAL_REVIEW_SUMMARY_SCHEMA
|
|
326
428
|
or commercial_review["profile"] != binding["commercial_profile"]
|
|
429
|
+
or commercial_review["review_evidence_contract_sha256"]
|
|
430
|
+
!= _COMMERCIAL.public_review_evidence_contract(
|
|
431
|
+
binding["commercial_profile"],
|
|
432
|
+
)["sha256"]
|
|
327
433
|
or commercial_review["status"] not in {
|
|
328
434
|
"verified", "review_required",
|
|
329
435
|
}
|
|
@@ -336,9 +442,17 @@ def _binding(value: Any) -> tuple[dict[str, Any], bytes]:
|
|
|
336
442
|
]
|
|
337
443
|
or len(commercial_review["review_required_dimensions"])
|
|
338
444
|
!= len(set(commercial_review["review_required_dimensions"]))
|
|
445
|
+
or not _commercial_offer_scope(
|
|
446
|
+
commercial_review["review_required_offers"],
|
|
447
|
+
commercial_review["review_required_dimensions"],
|
|
448
|
+
commercial_review["offer_count"],
|
|
449
|
+
)
|
|
339
450
|
or (
|
|
340
451
|
commercial_review["status"] == "verified"
|
|
341
|
-
and
|
|
452
|
+
and (
|
|
453
|
+
commercial_review["review_required_dimensions"]
|
|
454
|
+
or commercial_review["review_required_offers"]
|
|
455
|
+
)
|
|
342
456
|
)
|
|
343
457
|
or (
|
|
344
458
|
commercial_review["status"] == "review_required"
|
|
@@ -355,6 +469,45 @@ def _binding(value: Any) -> tuple[dict[str, Any], bytes]:
|
|
|
355
469
|
or type(binding["independent_review_required"]) is not bool
|
|
356
470
|
):
|
|
357
471
|
_fail("binding_invalid")
|
|
472
|
+
try:
|
|
473
|
+
if (
|
|
474
|
+
commercial_review is not None
|
|
475
|
+
and commercial_review["status"] == "review_required"
|
|
476
|
+
):
|
|
477
|
+
_COMMERCIAL.validate_review_routing_context(
|
|
478
|
+
commercial_review_routing,
|
|
479
|
+
binding["source_text"],
|
|
480
|
+
binding["target_text"],
|
|
481
|
+
commercial_review,
|
|
482
|
+
binding["commercial_profile"],
|
|
483
|
+
)
|
|
484
|
+
elif commercial_review_routing is not None:
|
|
485
|
+
raise _COMMERCIAL.CommercialReviewBlocked(
|
|
486
|
+
"review.commercial.routing_invalid",
|
|
487
|
+
)
|
|
488
|
+
except _COMMERCIAL.CommercialReviewBlocked:
|
|
489
|
+
_fail("binding_invalid")
|
|
490
|
+
expected_resolution_contract_sha256 = (
|
|
491
|
+
_COMMERCIAL.public_review_resolution_contract(
|
|
492
|
+
binding["commercial_profile"],
|
|
493
|
+
)["sha256"]
|
|
494
|
+
if commercial_review is not None
|
|
495
|
+
and commercial_review["status"] == "review_required"
|
|
496
|
+
else None
|
|
497
|
+
)
|
|
498
|
+
if (
|
|
499
|
+
binding["commercial_review_routing_contract_sha256"]
|
|
500
|
+
!= (
|
|
501
|
+
_COMMERCIAL.public_review_routing_contract(
|
|
502
|
+
binding["commercial_profile"]
|
|
503
|
+
)["sha256"]
|
|
504
|
+
if commercial_review is not None
|
|
505
|
+
else None
|
|
506
|
+
)
|
|
507
|
+
or binding["commercial_review_resolution_contract_sha256"]
|
|
508
|
+
!= expected_resolution_contract_sha256
|
|
509
|
+
):
|
|
510
|
+
_fail("binding_invalid")
|
|
358
511
|
return binding, _canonical_json(
|
|
359
512
|
binding, code="binding_invalid", maximum=MAX_REQUEST_BYTES,
|
|
360
513
|
)
|