blun-king-cli 9.1.587 → 9.1.588
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 +11 -185
- package/LIESMICH.txt +51 -13
- package/README.md +44 -47
- package/agent-spine-plugin/.codex-plugin/plugin.json +16 -4
- package/agent-spine-plugin/CHANGELOG.md +37 -5
- package/agent-spine-plugin/README.md +3 -3
- package/agent-spine-plugin/blun.plugin.json +45 -10
- package/agent-spine-plugin/docs/artifact-evaluation.md +93 -0
- package/agent-spine-plugin/docs/host-integration.md +42 -27
- package/agent-spine-plugin/docs/preflight-recall.md +4 -2
- package/agent-spine-plugin/docs/session-timeline.md +97 -236
- package/agent-spine-plugin/docs/world-model.md +25 -0
- package/agent-spine-plugin/hooks/codex.json +1 -1
- package/agent-spine-plugin/hooks/hooks.json +1 -1
- package/agent-spine-plugin/package.json +1 -3
- package/agent-spine-plugin/scripts/check-hosts.js +3 -3
- package/agent-spine-plugin/scripts/release-check.js +10 -5
- package/agent-spine-plugin/scripts/run-checks.js +4 -1
- package/agent-spine-plugin/scripts/run-tests-hermetic.js +32 -6
- package/agent-spine-plugin/src/cli-learning.js +15 -0
- package/agent-spine-plugin/src/cli.js +2 -0
- package/agent-spine-plugin/src/hook.js +32 -32
- package/agent-spine-plugin/src/lib/action-lesson-recall.js +73 -8
- package/agent-spine-plugin/src/lib/briefing.js +146 -36
- package/agent-spine-plugin/src/lib/channel-continuity.js +19 -0
- package/agent-spine-plugin/src/lib/delivery-agent-usage.js +14 -7
- package/agent-spine-plugin/src/lib/gateway-group-response.js +128 -0
- package/agent-spine-plugin/src/lib/gateway-runs.js +24 -15
- package/agent-spine-plugin/src/lib/hook-briefing-use.js +13 -3
- package/agent-spine-plugin/src/lib/hook-context.js +16 -3
- package/agent-spine-plugin/src/lib/hook-output.js +129 -5
- package/agent-spine-plugin/src/lib/hook-timeline.js +5 -3
- package/agent-spine-plugin/src/lib/indexed-memory.js +2 -2
- package/agent-spine-plugin/src/lib/learning-artifact-evaluator.js +114 -0
- package/agent-spine-plugin/src/lib/learning-context.js +11 -4
- package/agent-spine-plugin/src/lib/learning-measurements.js +2 -2
- package/agent-spine-plugin/src/lib/mcp-runtime.js +89 -3
- package/agent-spine-plugin/src/lib/mcp-source-context.js +12 -2
- package/agent-spine-plugin/src/lib/mcp-timeline-tools.js +91 -8
- package/agent-spine-plugin/src/lib/mcp-world-tools.js +2 -2
- package/agent-spine-plugin/src/lib/owned-file-lock.js +20 -1
- package/agent-spine-plugin/src/lib/persona-runtime.js +2 -2
- package/agent-spine-plugin/src/lib/preflight-delivery-id.js +27 -0
- package/agent-spine-plugin/src/lib/preflight.js +4 -4
- package/agent-spine-plugin/src/lib/session-timeline-codex.js +15 -0
- package/agent-spine-plugin/src/lib/session-timeline-contract.js +12 -4
- package/agent-spine-plugin/src/lib/session-timeline-event-extract.js +36 -7
- package/agent-spine-plugin/src/lib/session-timeline-host-origin.js +13 -10
- package/agent-spine-plugin/src/lib/session-timeline-invocation.js +1 -1
- package/agent-spine-plugin/src/lib/session-timeline-king.js +14 -0
- package/agent-spine-plugin/src/lib/session-timeline-prior.js +18 -12
- package/agent-spine-plugin/src/lib/session-timeline-provider.js +5 -0
- package/agent-spine-plugin/src/lib/session-timeline-query.js +2 -0
- package/agent-spine-plugin/src/lib/session-timeline-results.js +35 -10
- package/agent-spine-plugin/src/lib/session-timeline-source-open.js +30 -0
- package/agent-spine-plugin/src/lib/session-timeline.js +122 -75
- package/agent-spine-plugin/src/lib/source-roots.js +3 -2
- package/agent-spine-plugin/src/lib/task-knowledge-context.js +22 -1
- package/agent-spine-plugin/src/lib/timeline-continuation-update.js +100 -0
- package/agent-spine-plugin/src/lib/timeline-tool-guard.js +30 -7
- package/agent-spine-plugin/src/lib/timeline-user-feedback.js +217 -0
- package/agent-spine-plugin/src/lib/timeline-world-capture.js +233 -0
- package/agent-spine-plugin/src/lib/world-knowledge.js +59 -2
- package/agent-spine-plugin/src/lib/world-model.js +64 -9
- package/agent-spine-plugin/src/worker.js +13 -1
- package/bin/blun.js +43 -28
- package/bin/core-bootstrap.js +5 -4
- package/bin/king.js +43 -28
- package/bin/launcher-mode.js +1 -10
- package/bin/launcher-runtime.js +128 -295
- package/bin/managed-node.js +0 -0
- package/bin/managed-plugin-selection.cjs +0 -1
- package/bin/native-module-repair.js +0 -0
- package/bin/node-runtime.js +0 -0
- package/bin/node-version.js +0 -0
- package/bin/plugin-bootstrap.js +56 -120
- package/bin/private-paths.js +11 -34
- package/bin/standard-tools-bootstrap.js +34 -114
- package/bin/turn-thinking-policy.cjs +3 -11
- package/bin/update-copy.js +200 -0
- package/bin/update-lease.js +0 -0
- package/bin/update-notice.js +136 -289
- package/bin/verify-agent-behavior.cjs +122 -0
- package/bin/verify-agent-components.cjs +104 -0
- package/bin/verify-bundled-agent-sources.cjs +57 -0
- package/blun.mjs +143076 -135288
- package/bundled-agent-sources.json +701 -0
- package/package.json +12 -15
- package/standard-skills/translate-native/README.md +1293 -0
- package/standard-skills/translate-native/SKILL.md +172 -22
- package/standard-skills/translate-native/VERSION +1 -1
- package/standard-skills/translate-native/agents/openai.yaml +18 -0
- package/standard-skills/translate-native/assets/icon.svg +8 -0
- package/standard-skills/translate-native/docs/BLUN_CODE_INTEGRATION.md +76 -0
- package/standard-skills/translate-native/docs/PREMORTEM.md +489 -0
- package/standard-skills/translate-native/docs/WEBSITE_LOCALIZATION.md +2035 -0
- package/standard-skills/translate-native/docs/WEBSITE_LOCALIZATION_API.md +1302 -0
- package/standard-skills/translate-native/docs/WEBSITE_LOCALIZATION_EVIDENCE_HTTP.md +136 -0
- package/standard-skills/translate-native/docs/WEBSITE_LOCALIZATION_HEALTH_HTTP.md +130 -0
- package/standard-skills/translate-native/docs/WEBSITE_LOCALIZATION_HTTP_PROVIDER.md +175 -0
- package/standard-skills/translate-native/docs/WEBSITE_LOCALIZATION_RECEIPT_VERIFIER_HTTP.md +86 -0
- package/standard-skills/translate-native/integrations/AGENT_RULES.md +32 -0
- package/standard-skills/translate-native/integrations/adapters/blun-code-language-guard.js +514 -0
- package/standard-skills/translate-native/integrations/adapters/node-language-guard.js +230 -0
- package/standard-skills/translate-native/integrations/audit_log.py +327 -0
- package/standard-skills/translate-native/integrations/claude_language_hook.js +1536 -0
- package/standard-skills/translate-native/integrations/commercial_localization_profile.py +42 -0
- package/standard-skills/translate-native/integrations/delivery-policy.example.json +28 -0
- package/standard-skills/translate-native/integrations/enforced_delivery.py +543 -0
- package/standard-skills/translate-native/integrations/guard_service.py +435 -0
- package/standard-skills/translate-native/integrations/language_gateway.py +67 -0
- package/standard-skills/translate-native/integrations/mcp_auth_headers.py +198 -0
- package/standard-skills/translate-native/integrations/mcp_http_gateway.py +429 -0
- package/standard-skills/translate-native/integrations/non_language_html_entities.js +1485 -0
- package/standard-skills/translate-native/integrations/pre_output_guard.py +65 -0
- package/standard-skills/translate-native/integrations/task_router.py +101 -0
- package/standard-skills/translate-native/integrations/website_localization.py +401 -0
- package/standard-skills/translate-native/integrations/website_localization_api.py +581 -0
- package/standard-skills/translate-native/integrations/website_localization_benchmark.py +1885 -0
- package/standard-skills/translate-native/integrations/website_localization_benchmark_campaign.py +1772 -0
- package/standard-skills/translate-native/integrations/website_localization_benchmark_candidate.py +506 -0
- package/standard-skills/translate-native/integrations/website_localization_benchmark_http.py +400 -0
- package/standard-skills/translate-native/integrations/website_localization_benchmark_review_store.py +781 -0
- package/standard-skills/translate-native/integrations/website_localization_benchmark_reviewer_http.py +500 -0
- package/standard-skills/translate-native/integrations/website_localization_benchmark_runtime.py +1107 -0
- package/standard-skills/translate-native/integrations/website_localization_benchmark_suite.py +463 -0
- package/standard-skills/translate-native/integrations/website_localization_cms.py +2835 -0
- package/standard-skills/translate-native/integrations/website_localization_cms_client.py +875 -0
- package/standard-skills/translate-native/integrations/website_localization_cms_dispatch.py +805 -0
- package/standard-skills/translate-native/integrations/website_localization_cms_http.py +588 -0
- package/standard-skills/translate-native/integrations/website_localization_cms_lifecycle_monitor.py +991 -0
- package/standard-skills/translate-native/integrations/website_localization_cms_receiver.py +1441 -0
- package/standard-skills/translate-native/integrations/website_localization_cms_receiver_runtime.py +414 -0
- package/standard-skills/translate-native/integrations/website_localization_cms_receiver_store.py +1073 -0
- package/standard-skills/translate-native/integrations/website_localization_cms_removal_dispatch.py +865 -0
- package/standard-skills/translate-native/integrations/website_localization_cms_source_client.py +583 -0
- package/standard-skills/translate-native/integrations/website_localization_cms_source_delivery.py +964 -0
- package/standard-skills/translate-native/integrations/website_localization_cms_source_delivery_runtime.py +665 -0
- package/standard-skills/translate-native/integrations/website_localization_cms_source_http.py +1153 -0
- package/standard-skills/translate-native/integrations/website_localization_cms_source_runtime.py +675 -0
- package/standard-skills/translate-native/integrations/website_localization_cms_source_service.py +1125 -0
- package/standard-skills/translate-native/integrations/website_localization_cms_terminal_notification.py +674 -0
- package/standard-skills/translate-native/integrations/website_localization_cms_terminal_notification_http.py +444 -0
- package/standard-skills/translate-native/integrations/website_localization_cms_terminal_notification_receiver.py +1469 -0
- package/standard-skills/translate-native/integrations/website_localization_cms_terminal_notification_receiver_runtime.py +1142 -0
- package/standard-skills/translate-native/integrations/website_localization_cms_terminal_processing_monitor.py +634 -0
- package/standard-skills/translate-native/integrations/website_localization_cms_terminal_receiver_client.py +804 -0
- package/standard-skills/translate-native/integrations/website_localization_deepl_baseline.py +922 -0
- package/standard-skills/translate-native/integrations/website_localization_evidence_http.py +482 -0
- package/standard-skills/translate-native/integrations/website_localization_health.py +1541 -0
- package/standard-skills/translate-native/integrations/website_localization_health_http.py +372 -0
- package/standard-skills/translate-native/integrations/website_localization_http_provider.py +297 -0
- package/standard-skills/translate-native/integrations/website_localization_native_reference_http.py +479 -0
- package/standard-skills/translate-native/integrations/website_localization_native_reference_intake.py +363 -0
- package/standard-skills/translate-native/integrations/website_localization_native_reference_queue.py +1449 -0
- package/standard-skills/translate-native/integrations/website_localization_native_reference_store.py +420 -0
- package/standard-skills/translate-native/integrations/website_localization_quality_profiles.py +235 -0
- package/standard-skills/translate-native/integrations/website_localization_queue.py +671 -0
- package/standard-skills/translate-native/integrations/website_localization_receipt_verifier_http.py +516 -0
- package/standard-skills/translate-native/integrations/website_localization_release.py +928 -0
- package/standard-skills/translate-native/integrations/website_localization_release_coordinator.py +1008 -0
- package/standard-skills/translate-native/integrations/website_localization_runner.py +276 -0
- package/standard-skills/translate-native/integrations/website_localization_runtime.py +862 -0
- package/standard-skills/translate-native/integrations/website_localization_service.py +350 -0
- package/standard-skills/translate-native/integrations/website_localization_supervisor.py +511 -0
- package/standard-skills/translate-native/integrations/website_localization_worker.py +663 -0
- package/standard-skills/translate-native/provenance.json +3 -4
- package/standard-skills/translate-native/references/commercial-localization.md +177 -0
- package/standard-skills/translate-native/scripts/blun_language_guard.py +7 -1
- package/standard-skills/translate-native/scripts/check_commercial_review.py +80 -0
- package/standard-skills/translate-native/scripts/commercial_localization_profile.py +333 -0
- package/standard-tools/language-guard/LICENSE +21 -0
- package/standard-tools/language-guard/VERSION +1 -0
- package/standard-tools/language-guard/blun_language_guard.py +7 -1
- package/standard-tools/language-guard/check_commercial_review.py +80 -0
- package/standard-tools/language-guard/commercial_localization_profile.py +333 -0
- package/standard-tools/language-guard/language_gateway.py +62 -0
- package/standard-tools/language-guard/pre_output_guard.py +64 -0
- package/standard-tools/language-guard/provenance.json +4 -11
- package/standard-tools/manifest.json +34 -11
- package/telegram-plugin/commands/access.md +2 -10
- package/telegram-plugin/dist/bridge.mjs +64041 -687
- package/telegram-plugin/dist/mcp-server.mjs +72810 -9027
- package/telegram-plugin/dist/noise.mjs +28 -63511
- package/agent-spine-plugin/CONTRIBUTING.md +0 -52
- package/agent-spine-plugin/SECURITY.md +0 -47
- package/agent-spine-plugin/docs/assignment-continuation.md +0 -48
- package/agent-spine-plugin/docs/releasing.md +0 -85
- package/agent-spine-plugin/docs/structured-completion.md +0 -67
- package/bin/abort-listener-policy.cjs +0 -43
- package/bin/active-steer-priority-policy.cjs +0 -24
- package/bin/agent-api-http-adapter.mjs +0 -446
- package/bin/agent-api-private-http-server.mjs +0 -288
- package/bin/agent-api-runtime.mjs +0 -252
- package/bin/agent-api-service-environment.mjs +0 -236
- package/bin/agent-api-service-host.mjs +0 -209
- package/bin/agent-api-service-process.mjs +0 -171
- package/bin/agent-api-session-registry.mjs +0 -428
- package/bin/agent-api-tool-broker.cjs +0 -248
- package/bin/agent-api-turn-controller.mjs +0 -461
- package/bin/agent-api-usage-journal.cjs +0 -259
- package/bin/agent-resume-snapshot.cjs +0 -241
- package/bin/agentspine-king-goal-inbox.mjs +0 -111
- package/bin/agentspine-king-goal-intake.mjs +0 -106
- package/bin/approval-rejection-stop.cjs +0 -15
- package/bin/assistant-message-offload-policy.cjs +0 -284
- package/bin/baseline-skill-performance-policy.cjs +0 -39
- package/bin/bash-search-scope-policy.cjs +0 -49
- package/bin/codebase-search-runtime.cjs +0 -23
- package/bin/cognitive-action-checkpoint.cjs +0 -1104
- package/bin/cognitive-attention-delivery.cjs +0 -76
- package/bin/cognitive-attention-policy.cjs +0 -143
- package/bin/cognitive-attention-runtime.cjs +0 -91
- package/bin/cognitive-context-projection.cjs +0 -73
- package/bin/cognitive-cross-portal-acceptance.cjs +0 -443
- package/bin/cognitive-effective-view.cjs +0 -77
- package/bin/cognitive-focus-projection.cjs +0 -206
- package/bin/cognitive-focus-scope.cjs +0 -37
- package/bin/cognitive-goal-autostart-policy.cjs +0 -72
- package/bin/cognitive-goal-time-trigger-controller.cjs +0 -146
- package/bin/cognitive-memory-adapter.cjs +0 -282
- package/bin/cognitive-memory-command.cjs +0 -293
- package/bin/cognitive-memory-provider.cjs +0 -92
- package/bin/cognitive-salience-policy.cjs +0 -159
- package/bin/cognitive-state-store.cjs +0 -508
- package/bin/cognitive-turn-lifecycle.cjs +0 -624
- package/bin/cognitive-work-focus.cjs +0 -180
- package/bin/compaction-history-archive.cjs +0 -166
- package/bin/compaction-history-startup.cjs +0 -50
- package/bin/compaction-model-policy.cjs +0 -31
- package/bin/compaction-stage-policy.cjs +0 -21
- package/bin/compaction-transaction-policy.cjs +0 -122
- package/bin/config-write-dedup-policy.cjs +0 -27
- package/bin/context-budget-ledger.cjs +0 -31
- package/bin/context-doctor-policy.cjs +0 -70
- package/bin/context-insight-policy.cjs +0 -36
- package/bin/context-performance-policy.cjs +0 -19
- package/bin/context-pressure-policy.cjs +0 -20
- package/bin/cron-run-output.cjs +0 -45
- package/bin/cron-run-store.cjs +0 -145
- package/bin/curiosity-scout-policy.cjs +0 -49
- package/bin/default-model-output-budget-policy.cjs +0 -28
- package/bin/durable-task-resume-policy.cjs +0 -130
- package/bin/durable-task-resume-runtime.cjs +0 -117
- package/bin/durable-task-resume-store.cjs +0 -88
- package/bin/editable-tool-approval-policy.cjs +0 -540
- package/bin/editable-tool-approval-runtime.cjs +0 -99
- package/bin/effective-system-prompt-cache-policy.cjs +0 -33
- package/bin/error-memory-performance-policy.cjs +0 -113
- package/bin/file-observation-policy.cjs +0 -133
- package/bin/foreground-output-capture-policy.cjs +0 -41
- package/bin/generated-source-health.cjs +0 -142
- package/bin/glob-pattern-policy.cjs +0 -13
- package/bin/goal-completion-evidence-policy.cjs +0 -120
- package/bin/grep-output-limit-policy.cjs +0 -39
- package/bin/historical-media-projection-policy.cjs +0 -48
- package/bin/history-offload-pressure-policy.cjs +0 -33
- package/bin/html-to-research-markdown.cjs +0 -147
- package/bin/identity-context-policy.cjs +0 -764
- package/bin/identity-journal-policy.cjs +0 -107
- package/bin/input-draft-persistence.cjs +0 -77
- package/bin/king-tui-function-contract.json +0 -33
- package/bin/launcher-restart-policy.cjs +0 -150
- package/bin/live-response-repetition-guard.cjs +0 -196
- package/bin/llm-config-log-dedup-policy.cjs +0 -76
- package/bin/loop-event-record-policy.cjs +0 -174
- package/bin/managed-context-startup-policy.cjs +0 -27
- package/bin/media-activity-layout-policy.cjs +0 -34
- package/bin/media-auto-retrieval-policy.cjs +0 -90
- package/bin/media-result-policy.cjs +0 -59
- package/bin/micro-compaction-policy.cjs +0 -145
- package/bin/mistake-relevance-policy.cjs +0 -319
- package/bin/model-retry-progress-policy.cjs +0 -46
- package/bin/native-large-file-io.cjs +0 -42
- package/bin/native-runtime-cache.cjs +0 -76
- package/bin/natural-presence-policy.cjs +0 -28
- package/bin/noninteractive-shell-env-policy.cjs +0 -19
- package/bin/observer-hooks.cjs +0 -14
- package/bin/outbound-claim-provenance.cjs +0 -150
- package/bin/oversized-context-offload-policy.cjs +0 -86
- package/bin/pending-media-policy.cjs +0 -182
- package/bin/pending-token-estimate-policy.cjs +0 -41
- package/bin/personal-memory-consent-policy.cjs +0 -72
- package/bin/personal-memory-performance-policy.cjs +0 -12
- package/bin/personality-choice-policy.cjs +0 -101
- package/bin/personality-memory-adapter.cjs +0 -379
- package/bin/personality-mode.cjs +0 -46
- package/bin/personality-setup-policy.cjs +0 -197
- package/bin/proactive-compaction-policy.cjs +0 -25
- package/bin/profile-identity-resolution.cjs +0 -136
- package/bin/profile-runtime.cjs +0 -318
- package/bin/profile-tool-exclusion-policy.cjs +0 -37
- package/bin/programmatic-context-isolation.cjs +0 -25
- package/bin/programmatic-tool-runtime.mjs +0 -627
- package/bin/provider-idle-timeout-policy.cjs +0 -14
- package/bin/provider-model-refresh-deadline.cjs +0 -53
- package/bin/provider-model-refresh-policy.cjs +0 -107
- package/bin/rate-limit-recovery-policy.cjs +0 -47
- package/bin/read-batch-policy.cjs +0 -32
- package/bin/read-continuation-policy.cjs +0 -59
- package/bin/recurring-cron-history-policy.cjs +0 -124
- package/bin/relationship-continuity-policy.cjs +0 -143
- package/bin/relationship-curiosity-policy.cjs +0 -107
- package/bin/relationship-learning-policy.cjs +0 -168
- package/bin/release-artifact-freeze-policy.cjs +0 -30
- package/bin/reload-plugin-bootstrap.cjs +0 -18
- package/bin/reload-queue-policy.cjs +0 -38
- package/bin/repeated-assistant-response-policy.cjs +0 -232
- package/bin/repeated-injection-projection.cjs +0 -107
- package/bin/repeated-user-message-projection.cjs +0 -8
- package/bin/research-page-result.cjs +0 -74
- package/bin/retry-checkpoint-policy.cjs +0 -13
- package/bin/runtime-exit-ledger.cjs +0 -144
- package/bin/scoped-cron-run-policy.cjs +0 -358
- package/bin/session-checkpoint-policy.cjs +0 -25
- package/bin/session-compaction-policy.cjs +0 -84
- package/bin/session-replay-policy.cjs +0 -20
- package/bin/session-replay-window-policy.cjs +0 -40
- package/bin/session-resume-checkpoint.cjs +0 -254
- package/bin/session-scrollback-archive.cjs +0 -229
- package/bin/skill-activation-performance-policy.cjs +0 -69
- package/bin/skill-listing-performance-policy.cjs +0 -92
- package/bin/soul-organization-policy.cjs +0 -78
- package/bin/soul-preservation-policy.cjs +0 -20
- package/bin/startup-preferences.cjs +0 -131
- package/bin/streaming-flush-performance-policy.cjs +0 -28
- package/bin/structured-agent-swarm-output.cjs +0 -325
- package/bin/structured-subagent-output.cjs +0 -252
- package/bin/subagent-context-fork-policy.cjs +0 -155
- package/bin/subagent-max-tokens-handoff-policy.cjs +0 -69
- package/bin/subagent-parent-responsiveness.cjs +0 -19
- package/bin/subagent-skill-policy.cjs +0 -206
- package/bin/subagent-timeout-policy.cjs +0 -182
- package/bin/subagent-tool-policy.cjs +0 -60
- package/bin/subagent-usage-rollup-policy.cjs +0 -29
- package/bin/system-prompt-context-policy.cjs +0 -124
- package/bin/system-prompt-token-cache-policy.cjs +0 -60
- package/bin/telegram-addressed-focus.cjs +0 -55
- package/bin/telegram-addressed-priority.cjs +0 -12
- package/bin/telegram-approval-relay.cjs +0 -290
- package/bin/telegram-bot-priority.cjs +0 -17
- package/bin/telegram-console-status-policy.cjs +0 -174
- package/bin/telegram-context-projection-policy.cjs +0 -141
- package/bin/telegram-delivery-lifecycle.cjs +0 -125
- package/bin/telegram-direct-focus-policy.cjs +0 -273
- package/bin/telegram-mcp-compatibility.cjs +0 -49
- package/bin/telegram-media-delivery-policy.cjs +0 -42
- package/bin/telegram-private-conversation-policy.cjs +0 -185
- package/bin/telegram-queue-handoff-policy.cjs +0 -73
- package/bin/telegram-remote-status-policy.cjs +0 -120
- package/bin/telegram-session-queue-runtime.mjs +0 -306
- package/bin/telegram-text-chunk-policy.cjs +0 -63
- package/bin/telegram-truncated-reply-policy.cjs +0 -37
- package/bin/telegram-urgent-policy.cjs +0 -45
- package/bin/telemetry-spool-policy.cjs +0 -57
- package/bin/thinking-activity-status-policy.cjs +0 -132
- package/bin/thinking-only-guard.cjs +0 -80
- package/bin/todo-list-turn-policy.cjs +0 -131
- package/bin/tool-call-loop-policy.cjs +0 -51
- package/bin/tool-file-persistence.cjs +0 -141
- package/bin/tool-result-offload-policy.cjs +0 -359
- package/bin/tool-result-offload-telemetry.cjs +0 -12
- package/bin/tool-schema-token-cache-policy.cjs +0 -41
- package/bin/tool-stream-preview-policy.cjs +0 -9
- package/bin/tui-functional-contract.cjs +0 -55
- package/bin/turn-tool-performance-policy.cjs +0 -486
- package/bin/usage-cache-efficiency-policy.cjs +0 -26
- package/bin/user-home-path-policy.cjs +0 -13
- package/bin/user-message-offload-policy.cjs +0 -103
- package/bin/user-prompt-hook-origin-policy.cjs +0 -34
- package/bin/user-tool-record-policy.cjs +0 -7
- package/bin/validated-learning-insight-policy.cjs +0 -58
- package/bin/validated-learning-outcome-trace.cjs +0 -107
- package/bin/validated-learning-performance-policy.cjs +0 -53
- package/bin/validated-learning-signal.cjs +0 -463
- package/bin/windows-bash-dialect-policy.cjs +0 -25
- package/bin/windows-node-crash-dump.cjs +0 -110
- package/bin/write-continuation-policy.cjs +0 -69
- package/codebase-index/README.md +0 -82
- package/codebase-index/codebase_index.py +0 -470
- package/standard-skills/agent-browser/SKILL.md +0 -19
- package/standard-skills/agent-browser/references/runtime.md +0 -8
- package/standard-skills/blun-session-inspector/SKILL.md +0 -41
- package/standard-skills/blun-session-inspector/scripts/inspect-session.cjs +0 -437
- package/standard-skills/design-taste-frontend/SKILL.md +0 -1206
- package/standard-skills/full-output-enforcement/SKILL.md +0 -49
- package/standard-skills/high-end-visual-design/SKILL.md +0 -98
- package/standard-skills/image-to-code/SKILL.md +0 -1228
- package/standard-skills/industrial-brutalist-ui/SKILL.md +0 -92
- package/standard-skills/minimalist-ui/SKILL.md +0 -85
- package/standard-skills/motion-design-taste/SKILL.md +0 -74
- package/standard-skills/playwright-testing/SKILL.md +0 -19
- package/standard-skills/playwright-testing/references/runtime.md +0 -7
- package/standard-skills/premortem/SKILL.md +0 -148
- package/standard-skills/redesign-existing-projects/SKILL.md +0 -178
- package/standard-skills/research-evidence/SKILL.md +0 -39
- package/standard-skills/research-evidence/references/evidence-format.md +0 -104
- package/standard-skills/research-evidence/scripts/evidence-collection.cjs +0 -260
- package/standard-skills/research-evidence/scripts/score-report.cjs +0 -130
- package/standard-skills/screenshot-lesen/SKILL.md +0 -52
- package/standard-skills/stitch-design-taste/DESIGN.md +0 -121
- package/standard-skills/stitch-design-taste/SKILL.md +0 -184
- package/standard-skills/telegram-channel/SKILL.md +0 -18
- package/standard-skills/telegram-channel/references/runtime.md +0 -7
- package/standard-skills/venture-flywheel/SKILL.md +0 -32
- package/standard-skills/venture-flywheel/identity/project-identity.cjs +0 -146
- package/standard-skills/venture-flywheel/policy/capability-engine.cjs +0 -114
- package/standard-skills/venture-flywheel/policy/repository-trust.cjs +0 -229
- package/standard-skills/venture-flywheel/references/BEISPIELE-phase0.md +0 -146
- package/standard-skills/venture-flywheel/references/CAPABILITY-MAP.md +0 -34
- package/standard-skills/venture-flywheel/references/SPEC-phase0-identity-trust.md +0 -77
- package/standard-skills/venture-flywheel/references/SPEC-phase0-state-events.md +0 -93
- package/standard-skills/venture-flywheel/schemas/capability-decision.schema.json +0 -13
- package/standard-skills/venture-flywheel/schemas/execution-event.schema.json +0 -44
- package/standard-skills/venture-flywheel/schemas/project-identity.schema.json +0 -32
- package/standard-skills/venture-flywheel/schemas/repository-trust.schema.json +0 -57
- package/standard-skills/venture-flywheel/schemas/run-transition.schema.json +0 -59
- package/standard-skills/venture-flywheel/state/execution-event.cjs +0 -191
- package/standard-skills/venture-flywheel/state/task-state-machine.cjs +0 -190
- package/standard-skills/web-lesen/SKILL.md +0 -73
- package/standard-skills/web-lesen/scripts/crawl_public.py +0 -379
- package/standard-skills/windows-mcp/SKILL.md +0 -19
- package/standard-skills/windows-mcp/references/runtime.md +0 -9
- package/telegram-plugin/DELIVERY.md +0 -36
- package/telegram-plugin/bin/telegram-approval-relay.cjs +0 -290
- package/telegram-plugin/bin/telegram-console-status-policy.cjs +0 -175
- package/telegram-plugin/bin/telegram-delivery-lifecycle.cjs +0 -125
- package/telegram-plugin/bin/telegram-direct-reply-policy.cjs +0 -48
- package/telegram-plugin/bin/telegram-launcher-status-queue.cjs +0 -122
- package/telegram-plugin/bin/telegram-private-conversation-policy.cjs +0 -186
- package/telegram-plugin/bin/telegram-remote-status-policy.cjs +0 -121
- package/telegram-plugin/bin/telegram-reply-parts.cjs +0 -149
- package/telegram-plugin/bin/telegram-text-chunk-policy.cjs +0 -63
- package/telegram-plugin/bin/telegram-typing-keepalive.cjs +0 -89
- package/telegram-plugin/compat/mcp-server-fa511cd1.mjs +0 -73825
- /package/{bin → scripts}/fix-node-pty-perms.js +0 -0
|
@@ -0,0 +1,588 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Secure provider-neutral HTTP publisher for signed CMS localization bundles."""
|
|
3
|
+
|
|
4
|
+
from __future__ import annotations
|
|
5
|
+
|
|
6
|
+
import hashlib
|
|
7
|
+
import importlib.util
|
|
8
|
+
import json
|
|
9
|
+
import re
|
|
10
|
+
import secrets
|
|
11
|
+
import socket
|
|
12
|
+
import sys
|
|
13
|
+
import urllib.error
|
|
14
|
+
import urllib.parse
|
|
15
|
+
import urllib.request
|
|
16
|
+
from dataclasses import dataclass
|
|
17
|
+
from pathlib import Path
|
|
18
|
+
from typing import Any, Callable, Mapping, Protocol
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
MAX_ENDPOINT_LENGTH = 2048
|
|
22
|
+
MAX_HEADER_VALUE_LENGTH = 4096
|
|
23
|
+
MAX_RESPONSE_BYTES = 65_536
|
|
24
|
+
MAX_REQUEST_BYTES = 4_000_000
|
|
25
|
+
HEADER_NAME = re.compile(r"^[!#$%&'*+.^_`|~0-9A-Za-z-]{1,128}$")
|
|
26
|
+
TOKEN = re.compile(r"^[A-Za-z0-9_.:-]{1,256}$")
|
|
27
|
+
SIGNATURE_VALUE = re.compile(r"^[A-Za-z0-9_.:/+=-]{1,4096}$")
|
|
28
|
+
ERROR_CODE = re.compile(r"^[a-z][a-z0-9_.-]{0,127}$")
|
|
29
|
+
RESERVED_HEADERS = {
|
|
30
|
+
"accept", "accept-encoding", "connection", "content-length", "content-type",
|
|
31
|
+
"host", "idempotency-key", "transfer-encoding", "x-localization-delivery-id",
|
|
32
|
+
"x-localization-payload-sha256",
|
|
33
|
+
}
|
|
34
|
+
PUBLICATION_FIELDS = {
|
|
35
|
+
"schema", "delivery_id", "event_id", "site_id", "website_version", "plan_id",
|
|
36
|
+
"source_id", "source_revision", "source_sequence", "source_sha256", "localizations",
|
|
37
|
+
}
|
|
38
|
+
TOMBSTONE_FIELDS = {
|
|
39
|
+
"schema", "delivery_id", "tombstone_id", "event_id", "site_id",
|
|
40
|
+
"website_version", "plan_id", "source_id", "source_sequence",
|
|
41
|
+
"publication_delivery_id", "publication_payload_sha256", "locales",
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def _load_module(name: str, path: Path):
|
|
46
|
+
spec = importlib.util.spec_from_file_location(name, path)
|
|
47
|
+
if spec is None or spec.loader is None:
|
|
48
|
+
raise RuntimeError(f"cannot load required HTTP publisher dependency: {path.name}")
|
|
49
|
+
module = importlib.util.module_from_spec(spec)
|
|
50
|
+
sys.modules[spec.name] = module
|
|
51
|
+
spec.loader.exec_module(module)
|
|
52
|
+
return module
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
_ROOT = Path(__file__).resolve().parents[1]
|
|
56
|
+
_CMS = _load_module(
|
|
57
|
+
"blun_website_localization_http_publisher_cms",
|
|
58
|
+
_ROOT / "integrations" / "website_localization_cms.py",
|
|
59
|
+
)
|
|
60
|
+
REQUEST_SCHEMA = _CMS.PUBLICATION_HTTP_REQUEST_SCHEMA
|
|
61
|
+
RESPONSE_SCHEMA = _CMS.PUBLICATION_HTTP_RESPONSE_SCHEMA
|
|
62
|
+
TOMBSTONE_REQUEST_SCHEMA = _CMS.TOMBSTONE_HTTP_REQUEST_SCHEMA
|
|
63
|
+
TOMBSTONE_RESPONSE_SCHEMA = _CMS.TOMBSTONE_HTTP_RESPONSE_SCHEMA
|
|
64
|
+
HEALTH_SCHEMA = _CMS.PUBLICATION_HEALTH_SCHEMA
|
|
65
|
+
HEALTH_ACK_SCHEMA = _CMS.PUBLICATION_HEALTH_ACK_SCHEMA
|
|
66
|
+
HEALTH_REQUEST_SCHEMA = _CMS.PUBLICATION_HEALTH_HTTP_REQUEST_SCHEMA
|
|
67
|
+
HEALTH_RESPONSE_SCHEMA = _CMS.PUBLICATION_HEALTH_HTTP_RESPONSE_SCHEMA
|
|
68
|
+
RESERVED_HEADERS.update(
|
|
69
|
+
name.lower() for name, _ in _CMS.PUBLICATION_HTTP_BINDING_HEADERS
|
|
70
|
+
)
|
|
71
|
+
RESERVED_HEADERS.update(
|
|
72
|
+
name.lower() for name, _ in _CMS.PUBLICATION_HEALTH_HTTP_BINDING_HEADERS
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
class HTTPPublisherFailed(RuntimeError):
|
|
77
|
+
"""Content-free failure understood by the durable CMS outbox."""
|
|
78
|
+
|
|
79
|
+
cms_publish_failure = True
|
|
80
|
+
|
|
81
|
+
def __init__(self, code: str, *, retryable: bool):
|
|
82
|
+
if not isinstance(code, str) or ERROR_CODE.fullmatch(code) is None:
|
|
83
|
+
raise ValueError("HTTP publisher error code is invalid")
|
|
84
|
+
if not isinstance(retryable, bool):
|
|
85
|
+
raise ValueError("HTTP publisher retryability must be boolean")
|
|
86
|
+
super().__init__(code)
|
|
87
|
+
self.code = code
|
|
88
|
+
self.retryable = retryable
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
@dataclass(frozen=True)
|
|
92
|
+
class HTTPResult:
|
|
93
|
+
status: int
|
|
94
|
+
headers: tuple[tuple[str, str], ...]
|
|
95
|
+
body: bytes
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
class HTTPTransport(Protocol):
|
|
99
|
+
def post(
|
|
100
|
+
self,
|
|
101
|
+
url: str,
|
|
102
|
+
headers: Mapping[str, str],
|
|
103
|
+
body: bytes,
|
|
104
|
+
*,
|
|
105
|
+
timeout: float,
|
|
106
|
+
) -> HTTPResult: ...
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
class _NoRedirect(urllib.request.HTTPRedirectHandler):
|
|
110
|
+
def redirect_request(self, req, fp, code, msg, headers, newurl):
|
|
111
|
+
return None
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
class URLTransport:
|
|
115
|
+
"""One bounded stdlib HTTP attempt with redirects disabled."""
|
|
116
|
+
|
|
117
|
+
def __init__(self):
|
|
118
|
+
self._opener = urllib.request.build_opener(_NoRedirect)
|
|
119
|
+
|
|
120
|
+
def post(
|
|
121
|
+
self,
|
|
122
|
+
url: str,
|
|
123
|
+
headers: Mapping[str, str],
|
|
124
|
+
body: bytes,
|
|
125
|
+
*,
|
|
126
|
+
timeout: float,
|
|
127
|
+
) -> HTTPResult:
|
|
128
|
+
request = urllib.request.Request(
|
|
129
|
+
url,
|
|
130
|
+
data=body,
|
|
131
|
+
headers=dict(headers),
|
|
132
|
+
method="POST",
|
|
133
|
+
)
|
|
134
|
+
try:
|
|
135
|
+
response = self._opener.open(request, timeout=timeout)
|
|
136
|
+
except urllib.error.HTTPError as error:
|
|
137
|
+
response_headers = tuple(error.headers.items()) if error.headers is not None else ()
|
|
138
|
+
return HTTPResult(int(error.code), response_headers, b"")
|
|
139
|
+
except (urllib.error.URLError, TimeoutError, socket.timeout, OSError):
|
|
140
|
+
raise HTTPPublisherFailed("network", retryable=True) from None
|
|
141
|
+
try:
|
|
142
|
+
body = response.read(MAX_RESPONSE_BYTES + 1)
|
|
143
|
+
return HTTPResult(int(response.status), tuple(response.headers.items()), body)
|
|
144
|
+
except (TimeoutError, socket.timeout, OSError):
|
|
145
|
+
raise HTTPPublisherFailed("network", retryable=True) from None
|
|
146
|
+
finally:
|
|
147
|
+
response.close()
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
def _canonical_json(value: Any, *, code: str, maximum: int) -> bytes:
|
|
151
|
+
try:
|
|
152
|
+
encoded = json.dumps(
|
|
153
|
+
value,
|
|
154
|
+
ensure_ascii=False,
|
|
155
|
+
allow_nan=False,
|
|
156
|
+
sort_keys=True,
|
|
157
|
+
separators=(",", ":"),
|
|
158
|
+
).encode("utf-8")
|
|
159
|
+
except (TypeError, ValueError, RecursionError):
|
|
160
|
+
raise HTTPPublisherFailed(code, retryable=False) from None
|
|
161
|
+
if not encoded or len(encoded) > maximum:
|
|
162
|
+
raise HTTPPublisherFailed(code, retryable=False)
|
|
163
|
+
return encoded
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
def _pairs(items):
|
|
167
|
+
result = {}
|
|
168
|
+
for key, value in items:
|
|
169
|
+
if key in result:
|
|
170
|
+
raise ValueError("duplicate JSON key")
|
|
171
|
+
result[key] = value
|
|
172
|
+
return result
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
def _constant(value):
|
|
176
|
+
raise ValueError("non-finite JSON number")
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
def _endpoint(value: Any, allow_loopback_http: bool) -> str:
|
|
180
|
+
if (
|
|
181
|
+
not isinstance(value, str)
|
|
182
|
+
or value != value.strip()
|
|
183
|
+
or not value
|
|
184
|
+
or not value.isascii()
|
|
185
|
+
or len(value) > MAX_ENDPOINT_LENGTH
|
|
186
|
+
or any(ord(character) <= 32 or ord(character) == 127 for character in value)
|
|
187
|
+
):
|
|
188
|
+
raise ValueError("endpoint is invalid")
|
|
189
|
+
try:
|
|
190
|
+
parsed = urllib.parse.urlsplit(value)
|
|
191
|
+
hostname = parsed.hostname
|
|
192
|
+
port = parsed.port
|
|
193
|
+
except ValueError:
|
|
194
|
+
raise ValueError("endpoint is invalid") from None
|
|
195
|
+
if (
|
|
196
|
+
not hostname
|
|
197
|
+
or not hostname.isascii()
|
|
198
|
+
or parsed.username is not None
|
|
199
|
+
or parsed.password is not None
|
|
200
|
+
or parsed.query
|
|
201
|
+
or parsed.fragment
|
|
202
|
+
or not parsed.path.startswith("/")
|
|
203
|
+
or parsed.path.startswith("//")
|
|
204
|
+
):
|
|
205
|
+
raise ValueError("endpoint is invalid")
|
|
206
|
+
if parsed.scheme == "https":
|
|
207
|
+
pass
|
|
208
|
+
elif (
|
|
209
|
+
parsed.scheme == "http"
|
|
210
|
+
and allow_loopback_http
|
|
211
|
+
and hostname.lower() in {"localhost", "127.0.0.1", "::1"}
|
|
212
|
+
):
|
|
213
|
+
pass
|
|
214
|
+
else:
|
|
215
|
+
raise ValueError("endpoint must use HTTPS")
|
|
216
|
+
if port is not None and not 1 <= port <= 65535:
|
|
217
|
+
raise ValueError("endpoint is invalid")
|
|
218
|
+
return value
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
def _authentication_headers(provider: Callable[[], Mapping[str, str]]) -> dict[str, str]:
|
|
222
|
+
try:
|
|
223
|
+
supplied = provider()
|
|
224
|
+
except Exception:
|
|
225
|
+
raise HTTPPublisherFailed("authentication", retryable=False) from None
|
|
226
|
+
if not isinstance(supplied, Mapping) or not supplied:
|
|
227
|
+
raise HTTPPublisherFailed("authentication", retryable=False)
|
|
228
|
+
result: dict[str, str] = {}
|
|
229
|
+
normalized_names: set[str] = set()
|
|
230
|
+
for name, value in supplied.items():
|
|
231
|
+
normalized = name.lower() if isinstance(name, str) else ""
|
|
232
|
+
if (
|
|
233
|
+
not isinstance(name, str)
|
|
234
|
+
or HEADER_NAME.fullmatch(name) is None
|
|
235
|
+
or normalized in RESERVED_HEADERS
|
|
236
|
+
or normalized in normalized_names
|
|
237
|
+
):
|
|
238
|
+
raise HTTPPublisherFailed("authentication", retryable=False)
|
|
239
|
+
if (
|
|
240
|
+
not isinstance(value, str)
|
|
241
|
+
or not value
|
|
242
|
+
or not value.isascii()
|
|
243
|
+
or len(value) > MAX_HEADER_VALUE_LENGTH
|
|
244
|
+
or "\r" in value
|
|
245
|
+
or "\n" in value
|
|
246
|
+
or any(ord(character) < 32 or ord(character) == 127 for character in value)
|
|
247
|
+
):
|
|
248
|
+
raise HTTPPublisherFailed("authentication", retryable=False)
|
|
249
|
+
normalized_names.add(normalized)
|
|
250
|
+
result[name] = value
|
|
251
|
+
return result
|
|
252
|
+
|
|
253
|
+
|
|
254
|
+
def _signature(value: Any) -> tuple[_CMS.CMSMessageSignature, dict[str, str]]:
|
|
255
|
+
try:
|
|
256
|
+
algorithm = value.algorithm
|
|
257
|
+
key_id = value.key_id
|
|
258
|
+
signature = value.signature
|
|
259
|
+
except Exception:
|
|
260
|
+
raise HTTPPublisherFailed("request_invalid", retryable=False) from None
|
|
261
|
+
if (
|
|
262
|
+
not isinstance(algorithm, str)
|
|
263
|
+
or TOKEN.fullmatch(algorithm) is None
|
|
264
|
+
or not isinstance(key_id, str)
|
|
265
|
+
or TOKEN.fullmatch(key_id) is None
|
|
266
|
+
or not isinstance(signature, str)
|
|
267
|
+
or SIGNATURE_VALUE.fullmatch(signature) is None
|
|
268
|
+
):
|
|
269
|
+
raise HTTPPublisherFailed("request_invalid", retryable=False)
|
|
270
|
+
return (
|
|
271
|
+
_CMS.CMSMessageSignature(algorithm, key_id, signature),
|
|
272
|
+
{"algorithm": algorithm, "key_id": key_id, "signature": signature},
|
|
273
|
+
)
|
|
274
|
+
|
|
275
|
+
|
|
276
|
+
def _response_headers(value: Any) -> dict[str, str]:
|
|
277
|
+
if not isinstance(value, tuple):
|
|
278
|
+
raise HTTPPublisherFailed("transport_invalid", retryable=True)
|
|
279
|
+
result: dict[str, str] = {}
|
|
280
|
+
for item in value:
|
|
281
|
+
if (
|
|
282
|
+
not isinstance(item, tuple)
|
|
283
|
+
or len(item) != 2
|
|
284
|
+
or not all(isinstance(part, str) for part in item)
|
|
285
|
+
):
|
|
286
|
+
raise HTTPPublisherFailed("transport_invalid", retryable=True)
|
|
287
|
+
name, content = item[0].lower(), item[1].strip()
|
|
288
|
+
if name in {"content-type", "content-length"}:
|
|
289
|
+
if name in result:
|
|
290
|
+
raise HTTPPublisherFailed("response_headers", retryable=True)
|
|
291
|
+
result[name] = content
|
|
292
|
+
return result
|
|
293
|
+
|
|
294
|
+
|
|
295
|
+
class HTTPPublisherAdapter:
|
|
296
|
+
"""Publish one exact signed outbox bundle and verify one signed CMS acknowledgement."""
|
|
297
|
+
|
|
298
|
+
def __init__(
|
|
299
|
+
self,
|
|
300
|
+
endpoint: str,
|
|
301
|
+
authentication_headers: Callable[[], Mapping[str, str]],
|
|
302
|
+
acknowledgement_verifier: Any,
|
|
303
|
+
*,
|
|
304
|
+
transport: HTTPTransport | None = None,
|
|
305
|
+
timeout: float = 30.0,
|
|
306
|
+
allow_loopback_http: bool = False,
|
|
307
|
+
probe_id_factory: Callable[[], str] | None = None,
|
|
308
|
+
):
|
|
309
|
+
if not isinstance(allow_loopback_http, bool):
|
|
310
|
+
raise TypeError("allow_loopback_http must be boolean")
|
|
311
|
+
self.endpoint = _endpoint(endpoint, allow_loopback_http)
|
|
312
|
+
if not callable(authentication_headers):
|
|
313
|
+
raise TypeError("authentication_headers must be callable")
|
|
314
|
+
if not callable(getattr(acknowledgement_verifier, "verify", None)):
|
|
315
|
+
raise TypeError("acknowledgement_verifier must provide verify")
|
|
316
|
+
if (
|
|
317
|
+
isinstance(timeout, bool)
|
|
318
|
+
or not isinstance(timeout, (int, float))
|
|
319
|
+
or not 0 < timeout <= 300
|
|
320
|
+
):
|
|
321
|
+
raise ValueError("timeout is outside the supported range")
|
|
322
|
+
self.authentication_headers = authentication_headers
|
|
323
|
+
self.acknowledgement_verifier = acknowledgement_verifier
|
|
324
|
+
self.transport = URLTransport() if transport is None else transport
|
|
325
|
+
if not callable(getattr(self.transport, "post", None)):
|
|
326
|
+
raise TypeError("transport must provide post")
|
|
327
|
+
self.timeout = float(timeout)
|
|
328
|
+
self.probe_id_factory = (
|
|
329
|
+
secrets.token_hex if probe_id_factory is None else probe_id_factory
|
|
330
|
+
)
|
|
331
|
+
if not callable(self.probe_id_factory):
|
|
332
|
+
raise TypeError("probe_id_factory must be callable")
|
|
333
|
+
|
|
334
|
+
def publish(self, request: Any) -> Mapping[str, Any]:
|
|
335
|
+
try:
|
|
336
|
+
delivery_id = request.delivery_id
|
|
337
|
+
payload = request.payload
|
|
338
|
+
payload_sha256 = request.payload_sha256
|
|
339
|
+
raw_signature = request.signature
|
|
340
|
+
except Exception:
|
|
341
|
+
raise HTTPPublisherFailed("request_invalid", retryable=False) from None
|
|
342
|
+
tombstone = isinstance(payload, dict) and payload.get("schema") == _CMS.TOMBSTONE_DELIVERY_SCHEMA
|
|
343
|
+
expected_fields = TOMBSTONE_FIELDS if tombstone else PUBLICATION_FIELDS
|
|
344
|
+
if (
|
|
345
|
+
not isinstance(delivery_id, str)
|
|
346
|
+
or TOKEN.fullmatch(delivery_id) is None
|
|
347
|
+
or not isinstance(payload_sha256, str)
|
|
348
|
+
or re.fullmatch(r"[a-f0-9]{64}", payload_sha256) is None
|
|
349
|
+
or not isinstance(payload, dict)
|
|
350
|
+
or set(payload) != expected_fields
|
|
351
|
+
or payload.get("schema") not in {
|
|
352
|
+
_CMS.PUBLICATION_SCHEMA, _CMS.TOMBSTONE_DELIVERY_SCHEMA,
|
|
353
|
+
}
|
|
354
|
+
or payload.get("delivery_id") != delivery_id
|
|
355
|
+
or isinstance(payload.get("source_sequence"), bool)
|
|
356
|
+
or not isinstance(payload.get("source_sequence"), int)
|
|
357
|
+
or payload["source_sequence"] <= 0
|
|
358
|
+
or not isinstance(
|
|
359
|
+
payload.get("locales") if tombstone else payload.get("localizations"),
|
|
360
|
+
list,
|
|
361
|
+
)
|
|
362
|
+
or not (payload.get("locales") if tombstone else payload.get("localizations"))
|
|
363
|
+
):
|
|
364
|
+
raise HTTPPublisherFailed("request_invalid", retryable=False)
|
|
365
|
+
if tombstone and (
|
|
366
|
+
payload["locales"] != sorted(set(payload["locales"]))
|
|
367
|
+
or any(
|
|
368
|
+
not isinstance(locale, str) or TOKEN.fullmatch(locale) is None
|
|
369
|
+
for locale in payload["locales"]
|
|
370
|
+
)
|
|
371
|
+
or not isinstance(payload.get("publication_payload_sha256"), str)
|
|
372
|
+
or re.fullmatch(
|
|
373
|
+
r"[a-f0-9]{64}", payload["publication_payload_sha256"],
|
|
374
|
+
) is None
|
|
375
|
+
):
|
|
376
|
+
raise HTTPPublisherFailed("request_invalid", retryable=False)
|
|
377
|
+
payload_bytes = _canonical_json(
|
|
378
|
+
payload,
|
|
379
|
+
code="request_invalid",
|
|
380
|
+
maximum=MAX_REQUEST_BYTES,
|
|
381
|
+
)
|
|
382
|
+
if hashlib.sha256(payload_bytes).hexdigest() != payload_sha256:
|
|
383
|
+
raise HTTPPublisherFailed("request_binding", retryable=False)
|
|
384
|
+
_, signature_payload = _signature(raw_signature)
|
|
385
|
+
body = _canonical_json(
|
|
386
|
+
{
|
|
387
|
+
"schema": TOMBSTONE_REQUEST_SCHEMA if tombstone else REQUEST_SCHEMA,
|
|
388
|
+
"payload_sha256": payload_sha256,
|
|
389
|
+
"tombstone" if tombstone else "publication": payload,
|
|
390
|
+
"signature": signature_payload,
|
|
391
|
+
},
|
|
392
|
+
code="request_invalid",
|
|
393
|
+
maximum=MAX_REQUEST_BYTES,
|
|
394
|
+
)
|
|
395
|
+
headers = _authentication_headers(self.authentication_headers)
|
|
396
|
+
binding_values = {
|
|
397
|
+
"delivery_id": delivery_id,
|
|
398
|
+
"payload_sha256": payload_sha256,
|
|
399
|
+
}
|
|
400
|
+
try:
|
|
401
|
+
protocol_headers = {
|
|
402
|
+
name: binding_values[binding]
|
|
403
|
+
for name, binding in _CMS.PUBLICATION_HTTP_BINDING_HEADERS
|
|
404
|
+
}
|
|
405
|
+
except (KeyError, TypeError, ValueError):
|
|
406
|
+
raise HTTPPublisherFailed("request_invalid", retryable=False) from None
|
|
407
|
+
headers.update({
|
|
408
|
+
"Accept": "application/json",
|
|
409
|
+
"Accept-Encoding": "identity",
|
|
410
|
+
"Content-Type": "application/json; charset=utf-8",
|
|
411
|
+
**protocol_headers,
|
|
412
|
+
})
|
|
413
|
+
envelope = self._post_json(headers, body)
|
|
414
|
+
if not isinstance(envelope, dict) or set(envelope) != {
|
|
415
|
+
"schema", "acknowledgement", "signature",
|
|
416
|
+
}:
|
|
417
|
+
raise HTTPPublisherFailed("acknowledgement_invalid", retryable=False)
|
|
418
|
+
acknowledgement = envelope["acknowledgement"]
|
|
419
|
+
expected = {
|
|
420
|
+
"schema": _CMS.TOMBSTONE_ACK_SCHEMA if tombstone else _CMS.ACK_SCHEMA,
|
|
421
|
+
"delivery_id": delivery_id,
|
|
422
|
+
"payload_sha256": payload_sha256,
|
|
423
|
+
"status": "deleted" if tombstone else "accepted",
|
|
424
|
+
}
|
|
425
|
+
expected_response_schema = (
|
|
426
|
+
TOMBSTONE_RESPONSE_SCHEMA if tombstone else RESPONSE_SCHEMA
|
|
427
|
+
)
|
|
428
|
+
if envelope["schema"] != expected_response_schema or acknowledgement != expected:
|
|
429
|
+
raise HTTPPublisherFailed("acknowledgement_binding", retryable=False)
|
|
430
|
+
signature_mapping = envelope["signature"]
|
|
431
|
+
if not isinstance(signature_mapping, dict) or set(signature_mapping) != {
|
|
432
|
+
"algorithm", "key_id", "signature",
|
|
433
|
+
}:
|
|
434
|
+
raise HTTPPublisherFailed("acknowledgement_invalid", retryable=False)
|
|
435
|
+
try:
|
|
436
|
+
acknowledgement_signature, _ = _signature(type("Signature", (), signature_mapping)())
|
|
437
|
+
except HTTPPublisherFailed:
|
|
438
|
+
raise HTTPPublisherFailed("acknowledgement_invalid", retryable=False) from None
|
|
439
|
+
acknowledgement_bytes = _canonical_json(
|
|
440
|
+
acknowledgement,
|
|
441
|
+
code="acknowledgement_invalid",
|
|
442
|
+
maximum=MAX_RESPONSE_BYTES,
|
|
443
|
+
)
|
|
444
|
+
try:
|
|
445
|
+
verified = self.acknowledgement_verifier.verify(
|
|
446
|
+
acknowledgement_bytes,
|
|
447
|
+
acknowledgement_signature,
|
|
448
|
+
) is True
|
|
449
|
+
except Exception:
|
|
450
|
+
verified = False
|
|
451
|
+
if not verified:
|
|
452
|
+
raise HTTPPublisherFailed("acknowledgement_signature", retryable=False)
|
|
453
|
+
return acknowledgement
|
|
454
|
+
|
|
455
|
+
def check(self, *, contract_sha256: str) -> Mapping[str, Any]:
|
|
456
|
+
"""Challenge the configured receiver without sending localized content."""
|
|
457
|
+
if (
|
|
458
|
+
not isinstance(contract_sha256, str)
|
|
459
|
+
or re.fullmatch(r"[a-f0-9]{64}", contract_sha256) is None
|
|
460
|
+
):
|
|
461
|
+
raise HTTPPublisherFailed("health_request_invalid", retryable=False)
|
|
462
|
+
try:
|
|
463
|
+
probe_id = self.probe_id_factory()
|
|
464
|
+
except Exception:
|
|
465
|
+
raise HTTPPublisherFailed("health_request_invalid", retryable=False) from None
|
|
466
|
+
if (
|
|
467
|
+
not isinstance(probe_id, str)
|
|
468
|
+
or not 16 <= len(probe_id) <= 256
|
|
469
|
+
or TOKEN.fullmatch(probe_id) is None
|
|
470
|
+
):
|
|
471
|
+
raise HTTPPublisherFailed("health_request_invalid", retryable=False)
|
|
472
|
+
probe = {
|
|
473
|
+
"schema": HEALTH_SCHEMA,
|
|
474
|
+
"probe_id": probe_id,
|
|
475
|
+
"contract_sha256": contract_sha256,
|
|
476
|
+
}
|
|
477
|
+
body = _canonical_json(
|
|
478
|
+
{"schema": HEALTH_REQUEST_SCHEMA, "probe": probe},
|
|
479
|
+
code="health_request_invalid",
|
|
480
|
+
maximum=MAX_REQUEST_BYTES,
|
|
481
|
+
)
|
|
482
|
+
headers = _authentication_headers(self.authentication_headers)
|
|
483
|
+
bindings = {"probe_id": probe_id, "contract_sha256": contract_sha256}
|
|
484
|
+
try:
|
|
485
|
+
protocol_headers = {
|
|
486
|
+
name: bindings[binding]
|
|
487
|
+
for name, binding in _CMS.PUBLICATION_HEALTH_HTTP_BINDING_HEADERS
|
|
488
|
+
}
|
|
489
|
+
except (KeyError, TypeError, ValueError):
|
|
490
|
+
raise HTTPPublisherFailed("health_request_invalid", retryable=False) from None
|
|
491
|
+
headers.update({
|
|
492
|
+
"Accept": "application/json",
|
|
493
|
+
"Accept-Encoding": "identity",
|
|
494
|
+
"Content-Type": "application/json; charset=utf-8",
|
|
495
|
+
**protocol_headers,
|
|
496
|
+
})
|
|
497
|
+
envelope = self._post_json(headers, body)
|
|
498
|
+
if not isinstance(envelope, dict) or set(envelope) != {
|
|
499
|
+
"schema", "acknowledgement", "signature",
|
|
500
|
+
}:
|
|
501
|
+
raise HTTPPublisherFailed(
|
|
502
|
+
"health_acknowledgement_invalid", retryable=False,
|
|
503
|
+
)
|
|
504
|
+
acknowledgement = envelope["acknowledgement"]
|
|
505
|
+
expected = {
|
|
506
|
+
"schema": HEALTH_ACK_SCHEMA,
|
|
507
|
+
"probe_id": probe_id,
|
|
508
|
+
"contract_sha256": contract_sha256,
|
|
509
|
+
"status": "healthy",
|
|
510
|
+
}
|
|
511
|
+
if envelope["schema"] != HEALTH_RESPONSE_SCHEMA or acknowledgement != expected:
|
|
512
|
+
raise HTTPPublisherFailed(
|
|
513
|
+
"health_acknowledgement_binding", retryable=False,
|
|
514
|
+
)
|
|
515
|
+
signature_mapping = envelope["signature"]
|
|
516
|
+
if not isinstance(signature_mapping, dict) or set(signature_mapping) != {
|
|
517
|
+
"algorithm", "key_id", "signature",
|
|
518
|
+
}:
|
|
519
|
+
raise HTTPPublisherFailed(
|
|
520
|
+
"health_acknowledgement_invalid", retryable=False,
|
|
521
|
+
)
|
|
522
|
+
try:
|
|
523
|
+
signature, _ = _signature(type("Signature", (), signature_mapping)())
|
|
524
|
+
acknowledgement_bytes = _canonical_json(
|
|
525
|
+
acknowledgement,
|
|
526
|
+
code="health_acknowledgement_invalid",
|
|
527
|
+
maximum=MAX_RESPONSE_BYTES,
|
|
528
|
+
)
|
|
529
|
+
verified = self.acknowledgement_verifier.verify(
|
|
530
|
+
acknowledgement_bytes, signature,
|
|
531
|
+
) is True
|
|
532
|
+
except Exception:
|
|
533
|
+
verified = False
|
|
534
|
+
if not verified:
|
|
535
|
+
raise HTTPPublisherFailed(
|
|
536
|
+
"health_acknowledgement_signature", retryable=False,
|
|
537
|
+
)
|
|
538
|
+
return acknowledgement
|
|
539
|
+
|
|
540
|
+
def _post_json(self, headers: Mapping[str, str], body: bytes) -> Any:
|
|
541
|
+
try:
|
|
542
|
+
result = self.transport.post(
|
|
543
|
+
self.endpoint, headers, body, timeout=self.timeout,
|
|
544
|
+
)
|
|
545
|
+
except HTTPPublisherFailed:
|
|
546
|
+
raise
|
|
547
|
+
except Exception:
|
|
548
|
+
raise HTTPPublisherFailed("network", retryable=True) from None
|
|
549
|
+
if (
|
|
550
|
+
not isinstance(result, HTTPResult)
|
|
551
|
+
or isinstance(result.status, bool)
|
|
552
|
+
or not isinstance(result.status, int)
|
|
553
|
+
or not 100 <= result.status <= 599
|
|
554
|
+
):
|
|
555
|
+
raise HTTPPublisherFailed("transport_invalid", retryable=True)
|
|
556
|
+
if result.status != 200:
|
|
557
|
+
if 300 <= result.status <= 399:
|
|
558
|
+
raise HTTPPublisherFailed("redirect", retryable=False)
|
|
559
|
+
retryable = result.status in {408, 425, 429} or 500 <= result.status <= 599
|
|
560
|
+
raise HTTPPublisherFailed("http_status", retryable=retryable)
|
|
561
|
+
response_headers = _response_headers(result.headers)
|
|
562
|
+
content_type = (
|
|
563
|
+
response_headers.get("content-type", "").lower().replace(" ", "")
|
|
564
|
+
)
|
|
565
|
+
if content_type not in {"application/json", "application/json;charset=utf-8"}:
|
|
566
|
+
raise HTTPPublisherFailed("response_content_type", retryable=True)
|
|
567
|
+
if (
|
|
568
|
+
not isinstance(result.body, bytes)
|
|
569
|
+
or not result.body
|
|
570
|
+
or len(result.body) > MAX_RESPONSE_BYTES
|
|
571
|
+
):
|
|
572
|
+
raise HTTPPublisherFailed("response_size", retryable=True)
|
|
573
|
+
declared = response_headers.get("content-length")
|
|
574
|
+
if declared is not None and (
|
|
575
|
+
not declared.isascii()
|
|
576
|
+
or not declared.isdecimal()
|
|
577
|
+
or int(declared) != len(result.body)
|
|
578
|
+
):
|
|
579
|
+
raise HTTPPublisherFailed("response_size", retryable=True)
|
|
580
|
+
try:
|
|
581
|
+
text = result.body.decode("utf-8")
|
|
582
|
+
if text.startswith("\ufeff"):
|
|
583
|
+
raise ValueError("BOM rejected")
|
|
584
|
+
return json.loads(
|
|
585
|
+
text, object_pairs_hook=_pairs, parse_constant=_constant,
|
|
586
|
+
)
|
|
587
|
+
except (UnicodeDecodeError, json.JSONDecodeError, ValueError, RecursionError):
|
|
588
|
+
raise HTTPPublisherFailed("response_json", retryable=True) from None
|