blun-king-cli 9.1.597 → 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 +39 -0
- package/README.md +17 -1
- 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 +798 -143
- 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 +215 -82
- package/agent-spine-plugin/scripts/check-install-king.js +0 -37
package/standard-skills/translate-native/integrations/website_localization_cms_source_http.py
CHANGED
|
@@ -23,15 +23,17 @@ AUTH_REQUEST_SCHEMA = "blun.cms-source-runtime-http-auth-request.v1"
|
|
|
23
23
|
PRINCIPAL_SCHEMA = "blun.cms-source-runtime-principal.v1"
|
|
24
24
|
STATUS_PRINCIPAL_SCHEMA = "blun.cms-source-status-principal.v1"
|
|
25
25
|
CHANGE_REQUEST_SCHEMA = "blun.cms-source-change-enqueue-request.v1"
|
|
26
|
-
CHANGE_RESPONSE_SCHEMA = "blun.cms-source-change-enqueue-response.
|
|
26
|
+
CHANGE_RESPONSE_SCHEMA = "blun.cms-source-change-enqueue-response.v3"
|
|
27
27
|
REMOVAL_REQUEST_SCHEMA = "blun.cms-source-removal-enqueue-request.v1"
|
|
28
|
-
REMOVAL_RESPONSE_SCHEMA = "blun.cms-source-removal-enqueue-response.
|
|
28
|
+
REMOVAL_RESPONSE_SCHEMA = "blun.cms-source-removal-enqueue-response.v3"
|
|
29
29
|
STATUS_REQUEST_SCHEMA = "blun.cms-source-status-request.v1"
|
|
30
|
-
STATUS_RESPONSE_SCHEMA = "blun.cms-source-status-response.
|
|
31
|
-
HEALTH_RESPONSE_SCHEMA = "blun.cms-source-health-response.
|
|
32
|
-
READINESS_RESPONSE_SCHEMA = "blun.cms-source-readiness-response.
|
|
33
|
-
CAPABILITIES_SCHEMA = "blun.cms-source-runtime-capabilities.
|
|
34
|
-
CAPABILITIES_RESPONSE_SCHEMA = "blun.cms-source-capabilities-response.
|
|
30
|
+
STATUS_RESPONSE_SCHEMA = "blun.cms-source-status-response.v6"
|
|
31
|
+
HEALTH_RESPONSE_SCHEMA = "blun.cms-source-health-response.v6"
|
|
32
|
+
READINESS_RESPONSE_SCHEMA = "blun.cms-source-readiness-response.v3"
|
|
33
|
+
CAPABILITIES_SCHEMA = "blun.cms-source-runtime-capabilities.v2"
|
|
34
|
+
CAPABILITIES_RESPONSE_SCHEMA = "blun.cms-source-capabilities-response.v2"
|
|
35
|
+
CAPABILITY_BINDING_SCHEMA = "blun.cms-source-capability-binding.v2"
|
|
36
|
+
CAPABILITY_DATABASE_ROLES = ("changes", "removals", "lifecycle")
|
|
35
37
|
|
|
36
38
|
CHANGE_PATH = "/v1/localization/source/changes"
|
|
37
39
|
REMOVAL_PATH = "/v1/localization/source/removals"
|
|
@@ -261,6 +263,7 @@ def _source_status_payload(
|
|
|
261
263
|
"queue_counts", "notification_state", "notification_id",
|
|
262
264
|
"notification_sha256", "notification_attempts",
|
|
263
265
|
"notification_max_attempts", "notification_error_code",
|
|
266
|
+
"terminal_receiver_capabilities_sha256",
|
|
264
267
|
"terminal_processing_state", "terminal_processing_poll_attempts",
|
|
265
268
|
"terminal_processing_failures", "terminal_processing_error_code",
|
|
266
269
|
"receiver_processing_state", "receiver_processing_attempts",
|
|
@@ -269,7 +272,7 @@ def _source_status_payload(
|
|
|
269
272
|
}
|
|
270
273
|
if not isinstance(payload, Mapping) or set(payload) != fields:
|
|
271
274
|
raise ValueError
|
|
272
|
-
if payload["schema"] != "blun.cms-source-service-status.
|
|
275
|
+
if payload["schema"] != "blun.cms-source-service-status.v4":
|
|
273
276
|
raise ValueError
|
|
274
277
|
event_id = _token(payload["event_id"])
|
|
275
278
|
site_id = _token(payload["site_id"])
|
|
@@ -360,6 +363,16 @@ def _source_status_payload(
|
|
|
360
363
|
notification_error = _optional_error(
|
|
361
364
|
payload["notification_error_code"]
|
|
362
365
|
)
|
|
366
|
+
terminal_receiver_capabilities_sha256 = payload[
|
|
367
|
+
"terminal_receiver_capabilities_sha256"
|
|
368
|
+
]
|
|
369
|
+
if (
|
|
370
|
+
terminal_receiver_capabilities_sha256 is not None
|
|
371
|
+
and SHA256.fullmatch(
|
|
372
|
+
terminal_receiver_capabilities_sha256
|
|
373
|
+
) is None
|
|
374
|
+
):
|
|
375
|
+
raise ValueError
|
|
363
376
|
durable_notification = notification_state in STATUSES
|
|
364
377
|
if durable_notification != (notification_id is not None):
|
|
365
378
|
raise ValueError
|
|
@@ -386,6 +399,10 @@ def _source_status_payload(
|
|
|
386
399
|
}
|
|
387
400
|
if terminal_processing_state not in processing_states:
|
|
388
401
|
raise ValueError
|
|
402
|
+
if (terminal_processing_state == "disabled") != (
|
|
403
|
+
terminal_receiver_capabilities_sha256 is None
|
|
404
|
+
):
|
|
405
|
+
raise ValueError
|
|
389
406
|
terminal_processing_poll_attempts = _count(
|
|
390
407
|
payload["terminal_processing_poll_attempts"]
|
|
391
408
|
)
|
|
@@ -531,6 +548,9 @@ def _source_status_payload(
|
|
|
531
548
|
"notification_attempts": notification_attempts,
|
|
532
549
|
"notification_max_attempts": notification_max_attempts,
|
|
533
550
|
"notification_error_code": notification_error,
|
|
551
|
+
"terminal_receiver_capabilities_sha256": (
|
|
552
|
+
terminal_receiver_capabilities_sha256
|
|
553
|
+
),
|
|
534
554
|
"terminal_processing_state": terminal_processing_state,
|
|
535
555
|
"terminal_processing_poll_attempts": (
|
|
536
556
|
terminal_processing_poll_attempts
|
|
@@ -557,12 +577,15 @@ def _component(
|
|
|
557
577
|
*,
|
|
558
578
|
operations: bool = False,
|
|
559
579
|
remote_failures: bool = False,
|
|
580
|
+
receiver_capabilities: bool = False,
|
|
560
581
|
) -> dict[str, Any]:
|
|
561
582
|
fields = {"status", "counts", "due", "expired_leases", "failed"}
|
|
562
583
|
if operations:
|
|
563
584
|
fields.add("operations")
|
|
564
585
|
if remote_failures:
|
|
565
586
|
fields.add("remote_failures")
|
|
587
|
+
if receiver_capabilities:
|
|
588
|
+
fields.add("expected_capabilities_sha256")
|
|
566
589
|
if not isinstance(value, Mapping) or set(value) != fields:
|
|
567
590
|
raise ValueError
|
|
568
591
|
if value["status"] not in {"ok", "blocked"}:
|
|
@@ -589,6 +612,11 @@ def _component(
|
|
|
589
612
|
}
|
|
590
613
|
if remote_failures:
|
|
591
614
|
normalized["remote_failures"] = _count(value["remote_failures"])
|
|
615
|
+
if receiver_capabilities:
|
|
616
|
+
expected = value["expected_capabilities_sha256"]
|
|
617
|
+
if not isinstance(expected, str) or SHA256.fullmatch(expected) is None:
|
|
618
|
+
raise ValueError
|
|
619
|
+
normalized["expected_capabilities_sha256"] = expected
|
|
592
620
|
return normalized
|
|
593
621
|
|
|
594
622
|
|
|
@@ -599,10 +627,11 @@ def _health_payload(value: Any) -> dict[str, Any]:
|
|
|
599
627
|
"schema", "status", "pending_lifecycle_registrations",
|
|
600
628
|
"pending_terminal_notifications", "changes", "removals",
|
|
601
629
|
"pending_terminal_processing", "lifecycle", "notifications",
|
|
602
|
-
"terminal_processing", "
|
|
630
|
+
"terminal_processing", "terminal_receiver_capabilities_sha256",
|
|
631
|
+
"error_code",
|
|
603
632
|
}:
|
|
604
633
|
raise ValueError
|
|
605
|
-
if payload["schema"] != "blun.cms-source-service-health.
|
|
634
|
+
if payload["schema"] != "blun.cms-source-service-health.v4":
|
|
606
635
|
raise ValueError
|
|
607
636
|
if payload["status"] not in HEALTH_STATUSES:
|
|
608
637
|
raise ValueError
|
|
@@ -637,10 +666,34 @@ def _health_payload(value: Any) -> dict[str, Any]:
|
|
|
637
666
|
else _component(payload["terminal_processing"], {
|
|
638
667
|
"pending", "leased", "watching", "retry_wait",
|
|
639
668
|
"succeeded", "failed",
|
|
640
|
-
})
|
|
669
|
+
}, receiver_capabilities=True)
|
|
641
670
|
),
|
|
671
|
+
"terminal_receiver_capabilities_sha256": payload[
|
|
672
|
+
"terminal_receiver_capabilities_sha256"
|
|
673
|
+
],
|
|
642
674
|
"error_code": payload["error_code"],
|
|
643
675
|
}
|
|
676
|
+
receiver_capabilities_sha256 = result[
|
|
677
|
+
"terminal_receiver_capabilities_sha256"
|
|
678
|
+
]
|
|
679
|
+
if (
|
|
680
|
+
receiver_capabilities_sha256 is not None
|
|
681
|
+
and (
|
|
682
|
+
not isinstance(receiver_capabilities_sha256, str)
|
|
683
|
+
or SHA256.fullmatch(receiver_capabilities_sha256) is None
|
|
684
|
+
)
|
|
685
|
+
):
|
|
686
|
+
raise ValueError
|
|
687
|
+
if result["terminal_processing"]:
|
|
688
|
+
if (
|
|
689
|
+
receiver_capabilities_sha256
|
|
690
|
+
!= result["terminal_processing"][
|
|
691
|
+
"expected_capabilities_sha256"
|
|
692
|
+
]
|
|
693
|
+
):
|
|
694
|
+
raise ValueError
|
|
695
|
+
elif receiver_capabilities_sha256 is not None and result["status"] != "blocked":
|
|
696
|
+
raise ValueError
|
|
644
697
|
if result["error_code"] is not None and ERROR_CODE.fullmatch(
|
|
645
698
|
result["error_code"]
|
|
646
699
|
) is None:
|
|
@@ -693,6 +746,42 @@ def _readiness_payload(value: Any) -> dict[str, Any]:
|
|
|
693
746
|
) from None
|
|
694
747
|
|
|
695
748
|
|
|
749
|
+
def _capability_binding_payload(value: Any) -> dict[str, Any]:
|
|
750
|
+
"""Validate the exact content-free durable runtime binding."""
|
|
751
|
+
|
|
752
|
+
try:
|
|
753
|
+
if not isinstance(value, Mapping) or set(value) != {
|
|
754
|
+
"schema", "status", "capabilities_sha256",
|
|
755
|
+
"commercial_rendering_registry_sha256", "database_roles",
|
|
756
|
+
}:
|
|
757
|
+
raise ValueError
|
|
758
|
+
if (
|
|
759
|
+
value["schema"] != CAPABILITY_BINDING_SCHEMA
|
|
760
|
+
or value["status"] != "verified"
|
|
761
|
+
or SHA256.fullmatch(value["capabilities_sha256"]) is None
|
|
762
|
+
or SHA256.fullmatch(
|
|
763
|
+
value["commercial_rendering_registry_sha256"]
|
|
764
|
+
) is None
|
|
765
|
+
or value["database_roles"] != list(CAPABILITY_DATABASE_ROLES)
|
|
766
|
+
):
|
|
767
|
+
raise ValueError
|
|
768
|
+
result = {
|
|
769
|
+
"schema": value["schema"],
|
|
770
|
+
"status": value["status"],
|
|
771
|
+
"capabilities_sha256": value["capabilities_sha256"],
|
|
772
|
+
"commercial_rendering_registry_sha256": value[
|
|
773
|
+
"commercial_rendering_registry_sha256"
|
|
774
|
+
],
|
|
775
|
+
"database_roles": list(value["database_roles"]),
|
|
776
|
+
}
|
|
777
|
+
_canonical_json(result)
|
|
778
|
+
return result
|
|
779
|
+
except Exception:
|
|
780
|
+
raise CMSSourceHTTPBlocked(
|
|
781
|
+
"source_http.capability_binding_invalid", 503,
|
|
782
|
+
) from None
|
|
783
|
+
|
|
784
|
+
|
|
696
785
|
def _capabilities_payload() -> dict[str, Any]:
|
|
697
786
|
"""Build the public contract from the exact active HTTP constants."""
|
|
698
787
|
expected_scopes = {
|
|
@@ -729,7 +818,9 @@ def _capabilities_payload() -> dict[str, Any]:
|
|
|
729
818
|
"request_schema": None,
|
|
730
819
|
"request_fields": [],
|
|
731
820
|
"response_schema": CAPABILITIES_RESPONSE_SCHEMA,
|
|
732
|
-
"response_fields": [
|
|
821
|
+
"response_fields": [
|
|
822
|
+
"schema", "capabilities", "capability_binding",
|
|
823
|
+
],
|
|
733
824
|
"success_status": 200,
|
|
734
825
|
},
|
|
735
826
|
"change": {
|
|
@@ -743,7 +834,7 @@ def _capabilities_payload() -> dict[str, Any]:
|
|
|
743
834
|
"response_fields": [
|
|
744
835
|
"schema", "operation", "request_id", "event_id",
|
|
745
836
|
"payload_sha256", "status", "attempts", "max_attempts",
|
|
746
|
-
"capabilities_sha256",
|
|
837
|
+
"capabilities_sha256", "capability_binding",
|
|
747
838
|
],
|
|
748
839
|
"success_status": 202,
|
|
749
840
|
},
|
|
@@ -755,7 +846,10 @@ def _capabilities_payload() -> dict[str, Any]:
|
|
|
755
846
|
"request_schema": None,
|
|
756
847
|
"request_fields": [],
|
|
757
848
|
"response_schema": HEALTH_RESPONSE_SCHEMA,
|
|
758
|
-
"response_fields": [
|
|
849
|
+
"response_fields": [
|
|
850
|
+
"schema", "health", "capabilities_sha256",
|
|
851
|
+
"capability_binding",
|
|
852
|
+
],
|
|
759
853
|
"success_status": 200,
|
|
760
854
|
},
|
|
761
855
|
"removal": {
|
|
@@ -769,7 +863,7 @@ def _capabilities_payload() -> dict[str, Any]:
|
|
|
769
863
|
"response_fields": [
|
|
770
864
|
"schema", "operation", "request_id", "event_id",
|
|
771
865
|
"payload_sha256", "status", "attempts", "max_attempts",
|
|
772
|
-
"capabilities_sha256",
|
|
866
|
+
"capabilities_sha256", "capability_binding",
|
|
773
867
|
],
|
|
774
868
|
"success_status": 202,
|
|
775
869
|
},
|
|
@@ -783,6 +877,7 @@ def _capabilities_payload() -> dict[str, Any]:
|
|
|
783
877
|
"response_schema": READINESS_RESPONSE_SCHEMA,
|
|
784
878
|
"response_fields": [
|
|
785
879
|
"schema", "readiness", "capabilities_sha256",
|
|
880
|
+
"capability_binding",
|
|
786
881
|
],
|
|
787
882
|
"success_status": 200,
|
|
788
883
|
},
|
|
@@ -794,7 +889,10 @@ def _capabilities_payload() -> dict[str, Any]:
|
|
|
794
889
|
"request_schema": STATUS_REQUEST_SCHEMA,
|
|
795
890
|
"request_fields": ["schema", "event_id", "site_id"],
|
|
796
891
|
"response_schema": STATUS_RESPONSE_SCHEMA,
|
|
797
|
-
"response_fields": [
|
|
892
|
+
"response_fields": [
|
|
893
|
+
"schema", "status", "capabilities_sha256",
|
|
894
|
+
"capability_binding",
|
|
895
|
+
],
|
|
798
896
|
"success_status": 200,
|
|
799
897
|
},
|
|
800
898
|
}
|
|
@@ -851,7 +949,7 @@ class CMSSourceHTTPApplication:
|
|
|
851
949
|
def __init__(self, runtime: Any, authenticator: Callable[[dict[str, Any]], Any]):
|
|
852
950
|
if not all(callable(getattr(runtime, name, None)) for name in (
|
|
853
951
|
"enqueue_change", "enqueue_removal", "status", "health",
|
|
854
|
-
"worker_readiness", "require_worker_ready",
|
|
952
|
+
"worker_readiness", "require_worker_ready", "capability_binding",
|
|
855
953
|
)):
|
|
856
954
|
raise TypeError("runtime must provide source CMS operations")
|
|
857
955
|
if not callable(authenticator):
|
|
@@ -859,6 +957,18 @@ class CMSSourceHTTPApplication:
|
|
|
859
957
|
self.runtime = runtime
|
|
860
958
|
self.authenticator = authenticator
|
|
861
959
|
|
|
960
|
+
def _runtime_binding(self) -> dict[str, Any]:
|
|
961
|
+
try:
|
|
962
|
+
return _capability_binding_payload(
|
|
963
|
+
self.runtime.capability_binding()
|
|
964
|
+
)
|
|
965
|
+
except CMSSourceHTTPBlocked:
|
|
966
|
+
raise
|
|
967
|
+
except Exception:
|
|
968
|
+
raise CMSSourceHTTPBlocked(
|
|
969
|
+
"source_http.runtime_blocked", 503,
|
|
970
|
+
) from None
|
|
971
|
+
|
|
862
972
|
@staticmethod
|
|
863
973
|
def _headers(environ: Mapping[str, Any]) -> tuple[tuple[str, str], ...]:
|
|
864
974
|
headers = []
|
|
@@ -1006,11 +1116,13 @@ class CMSSourceHTTPApplication:
|
|
|
1006
1116
|
raise CMSSourceHTTPBlocked("source_http.content_type_invalid", 415)
|
|
1007
1117
|
body = self._body(environ, required=True)
|
|
1008
1118
|
principal = self._authenticate(environ, path, body)
|
|
1119
|
+
binding = self._runtime_binding()
|
|
1009
1120
|
|
|
1010
1121
|
if path == CAPABILITIES_PATH:
|
|
1011
1122
|
return self._send(start_response, 200, {
|
|
1012
1123
|
"schema": CAPABILITIES_RESPONSE_SCHEMA,
|
|
1013
1124
|
"capabilities": _capabilities_payload(),
|
|
1125
|
+
"capability_binding": binding,
|
|
1014
1126
|
})
|
|
1015
1127
|
|
|
1016
1128
|
if path == HEALTH_PATH:
|
|
@@ -1026,6 +1138,7 @@ class CMSSourceHTTPApplication:
|
|
|
1026
1138
|
"schema": HEALTH_RESPONSE_SCHEMA,
|
|
1027
1139
|
"health": report,
|
|
1028
1140
|
"capabilities_sha256": _capabilities_payload()["sha256"],
|
|
1141
|
+
"capability_binding": self._runtime_binding(),
|
|
1029
1142
|
})
|
|
1030
1143
|
|
|
1031
1144
|
if path == READINESS_PATH:
|
|
@@ -1041,6 +1154,7 @@ class CMSSourceHTTPApplication:
|
|
|
1041
1154
|
"schema": READINESS_RESPONSE_SCHEMA,
|
|
1042
1155
|
"readiness": report,
|
|
1043
1156
|
"capabilities_sha256": _capabilities_payload()["sha256"],
|
|
1157
|
+
"capability_binding": self._runtime_binding(),
|
|
1044
1158
|
})
|
|
1045
1159
|
|
|
1046
1160
|
request = self._request(body)
|
|
@@ -1085,6 +1199,7 @@ class CMSSourceHTTPApplication:
|
|
|
1085
1199
|
expected_site_id=site_id,
|
|
1086
1200
|
),
|
|
1087
1201
|
"capabilities_sha256": _capabilities_payload()["sha256"],
|
|
1202
|
+
"capability_binding": self._runtime_binding(),
|
|
1088
1203
|
})
|
|
1089
1204
|
|
|
1090
1205
|
envelope_key = "change" if path == CHANGE_PATH else "removal"
|
|
@@ -1143,6 +1258,7 @@ class CMSSourceHTTPApplication:
|
|
|
1143
1258
|
),
|
|
1144
1259
|
**payload,
|
|
1145
1260
|
"capabilities_sha256": _capabilities_payload()["sha256"],
|
|
1261
|
+
"capability_binding": self._runtime_binding(),
|
|
1146
1262
|
})
|
|
1147
1263
|
except CMSSourceHTTPBlocked as failure:
|
|
1148
1264
|
return self._error(start_response, failure)
|