@adcp/sdk 8.1.0-beta.13 → 8.1.0-beta.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/bin/adcp-registry.js +2 -2
- package/dist/lib/canonical-references/index.d.ts +107 -0
- package/dist/lib/canonical-references/index.d.ts.map +1 -0
- package/dist/lib/canonical-references/index.js +551 -0
- package/dist/lib/canonical-references/index.js.map +1 -0
- package/dist/lib/core/ConversationTypes.d.ts +7 -0
- package/dist/lib/core/ConversationTypes.d.ts.map +1 -1
- package/dist/lib/core/ProtocolResponseParser.d.ts +10 -0
- package/dist/lib/core/ProtocolResponseParser.d.ts.map +1 -1
- package/dist/lib/core/ProtocolResponseParser.js +110 -0
- package/dist/lib/core/ProtocolResponseParser.js.map +1 -1
- package/dist/lib/core/ResponseValidator.d.ts +2 -0
- package/dist/lib/core/ResponseValidator.d.ts.map +1 -1
- package/dist/lib/core/ResponseValidator.js +3 -3
- package/dist/lib/core/ResponseValidator.js.map +1 -1
- package/dist/lib/core/TaskExecutor.d.ts +2 -0
- package/dist/lib/core/TaskExecutor.d.ts.map +1 -1
- package/dist/lib/core/TaskExecutor.js +32 -8
- package/dist/lib/core/TaskExecutor.js.map +1 -1
- package/dist/lib/index.d.ts +5 -4
- package/dist/lib/index.d.ts.map +1 -1
- package/dist/lib/index.js +27 -11
- package/dist/lib/index.js.map +1 -1
- package/dist/lib/mock-server/creative-ad-server/server.d.ts +2 -0
- package/dist/lib/mock-server/creative-ad-server/server.d.ts.map +1 -1
- package/dist/lib/mock-server/creative-ad-server/server.js +37 -1
- package/dist/lib/mock-server/creative-ad-server/server.js.map +1 -1
- package/dist/lib/mock-server/creative-template/server.d.ts +2 -0
- package/dist/lib/mock-server/creative-template/server.d.ts.map +1 -1
- package/dist/lib/mock-server/creative-template/server.js +29 -2
- package/dist/lib/mock-server/creative-template/server.js.map +1 -1
- package/dist/lib/mock-server/index.d.ts +10 -1
- package/dist/lib/mock-server/index.d.ts.map +1 -1
- package/dist/lib/mock-server/index.js +38 -8
- package/dist/lib/mock-server/index.js.map +1 -1
- package/dist/lib/mock-server/sales-guaranteed/server.d.ts +2 -0
- package/dist/lib/mock-server/sales-guaranteed/server.d.ts.map +1 -1
- package/dist/lib/mock-server/sales-guaranteed/server.js +64 -7
- package/dist/lib/mock-server/sales-guaranteed/server.js.map +1 -1
- package/dist/lib/mock-server/sales-non-guaranteed/server.d.ts +2 -0
- package/dist/lib/mock-server/sales-non-guaranteed/server.d.ts.map +1 -1
- package/dist/lib/mock-server/sales-non-guaranteed/server.js +44 -1
- package/dist/lib/mock-server/sales-non-guaranteed/server.js.map +1 -1
- package/dist/lib/mock-server/sales-social/server.d.ts +2 -0
- package/dist/lib/mock-server/sales-social/server.d.ts.map +1 -1
- package/dist/lib/mock-server/sales-social/server.js +64 -4
- package/dist/lib/mock-server/sales-social/server.js.map +1 -1
- package/dist/lib/mock-server/scenario.d.ts +97 -0
- package/dist/lib/mock-server/scenario.d.ts.map +1 -0
- package/dist/lib/mock-server/scenario.js +464 -0
- package/dist/lib/mock-server/scenario.js.map +1 -0
- package/dist/lib/mock-server/signal-marketplace/server.d.ts +2 -0
- package/dist/lib/mock-server/signal-marketplace/server.d.ts.map +1 -1
- package/dist/lib/mock-server/signal-marketplace/server.js +29 -1
- package/dist/lib/mock-server/signal-marketplace/server.js.map +1 -1
- package/dist/lib/mock-server/sponsored-intelligence/server.d.ts +2 -0
- package/dist/lib/mock-server/sponsored-intelligence/server.d.ts.map +1 -1
- package/dist/lib/mock-server/sponsored-intelligence/server.js +47 -9
- package/dist/lib/mock-server/sponsored-intelligence/server.js.map +1 -1
- package/dist/lib/protocols/index.d.ts +4 -2
- package/dist/lib/protocols/index.d.ts.map +1 -1
- package/dist/lib/protocols/index.js +10 -3
- package/dist/lib/protocols/index.js.map +1 -1
- package/dist/lib/registry/index.d.ts +42 -16
- package/dist/lib/registry/index.d.ts.map +1 -1
- package/dist/lib/registry/index.js +191 -24
- package/dist/lib/registry/index.js.map +1 -1
- package/dist/lib/registry/types.d.ts +39 -8
- package/dist/lib/registry/types.d.ts.map +1 -1
- package/dist/lib/registry/types.generated.d.ts +2873 -699
- package/dist/lib/registry/types.generated.d.ts.map +1 -1
- package/dist/lib/registry/types.generated.js +2 -2
- package/dist/lib/registry/types.generated.js.map +1 -1
- package/dist/lib/schemas-data/v2.5/_provenance.json +1 -1
- package/dist/lib/server/a2a-adapter.d.ts +3 -1
- package/dist/lib/server/a2a-adapter.d.ts.map +1 -1
- package/dist/lib/server/a2a-adapter.js +11 -2
- package/dist/lib/server/a2a-adapter.js.map +1 -1
- package/dist/lib/server/adcp-server.js +32 -0
- package/dist/lib/server/adcp-server.js.map +1 -1
- package/dist/lib/server/create-adcp-server.d.ts +12 -6
- package/dist/lib/server/create-adcp-server.d.ts.map +1 -1
- package/dist/lib/server/create-adcp-server.js +72 -11
- package/dist/lib/server/create-adcp-server.js.map +1 -1
- package/dist/lib/server/decisioning/account.d.ts +17 -17
- package/dist/lib/server/decisioning/account.d.ts.map +1 -1
- package/dist/lib/server/decisioning/account.js.map +1 -1
- package/dist/lib/server/decisioning/buyer-agent.d.ts +27 -10
- package/dist/lib/server/decisioning/buyer-agent.d.ts.map +1 -1
- package/dist/lib/server/decisioning/buyer-agent.js +25 -7
- package/dist/lib/server/decisioning/buyer-agent.js.map +1 -1
- package/dist/lib/server/decisioning/capabilities.d.ts +35 -7
- package/dist/lib/server/decisioning/capabilities.d.ts.map +1 -1
- package/dist/lib/server/decisioning/errors-typed.d.ts +18 -16
- package/dist/lib/server/decisioning/errors-typed.d.ts.map +1 -1
- package/dist/lib/server/decisioning/errors-typed.js +26 -24
- package/dist/lib/server/decisioning/errors-typed.js.map +1 -1
- package/dist/lib/server/decisioning/index.d.ts +1 -1
- package/dist/lib/server/decisioning/index.d.ts.map +1 -1
- package/dist/lib/server/decisioning/index.js +4 -2
- package/dist/lib/server/decisioning/index.js.map +1 -1
- package/dist/lib/server/decisioning/platform-helpers.d.ts +3 -2
- package/dist/lib/server/decisioning/platform-helpers.d.ts.map +1 -1
- package/dist/lib/server/decisioning/platform-helpers.js +3 -2
- package/dist/lib/server/decisioning/platform-helpers.js.map +1 -1
- package/dist/lib/server/decisioning/platform.d.ts +27 -10
- package/dist/lib/server/decisioning/platform.d.ts.map +1 -1
- package/dist/lib/server/decisioning/platform.js.map +1 -1
- package/dist/lib/server/decisioning/runtime/from-platform.d.ts +14 -10
- package/dist/lib/server/decisioning/runtime/from-platform.d.ts.map +1 -1
- package/dist/lib/server/decisioning/runtime/from-platform.js +374 -59
- package/dist/lib/server/decisioning/runtime/from-platform.js.map +1 -1
- package/dist/lib/server/decisioning/runtime/validate-platform.d.ts.map +1 -1
- package/dist/lib/server/decisioning/runtime/validate-platform.js +3 -8
- package/dist/lib/server/decisioning/runtime/validate-platform.js.map +1 -1
- package/dist/lib/server/decisioning/specialisms/sponsored-intelligence.d.ts +9 -11
- package/dist/lib/server/decisioning/specialisms/sponsored-intelligence.d.ts.map +1 -1
- package/dist/lib/server/decisioning/specialisms/sponsored-intelligence.js +9 -11
- package/dist/lib/server/decisioning/specialisms/sponsored-intelligence.js.map +1 -1
- package/dist/lib/server/operational-platform.d.ts +6 -8
- package/dist/lib/server/operational-platform.d.ts.map +1 -1
- package/dist/lib/server/operational-platform.js +4 -6
- package/dist/lib/server/operational-platform.js.map +1 -1
- package/dist/lib/server/test-controller-bridge.d.ts +14 -14
- package/dist/lib/server/test-controller-bridge.d.ts.map +1 -1
- package/dist/lib/server/test-controller-bridge.js +16 -16
- package/dist/lib/server/test-controller-bridge.js.map +1 -1
- package/dist/lib/server/test-controller.d.ts +31 -9
- package/dist/lib/server/test-controller.d.ts.map +1 -1
- package/dist/lib/server/test-controller.js +106 -54
- package/dist/lib/server/test-controller.js.map +1 -1
- package/dist/lib/signing/canonicalize.d.ts +0 -53
- package/dist/lib/signing/canonicalize.d.ts.map +1 -1
- package/dist/lib/signing/canonicalize.js +1 -33
- package/dist/lib/signing/canonicalize.js.map +1 -1
- package/dist/lib/signing/client.d.ts +5 -5
- package/dist/lib/signing/client.d.ts.map +1 -1
- package/dist/lib/signing/client.js +1 -10
- package/dist/lib/signing/client.js.map +1 -1
- package/dist/lib/signing/errors.d.ts +0 -11
- package/dist/lib/signing/errors.d.ts.map +1 -1
- package/dist/lib/signing/errors.js +1 -11
- package/dist/lib/signing/errors.js.map +1 -1
- package/dist/lib/signing/jwks-helpers.d.ts +2 -4
- package/dist/lib/signing/jwks-helpers.d.ts.map +1 -1
- package/dist/lib/signing/jwks-helpers.js +9 -0
- package/dist/lib/signing/jwks-helpers.js.map +1 -1
- package/dist/lib/signing/provider.d.ts +11 -10
- package/dist/lib/signing/provider.d.ts.map +1 -1
- package/dist/lib/signing/request-context.d.ts +8 -11
- package/dist/lib/signing/request-context.d.ts.map +1 -1
- package/dist/lib/signing/request-context.js +7 -10
- package/dist/lib/signing/request-context.js.map +1 -1
- package/dist/lib/signing/server.d.ts +3 -4
- package/dist/lib/signing/server.d.ts.map +1 -1
- package/dist/lib/signing/server.js +1 -9
- package/dist/lib/signing/server.js.map +1 -1
- package/dist/lib/signing/signer-async.d.ts +2 -8
- package/dist/lib/signing/signer-async.d.ts.map +1 -1
- package/dist/lib/signing/signer-async.js +0 -12
- package/dist/lib/signing/signer-async.js.map +1 -1
- package/dist/lib/signing/signer.d.ts +4 -111
- package/dist/lib/signing/signer.d.ts.map +1 -1
- package/dist/lib/signing/signer.js +2 -98
- package/dist/lib/signing/signer.js.map +1 -1
- package/dist/lib/signing/testing.d.ts +10 -10
- package/dist/lib/signing/testing.d.ts.map +1 -1
- package/dist/lib/signing/testing.js +6 -13
- package/dist/lib/signing/testing.js.map +1 -1
- package/dist/lib/signing/types.d.ts +0 -36
- package/dist/lib/signing/types.d.ts.map +1 -1
- package/dist/lib/signing/types.js +1 -37
- package/dist/lib/signing/types.js.map +1 -1
- package/dist/lib/testing/agent-tester.d.ts +1 -0
- package/dist/lib/testing/agent-tester.d.ts.map +1 -1
- package/dist/lib/testing/agent-tester.js.map +1 -1
- package/dist/lib/testing/client.d.ts +1 -1
- package/dist/lib/testing/client.d.ts.map +1 -1
- package/dist/lib/testing/client.js +4 -2
- package/dist/lib/testing/client.js.map +1 -1
- package/dist/lib/testing/compliance/comply.d.ts +8 -0
- package/dist/lib/testing/compliance/comply.d.ts.map +1 -1
- package/dist/lib/testing/compliance/comply.js +98 -7
- package/dist/lib/testing/compliance/comply.js.map +1 -1
- package/dist/lib/testing/compliance/spec-conformance.d.ts +6 -6
- package/dist/lib/testing/compliance/spec-conformance.d.ts.map +1 -1
- package/dist/lib/testing/compliance/spec-conformance.js +6 -6
- package/dist/lib/testing/compliance/spec-conformance.js.map +1 -1
- package/dist/lib/testing/compliance/summary.d.ts +5 -0
- package/dist/lib/testing/compliance/summary.d.ts.map +1 -1
- package/dist/lib/testing/compliance/summary.js +17 -0
- package/dist/lib/testing/compliance/summary.js.map +1 -1
- package/dist/lib/testing/comply-controller.d.ts +19 -0
- package/dist/lib/testing/comply-controller.d.ts.map +1 -1
- package/dist/lib/testing/comply-controller.js +11 -8
- package/dist/lib/testing/comply-controller.js.map +1 -1
- package/dist/lib/testing/index.d.ts +4 -3
- package/dist/lib/testing/index.d.ts.map +1 -1
- package/dist/lib/testing/index.js +13 -1
- package/dist/lib/testing/index.js.map +1 -1
- package/dist/lib/testing/storyboard/canonical-format-satisfaction.d.ts +4 -0
- package/dist/lib/testing/storyboard/canonical-format-satisfaction.d.ts.map +1 -0
- package/dist/lib/testing/storyboard/canonical-format-satisfaction.js +881 -0
- package/dist/lib/testing/storyboard/canonical-format-satisfaction.js.map +1 -0
- package/dist/lib/testing/storyboard/compliance.d.ts +6 -0
- package/dist/lib/testing/storyboard/compliance.d.ts.map +1 -1
- package/dist/lib/testing/storyboard/compliance.js +8 -4
- package/dist/lib/testing/storyboard/compliance.js.map +1 -1
- package/dist/lib/testing/storyboard/index.d.ts +2 -1
- package/dist/lib/testing/storyboard/index.d.ts.map +1 -1
- package/dist/lib/testing/storyboard/index.js +9 -1
- package/dist/lib/testing/storyboard/index.js.map +1 -1
- package/dist/lib/testing/storyboard/loader.d.ts +3 -2
- package/dist/lib/testing/storyboard/loader.d.ts.map +1 -1
- package/dist/lib/testing/storyboard/loader.js +73 -2
- package/dist/lib/testing/storyboard/loader.js.map +1 -1
- package/dist/lib/testing/storyboard/path.d.ts +21 -0
- package/dist/lib/testing/storyboard/path.d.ts.map +1 -1
- package/dist/lib/testing/storyboard/path.js +86 -0
- package/dist/lib/testing/storyboard/path.js.map +1 -1
- package/dist/lib/testing/storyboard/rate-limit-trip.d.ts +92 -0
- package/dist/lib/testing/storyboard/rate-limit-trip.d.ts.map +1 -0
- package/dist/lib/testing/storyboard/rate-limit-trip.js +276 -0
- package/dist/lib/testing/storyboard/rate-limit-trip.js.map +1 -0
- package/dist/lib/testing/storyboard/runner.d.ts.map +1 -1
- package/dist/lib/testing/storyboard/runner.js +361 -28
- package/dist/lib/testing/storyboard/runner.js.map +1 -1
- package/dist/lib/testing/storyboard/seeding.d.ts +5 -4
- package/dist/lib/testing/storyboard/seeding.d.ts.map +1 -1
- package/dist/lib/testing/storyboard/seeding.js +23 -3
- package/dist/lib/testing/storyboard/seeding.js.map +1 -1
- package/dist/lib/testing/storyboard/types.d.ts +108 -20
- package/dist/lib/testing/storyboard/types.d.ts.map +1 -1
- package/dist/lib/testing/storyboard/types.js +1 -0
- package/dist/lib/testing/storyboard/types.js.map +1 -1
- package/dist/lib/testing/storyboard/validations.d.ts +18 -0
- package/dist/lib/testing/storyboard/validations.d.ts.map +1 -1
- package/dist/lib/testing/storyboard/validations.js +238 -33
- package/dist/lib/testing/storyboard/validations.js.map +1 -1
- package/dist/lib/testing/test-controller.d.ts +17 -25
- package/dist/lib/testing/test-controller.d.ts.map +1 -1
- package/dist/lib/testing/test-controller.js.map +1 -1
- package/dist/lib/testing/types.d.ts +13 -1
- package/dist/lib/testing/types.d.ts.map +1 -1
- package/dist/lib/types/check-governance.d.ts +1 -1
- package/dist/lib/types/comply-test-controller.d.ts +243 -3
- package/dist/lib/types/core.generated.d.ts +261 -21
- package/dist/lib/types/core.generated.d.ts.map +1 -1
- package/dist/lib/types/core.generated.js +1 -1
- package/dist/lib/types/create-media-buy.d.ts +107 -3
- package/dist/lib/types/error-codes.d.ts +2 -2
- package/dist/lib/types/get-adcp-capabilities.d.ts +1 -1
- package/dist/lib/types/get-media-buys.d.ts +107 -3
- package/dist/lib/types/get-plan-audit-logs.d.ts +1 -1
- package/dist/lib/types/get-products.d.ts +104 -3
- package/dist/lib/types/inline-enums.generated.d.ts +27 -17
- package/dist/lib/types/inline-enums.generated.d.ts.map +1 -1
- package/dist/lib/types/inline-enums.generated.js +39 -28
- package/dist/lib/types/inline-enums.generated.js.map +1 -1
- package/dist/lib/types/manifest.generated.d.ts +7 -4
- package/dist/lib/types/manifest.generated.d.ts.map +1 -1
- package/dist/lib/types/manifest.generated.js +2 -2
- package/dist/lib/types/manifest.generated.js.map +1 -1
- package/dist/lib/types/report-plan-outcome.d.ts +1 -1
- package/dist/lib/types/schemas.generated.d.ts +714 -193
- package/dist/lib/types/schemas.generated.d.ts.map +1 -1
- package/dist/lib/types/schemas.generated.js +194 -79
- package/dist/lib/types/schemas.generated.js.map +1 -1
- package/dist/lib/types/sync-plans.d.ts +1 -1
- package/dist/lib/types/tools.generated.d.ts +331 -28
- package/dist/lib/types/tools.generated.d.ts.map +1 -1
- package/dist/lib/types/update-media-buy.d.ts +107 -3
- package/dist/lib/types/v3-1-beta/tools.generated.d.ts +20 -0
- package/dist/lib/types/v3-1-beta/tools.generated.d.ts.map +1 -1
- package/dist/lib/upstream-recorder/constants.d.ts +2 -0
- package/dist/lib/upstream-recorder/constants.d.ts.map +1 -0
- package/dist/lib/upstream-recorder/constants.js +5 -0
- package/dist/lib/upstream-recorder/constants.js.map +1 -0
- package/dist/lib/upstream-recorder/index.d.ts +20 -10
- package/dist/lib/upstream-recorder/index.d.ts.map +1 -1
- package/dist/lib/upstream-recorder/index.js +21 -10
- package/dist/lib/upstream-recorder/index.js.map +1 -1
- package/dist/lib/upstream-recorder/recorder.d.ts +65 -0
- package/dist/lib/upstream-recorder/recorder.d.ts.map +1 -1
- package/dist/lib/upstream-recorder/recorder.js +500 -47
- package/dist/lib/upstream-recorder/recorder.js.map +1 -1
- package/dist/lib/upstream-recorder/types.d.ts +109 -13
- package/dist/lib/upstream-recorder/types.d.ts.map +1 -1
- package/dist/lib/upstream-recorder/types.js.map +1 -1
- package/dist/lib/utils/adcp-version-config.d.ts +1 -0
- package/dist/lib/utils/adcp-version-config.d.ts.map +1 -1
- package/dist/lib/utils/adcp-version-config.js +21 -0
- package/dist/lib/utils/adcp-version-config.js.map +1 -1
- package/dist/lib/utils/capability-rollups.d.ts +5 -5
- package/dist/lib/utils/capability-rollups.d.ts.map +1 -1
- package/dist/lib/utils/capability-rollups.js +1 -1
- package/dist/lib/utils/capability-rollups.js.map +1 -1
- package/dist/lib/utils/json-depth.d.ts +2 -0
- package/dist/lib/utils/json-depth.d.ts.map +1 -0
- package/dist/lib/utils/json-depth.js +5 -0
- package/dist/lib/utils/json-depth.js.map +1 -0
- package/dist/lib/utils/media-buy-delivery-notification-builders.d.ts +1 -1
- package/dist/lib/utils/media-buy-delivery-notification-builders.d.ts.map +1 -1
- package/dist/lib/utils/preview-creative-builders.d.ts +1 -1
- package/dist/lib/utils/preview-creative-builders.d.ts.map +1 -1
- package/dist/lib/utils/redact-secrets.d.ts +13 -2
- package/dist/lib/utils/redact-secrets.d.ts.map +1 -1
- package/dist/lib/utils/redact-secrets.js +40 -13
- package/dist/lib/utils/redact-secrets.js.map +1 -1
- package/dist/lib/utils/response-schemas.d.ts +1 -0
- package/dist/lib/utils/response-schemas.d.ts.map +1 -1
- package/dist/lib/utils/response-schemas.js +15 -0
- package/dist/lib/utils/response-schemas.js.map +1 -1
- package/dist/lib/utils/response-unwrapper.d.ts +2 -1
- package/dist/lib/utils/response-unwrapper.d.ts.map +1 -1
- package/dist/lib/utils/response-unwrapper.js +11 -3
- package/dist/lib/utils/response-unwrapper.js.map +1 -1
- package/dist/lib/utils/tool-request-schemas.d.ts +31 -1
- package/dist/lib/utils/tool-request-schemas.d.ts.map +1 -1
- package/dist/lib/v2/format-schema/fetch.d.ts +13 -5
- package/dist/lib/v2/format-schema/fetch.d.ts.map +1 -1
- package/dist/lib/v2/format-schema/fetch.js +27 -16
- package/dist/lib/v2/format-schema/fetch.js.map +1 -1
- package/dist/lib/v2/format-schema/index.d.ts +13 -11
- package/dist/lib/v2/format-schema/index.d.ts.map +1 -1
- package/dist/lib/v2/format-schema/index.js +19 -12
- package/dist/lib/v2/format-schema/index.js.map +1 -1
- package/dist/lib/v2/format-schema/resolver.d.ts +71 -0
- package/dist/lib/v2/format-schema/resolver.d.ts.map +1 -0
- package/dist/lib/v2/format-schema/resolver.js +284 -0
- package/dist/lib/v2/format-schema/resolver.js.map +1 -0
- package/dist/lib/v2/format-schema/sandbox-refs.d.ts +6 -0
- package/dist/lib/v2/format-schema/sandbox-refs.d.ts.map +1 -1
- package/dist/lib/v2/format-schema/sandbox-refs.js +36 -15
- package/dist/lib/v2/format-schema/sandbox-refs.js.map +1 -1
- package/dist/lib/validation/schema-loader.d.ts.map +1 -1
- package/dist/lib/validation/schema-loader.js +48 -3
- package/dist/lib/validation/schema-loader.js.map +1 -1
- package/dist/lib/version.d.ts +3 -3
- package/dist/lib/version.js +3 -3
- package/docs/guides/BUILD-AN-AGENT.md +7 -7
- package/docs/guides/CANONICAL-REFERENCE-RESOLVER.md +75 -0
- package/docs/llms.txt +37 -8
- package/examples/README.md +29 -16
- package/examples/hello_creative_adapter_ad_server.ts +8 -2
- package/examples/hello_seller_adapter_guaranteed.ts +26 -18
- package/examples/hello_seller_adapter_multi_tenant.ts +6 -6
- package/examples/hello_seller_adapter_social.ts +80 -4
- package/examples/hello_si_adapter_brand.ts +10 -21
- package/examples/hello_signals_adapter_marketplace.ts +184 -9
- package/examples/proxy-seller-snap/README.md +47 -0
- package/examples/proxy-seller-snap/index.ts +321 -0
- package/package.json +19 -4
- package/skills/build-creative-agent/SKILL.md +1 -15
- package/skills/build-decisioning-platform/SKILL.md +6 -1
- package/skills/build-seller-agent/SKILL.md +5 -2
- package/skills/build-si-agent/SKILL.md +2 -2
- package/skills/call-adcp-agent/SKILL.md +4 -1
- package/dist/lib/signing/response-verifier.d.ts +0 -105
- package/dist/lib/signing/response-verifier.d.ts.map +0 -1
- package/dist/lib/signing/response-verifier.js +0 -271
- package/dist/lib/signing/response-verifier.js.map +0 -1
|
@@ -33,6 +33,8 @@ import {
|
|
|
33
33
|
type AccountStore,
|
|
34
34
|
type Account,
|
|
35
35
|
type BuyerAgent,
|
|
36
|
+
type BuyerAgentBillingMode,
|
|
37
|
+
type BuyerAgentStatus,
|
|
36
38
|
type CachedBuyerAgentRegistry,
|
|
37
39
|
} from '@adcp/sdk/server';
|
|
38
40
|
import type { GetSignalsResponse, ActivateSignalRequest, ActivateSignalSuccess } from '@adcp/sdk/types';
|
|
@@ -283,6 +285,114 @@ const ONBOARDING_LEDGER = new Map<string, BuyerAgent>([
|
|
|
283
285
|
],
|
|
284
286
|
]);
|
|
285
287
|
|
|
288
|
+
const SEEDED_BUYER_AGENT_OVERLAY = new Map<string, BuyerAgent>();
|
|
289
|
+
|
|
290
|
+
const BUYER_AGENT_BILLING_MODES = new Set<BuyerAgentBillingMode>(['operator', 'agent', 'advertiser']);
|
|
291
|
+
const BUYER_AGENT_STATUSES = new Set<BuyerAgentStatus>(['active', 'suspended', 'blocked']);
|
|
292
|
+
|
|
293
|
+
function readRequestAccountRef(input: Record<string, unknown> | undefined): Record<string, unknown> | undefined {
|
|
294
|
+
const topLevel = input?.['account'];
|
|
295
|
+
if (topLevel != null && typeof topLevel === 'object') return topLevel as Record<string, unknown>;
|
|
296
|
+
const context = input?.['context'];
|
|
297
|
+
if (context != null && typeof context === 'object') {
|
|
298
|
+
const contextAccount = (context as { account?: unknown }).account;
|
|
299
|
+
if (contextAccount != null && typeof contextAccount === 'object') return contextAccount as Record<string, unknown>;
|
|
300
|
+
}
|
|
301
|
+
return undefined;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
function readSessionId(input: Record<string, unknown> | undefined): string | undefined {
|
|
305
|
+
const context = input?.['context'];
|
|
306
|
+
if (context == null || typeof context !== 'object') return undefined;
|
|
307
|
+
const sessionId = (context as { session_id?: unknown }).session_id;
|
|
308
|
+
return typeof sessionId === 'string' && sessionId.length > 0 ? sessionId : undefined;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
function accountScope(ref: Record<string, unknown> | undefined): string | undefined {
|
|
312
|
+
if (!ref) return undefined;
|
|
313
|
+
const accountId = ref['account_id'];
|
|
314
|
+
if (typeof accountId === 'string' && accountId.length > 0) return `account_id:${accountId}`;
|
|
315
|
+
const brand = ref['brand'];
|
|
316
|
+
const brandDomain = brand != null && typeof brand === 'object' ? (brand as { domain?: unknown }).domain : undefined;
|
|
317
|
+
const operator = ref['operator'];
|
|
318
|
+
const sandbox = ref['sandbox'];
|
|
319
|
+
if (typeof brandDomain !== 'string' && typeof operator !== 'string' && typeof sandbox !== 'boolean') {
|
|
320
|
+
return undefined;
|
|
321
|
+
}
|
|
322
|
+
return JSON.stringify({
|
|
323
|
+
...(typeof brandDomain === 'string' && { brand_domain: brandDomain }),
|
|
324
|
+
...(typeof operator === 'string' && { operator }),
|
|
325
|
+
...(typeof sandbox === 'boolean' && { sandbox }),
|
|
326
|
+
});
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
function buyerAgentOverlayKey(
|
|
330
|
+
agentUrl: string,
|
|
331
|
+
input: Record<string, unknown> | undefined,
|
|
332
|
+
ref: Record<string, unknown> | undefined
|
|
333
|
+
): string | undefined {
|
|
334
|
+
const parts: string[] = [];
|
|
335
|
+
const sessionId = readSessionId(input);
|
|
336
|
+
if (sessionId) parts.push(`session:${sessionId}`);
|
|
337
|
+
const scope = accountScope(ref);
|
|
338
|
+
if (scope) parts.push(`account:${scope}`);
|
|
339
|
+
return parts.length > 0 ? `${parts.join('|')}:agent:${agentUrl}` : undefined;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
function overlayBuyerAgent(
|
|
343
|
+
base: BuyerAgent,
|
|
344
|
+
input: Record<string, unknown> | undefined,
|
|
345
|
+
ref: Record<string, unknown> | undefined
|
|
346
|
+
): BuyerAgent {
|
|
347
|
+
const key = buyerAgentOverlayKey(base.agent_url, input, ref);
|
|
348
|
+
const seeded = key ? SEEDED_BUYER_AGENT_OVERLAY.get(key) : undefined;
|
|
349
|
+
if (!seeded) {
|
|
350
|
+
return base;
|
|
351
|
+
}
|
|
352
|
+
return {
|
|
353
|
+
...base,
|
|
354
|
+
...seeded,
|
|
355
|
+
billing_capabilities: new Set(seeded.billing_capabilities),
|
|
356
|
+
};
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
function seedBuyerAgentOverlay(
|
|
360
|
+
params: {
|
|
361
|
+
agent_url: string;
|
|
362
|
+
display_name?: string;
|
|
363
|
+
status?: BuyerAgentStatus;
|
|
364
|
+
billing_capabilities?: BuyerAgentBillingMode[];
|
|
365
|
+
sandbox_only?: boolean;
|
|
366
|
+
},
|
|
367
|
+
input: Record<string, unknown>
|
|
368
|
+
): void {
|
|
369
|
+
const existing =
|
|
370
|
+
Array.from(ONBOARDING_LEDGER.values()).find(agent => agent.agent_url === params.agent_url) ??
|
|
371
|
+
Array.from(SEEDED_BUYER_AGENT_OVERLAY.values()).find(agent => agent.agent_url === params.agent_url);
|
|
372
|
+
const key = buyerAgentOverlayKey(params.agent_url, input, readRequestAccountRef(input));
|
|
373
|
+
if (!key) {
|
|
374
|
+
throw new AdcpError('INVALID_REQUEST', {
|
|
375
|
+
message: 'seed_buyer_agent requires a session_id or account scope for this example overlay.',
|
|
376
|
+
});
|
|
377
|
+
}
|
|
378
|
+
const modes = (params.billing_capabilities ?? Array.from(existing?.billing_capabilities ?? ['operator'])).filter(
|
|
379
|
+
(mode): mode is BuyerAgentBillingMode => BUYER_AGENT_BILLING_MODES.has(mode as BuyerAgentBillingMode)
|
|
380
|
+
);
|
|
381
|
+
const status =
|
|
382
|
+
params.status && BUYER_AGENT_STATUSES.has(params.status) ? params.status : (existing?.status ?? 'active');
|
|
383
|
+
|
|
384
|
+
SEEDED_BUYER_AGENT_OVERLAY.set(key, {
|
|
385
|
+
agent_url: params.agent_url,
|
|
386
|
+
display_name: params.display_name ?? existing?.display_name ?? 'Compliance seeded buyer agent',
|
|
387
|
+
status,
|
|
388
|
+
billing_capabilities: new Set(modes.length > 0 ? modes : ['operator']),
|
|
389
|
+
sandbox_only: params.sandbox_only ?? existing?.sandbox_only ?? true,
|
|
390
|
+
});
|
|
391
|
+
// Mutating commercial state must purge cached registry entries; otherwise a
|
|
392
|
+
// just-suspended test buyer could remain active until the TTL expires.
|
|
393
|
+
agentRegistry.clear();
|
|
394
|
+
}
|
|
395
|
+
|
|
286
396
|
/**
|
|
287
397
|
* `bearerOnly` because this example authenticates via `verifyApiKey`.
|
|
288
398
|
* Sellers wiring `verifySignatureAsAuthenticator` swap to `signingOnly`
|
|
@@ -293,7 +403,7 @@ const ONBOARDING_LEDGER = new Map<string, BuyerAgent>([
|
|
|
293
403
|
* onboarding ledger. `invalidate(credential)` purges a stale entry when
|
|
294
404
|
* the seller mutates an agent's record (status flip, etc.).
|
|
295
405
|
*/
|
|
296
|
-
const
|
|
406
|
+
const baseAgentRegistry: CachedBuyerAgentRegistry = BuyerAgentRegistry.cached(
|
|
297
407
|
BuyerAgentRegistry.bearerOnly({
|
|
298
408
|
resolveByCredential: async credential => {
|
|
299
409
|
// bearerOnly receives every credential kind; MUST kind-discriminate
|
|
@@ -305,6 +415,16 @@ const agentRegistry: CachedBuyerAgentRegistry = BuyerAgentRegistry.cached(
|
|
|
305
415
|
{ ttlSeconds: 60 }
|
|
306
416
|
);
|
|
307
417
|
|
|
418
|
+
const agentRegistry: CachedBuyerAgentRegistry = {
|
|
419
|
+
async resolve(authInfo) {
|
|
420
|
+
const base = await baseAgentRegistry.resolve(authInfo);
|
|
421
|
+
if (!base) return base;
|
|
422
|
+
return overlayBuyerAgent(base, authInfo.input, readRequestAccountRef(authInfo.input));
|
|
423
|
+
},
|
|
424
|
+
invalidate: credential => baseAgentRegistry.invalidate(credential),
|
|
425
|
+
clear: () => baseAgentRegistry.clear(),
|
|
426
|
+
};
|
|
427
|
+
|
|
308
428
|
// ---------------------------------------------------------------------------
|
|
309
429
|
// AdCP-side adapter — typed against SignalsPlatform.
|
|
310
430
|
// ---------------------------------------------------------------------------
|
|
@@ -358,9 +478,12 @@ class SignalMarketplaceAdapter implements DecisioningPlatform<Record<string, nev
|
|
|
358
478
|
* Buyer-agent registry — framework runs `agentRegistry.resolve(authInfo)`
|
|
359
479
|
* once per request and threads the resolved record through `ctx.agent`
|
|
360
480
|
* to specialism handlers AND to `accounts.resolve` (below). When the
|
|
361
|
-
* resolved agent's `status` is `suspended` / `blocked`, the
|
|
362
|
-
* rejects the request with
|
|
363
|
-
*
|
|
481
|
+
* resolved agent's durable `status` is `suspended` / `blocked`, the
|
|
482
|
+
* framework rejects the request with the AdCP 3.1 `AGENT_SUSPENDED` /
|
|
483
|
+
* `AGENT_BLOCKED` codes before invoking any handler. The scoped
|
|
484
|
+
* compliance overlay below mirrors that behavior inside `accounts.resolve`
|
|
485
|
+
* so `seed_buyer_agent` can vary test-only state without mutating the
|
|
486
|
+
* process-wide onboarding ledger.
|
|
364
487
|
*/
|
|
365
488
|
agentRegistry = agentRegistry;
|
|
366
489
|
|
|
@@ -371,20 +494,61 @@ class SignalMarketplaceAdapter implements DecisioningPlatform<Record<string, nev
|
|
|
371
494
|
* tenant resolution against the durable buyer-agent identity here
|
|
372
495
|
* rather than re-deriving from the credential. */
|
|
373
496
|
resolve: async (ref, ctx) => {
|
|
374
|
-
|
|
497
|
+
let effectiveRef = ref;
|
|
498
|
+
if (!effectiveRef) {
|
|
499
|
+
const inputAccount = readRequestAccountRef(ctx?.input as Record<string, unknown> | undefined);
|
|
500
|
+
// TEST-ONLY: comply_test_controller discovery and account-less
|
|
501
|
+
// controller calls resolve through this auth-derived path. When the
|
|
502
|
+
// caller supplied an account in the controller payload/context, use
|
|
503
|
+
// it; otherwise fall back to the single operator this worked example
|
|
504
|
+
// uses for the conformance harness. Production sellers replace this
|
|
505
|
+
// with a real principal → sandbox-account lookup.
|
|
506
|
+
if (inputAccount != null && 'account_id' in inputAccount) return null;
|
|
507
|
+
const inputBrand = inputAccount?.['brand'];
|
|
508
|
+
const inputBrandDomain =
|
|
509
|
+
inputBrand != null && typeof inputBrand === 'object'
|
|
510
|
+
? (inputBrand as { domain?: unknown }).domain
|
|
511
|
+
: undefined;
|
|
512
|
+
const inputOperator = inputAccount?.['operator'];
|
|
513
|
+
const inputSandbox = inputAccount?.['sandbox'];
|
|
514
|
+
effectiveRef =
|
|
515
|
+
typeof inputOperator === 'string'
|
|
516
|
+
? {
|
|
517
|
+
brand: { domain: typeof inputBrandDomain === 'string' ? inputBrandDomain : 'acmeoutdoor.example' },
|
|
518
|
+
operator: inputOperator,
|
|
519
|
+
...(typeof inputSandbox === 'boolean' && { sandbox: inputSandbox }),
|
|
520
|
+
}
|
|
521
|
+
: {
|
|
522
|
+
brand: { domain: 'acmeoutdoor.example' },
|
|
523
|
+
operator: 'pinnacle-agency.example',
|
|
524
|
+
sandbox: true,
|
|
525
|
+
};
|
|
526
|
+
}
|
|
527
|
+
if (!effectiveRef) return null;
|
|
375
528
|
// AccountReference discriminated union: `{ account_id }` post-sync,
|
|
376
529
|
// or `{ brand, operator, sandbox? }` on initial discovery. Mock has
|
|
377
530
|
// no account_id index; SWAP: production keeps an account_id →
|
|
378
531
|
// operator_id index populated during list_accounts.
|
|
379
|
-
if ('account_id' in
|
|
380
|
-
const adcpOperator =
|
|
532
|
+
if ('account_id' in effectiveRef) return null;
|
|
533
|
+
const adcpOperator = effectiveRef.operator;
|
|
381
534
|
if (!adcpOperator) return null;
|
|
382
535
|
// Optional: gate the operator on the buyer agent's allowed_brands /
|
|
383
536
|
// billing_capabilities. Sellers who don't cross-check operator vs.
|
|
384
537
|
// agent here let any onboarded agent operate on any operator —
|
|
385
538
|
// legitimate for some marketplaces, a leak for others.
|
|
386
|
-
const buyerAgent =
|
|
387
|
-
|
|
539
|
+
const buyerAgent =
|
|
540
|
+
ctx?.agent && ctx.input
|
|
541
|
+
? overlayBuyerAgent(ctx.agent, ctx.input, effectiveRef as Record<string, unknown>)
|
|
542
|
+
: ctx?.agent;
|
|
543
|
+
if (buyerAgent?.status === 'suspended' || buyerAgent?.status === 'blocked') {
|
|
544
|
+
throw new AdcpError(buyerAgent.status === 'suspended' ? 'AGENT_SUSPENDED' : 'AGENT_BLOCKED', {
|
|
545
|
+
message:
|
|
546
|
+
buyerAgent.status === 'suspended'
|
|
547
|
+
? 'Buyer agent is suspended. Contact the seller to restore access.'
|
|
548
|
+
: 'Buyer agent is blocked.',
|
|
549
|
+
recovery: 'terminal',
|
|
550
|
+
});
|
|
551
|
+
}
|
|
388
552
|
const operatorId = await upstream.lookupOperator(adcpOperator);
|
|
389
553
|
if (!operatorId) return null;
|
|
390
554
|
return {
|
|
@@ -392,6 +556,7 @@ class SignalMarketplaceAdapter implements DecisioningPlatform<Record<string, nev
|
|
|
392
556
|
name: adcpOperator,
|
|
393
557
|
status: 'active',
|
|
394
558
|
operator: adcpOperator,
|
|
559
|
+
mode: 'sandbox',
|
|
395
560
|
ctx_metadata: { operator_id: operatorId },
|
|
396
561
|
// The upstream here is the AdCP mock-server. Every account it
|
|
397
562
|
// returns is a sandbox account by definition. Production
|
|
@@ -540,6 +705,16 @@ serve(
|
|
|
540
705
|
// account's `mode` is `'sandbox'` or `'mock'` (per `Account.mode`,
|
|
541
706
|
// AdCP 6.7+). See `docs/proposals/lifecycle-state-and-sandbox-authority.md`.
|
|
542
707
|
complyTest: {
|
|
708
|
+
seed: {
|
|
709
|
+
// Test-only commercial-state setup for AdCP 3.1 storyboards that
|
|
710
|
+
// declare `fixtures.buyer_agents[]`. This overlays the static
|
|
711
|
+
// onboarding ledger by agent_url, so the existing bearer credential
|
|
712
|
+
// still authenticates normally while the storyboard can vary status,
|
|
713
|
+
// sandbox_only, or billing_capabilities for the resolved buyer agent.
|
|
714
|
+
buyer_agent: (params, ctx) => {
|
|
715
|
+
seedBuyerAgentOverlay(params, ctx.input);
|
|
716
|
+
},
|
|
717
|
+
},
|
|
543
718
|
// Adapter resolves principal from auth context. The same string
|
|
544
719
|
// MUST be returned by both record-time (runWithPrincipal) and
|
|
545
720
|
// query-time — mismatch returns zero per cross-tenant isolation.
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# Proxy Seller Snap Example
|
|
2
|
+
|
|
3
|
+
This is the fork target for a seller whose AdCP read path proxies an upstream ads platform: Snap, Meta, TikTok, Pinterest, LinkedIn, Google, Amazon, Reddit, Spotify, Criteo, CitrusAd, FlashTalking, UniversalAds, and similar DSP or walled-garden adapters.
|
|
4
|
+
|
|
5
|
+
## Diagnostic
|
|
6
|
+
|
|
7
|
+
If `get_products`, `list_creatives`, `list_property_lists`, or another read tool calls the upstream platform API directly, `comply_test_controller.seed_product` and `seed_creative` are otherwise dead writes. The seed lands in your local test controller, but your read handler asks Snap for products or creatives and never sees it.
|
|
8
|
+
|
|
9
|
+
Wire `TestControllerBridge` for storyboard conformance. The handler still runs first; the SDK merges seeded fixtures into sandbox responses after the upstream call succeeds. That proves AdCP wire conformance, not live Snap adapter health.
|
|
10
|
+
|
|
11
|
+
## What This Example Wires
|
|
12
|
+
|
|
13
|
+
- `TestControllerBridge<SnapAccount>` through `bridgeFromSessionStore`.
|
|
14
|
+
- Resolved-account session loading: `loadSession: (_input, ctx) => sessionStore.loadForAccount(ctx.account)`.
|
|
15
|
+
- Seed selectors for `get_products`, `list_creatives`, and property-list governance reads.
|
|
16
|
+
- `resolveAccount` as the trust boundary. The bridge keys on the resolved `ctx.account`, not on caller-supplied request fields.
|
|
17
|
+
- Optional `comply_test_controller` registration gated by `ADCP_SANDBOX=1`.
|
|
18
|
+
|
|
19
|
+
## Verification Scope
|
|
20
|
+
|
|
21
|
+
Bridge-augmented storyboard passes are wire-conformance evidence. They do not prove your Snap OAuth token, account lookup, product catalog, or creative library calls are healthy. Pair this with a live-OAuth sandbox runner that disables or records bridge participation and asserts real upstream traffic.
|
|
22
|
+
|
|
23
|
+
Run:
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
ADCP_SANDBOX=1 npx tsx examples/proxy-seller-snap/index.ts
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Then seed and read with a sandbox account:
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
adcp call http://127.0.0.1:3018/mcp comply_test_controller \
|
|
33
|
+
'{"scenario":"seed_product","params":{"product_id":"snap-storyboard-product","fixture":{"name":"Seeded product"}},"account":{"account_id":"snap_sandbox_acme","sandbox":true}}' \
|
|
34
|
+
--auth sk_snap_proxy_harness_do_not_use_in_prod
|
|
35
|
+
|
|
36
|
+
adcp call http://127.0.0.1:3018/mcp get_products \
|
|
37
|
+
'{"buying_mode":"brief","brief":"outdoor apparel","account":{"account_id":"snap_sandbox_acme","sandbox":true}}' \
|
|
38
|
+
--auth sk_snap_proxy_harness_do_not_use_in_prod
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Forking checklist:
|
|
42
|
+
|
|
43
|
+
1. Replace `emptySnapClient` with real Snap OAuth and Marketing API calls.
|
|
44
|
+
2. Replace `resolveSnapAccount` with your production account resolver.
|
|
45
|
+
3. Keep bridge registration limited to sandbox or conformance deployments.
|
|
46
|
+
4. Keep bridge session reads keyed on the resolved account.
|
|
47
|
+
5. Add a live-OAuth test that exercises the adapter without relying on `_bridge`-augmented fixtures.
|
|
@@ -0,0 +1,321 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* proxy-seller-snap — fork target for proxy-shaped sellers whose AdCP read
|
|
3
|
+
* path fronts an upstream ads platform instead of a seller-owned datastore.
|
|
4
|
+
*
|
|
5
|
+
* Snap is the concrete shape because it is representative: account resolution
|
|
6
|
+
* maps the buyer's AdCP account to a Snap ad account, read handlers call a
|
|
7
|
+
* Snap-shaped client, and `TestControllerBridge` makes storyboard-seeded
|
|
8
|
+
* fixtures visible on sandbox reads without pretending the adapter's live
|
|
9
|
+
* upstream path is healthy.
|
|
10
|
+
*
|
|
11
|
+
* FORK CHECKLIST
|
|
12
|
+
* 1. Replace `emptySnapClient` with your real upstream OAuth/API client.
|
|
13
|
+
* 2. Replace `resolveSnapAccount` with your production account resolver.
|
|
14
|
+
* 3. Keep `bridgeFromSessionStore` keyed on resolved `ctx.account`.
|
|
15
|
+
* 4. Register `comply_test_controller` only in sandbox/conformance hosts.
|
|
16
|
+
* 5. Add a live-OAuth sandbox runner that proves adapter health without
|
|
17
|
+
* relying on `_bridge`-augmented fixtures.
|
|
18
|
+
*
|
|
19
|
+
* Run locally:
|
|
20
|
+
*
|
|
21
|
+
* ADCP_SANDBOX=1 npx tsx examples/proxy-seller-snap/index.ts
|
|
22
|
+
* adcp call http://127.0.0.1:3018/mcp get_products \
|
|
23
|
+
* '{"buying_mode":"brief","brief":"outdoor apparel","account":{"account_id":"snap_sandbox_acme","sandbox":true}}' \
|
|
24
|
+
* --auth sk_snap_proxy_harness_do_not_use_in_prod
|
|
25
|
+
*/
|
|
26
|
+
|
|
27
|
+
import { z } from 'zod';
|
|
28
|
+
import { createAdcpServer } from '@adcp/sdk/server/legacy/v5';
|
|
29
|
+
import {
|
|
30
|
+
AdcpError,
|
|
31
|
+
bridgeFromSessionStore,
|
|
32
|
+
InMemoryStateStore,
|
|
33
|
+
serve,
|
|
34
|
+
verifyApiKey,
|
|
35
|
+
type SeededCreative,
|
|
36
|
+
type TestControllerBridge,
|
|
37
|
+
} from '@adcp/sdk/server';
|
|
38
|
+
import { createComplyController } from '@adcp/sdk/testing';
|
|
39
|
+
import type { AccountReference, GetProductsResponse, PropertyList } from '@adcp/sdk/types';
|
|
40
|
+
|
|
41
|
+
type ValidationMode = 'strict' | 'warn' | 'off';
|
|
42
|
+
type Product = NonNullable<GetProductsResponse['products']>[number];
|
|
43
|
+
|
|
44
|
+
const PORT = Number(process.env['PORT'] ?? 3018);
|
|
45
|
+
const ADCP_AUTH_TOKEN = process.env['ADCP_AUTH_TOKEN'] ?? 'sk_snap_proxy_harness_do_not_use_in_prod';
|
|
46
|
+
const PUBLIC_AGENT_URL = process.env['PUBLIC_AGENT_URL'] ?? `http://127.0.0.1:${PORT}`;
|
|
47
|
+
const DEFAULT_SANDBOX_ACCOUNT_ID = 'snap_sandbox_acme';
|
|
48
|
+
|
|
49
|
+
interface SnapAccount {
|
|
50
|
+
id: string;
|
|
51
|
+
name: string;
|
|
52
|
+
status: 'active' | 'pending_approval' | 'rejected' | 'payment_required' | 'suspended' | 'closed';
|
|
53
|
+
sandbox?: boolean;
|
|
54
|
+
mode?: 'sandbox' | 'live';
|
|
55
|
+
brand?: { domain: string };
|
|
56
|
+
operator?: string;
|
|
57
|
+
ctx_metadata: {
|
|
58
|
+
snap_ad_account_id: string;
|
|
59
|
+
bridge_session_id: string;
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
interface SnapBridgeSession {
|
|
64
|
+
seededProducts: Map<string, Record<string, unknown>>;
|
|
65
|
+
seededCreatives: SeededCreative[];
|
|
66
|
+
seededPropertyLists: PropertyList[];
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export interface SnapClient {
|
|
70
|
+
listProducts(adAccountId: string): Promise<Product[]>;
|
|
71
|
+
listCreatives(adAccountId: string): Promise<SeededCreative[]>;
|
|
72
|
+
listPropertyLists(adAccountId: string): Promise<PropertyList[]>;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export class InMemorySnapSessionStore {
|
|
76
|
+
private readonly sessions = new Map<string, SnapBridgeSession>();
|
|
77
|
+
|
|
78
|
+
loadForAccount(account: SnapAccount): SnapBridgeSession {
|
|
79
|
+
return this.load(account.ctx_metadata.bridge_session_id);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
load(sessionId: string): SnapBridgeSession {
|
|
83
|
+
let session = this.sessions.get(sessionId);
|
|
84
|
+
if (!session) {
|
|
85
|
+
session = { seededProducts: new Map(), seededCreatives: [], seededPropertyLists: [] };
|
|
86
|
+
this.sessions.set(sessionId, session);
|
|
87
|
+
}
|
|
88
|
+
return session;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
loadForControllerInput(input: Record<string, unknown>): SnapBridgeSession {
|
|
92
|
+
const accountId = readAccountId(input) ?? DEFAULT_SANDBOX_ACCOUNT_ID;
|
|
93
|
+
return this.load(`snap:${accountId}`);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export function createInMemorySnapSessionStore(): InMemorySnapSessionStore {
|
|
98
|
+
return new InMemorySnapSessionStore();
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const emptySnapClient: SnapClient = {
|
|
102
|
+
// SWAP: production calls Snap Marketing API product/catalog endpoints.
|
|
103
|
+
async listProducts() {
|
|
104
|
+
return [];
|
|
105
|
+
},
|
|
106
|
+
// SWAP: production calls Snap creative library endpoints.
|
|
107
|
+
async listCreatives() {
|
|
108
|
+
return [];
|
|
109
|
+
},
|
|
110
|
+
// SWAP: production calls the upstream governance/list surface you proxy.
|
|
111
|
+
async listPropertyLists() {
|
|
112
|
+
return [];
|
|
113
|
+
},
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
// SWAP: production resolves the buyer principal + AccountReference through
|
|
117
|
+
// your account directory or upstream OAuth `/me/adaccounts` equivalent.
|
|
118
|
+
function resolveSnapAccount(ref: AccountReference): SnapAccount | null {
|
|
119
|
+
if ('account_id' in ref) {
|
|
120
|
+
if (!ref.account_id) return null;
|
|
121
|
+
const sandbox = ref.account_id.startsWith('snap_sandbox_');
|
|
122
|
+
return {
|
|
123
|
+
id: ref.account_id,
|
|
124
|
+
name: sandbox ? 'Snap sandbox advertiser' : 'Snap advertiser',
|
|
125
|
+
status: 'active',
|
|
126
|
+
sandbox,
|
|
127
|
+
mode: sandbox ? 'sandbox' : 'live',
|
|
128
|
+
ctx_metadata: {
|
|
129
|
+
snap_ad_account_id: ref.account_id.replace(/^snap_/, ''),
|
|
130
|
+
bridge_session_id: `snap:${ref.account_id}`,
|
|
131
|
+
},
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const domain = ref.brand.domain;
|
|
136
|
+
if (!domain) return null;
|
|
137
|
+
const sandbox = domain.endsWith('.sandbox');
|
|
138
|
+
return {
|
|
139
|
+
id: sandbox ? DEFAULT_SANDBOX_ACCOUNT_ID : `snap_${domain.replace(/[^a-z0-9]+/gi, '_').toLowerCase()}`,
|
|
140
|
+
name: `Snap advertiser for ${domain}`,
|
|
141
|
+
status: 'active',
|
|
142
|
+
sandbox,
|
|
143
|
+
mode: sandbox ? 'sandbox' : 'live',
|
|
144
|
+
brand: { domain },
|
|
145
|
+
...(ref.operator !== undefined && { operator: ref.operator }),
|
|
146
|
+
ctx_metadata: {
|
|
147
|
+
snap_ad_account_id: sandbox ? 'sandbox_acme' : domain,
|
|
148
|
+
bridge_session_id: `snap:${sandbox ? DEFAULT_SANDBOX_ACCOUNT_ID : domain}`,
|
|
149
|
+
},
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
function requireResolvedAccount(account: SnapAccount | undefined): SnapAccount {
|
|
154
|
+
if (!account) {
|
|
155
|
+
throw new Error('TestControllerBridge requires resolveAccount; no resolved Snap account was available');
|
|
156
|
+
}
|
|
157
|
+
return account;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
function readAccountId(input: Record<string, unknown>): string | undefined {
|
|
161
|
+
const account = input['account'];
|
|
162
|
+
if (!account || typeof account !== 'object') return undefined;
|
|
163
|
+
const accountId = (account as { account_id?: unknown }).account_id;
|
|
164
|
+
return typeof accountId === 'string' && accountId.length > 0 ? accountId : undefined;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
function makeSnapBridge(sessionStore: InMemorySnapSessionStore): TestControllerBridge<SnapAccount> {
|
|
168
|
+
return bridgeFromSessionStore<SnapBridgeSession, SnapAccount>({
|
|
169
|
+
loadSession: (_input, ctx) => sessionStore.loadForAccount(requireResolvedAccount(ctx.account)),
|
|
170
|
+
selectSeededProducts: session => session.seededProducts,
|
|
171
|
+
selectSeededCreatives: session => session.seededCreatives,
|
|
172
|
+
selectSeededPropertyLists: session => session.seededPropertyLists,
|
|
173
|
+
productDefaults: {
|
|
174
|
+
name: 'Seeded Snap storyboard product',
|
|
175
|
+
description: 'Storyboard fixture merged by TestControllerBridge for wire conformance.',
|
|
176
|
+
publisher_properties: [{ publisher_domain: 'snap.com', selection_type: 'all' }],
|
|
177
|
+
channels: ['social'],
|
|
178
|
+
delivery_type: 'non_guaranteed',
|
|
179
|
+
format_ids: [{ agent_url: PUBLIC_AGENT_URL, id: 'snap-single-image' }],
|
|
180
|
+
pricing_options: [
|
|
181
|
+
{
|
|
182
|
+
pricing_option_id: 'snap-cpm-floor',
|
|
183
|
+
pricing_model: 'cpm',
|
|
184
|
+
currency: 'USD',
|
|
185
|
+
floor_price: 5,
|
|
186
|
+
},
|
|
187
|
+
],
|
|
188
|
+
reporting_capabilities: {
|
|
189
|
+
available_reporting_frequencies: ['daily'],
|
|
190
|
+
expected_delay_minutes: 60,
|
|
191
|
+
timezone: 'UTC',
|
|
192
|
+
supports_webhooks: false,
|
|
193
|
+
available_metrics: ['impressions', 'clicks', 'spend'],
|
|
194
|
+
date_range_support: 'date_range',
|
|
195
|
+
},
|
|
196
|
+
},
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
export interface CreateProxySellerSnapServerOptions {
|
|
201
|
+
snapClient?: SnapClient;
|
|
202
|
+
sessionStore?: InMemorySnapSessionStore;
|
|
203
|
+
enableComplyTestController?: boolean;
|
|
204
|
+
validation?: {
|
|
205
|
+
requests?: ValidationMode;
|
|
206
|
+
responses?: ValidationMode;
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
export function createProxySellerSnapServer(options: CreateProxySellerSnapServerOptions = {}) {
|
|
211
|
+
const snapClient = options.snapClient ?? emptySnapClient;
|
|
212
|
+
const sessionStore = options.sessionStore ?? createInMemorySnapSessionStore();
|
|
213
|
+
|
|
214
|
+
const server = createAdcpServer<SnapAccount>({
|
|
215
|
+
name: 'proxy-seller-snap',
|
|
216
|
+
version: '1.0.0',
|
|
217
|
+
stateStore: new InMemoryStateStore(),
|
|
218
|
+
validation: options.validation ?? { requests: 'warn', responses: 'strict' },
|
|
219
|
+
resolveAccount: async ref => resolveSnapAccount(ref),
|
|
220
|
+
resolveSessionKey: ctx => ctx.account?.id ?? 'snap-anonymous',
|
|
221
|
+
mediaBuy: {
|
|
222
|
+
getProducts: async (_req, ctx) => ({
|
|
223
|
+
products: await snapClient.listProducts(requireResolvedAccount(ctx.account).ctx_metadata.snap_ad_account_id),
|
|
224
|
+
cache_scope: 'account',
|
|
225
|
+
}),
|
|
226
|
+
},
|
|
227
|
+
creative: {
|
|
228
|
+
listCreatives: async (_req, ctx) => {
|
|
229
|
+
const creatives = await snapClient.listCreatives(
|
|
230
|
+
requireResolvedAccount(ctx.account).ctx_metadata.snap_ad_account_id
|
|
231
|
+
);
|
|
232
|
+
return {
|
|
233
|
+
query_summary: { total_matching: creatives.length, returned: creatives.length },
|
|
234
|
+
pagination: { limit: 50, offset: 0, has_more: false },
|
|
235
|
+
creatives,
|
|
236
|
+
};
|
|
237
|
+
},
|
|
238
|
+
},
|
|
239
|
+
governance: {
|
|
240
|
+
listPropertyLists: async (_req, ctx) => {
|
|
241
|
+
const lists = await snapClient.listPropertyLists(
|
|
242
|
+
requireResolvedAccount(ctx.account).ctx_metadata.snap_ad_account_id
|
|
243
|
+
);
|
|
244
|
+
return {
|
|
245
|
+
lists,
|
|
246
|
+
pagination: { has_more: false },
|
|
247
|
+
};
|
|
248
|
+
},
|
|
249
|
+
getPropertyList: async (req, ctx) => {
|
|
250
|
+
const lists = await snapClient.listPropertyLists(
|
|
251
|
+
requireResolvedAccount(ctx.account).ctx_metadata.snap_ad_account_id
|
|
252
|
+
);
|
|
253
|
+
const list = lists.find(entry => entry.list_id === req.list_id);
|
|
254
|
+
if (!list) {
|
|
255
|
+
throw new AdcpError('REFERENCE_NOT_FOUND', {
|
|
256
|
+
message: 'Property list not found',
|
|
257
|
+
field: 'list_id',
|
|
258
|
+
});
|
|
259
|
+
}
|
|
260
|
+
return {
|
|
261
|
+
list,
|
|
262
|
+
identifiers: [],
|
|
263
|
+
pagination: { has_more: false },
|
|
264
|
+
};
|
|
265
|
+
},
|
|
266
|
+
},
|
|
267
|
+
testController: makeSnapBridge(sessionStore),
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
if (options.enableComplyTestController) {
|
|
271
|
+
createComplyController({
|
|
272
|
+
sandboxGate: input =>
|
|
273
|
+
process.env['ADCP_SANDBOX'] === '1' || readAccountId(input)?.startsWith('snap_sandbox_') === true,
|
|
274
|
+
inputSchema: {
|
|
275
|
+
account: z
|
|
276
|
+
.object({ account_id: z.string().optional(), sandbox: z.boolean().optional() })
|
|
277
|
+
.passthrough()
|
|
278
|
+
.optional(),
|
|
279
|
+
},
|
|
280
|
+
seed: {
|
|
281
|
+
product: ({ product_id, fixture }, ctx) => {
|
|
282
|
+
sessionStore.loadForControllerInput(ctx.input).seededProducts.set(product_id, fixture);
|
|
283
|
+
},
|
|
284
|
+
creative: ({ creative_id, fixture }, ctx) => {
|
|
285
|
+
const session = sessionStore.loadForControllerInput(ctx.input);
|
|
286
|
+
const withoutExisting = session.seededCreatives.filter(c => c.creative_id !== creative_id);
|
|
287
|
+
session.seededCreatives = [
|
|
288
|
+
...withoutExisting,
|
|
289
|
+
{
|
|
290
|
+
creative_id,
|
|
291
|
+
name: typeof fixture['name'] === 'string' ? fixture['name'] : `Seeded Snap creative ${creative_id}`,
|
|
292
|
+
format_id: { agent_url: PUBLIC_AGENT_URL, id: 'snap-single-image' },
|
|
293
|
+
status: readCreativeStatus(fixture['status']),
|
|
294
|
+
created_date: new Date().toISOString(),
|
|
295
|
+
updated_date: new Date().toISOString(),
|
|
296
|
+
} as SeededCreative,
|
|
297
|
+
];
|
|
298
|
+
},
|
|
299
|
+
},
|
|
300
|
+
}).register(server);
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
return server;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
function readCreativeStatus(value: unknown): SeededCreative['status'] {
|
|
307
|
+
return value === 'approved' || value === 'pending_review' || value === 'rejected' || value === 'archived'
|
|
308
|
+
? value
|
|
309
|
+
: 'pending_review';
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
if (require.main === module) {
|
|
313
|
+
serve(() => createProxySellerSnapServer({ enableComplyTestController: process.env['ADCP_SANDBOX'] === '1' }), {
|
|
314
|
+
port: PORT,
|
|
315
|
+
authenticate: verifyApiKey({
|
|
316
|
+
keys: { [ADCP_AUTH_TOKEN]: { principal: 'snap-proxy-harness' } },
|
|
317
|
+
}),
|
|
318
|
+
});
|
|
319
|
+
|
|
320
|
+
console.log(`proxy-seller-snap on http://127.0.0.1:${PORT}/mcp`);
|
|
321
|
+
}
|