@adcp/client 4.20.0 ā 4.22.0
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/AGENTS.md +278 -0
- package/README.md +96 -61
- package/bin/adcp.js +342 -4
- package/dist/lib/agents/index.generated.d.ts +9 -1
- package/dist/lib/agents/index.generated.d.ts.map +1 -1
- package/dist/lib/agents/index.generated.js +12 -0
- package/dist/lib/agents/index.generated.js.map +1 -1
- package/dist/lib/core/AgentClient.d.ts.map +1 -1
- package/dist/lib/core/SingleAgentClient.d.ts +2 -1
- package/dist/lib/core/SingleAgentClient.d.ts.map +1 -1
- package/dist/lib/core/SingleAgentClient.js +10 -1
- package/dist/lib/core/SingleAgentClient.js.map +1 -1
- package/dist/lib/discovery/property-crawler.d.ts +4 -0
- package/dist/lib/discovery/property-crawler.d.ts.map +1 -1
- package/dist/lib/discovery/property-crawler.js +10 -2
- package/dist/lib/discovery/property-crawler.js.map +1 -1
- package/dist/lib/index.d.ts +9 -9
- package/dist/lib/index.d.ts.map +1 -1
- package/dist/lib/index.js +13 -5
- package/dist/lib/index.js.map +1 -1
- package/dist/lib/protocols/index.d.ts.map +1 -1
- package/dist/lib/protocols/index.js +8 -6
- package/dist/lib/protocols/index.js.map +1 -1
- package/dist/lib/protocols/mcp.d.ts.map +1 -1
- package/dist/lib/protocols/mcp.js +24 -11
- package/dist/lib/protocols/mcp.js.map +1 -1
- package/dist/lib/registry/cursor-store.d.ts +19 -0
- package/dist/lib/registry/cursor-store.d.ts.map +1 -0
- package/dist/lib/registry/cursor-store.js +44 -0
- package/dist/lib/registry/cursor-store.js.map +1 -0
- package/dist/lib/registry/index.d.ts +21 -3
- package/dist/lib/registry/index.d.ts.map +1 -1
- package/dist/lib/registry/index.js +94 -1
- package/dist/lib/registry/index.js.map +1 -1
- package/dist/lib/registry/property-registry.d.ts +57 -0
- package/dist/lib/registry/property-registry.d.ts.map +1 -0
- package/dist/lib/registry/property-registry.js +92 -0
- package/dist/lib/registry/property-registry.js.map +1 -0
- package/dist/lib/registry/sync.d.ts +4 -0
- package/dist/lib/registry/sync.d.ts.map +1 -1
- package/dist/lib/registry/sync.js +14 -0
- package/dist/lib/registry/sync.js.map +1 -1
- package/dist/lib/registry/types.d.ts +35 -2
- package/dist/lib/registry/types.d.ts.map +1 -1
- package/dist/lib/registry/types.generated.d.ts +349 -321
- package/dist/lib/registry/types.generated.d.ts.map +1 -1
- package/dist/lib/registry/types.generated.js +1 -1
- package/dist/lib/server/index.d.ts +2 -0
- package/dist/lib/server/index.d.ts.map +1 -1
- package/dist/lib/server/index.js +3 -1
- package/dist/lib/server/index.js.map +1 -1
- package/dist/lib/server/serve.d.ts +45 -0
- package/dist/lib/server/serve.d.ts.map +1 -0
- package/dist/lib/server/serve.js +86 -0
- package/dist/lib/server/serve.js.map +1 -0
- package/dist/lib/testing/agent-tester.d.ts +1 -1
- package/dist/lib/testing/agent-tester.d.ts.map +1 -1
- package/dist/lib/testing/agent-tester.js +10 -1
- package/dist/lib/testing/agent-tester.js.map +1 -1
- package/dist/lib/testing/client.d.ts.map +1 -1
- package/dist/lib/testing/client.js +3 -0
- package/dist/lib/testing/client.js.map +1 -1
- package/dist/lib/testing/compliance/comply.d.ts.map +1 -1
- package/dist/lib/testing/compliance/comply.js +158 -203
- package/dist/lib/testing/compliance/comply.js.map +1 -1
- package/dist/lib/testing/compliance/storyboard-tracks.d.ts +24 -0
- package/dist/lib/testing/compliance/storyboard-tracks.d.ts.map +1 -0
- package/dist/lib/testing/compliance/storyboard-tracks.js +157 -0
- package/dist/lib/testing/compliance/storyboard-tracks.js.map +1 -0
- package/dist/lib/testing/compliance/types.d.ts +1 -1
- package/dist/lib/testing/compliance/types.d.ts.map +1 -1
- package/dist/lib/testing/index.d.ts +2 -1
- package/dist/lib/testing/index.d.ts.map +1 -1
- package/dist/lib/testing/index.js +26 -1
- package/dist/lib/testing/index.js.map +1 -1
- package/dist/lib/testing/orchestrator.d.ts +8 -0
- package/dist/lib/testing/orchestrator.d.ts.map +1 -1
- package/dist/lib/testing/orchestrator.js +11 -0
- package/dist/lib/testing/orchestrator.js.map +1 -1
- package/dist/lib/testing/scenarios/brand-rights.d.ts +23 -0
- package/dist/lib/testing/scenarios/brand-rights.d.ts.map +1 -0
- package/dist/lib/testing/scenarios/brand-rights.js +144 -0
- package/dist/lib/testing/scenarios/brand-rights.js.map +1 -0
- package/dist/lib/testing/scenarios/capabilities.d.ts.map +1 -1
- package/dist/lib/testing/scenarios/capabilities.js +11 -2
- package/dist/lib/testing/scenarios/capabilities.js.map +1 -1
- package/dist/lib/testing/scenarios/governance.d.ts.map +1 -1
- package/dist/lib/testing/scenarios/governance.js +5 -0
- package/dist/lib/testing/scenarios/governance.js.map +1 -1
- package/dist/lib/testing/scenarios/index.d.ts +2 -0
- package/dist/lib/testing/scenarios/index.d.ts.map +1 -1
- package/dist/lib/testing/scenarios/index.js +10 -2
- package/dist/lib/testing/scenarios/index.js.map +1 -1
- package/dist/lib/testing/scenarios/media-buy.d.ts.map +1 -1
- package/dist/lib/testing/scenarios/media-buy.js +22 -3
- package/dist/lib/testing/scenarios/media-buy.js.map +1 -1
- package/dist/lib/testing/scenarios/trusted-match.d.ts +22 -0
- package/dist/lib/testing/scenarios/trusted-match.d.ts.map +1 -0
- package/dist/lib/testing/scenarios/trusted-match.js +128 -0
- package/dist/lib/testing/scenarios/trusted-match.js.map +1 -0
- package/dist/lib/testing/storyboard/context.d.ts +34 -0
- package/dist/lib/testing/storyboard/context.d.ts.map +1 -0
- package/dist/lib/testing/storyboard/context.js +257 -0
- package/dist/lib/testing/storyboard/context.js.map +1 -0
- package/dist/lib/testing/storyboard/index.d.ts +15 -0
- package/dist/lib/testing/storyboard/index.d.ts.map +1 -0
- package/dist/lib/testing/storyboard/index.js +48 -0
- package/dist/lib/testing/storyboard/index.js.map +1 -0
- package/dist/lib/testing/storyboard/loader.d.ts +53 -0
- package/dist/lib/testing/storyboard/loader.d.ts.map +1 -0
- package/dist/lib/testing/storyboard/loader.js +114 -0
- package/dist/lib/testing/storyboard/loader.js.map +1 -0
- package/dist/lib/testing/storyboard/path.d.ts +29 -0
- package/dist/lib/testing/storyboard/path.d.ts.map +1 -0
- package/dist/lib/testing/storyboard/path.js +121 -0
- package/dist/lib/testing/storyboard/path.js.map +1 -0
- package/dist/lib/testing/storyboard/request-builder.d.ts +28 -0
- package/dist/lib/testing/storyboard/request-builder.d.ts.map +1 -0
- package/dist/lib/testing/storyboard/request-builder.js +410 -0
- package/dist/lib/testing/storyboard/request-builder.js.map +1 -0
- package/dist/lib/testing/storyboard/runner.d.ts +24 -0
- package/dist/lib/testing/storyboard/runner.d.ts.map +1 -0
- package/dist/lib/testing/storyboard/runner.js +280 -0
- package/dist/lib/testing/storyboard/runner.js.map +1 -0
- package/dist/lib/testing/storyboard/task-map.d.ts +21 -0
- package/dist/lib/testing/storyboard/task-map.d.ts.map +1 -0
- package/dist/lib/testing/storyboard/task-map.js +84 -0
- package/dist/lib/testing/storyboard/task-map.js.map +1 -0
- package/dist/lib/testing/storyboard/types.d.ts +156 -0
- package/dist/lib/testing/storyboard/types.d.ts.map +1 -0
- package/dist/lib/testing/storyboard/types.js +10 -0
- package/dist/lib/testing/storyboard/types.js.map +1 -0
- package/dist/lib/testing/storyboard/validations.d.ts +17 -0
- package/dist/lib/testing/storyboard/validations.d.ts.map +1 -0
- package/dist/lib/testing/storyboard/validations.js +166 -0
- package/dist/lib/testing/storyboard/validations.js.map +1 -0
- package/dist/lib/testing/types.d.ts +4 -1
- package/dist/lib/testing/types.d.ts.map +1 -1
- package/dist/lib/types/core.generated.d.ts +36 -23
- package/dist/lib/types/core.generated.d.ts.map +1 -1
- package/dist/lib/types/core.generated.js +1 -1
- package/dist/lib/types/schemas.generated.d.ts +1098 -770
- package/dist/lib/types/schemas.generated.d.ts.map +1 -1
- package/dist/lib/types/schemas.generated.js +163 -76
- package/dist/lib/types/schemas.generated.js.map +1 -1
- package/dist/lib/types/tools.generated.d.ts +314 -24
- package/dist/lib/types/tools.generated.d.ts.map +1 -1
- package/dist/lib/utils/capabilities.d.ts +4 -1
- package/dist/lib/utils/capabilities.d.ts.map +1 -1
- package/dist/lib/utils/capabilities.js +25 -1
- package/dist/lib/utils/capabilities.js.map +1 -1
- package/dist/lib/utils/response-schemas.d.ts.map +1 -1
- package/dist/lib/utils/response-schemas.js +34 -3
- package/dist/lib/utils/response-schemas.js.map +1 -1
- package/dist/lib/utils/validate-user-agent.d.ts +8 -0
- package/dist/lib/utils/validate-user-agent.d.ts.map +1 -0
- package/dist/lib/utils/validate-user-agent.js +15 -0
- package/dist/lib/utils/validate-user-agent.js.map +1 -0
- package/dist/lib/version.d.ts +9 -3
- package/dist/lib/version.d.ts.map +1 -1
- package/dist/lib/version.js +10 -4
- package/dist/lib/version.js.map +1 -1
- package/docs/README.md +42 -0
- package/docs/guides/BUILD-AN-AGENT.md +292 -0
- package/docs/llms.txt +634 -0
- package/examples/README.md +106 -0
- package/examples/adcp.config.json +30 -0
- package/examples/basic-a2a.ts +76 -0
- package/examples/basic-mcp.ts +50 -0
- package/examples/batch-preview-test.ts +266 -0
- package/examples/conversation-client.ts +291 -0
- package/examples/debug-preview-response.ts +73 -0
- package/examples/debug-preview-with-logging.ts +50 -0
- package/examples/easy-config-demo.ts +242 -0
- package/examples/env-config.ts +51 -0
- package/examples/error-compliant-server.ts +237 -0
- package/examples/generative-creative-demo.ts +205 -0
- package/examples/inspect-card-formats.ts +161 -0
- package/examples/logger-usage.ts +165 -0
- package/examples/oauth-cli-example.ts +154 -0
- package/examples/pr78-async-patterns-demo.ts +247 -0
- package/examples/signals-agent.ts +162 -0
- package/examples/simple-getting-started.ts +225 -0
- package/examples/simple-protocol-demo.ts +75 -0
- package/examples/test-helpers-demo.ts +239 -0
- package/examples/zod-validation-example.ts +126 -0
- package/package.json +12 -2
- package/skills/adcp/SKILL.md +13 -2
- package/storyboards/audience_sync.yaml +199 -0
- package/storyboards/behavioral_analysis.yaml +244 -0
- package/storyboards/brand_rights.yaml +131 -0
- package/storyboards/creative_ad_server.yaml +171 -0
- package/storyboards/creative_sales_agent.yaml +169 -0
- package/storyboards/creative_template.yaml +306 -0
- package/storyboards/deterministic_testing.yaml +925 -0
- package/storyboards/error_compliance.yaml +231 -0
- package/storyboards/governance_content_standards.yaml +213 -0
- package/storyboards/governance_property_lists.yaml +372 -0
- package/storyboards/media_buy_catalog_creative.yaml +457 -0
- package/storyboards/media_buy_governance_escalation.yaml +467 -0
- package/storyboards/media_buy_guaranteed_approval.yaml +396 -0
- package/storyboards/media_buy_non_guaranteed.yaml +288 -0
- package/storyboards/media_buy_proposal_mode.yaml +369 -0
- package/storyboards/media_buy_seller.yaml +560 -0
- package/storyboards/media_buy_state_machine.yaml +254 -0
- package/storyboards/schema.yaml +65 -0
- package/storyboards/schema_validation.yaml +166 -0
- package/storyboards/si_session.yaml +384 -0
- package/storyboards/signal_marketplace.yaml +283 -0
- package/storyboards/signal_owned.yaml +211 -0
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
// Environment Configuration Example
|
|
2
|
+
import { AdCPClient } from '@adcp/client';
|
|
3
|
+
|
|
4
|
+
async function envConfigExample() {
|
|
5
|
+
// Load agents from environment using factory method
|
|
6
|
+
// Set ADCP_AGENTS_CONFIG (or SALES_AGENTS_CONFIG) in your .env file like:
|
|
7
|
+
// ADCP_AGENTS_CONFIG='[{"id":"test-agent","name":"Test Agent","agent_uri":"https://test-agent.example.com","protocol":"mcp","auth_token":"your-token"}]'
|
|
8
|
+
|
|
9
|
+
const client = AdCPClient.fromEnv();
|
|
10
|
+
|
|
11
|
+
if (client.agentCount === 0) {
|
|
12
|
+
console.log('No agents configured. Set ADCP_AGENTS_CONFIG environment variable.');
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
console.log(`Loaded ${client.agentCount} agents from environment:`);
|
|
17
|
+
client.getAgentConfigs().forEach(agent => {
|
|
18
|
+
console.log(` - ${agent.name} (${agent.protocol.toUpperCase()}) at ${agent.agent_uri}`);
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
// Test all agents
|
|
22
|
+
try {
|
|
23
|
+
const agentCollection = client.allAgents();
|
|
24
|
+
const results = await agentCollection.getProducts({
|
|
25
|
+
brief: 'Looking for advertising inventory for Q4 campaigns',
|
|
26
|
+
promoted_offering: 'Holiday season promotions',
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
console.log('\nTest Results:');
|
|
30
|
+
results.forEach(result => {
|
|
31
|
+
if (result.status === 'completed') {
|
|
32
|
+
console.log(`${result.metadata.agent.name}: ā
(${result.data?.products?.length || 0} products)`);
|
|
33
|
+
} else {
|
|
34
|
+
console.log(`${result.metadata.agent.name}: ā Error: ${result.error || 'Unknown error'}`);
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
// Summary
|
|
39
|
+
const successful = results.filter(r => r.status === 'completed').length;
|
|
40
|
+
const failed = results.length - successful;
|
|
41
|
+
|
|
42
|
+
console.log(`\nSummary: ${successful} successful, ${failed} failed`);
|
|
43
|
+
} catch (error) {
|
|
44
|
+
console.error('Error testing agents:', error);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Run example
|
|
49
|
+
if (require.main === module) {
|
|
50
|
+
envConfigExample();
|
|
51
|
+
}
|
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Example: AdCP-Compliant MCP Server
|
|
3
|
+
*
|
|
4
|
+
* Demonstrates building a server using @adcp/client response builders
|
|
5
|
+
* for type-safe responses. Run with:
|
|
6
|
+
*
|
|
7
|
+
* npx tsx examples/error-compliant-server.ts
|
|
8
|
+
*
|
|
9
|
+
* Then test with:
|
|
10
|
+
*
|
|
11
|
+
* npx @adcp/client comply http://localhost:3456/mcp
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import {
|
|
15
|
+
createTaskCapableServer,
|
|
16
|
+
adcpError,
|
|
17
|
+
capabilitiesResponse,
|
|
18
|
+
productsResponse,
|
|
19
|
+
mediaBuyResponse,
|
|
20
|
+
deliveryResponse,
|
|
21
|
+
serve,
|
|
22
|
+
GetProductsRequestSchema,
|
|
23
|
+
CreateMediaBuyRequestSchema,
|
|
24
|
+
GetMediaBuyDeliveryRequestSchema,
|
|
25
|
+
} from '@adcp/client';
|
|
26
|
+
import type { Product, GetAdCPCapabilitiesResponse } from '@adcp/client';
|
|
27
|
+
|
|
28
|
+
// CreateMediaBuyRequestSchema requires account/brand per spec, but a lenient
|
|
29
|
+
// version lets intentionally-incomplete requests reach the handler so it can
|
|
30
|
+
// return proper AdCP structured errors instead of generic MCP validation errors.
|
|
31
|
+
const LenientCreateMediaBuyInput = CreateMediaBuyRequestSchema.extend({
|
|
32
|
+
account: CreateMediaBuyRequestSchema.shape.account.optional(),
|
|
33
|
+
brand: CreateMediaBuyRequestSchema.shape.brand.optional(),
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
// ---------------------------------------------------------------------------
|
|
37
|
+
// Product catalog ā typed as Product[] so the compiler enforces the schema
|
|
38
|
+
// ---------------------------------------------------------------------------
|
|
39
|
+
const PRODUCTS: Product[] = [
|
|
40
|
+
{
|
|
41
|
+
product_id: 'prod_display_300x250',
|
|
42
|
+
name: 'Display Banner 300x250',
|
|
43
|
+
description: 'Standard IAB display banner ad unit served across premium news and lifestyle sites.',
|
|
44
|
+
publisher_properties: [{ publisher_domain: 'example-publisher.com', selection_type: 'all' }],
|
|
45
|
+
channels: ['display'],
|
|
46
|
+
format_ids: [
|
|
47
|
+
{ agent_url: 'https://creatives.adcontextprotocol.org', id: 'display_static', width: 300, height: 250 },
|
|
48
|
+
],
|
|
49
|
+
delivery_type: 'non_guaranteed',
|
|
50
|
+
pricing_options: [
|
|
51
|
+
{
|
|
52
|
+
pricing_option_id: 'po_cpm',
|
|
53
|
+
pricing_model: 'cpm',
|
|
54
|
+
fixed_price: 5.0,
|
|
55
|
+
currency: 'USD',
|
|
56
|
+
min_spend_per_package: 500,
|
|
57
|
+
},
|
|
58
|
+
],
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
product_id: 'prod_video_pre_roll',
|
|
62
|
+
name: 'Pre-Roll Video 15s',
|
|
63
|
+
description: 'Skippable pre-roll video ads served on premium video content.',
|
|
64
|
+
publisher_properties: [{ publisher_domain: 'example-publisher.com', selection_type: 'all' }],
|
|
65
|
+
channels: ['olv'],
|
|
66
|
+
format_ids: [{ agent_url: 'https://creatives.adcontextprotocol.org', id: 'video_hosted' }],
|
|
67
|
+
delivery_type: 'non_guaranteed',
|
|
68
|
+
pricing_options: [
|
|
69
|
+
{
|
|
70
|
+
pricing_option_id: 'po_cpm',
|
|
71
|
+
pricing_model: 'cpm',
|
|
72
|
+
fixed_price: 12.0,
|
|
73
|
+
currency: 'USD',
|
|
74
|
+
min_spend_per_package: 1000,
|
|
75
|
+
},
|
|
76
|
+
],
|
|
77
|
+
},
|
|
78
|
+
];
|
|
79
|
+
|
|
80
|
+
// ---------------------------------------------------------------------------
|
|
81
|
+
// Rate limit state (module-scoped ā persists across per-request server instances)
|
|
82
|
+
// ---------------------------------------------------------------------------
|
|
83
|
+
let requestCount = 0;
|
|
84
|
+
const RATE_LIMIT = 50;
|
|
85
|
+
const RATE_WINDOW_MS = 60_000;
|
|
86
|
+
let windowStart = Date.now();
|
|
87
|
+
|
|
88
|
+
function checkRateLimit() {
|
|
89
|
+
const now = Date.now();
|
|
90
|
+
if (now - windowStart > RATE_WINDOW_MS) {
|
|
91
|
+
requestCount = 0;
|
|
92
|
+
windowStart = now;
|
|
93
|
+
}
|
|
94
|
+
requestCount++;
|
|
95
|
+
if (requestCount > RATE_LIMIT) {
|
|
96
|
+
return adcpError('RATE_LIMITED', {
|
|
97
|
+
message: 'Request rate exceeded',
|
|
98
|
+
retry_after: Math.ceil((windowStart + RATE_WINDOW_MS - now) / 1000),
|
|
99
|
+
details: { limit: RATE_LIMIT, remaining: 0, window_seconds: RATE_WINDOW_MS / 1000, scope: 'global' },
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
return null;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// ---------------------------------------------------------------------------
|
|
106
|
+
// Server factory (McpServer.connect() can only be called once per instance)
|
|
107
|
+
// ---------------------------------------------------------------------------
|
|
108
|
+
function createAgentServer() {
|
|
109
|
+
const server = createTaskCapableServer('Example AdCP Agent', '1.0.0');
|
|
110
|
+
|
|
111
|
+
// --- get_adcp_capabilities ---
|
|
112
|
+
server.tool('get_adcp_capabilities', {}, async () => {
|
|
113
|
+
const limited = checkRateLimit();
|
|
114
|
+
if (limited) return limited;
|
|
115
|
+
|
|
116
|
+
const capabilities: GetAdCPCapabilitiesResponse = {
|
|
117
|
+
adcp: { major_versions: [3] },
|
|
118
|
+
supported_protocols: ['media_buy'],
|
|
119
|
+
media_buy: {
|
|
120
|
+
features: {
|
|
121
|
+
inline_creative_management: false,
|
|
122
|
+
property_list_filtering: false,
|
|
123
|
+
content_standards: false,
|
|
124
|
+
},
|
|
125
|
+
},
|
|
126
|
+
};
|
|
127
|
+
return capabilitiesResponse(capabilities);
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
// --- get_products ---
|
|
131
|
+
server.tool('get_products', GetProductsRequestSchema.shape, async () => {
|
|
132
|
+
const limited = checkRateLimit();
|
|
133
|
+
if (limited) return limited;
|
|
134
|
+
|
|
135
|
+
return productsResponse({ products: PRODUCTS });
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
// --- create_media_buy ---
|
|
139
|
+
server.tool(
|
|
140
|
+
'create_media_buy',
|
|
141
|
+
LenientCreateMediaBuyInput.shape,
|
|
142
|
+
async ({ buyer_ref, start_time, end_time, packages }) => {
|
|
143
|
+
const limited = checkRateLimit();
|
|
144
|
+
if (limited) return limited;
|
|
145
|
+
|
|
146
|
+
if (new Date(end_time) <= new Date(start_time)) {
|
|
147
|
+
return adcpError('INVALID_REQUEST', {
|
|
148
|
+
message: 'end_time must be after start_time',
|
|
149
|
+
field: 'end_time',
|
|
150
|
+
suggestion: 'Set end_time to a date after start_time',
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
if (packages) {
|
|
155
|
+
for (let i = 0; i < packages.length; i++) {
|
|
156
|
+
const pkg = packages[i]!;
|
|
157
|
+
|
|
158
|
+
if (pkg.budget < 0) {
|
|
159
|
+
return adcpError('INVALID_REQUEST', {
|
|
160
|
+
message: 'Budget must be non-negative',
|
|
161
|
+
field: `packages[${i}].budget`,
|
|
162
|
+
suggestion: 'Set budget to 0 or greater',
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
const product = PRODUCTS.find(p => p.product_id === pkg.product_id);
|
|
167
|
+
if (!product) {
|
|
168
|
+
return adcpError('PRODUCT_NOT_FOUND', {
|
|
169
|
+
message: `Product '${pkg.product_id}' not found`,
|
|
170
|
+
field: `packages[${i}].product_id`,
|
|
171
|
+
suggestion: 'Use get_products to discover available products',
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
const pricing = product.pricing_options.find(po => po.pricing_option_id === pkg.pricing_option_id);
|
|
176
|
+
if (
|
|
177
|
+
pricing &&
|
|
178
|
+
'min_spend_per_package' in pricing &&
|
|
179
|
+
pricing.min_spend_per_package != null &&
|
|
180
|
+
pkg.budget < pricing.min_spend_per_package
|
|
181
|
+
) {
|
|
182
|
+
return adcpError('BUDGET_TOO_LOW', {
|
|
183
|
+
message: `Budget ${pkg.budget} is below minimum ${pricing.min_spend_per_package} for ${product.name}`,
|
|
184
|
+
field: `packages[${i}].budget`,
|
|
185
|
+
suggestion: `Increase budget to at least ${pricing.min_spend_per_package}`,
|
|
186
|
+
details: { minimum_budget: pricing.min_spend_per_package, currency: 'USD' },
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
const mediaBuyId = `mb_${Date.now()}`;
|
|
193
|
+
|
|
194
|
+
return mediaBuyResponse({
|
|
195
|
+
media_buy_id: mediaBuyId,
|
|
196
|
+
buyer_ref,
|
|
197
|
+
packages: (packages ?? []).map((pkg, i) => ({
|
|
198
|
+
package_id: `pkg_${i}_${Date.now()}`,
|
|
199
|
+
buyer_ref: pkg.buyer_ref,
|
|
200
|
+
product_id: pkg.product_id,
|
|
201
|
+
pricing_option_id: pkg.pricing_option_id,
|
|
202
|
+
budget: pkg.budget,
|
|
203
|
+
})),
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
);
|
|
207
|
+
|
|
208
|
+
// --- get_media_buy_delivery ---
|
|
209
|
+
server.tool('get_media_buy_delivery', GetMediaBuyDeliveryRequestSchema.shape, async ({ media_buy_ids }) => {
|
|
210
|
+
const limited = checkRateLimit();
|
|
211
|
+
if (limited) return limited;
|
|
212
|
+
|
|
213
|
+
const ids = media_buy_ids ?? [];
|
|
214
|
+
const now = new Date();
|
|
215
|
+
const yesterday = new Date(now.getTime() - 86400000);
|
|
216
|
+
|
|
217
|
+
return deliveryResponse({
|
|
218
|
+
reporting_period: {
|
|
219
|
+
start: yesterday.toISOString(),
|
|
220
|
+
end: now.toISOString(),
|
|
221
|
+
},
|
|
222
|
+
media_buy_deliveries: ids.map(id => ({
|
|
223
|
+
media_buy_id: id,
|
|
224
|
+
status: 'active' as const,
|
|
225
|
+
totals: { impressions: 0, spend: 0 },
|
|
226
|
+
by_package: [],
|
|
227
|
+
})),
|
|
228
|
+
});
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
return server;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
// ---------------------------------------------------------------------------
|
|
235
|
+
// Start the server
|
|
236
|
+
// ---------------------------------------------------------------------------
|
|
237
|
+
serve(createAgentServer, { port: 3456 });
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
#!/usr/bin/env tsx
|
|
2
|
+
// Demonstrates the new generative creative format support introduced in AdCP v1.7.0
|
|
3
|
+
// This example shows how to use the new format_id and assets structure for both
|
|
4
|
+
// static and generative creative workflows
|
|
5
|
+
|
|
6
|
+
import { ADCPMultiAgentClient, type SyncCreativesRequest } from '../src/lib';
|
|
7
|
+
|
|
8
|
+
async function demonstrateGenerativeCreatives() {
|
|
9
|
+
console.log('šØ Generative Creative Format Demo');
|
|
10
|
+
console.log('===================================\n');
|
|
11
|
+
|
|
12
|
+
// Initialize client from environment config
|
|
13
|
+
const client = ADCPMultiAgentClient.fromEnv();
|
|
14
|
+
|
|
15
|
+
// Get first available agent (just for demo purposes)
|
|
16
|
+
const agentIds = client.getAgentIds();
|
|
17
|
+
if (agentIds.length === 0) {
|
|
18
|
+
throw new Error('No agents configured. Please set SALES_AGENTS_CONFIG environment variable.');
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const agent = client.agent(agentIds[0]);
|
|
22
|
+
console.log(`ā
Using agent: ${agent.config.name}`);
|
|
23
|
+
console.log(` URI: ${agent.config.agent_uri}`);
|
|
24
|
+
console.log(` Protocol: ${agent.config.protocol}\n`);
|
|
25
|
+
|
|
26
|
+
// Example 1: Traditional static creative with new format
|
|
27
|
+
console.log('š Example 1: Static Creative with new format_id structure\n');
|
|
28
|
+
|
|
29
|
+
const staticCreativeRequest: SyncCreativesRequest = {
|
|
30
|
+
creatives: [
|
|
31
|
+
{
|
|
32
|
+
creative_id: `static_banner_${Date.now()}`,
|
|
33
|
+
name: 'Static Display Banner 300x250',
|
|
34
|
+
format_id: {
|
|
35
|
+
agent_url: 'https://creative.adcontextprotocol.org',
|
|
36
|
+
id: 'display_300x250',
|
|
37
|
+
},
|
|
38
|
+
assets: {
|
|
39
|
+
image: {
|
|
40
|
+
asset_type: 'image',
|
|
41
|
+
url: 'https://example.com/banner-300x250.jpg',
|
|
42
|
+
width: 300,
|
|
43
|
+
height: 250,
|
|
44
|
+
alt_text: 'Summer sale banner',
|
|
45
|
+
},
|
|
46
|
+
click_url: {
|
|
47
|
+
asset_type: 'url',
|
|
48
|
+
url: 'https://example.com/summer-sale',
|
|
49
|
+
description: 'Landing page for summer sale campaign',
|
|
50
|
+
},
|
|
51
|
+
},
|
|
52
|
+
tags: ['display', 'static', 'summer-sale'],
|
|
53
|
+
},
|
|
54
|
+
],
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
console.log('Static Creative Structure:');
|
|
58
|
+
console.log(JSON.stringify(staticCreativeRequest, null, 2));
|
|
59
|
+
console.log('\n' + '-'.repeat(50) + '\n');
|
|
60
|
+
|
|
61
|
+
// Example 2: Generative creative with brand context
|
|
62
|
+
console.log('š Example 2: Generative Creative with brand_manifest\n');
|
|
63
|
+
|
|
64
|
+
const generativeCreativeRequest: SyncCreativesRequest = {
|
|
65
|
+
creatives: [
|
|
66
|
+
{
|
|
67
|
+
creative_id: `gen_banner_${Date.now()}`,
|
|
68
|
+
name: 'AI-Generated Display Banner',
|
|
69
|
+
format_id: {
|
|
70
|
+
agent_url: 'https://creative.adcontextprotocol.org',
|
|
71
|
+
id: 'display_300x250_generative',
|
|
72
|
+
},
|
|
73
|
+
assets: {
|
|
74
|
+
brand_context: {
|
|
75
|
+
asset_type: 'url',
|
|
76
|
+
url: 'https://example.com',
|
|
77
|
+
description: 'Brand website for context extraction',
|
|
78
|
+
},
|
|
79
|
+
generation_prompt: {
|
|
80
|
+
asset_type: 'text',
|
|
81
|
+
content: 'Create a vibrant summer sale banner highlighting 30% off outdoor furniture',
|
|
82
|
+
},
|
|
83
|
+
logo: {
|
|
84
|
+
asset_type: 'image',
|
|
85
|
+
url: 'https://example.com/logo.png',
|
|
86
|
+
width: 100,
|
|
87
|
+
height: 100,
|
|
88
|
+
},
|
|
89
|
+
},
|
|
90
|
+
inputs: [
|
|
91
|
+
{
|
|
92
|
+
name: 'Desktop View',
|
|
93
|
+
macros: {
|
|
94
|
+
DEVICE_TYPE: 'desktop',
|
|
95
|
+
},
|
|
96
|
+
context_description: 'Preview for desktop browsers at 1920x1080',
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
name: 'Mobile View',
|
|
100
|
+
macros: {
|
|
101
|
+
DEVICE_TYPE: 'mobile',
|
|
102
|
+
},
|
|
103
|
+
context_description: 'Preview for mobile devices at 375x667',
|
|
104
|
+
},
|
|
105
|
+
],
|
|
106
|
+
tags: ['display', 'generative', 'ai', 'summer-sale'],
|
|
107
|
+
},
|
|
108
|
+
],
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
console.log('Generative Creative Structure:');
|
|
112
|
+
console.log(JSON.stringify(generativeCreativeRequest, null, 2));
|
|
113
|
+
console.log('\n' + '-'.repeat(50) + '\n');
|
|
114
|
+
|
|
115
|
+
// Example 3: Approval workflow for generative creative
|
|
116
|
+
console.log('š Example 3: Approving a Generative Creative\n');
|
|
117
|
+
|
|
118
|
+
const approvalRequest: SyncCreativesRequest = {
|
|
119
|
+
creatives: [
|
|
120
|
+
{
|
|
121
|
+
creative_id: 'gen_banner_12345', // Existing creative ID from previous sync
|
|
122
|
+
name: 'AI-Generated Display Banner',
|
|
123
|
+
format_id: {
|
|
124
|
+
agent_url: 'https://creative.adcontextprotocol.org',
|
|
125
|
+
id: 'display_300x250_generative',
|
|
126
|
+
},
|
|
127
|
+
assets: {
|
|
128
|
+
brand_context: {
|
|
129
|
+
asset_type: 'url',
|
|
130
|
+
url: 'https://example.com',
|
|
131
|
+
description: 'Brand website for context extraction',
|
|
132
|
+
},
|
|
133
|
+
generation_prompt: {
|
|
134
|
+
asset_type: 'text',
|
|
135
|
+
content: 'Create a vibrant summer sale banner highlighting 30% off outdoor furniture',
|
|
136
|
+
},
|
|
137
|
+
},
|
|
138
|
+
approved: true, // Approve the generated preview
|
|
139
|
+
},
|
|
140
|
+
],
|
|
141
|
+
patch: true, // Only update the approval status
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
console.log('Approval Request Structure:');
|
|
145
|
+
console.log(JSON.stringify(approvalRequest, null, 2));
|
|
146
|
+
console.log('\n' + '-'.repeat(50) + '\n');
|
|
147
|
+
|
|
148
|
+
// Example 4: Request regeneration with updated prompt
|
|
149
|
+
console.log('š Example 4: Request Regeneration\n');
|
|
150
|
+
|
|
151
|
+
const regenerationRequest: SyncCreativesRequest = {
|
|
152
|
+
creatives: [
|
|
153
|
+
{
|
|
154
|
+
creative_id: 'gen_banner_12345',
|
|
155
|
+
name: 'AI-Generated Display Banner',
|
|
156
|
+
format_id: {
|
|
157
|
+
agent_url: 'https://creative.adcontextprotocol.org',
|
|
158
|
+
id: 'display_300x250_generative',
|
|
159
|
+
},
|
|
160
|
+
assets: {
|
|
161
|
+
brand_context: {
|
|
162
|
+
asset_type: 'url',
|
|
163
|
+
url: 'https://example.com',
|
|
164
|
+
description: 'Brand website for context extraction',
|
|
165
|
+
},
|
|
166
|
+
generation_prompt: {
|
|
167
|
+
asset_type: 'text',
|
|
168
|
+
content:
|
|
169
|
+
'Create a warm, inviting summer sale banner with emphasis on comfort and quality. Show 30% off outdoor furniture with natural colors.',
|
|
170
|
+
},
|
|
171
|
+
},
|
|
172
|
+
approved: false, // Request regeneration with updated prompt
|
|
173
|
+
},
|
|
174
|
+
],
|
|
175
|
+
patch: true,
|
|
176
|
+
};
|
|
177
|
+
|
|
178
|
+
console.log('Regeneration Request Structure:');
|
|
179
|
+
console.log(JSON.stringify(regenerationRequest, null, 2));
|
|
180
|
+
console.log('\n' + '-'.repeat(50) + '\n');
|
|
181
|
+
|
|
182
|
+
console.log('š Demo complete!');
|
|
183
|
+
console.log('\nš Key Changes in AdCP v1.7.0:');
|
|
184
|
+
console.log(' ā format_id is now an object with agent_url and id');
|
|
185
|
+
console.log(' ā assets is now a flexible object keyed by asset_role');
|
|
186
|
+
console.log(' ā New asset types: url and brand_manifest');
|
|
187
|
+
console.log(' ā inputs array for defining preview contexts');
|
|
188
|
+
console.log(' ā approved field for generative creative workflows');
|
|
189
|
+
console.log(' ā context_description for AI-generated content guidance');
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// Run the demo
|
|
193
|
+
if (require.main === module) {
|
|
194
|
+
demonstrateGenerativeCreatives()
|
|
195
|
+
.then(() => {
|
|
196
|
+
console.log('\n⨠Done!');
|
|
197
|
+
process.exit(0);
|
|
198
|
+
})
|
|
199
|
+
.catch(error => {
|
|
200
|
+
console.error('Fatal error:', error);
|
|
201
|
+
process.exit(1);
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
export { demonstrateGenerativeCreatives };
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
#!/usr/bin/env tsx
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Inspect card format definitions from creative agent
|
|
5
|
+
*
|
|
6
|
+
* This will help us understand:
|
|
7
|
+
* 1. What format IDs are available for cards (product_card, format_card)
|
|
8
|
+
* 2. What assets those formats expect (using new `assets` field or deprecated `assets_required`)
|
|
9
|
+
* 3. How to properly structure creative_manifest for cards
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { AdCPClient } from '../src/lib/core/AdCPClient';
|
|
13
|
+
import { getFormatAssets, getRequiredAssets, getOptionalAssets, usesDeprecatedAssetsField } from '../src/lib';
|
|
14
|
+
|
|
15
|
+
const CREATIVE_AGENT_URL = process.env.CREATIVE_AGENT_URL || 'https://creative.adcontextprotocol.org/mcp';
|
|
16
|
+
const CREATIVE_AGENT_PROTOCOL = (process.env.CREATIVE_AGENT_PROTOCOL || 'mcp') as 'mcp' | 'a2a';
|
|
17
|
+
|
|
18
|
+
async function main() {
|
|
19
|
+
console.log('š Inspecting Card Format Definitions\n');
|
|
20
|
+
|
|
21
|
+
const creativeAgent = new AdCPClient({
|
|
22
|
+
id: 'creative_agent',
|
|
23
|
+
name: 'Creative Agent',
|
|
24
|
+
agent_uri: CREATIVE_AGENT_URL,
|
|
25
|
+
protocol: CREATIVE_AGENT_PROTOCOL,
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
console.log(`š” Connected to: ${CREATIVE_AGENT_URL}\n`);
|
|
29
|
+
|
|
30
|
+
// List all formats
|
|
31
|
+
console.log('š Fetching all creative formats...');
|
|
32
|
+
const result = await creativeAgent.listCreativeFormats({});
|
|
33
|
+
|
|
34
|
+
if (!result.success || !result.data) {
|
|
35
|
+
console.error('ā Failed to fetch formats:', result.error);
|
|
36
|
+
process.exit(1);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const formats = result.data.formats || [];
|
|
40
|
+
console.log(`ā
Found ${formats.length} formats\n`);
|
|
41
|
+
|
|
42
|
+
// Look for card formats
|
|
43
|
+
const cardFormats = formats.filter(
|
|
44
|
+
f =>
|
|
45
|
+
f.format_id.id.includes('card') ||
|
|
46
|
+
f.name?.toLowerCase().includes('card') ||
|
|
47
|
+
f.description?.toLowerCase().includes('card')
|
|
48
|
+
);
|
|
49
|
+
|
|
50
|
+
if (cardFormats.length === 0) {
|
|
51
|
+
console.log('ā ļø No formats with "card" in name/description found');
|
|
52
|
+
console.log('\nš All available format IDs:');
|
|
53
|
+
formats.forEach(f => {
|
|
54
|
+
console.log(` - ${f.format_id.id}: ${f.name || 'Unnamed'}`);
|
|
55
|
+
});
|
|
56
|
+
} else {
|
|
57
|
+
console.log('āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā');
|
|
58
|
+
console.log('CARD FORMATS FOUND');
|
|
59
|
+
console.log('āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā\n');
|
|
60
|
+
|
|
61
|
+
cardFormats.forEach((format, index) => {
|
|
62
|
+
console.log(`\n${'ā'.repeat(60)}`);
|
|
63
|
+
console.log(`Format ${index + 1}: ${format.name || 'Unnamed'}`);
|
|
64
|
+
console.log('ā'.repeat(60));
|
|
65
|
+
console.log(`Format ID: ${format.format_id.id}`);
|
|
66
|
+
console.log(`Agent URL: ${format.format_id.agent_url}`);
|
|
67
|
+
console.log(`Type: ${format.type || 'N/A'}`);
|
|
68
|
+
console.log(`Description: ${format.description || 'N/A'}`);
|
|
69
|
+
|
|
70
|
+
const assets = getFormatAssets(format);
|
|
71
|
+
if (assets.length > 0) {
|
|
72
|
+
const requiredAssets = getRequiredAssets(format);
|
|
73
|
+
const optionalAssets = getOptionalAssets(format);
|
|
74
|
+
console.log(
|
|
75
|
+
`\nš¦ Assets: ${assets.length} total (${requiredAssets.length} required, ${optionalAssets.length} optional)`
|
|
76
|
+
);
|
|
77
|
+
if (usesDeprecatedAssetsField(format)) {
|
|
78
|
+
console.log(' ā ļø Using deprecated assets_required field');
|
|
79
|
+
}
|
|
80
|
+
assets.forEach(asset => {
|
|
81
|
+
if (asset.item_type === 'individual') {
|
|
82
|
+
console.log(`\n Asset: ${asset.asset_id}`);
|
|
83
|
+
console.log(` Type: ${asset.asset_type}`);
|
|
84
|
+
console.log(` Required: ${asset.required ? 'yes' : 'no'}`);
|
|
85
|
+
if ((asset as any).description) {
|
|
86
|
+
console.log(` Description: ${(asset as any).description}`);
|
|
87
|
+
}
|
|
88
|
+
if ((asset as any).default_value) {
|
|
89
|
+
console.log(` Default: ${JSON.stringify((asset as any).default_value)}`);
|
|
90
|
+
}
|
|
91
|
+
} else {
|
|
92
|
+
console.log(`\n Asset Group: ${asset.asset_group_id}`);
|
|
93
|
+
console.log(` Required: ${asset.required ? 'yes' : 'no'}`);
|
|
94
|
+
console.log(` Count: ${asset.min_count}-${asset.max_count}`);
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
} else {
|
|
98
|
+
console.log('\nā ļø No assets defined (flexible format)');
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
if (format.preview_image) {
|
|
102
|
+
console.log(`\nš¼ļø Preview Image: ${format.preview_image}`);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
if (format.format_card) {
|
|
106
|
+
console.log('\nš“ This format has its own format_card:');
|
|
107
|
+
console.log(` Format ID: ${format.format_card.format_id.agent_url}/${format.format_card.format_id.id}`);
|
|
108
|
+
console.log(` Manifest: ${JSON.stringify(format.format_card.manifest, null, 2)}`);
|
|
109
|
+
}
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Also show what a typical format looks like
|
|
114
|
+
console.log('\n\nāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā');
|
|
115
|
+
console.log('EXAMPLE: Standard Display Format (for reference)');
|
|
116
|
+
console.log('āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā\n');
|
|
117
|
+
|
|
118
|
+
const displayFormat = formats.find(f => f.format_id.id.includes('display') && f.format_id.id.includes('300x250'));
|
|
119
|
+
|
|
120
|
+
if (displayFormat) {
|
|
121
|
+
console.log(`Format ID: ${displayFormat.format_id.id}`);
|
|
122
|
+
console.log(`Name: ${displayFormat.name || 'Unnamed'}`);
|
|
123
|
+
console.log(`Type: ${displayFormat.type || 'N/A'}`);
|
|
124
|
+
|
|
125
|
+
const displayAssets = getFormatAssets(displayFormat);
|
|
126
|
+
if (displayAssets.length > 0) {
|
|
127
|
+
const requiredAssets = getRequiredAssets(displayFormat);
|
|
128
|
+
const optionalAssets = getOptionalAssets(displayFormat);
|
|
129
|
+
console.log(
|
|
130
|
+
`\nš¦ Assets: ${displayAssets.length} total (${requiredAssets.length} required, ${optionalAssets.length} optional)`
|
|
131
|
+
);
|
|
132
|
+
if (usesDeprecatedAssetsField(displayFormat)) {
|
|
133
|
+
console.log(' ā ļø Using deprecated assets_required field');
|
|
134
|
+
}
|
|
135
|
+
displayAssets.forEach(asset => {
|
|
136
|
+
if (asset.item_type === 'individual') {
|
|
137
|
+
console.log(` - ${asset.asset_id} (${asset.asset_type})${asset.required ? ' *required*' : ' (optional)'}`);
|
|
138
|
+
} else {
|
|
139
|
+
console.log(
|
|
140
|
+
` - [Group] ${asset.asset_group_id} (${asset.min_count}-${asset.max_count})${asset.required ? ' *required*' : ' (optional)'}`
|
|
141
|
+
);
|
|
142
|
+
}
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
} else {
|
|
146
|
+
console.log('ā ļø No standard 300x250 display format found');
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
console.log('\n\nš” Key Insights:');
|
|
150
|
+
console.log('ā'.repeat(60));
|
|
151
|
+
console.log('1. Formats define their assets using `assets` field (v2.6+) or deprecated `assets_required`');
|
|
152
|
+
console.log('2. Use getFormatAssets() helper to access assets with backward compatibility');
|
|
153
|
+
console.log('3. creative_manifest.assets should map asset_id -> asset object');
|
|
154
|
+
console.log("4. Each asset_id must match the format's assets definition");
|
|
155
|
+
console.log('5. Asset types (ImageAsset, TextAsset, etc) depend on asset_type\n');
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
main().catch(error => {
|
|
159
|
+
console.error('Fatal error:', error);
|
|
160
|
+
process.exit(1);
|
|
161
|
+
});
|