@adcp/client 4.21.0 ā 4.22.1
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 +4 -4
- package/dist/lib/index.d.ts.map +1 -1
- package/dist/lib/index.js +6 -4
- 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/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 +73 -0
- package/dist/lib/server/serve.d.ts.map +1 -0
- package/dist/lib/server/serve.js +94 -0
- package/dist/lib/server/serve.js.map +1 -0
- package/dist/lib/testing/client.d.ts.map +1 -1
- package/dist/lib/testing/client.js +1 -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 +48 -63
- 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 +1 -0
- package/dist/lib/testing/index.d.ts.map +1 -1
- package/dist/lib/testing/index.js +23 -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 +8 -0
- package/dist/lib/testing/orchestrator.js.map +1 -1
- 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 +2 -0
- package/dist/lib/testing/types.d.ts.map +1 -1
- package/dist/lib/types/core.generated.d.ts +2 -2
- 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 +193 -34
- package/dist/lib/types/schemas.generated.d.ts.map +1 -1
- package/dist/lib/types/schemas.generated.js +87 -5
- package/dist/lib/types/schemas.generated.js.map +1 -1
- package/dist/lib/types/tools.generated.d.ts +280 -3
- package/dist/lib/types/tools.generated.d.ts.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 +6 -0
- package/dist/lib/version.d.ts.map +1 -1
- package/dist/lib/version.js +7 -1
- package/dist/lib/version.js.map +1 -1
- package/docs/README.md +42 -0
- package/docs/guides/BUILD-AN-AGENT.md +294 -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 +163 -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,165 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Logger Usage Examples
|
|
3
|
+
*
|
|
4
|
+
* Demonstrates how to use the structured logger in AdCP Client
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { logger, createLogger } from '../src/lib/utils/logger';
|
|
8
|
+
|
|
9
|
+
// ====== BASIC USAGE ======
|
|
10
|
+
|
|
11
|
+
console.log('\n=== Basic Usage ===\n');
|
|
12
|
+
|
|
13
|
+
logger.debug('Debugging info (only visible if LOG_LEVEL=debug)');
|
|
14
|
+
logger.info('General information');
|
|
15
|
+
logger.warn('Warning message');
|
|
16
|
+
logger.error('Error message');
|
|
17
|
+
|
|
18
|
+
// ====== LOGGING WITH METADATA ======
|
|
19
|
+
|
|
20
|
+
console.log('\n=== Logging with Metadata ===\n');
|
|
21
|
+
|
|
22
|
+
logger.info('Task completed', {
|
|
23
|
+
taskId: 'task_123',
|
|
24
|
+
duration: 1250,
|
|
25
|
+
status: 'success',
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
logger.error('Connection failed', {
|
|
29
|
+
agentId: 'agent_xyz',
|
|
30
|
+
error: 'ECONNREFUSED',
|
|
31
|
+
retryCount: 3,
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
// ====== CONTEXT-AWARE LOGGING ======
|
|
35
|
+
|
|
36
|
+
console.log('\n=== Context-Aware Logging ===\n');
|
|
37
|
+
|
|
38
|
+
// Create protocol-specific logger
|
|
39
|
+
const mcpLogger = logger.child('MCP');
|
|
40
|
+
mcpLogger.info('Connecting to agent', { url: 'https://agent.example.com' });
|
|
41
|
+
mcpLogger.debug('Sending initialize request');
|
|
42
|
+
|
|
43
|
+
// Create nested context
|
|
44
|
+
const toolLogger = mcpLogger.child('get_products');
|
|
45
|
+
toolLogger.info('Calling tool', { params: { brief: 'Coffee products' } });
|
|
46
|
+
toolLogger.debug('Parsing response');
|
|
47
|
+
|
|
48
|
+
// ====== CUSTOM LOGGER INSTANCES ======
|
|
49
|
+
|
|
50
|
+
console.log('\n=== Custom Logger Instances ===\n');
|
|
51
|
+
|
|
52
|
+
// Create a debug-level logger for development
|
|
53
|
+
const devLogger = createLogger({
|
|
54
|
+
level: 'debug',
|
|
55
|
+
enabled: true,
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
devLogger.debug('This message is visible with debug level');
|
|
59
|
+
devLogger.info('Standard info message');
|
|
60
|
+
|
|
61
|
+
// Create a production logger (warn level only)
|
|
62
|
+
const prodLogger = createLogger({
|
|
63
|
+
level: 'warn',
|
|
64
|
+
enabled: true,
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
prodLogger.debug('This will NOT be logged');
|
|
68
|
+
prodLogger.info('This will NOT be logged');
|
|
69
|
+
prodLogger.warn('This WILL be logged');
|
|
70
|
+
prodLogger.error('This WILL be logged');
|
|
71
|
+
|
|
72
|
+
// ====== CUSTOM LOG HANDLERS ======
|
|
73
|
+
|
|
74
|
+
console.log('\n=== Custom Log Handlers ===\n');
|
|
75
|
+
|
|
76
|
+
// Example: Send logs to external service
|
|
77
|
+
const externalLogger = createLogger({
|
|
78
|
+
level: 'info',
|
|
79
|
+
handler: {
|
|
80
|
+
debug: (msg, meta) => console.log(`[EXT-DEBUG] ${msg}`, meta || ''),
|
|
81
|
+
info: (msg, meta) => console.log(`[EXT-INFO] ${msg}`, meta || ''),
|
|
82
|
+
warn: (msg, meta) => console.log(`[EXT-WARN] ${msg}`, meta || ''),
|
|
83
|
+
error: (msg, meta) => console.log(`[EXT-ERROR] ${msg}`, meta || ''),
|
|
84
|
+
},
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
externalLogger.info('Using custom handler', { service: 'external' });
|
|
88
|
+
|
|
89
|
+
// ====== RUNTIME CONFIGURATION ======
|
|
90
|
+
|
|
91
|
+
console.log('\n=== Runtime Configuration ===\n');
|
|
92
|
+
|
|
93
|
+
// Start with info level
|
|
94
|
+
logger.configure({ level: 'info' });
|
|
95
|
+
logger.debug('This will NOT be logged');
|
|
96
|
+
logger.info('This will be logged');
|
|
97
|
+
|
|
98
|
+
// Change to debug level
|
|
99
|
+
logger.configure({ level: 'debug' });
|
|
100
|
+
logger.debug('Now this WILL be logged');
|
|
101
|
+
|
|
102
|
+
// Disable logging temporarily
|
|
103
|
+
logger.configure({ enabled: false });
|
|
104
|
+
logger.error('This will NOT be logged (disabled)');
|
|
105
|
+
|
|
106
|
+
// Re-enable
|
|
107
|
+
logger.configure({ enabled: true });
|
|
108
|
+
logger.info('Logging re-enabled');
|
|
109
|
+
|
|
110
|
+
// ====== PRACTICAL EXAMPLES ======
|
|
111
|
+
|
|
112
|
+
console.log('\n=== Practical Examples ===\n');
|
|
113
|
+
|
|
114
|
+
// Example 1: Component-specific logging
|
|
115
|
+
class MediaBuyService {
|
|
116
|
+
private logger = logger.child('MediaBuyService');
|
|
117
|
+
|
|
118
|
+
async createMediaBuy(params: any) {
|
|
119
|
+
this.logger.info('Creating media buy', { params });
|
|
120
|
+
|
|
121
|
+
try {
|
|
122
|
+
// Simulate API call
|
|
123
|
+
this.logger.debug('Calling agent API');
|
|
124
|
+
// ...
|
|
125
|
+
this.logger.info('Media buy created', { buyId: 'mb_123' });
|
|
126
|
+
} catch (error) {
|
|
127
|
+
this.logger.error('Failed to create media buy', {
|
|
128
|
+
error: error instanceof Error ? error.message : String(error),
|
|
129
|
+
params,
|
|
130
|
+
});
|
|
131
|
+
throw error;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
const service = new MediaBuyService();
|
|
137
|
+
service.createMediaBuy({ brief: 'Coffee campaign' });
|
|
138
|
+
|
|
139
|
+
// Example 2: Protocol client logging
|
|
140
|
+
class ProtocolClient {
|
|
141
|
+
private logger = logger.child('ProtocolClient');
|
|
142
|
+
|
|
143
|
+
async connect(protocol: string, url: string) {
|
|
144
|
+
const protocolLogger = this.logger.child(protocol.toUpperCase());
|
|
145
|
+
|
|
146
|
+
protocolLogger.info('Connecting to agent', { url });
|
|
147
|
+
|
|
148
|
+
try {
|
|
149
|
+
// Simulate connection
|
|
150
|
+
protocolLogger.debug('Sending initialize request');
|
|
151
|
+
protocolLogger.info('Connected successfully');
|
|
152
|
+
} catch (error) {
|
|
153
|
+
protocolLogger.error('Connection failed', {
|
|
154
|
+
url,
|
|
155
|
+
error: error instanceof Error ? error.message : String(error),
|
|
156
|
+
});
|
|
157
|
+
throw error;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
const client = new ProtocolClient();
|
|
163
|
+
client.connect('mcp', 'https://agent.example.com');
|
|
164
|
+
|
|
165
|
+
console.log('\n=== Examples Complete ===\n');
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Example: Using OAuth with the ADCP client
|
|
3
|
+
*
|
|
4
|
+
* OAuth tokens are stored directly in the AgentConfig,
|
|
5
|
+
* same place as static auth tokens.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { Client as MCPClient } from '@modelcontextprotocol/sdk/client/index.js';
|
|
9
|
+
import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js';
|
|
10
|
+
import { UnauthorizedError } from '@modelcontextprotocol/sdk/client/auth.js';
|
|
11
|
+
import {
|
|
12
|
+
createCLIOAuthProvider,
|
|
13
|
+
hasValidOAuthTokens,
|
|
14
|
+
clearOAuthTokens,
|
|
15
|
+
getEffectiveAuthToken,
|
|
16
|
+
} from '../src/lib/auth/oauth';
|
|
17
|
+
import type { AgentConfig } from '../src/lib/types/adcp';
|
|
18
|
+
|
|
19
|
+
// Example: Agent configured for OAuth
|
|
20
|
+
const agent: AgentConfig = {
|
|
21
|
+
id: 'scope3-snapadcp',
|
|
22
|
+
name: 'Scope3 SnapAdCP',
|
|
23
|
+
agent_uri: process.argv[2] || 'https://snapadcp.scope3.com/mcp',
|
|
24
|
+
protocol: 'mcp',
|
|
25
|
+
// Static token alternative (uncomment to use instead of OAuth):
|
|
26
|
+
// auth_token: 'your-api-key-here',
|
|
27
|
+
//
|
|
28
|
+
// After OAuth flow completes, these fields are populated:
|
|
29
|
+
// oauth_tokens: { access_token: '...', refresh_token: '...' }
|
|
30
|
+
// oauth_client: { client_id: '...' }
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
async function main() {
|
|
34
|
+
console.log('ADCP OAuth Example');
|
|
35
|
+
console.log('==================');
|
|
36
|
+
console.log(`Agent: ${agent.name}`);
|
|
37
|
+
console.log(`URI: ${agent.agent_uri}`);
|
|
38
|
+
console.log(`Has OAuth tokens: ${hasValidOAuthTokens(agent)}`);
|
|
39
|
+
console.log(`Has static token: ${!!agent.auth_token}`);
|
|
40
|
+
console.log('');
|
|
41
|
+
|
|
42
|
+
// Create OAuth provider - tokens stored in agent config
|
|
43
|
+
const provider = createCLIOAuthProvider(agent);
|
|
44
|
+
|
|
45
|
+
// Create MCP client
|
|
46
|
+
const client = new MCPClient({
|
|
47
|
+
name: 'adcp-oauth-example',
|
|
48
|
+
version: '1.0.0',
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
// Helper to create transport with OAuth provider
|
|
52
|
+
const createTransport = () =>
|
|
53
|
+
new StreamableHTTPClientTransport(new URL(agent.agent_uri), {
|
|
54
|
+
authProvider: provider,
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
let transport = createTransport();
|
|
58
|
+
|
|
59
|
+
try {
|
|
60
|
+
console.log('Connecting...');
|
|
61
|
+
await client.connect(transport);
|
|
62
|
+
console.log('Connected!');
|
|
63
|
+
} catch (error) {
|
|
64
|
+
if (error instanceof UnauthorizedError) {
|
|
65
|
+
console.log('');
|
|
66
|
+
console.log('OAuth authorization required.');
|
|
67
|
+
console.log('Opening browser...');
|
|
68
|
+
|
|
69
|
+
try {
|
|
70
|
+
// Wait for user to complete OAuth in browser
|
|
71
|
+
const code = await provider.waitForCallback();
|
|
72
|
+
console.log('Authorization received!');
|
|
73
|
+
|
|
74
|
+
// Finish OAuth flow to exchange code for tokens
|
|
75
|
+
await transport.finishAuth(code);
|
|
76
|
+
|
|
77
|
+
// Show that tokens are now in agent config
|
|
78
|
+
console.log('');
|
|
79
|
+
console.log('OAuth tokens saved to agent config:');
|
|
80
|
+
console.log(` access_token: ${agent.oauth_tokens?.access_token ? '***' : 'none'}`);
|
|
81
|
+
console.log(` refresh_token: ${agent.oauth_tokens?.refresh_token ? '***' : 'none'}`);
|
|
82
|
+
console.log(` expires_at: ${agent.oauth_tokens?.expires_at || 'unknown'}`);
|
|
83
|
+
|
|
84
|
+
// Create new transport and reconnect (tokens are now available)
|
|
85
|
+
console.log('');
|
|
86
|
+
console.log('Reconnecting with tokens...');
|
|
87
|
+
transport = createTransport();
|
|
88
|
+
await client.connect(transport);
|
|
89
|
+
console.log('Connected!');
|
|
90
|
+
} catch (authError) {
|
|
91
|
+
console.error('OAuth failed:', authError);
|
|
92
|
+
await provider.cleanup();
|
|
93
|
+
process.exit(1);
|
|
94
|
+
}
|
|
95
|
+
} else {
|
|
96
|
+
console.error('Connection failed:', error);
|
|
97
|
+
await provider.cleanup();
|
|
98
|
+
process.exit(1);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// List available tools
|
|
103
|
+
try {
|
|
104
|
+
console.log('');
|
|
105
|
+
console.log('Listing tools...');
|
|
106
|
+
const result = await client.request({ method: 'tools/list', params: {} }, { tools: [] } as any);
|
|
107
|
+
|
|
108
|
+
console.log('Available tools:');
|
|
109
|
+
for (const tool of (result as any).tools || []) {
|
|
110
|
+
console.log(` - ${tool.name}`);
|
|
111
|
+
}
|
|
112
|
+
} catch (error) {
|
|
113
|
+
console.error('Failed to list tools:', error);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// Clean up
|
|
117
|
+
await provider.cleanup();
|
|
118
|
+
await client.close();
|
|
119
|
+
|
|
120
|
+
// Show final agent config (for saving to file/db)
|
|
121
|
+
console.log('');
|
|
122
|
+
console.log('Final agent config (save this to persist OAuth tokens):');
|
|
123
|
+
console.log(JSON.stringify(agent, null, 2));
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// Utility: clear tokens and re-auth
|
|
127
|
+
async function clearAndReauth() {
|
|
128
|
+
console.log('Clearing OAuth tokens...');
|
|
129
|
+
clearOAuthTokens(agent);
|
|
130
|
+
console.log('Tokens cleared. Run again to re-authenticate.');
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// Entry point
|
|
134
|
+
const command = process.argv[2];
|
|
135
|
+
if (command === '--clear') {
|
|
136
|
+
clearAndReauth();
|
|
137
|
+
} else if (command === '--help' || command === '-h') {
|
|
138
|
+
console.log(`
|
|
139
|
+
Usage: npx ts-node examples/oauth-cli-example.ts [options] [server-url]
|
|
140
|
+
|
|
141
|
+
Options:
|
|
142
|
+
<server-url> Connect to a specific MCP server (default: https://snapadcp.scope3.com/mcp)
|
|
143
|
+
--clear Clear OAuth tokens and exit
|
|
144
|
+
--help, -h Show this help
|
|
145
|
+
|
|
146
|
+
The OAuth flow:
|
|
147
|
+
1. Browser opens for authorization
|
|
148
|
+
2. User logs in and approves
|
|
149
|
+
3. Tokens are saved to agent.oauth_tokens
|
|
150
|
+
4. Save agent config to persist tokens for future use
|
|
151
|
+
`);
|
|
152
|
+
} else {
|
|
153
|
+
main().catch(console.error);
|
|
154
|
+
}
|
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* PR #78 Async Patterns Demo
|
|
5
|
+
*
|
|
6
|
+
* Demonstrates the new handler-controlled async execution patterns
|
|
7
|
+
* that align with ADCP spec PR #78
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import {
|
|
11
|
+
ADCPMultiAgentClient,
|
|
12
|
+
ADCP_STATUS,
|
|
13
|
+
autoApproveHandler,
|
|
14
|
+
deferAllHandler,
|
|
15
|
+
createFieldHandler,
|
|
16
|
+
InputRequiredError,
|
|
17
|
+
type TaskResult,
|
|
18
|
+
type DeferredContinuation,
|
|
19
|
+
type SubmittedContinuation,
|
|
20
|
+
} from '../src/lib/index';
|
|
21
|
+
|
|
22
|
+
async function main() {
|
|
23
|
+
console.log('š PR #78 Async Patterns Demo\n');
|
|
24
|
+
|
|
25
|
+
// Example 1: Immediate completion (status: completed)
|
|
26
|
+
console.log('š Example 1: Immediate Task Completion');
|
|
27
|
+
console.log('ā'.repeat(50));
|
|
28
|
+
|
|
29
|
+
const client = ADCPMultiAgentClient.simple('https://demo.example.com', {
|
|
30
|
+
agentId: 'demo-agent',
|
|
31
|
+
agentName: 'Demo Agent',
|
|
32
|
+
protocol: 'mcp',
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
// Simulate immediate completion
|
|
36
|
+
console.log('⢠Calling getProducts with auto-approve handler...');
|
|
37
|
+
try {
|
|
38
|
+
// This would complete immediately if server returns status: 'completed'
|
|
39
|
+
const result = await simulateTaskResult('completed', {
|
|
40
|
+
products: ['Product A', 'Product B', 'Product C'],
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
console.log('ā
Task completed immediately!');
|
|
44
|
+
console.log(` Status: ${result.status}`);
|
|
45
|
+
console.log(` Products: ${JSON.stringify(result.data?.products || [])}`);
|
|
46
|
+
} catch (error) {
|
|
47
|
+
console.log(`ā¹ļø Note: ${error.message} (expected in demo)`);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
console.log('\nš Example 2: Working Status (keep connection open)');
|
|
51
|
+
console.log('ā'.repeat(50));
|
|
52
|
+
|
|
53
|
+
// Simulate server processing (status: working)
|
|
54
|
+
console.log('⢠Server is processing (working status)...');
|
|
55
|
+
console.log('⢠Client keeps connection open for up to 120 seconds');
|
|
56
|
+
try {
|
|
57
|
+
const result = await simulateWorkingTask();
|
|
58
|
+
console.log('ā
Task completed after server processing!');
|
|
59
|
+
console.log(` Status: ${result.status}`);
|
|
60
|
+
} catch (error) {
|
|
61
|
+
console.log(`ā¹ļø Note: ${error.message} (expected in demo)`);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
console.log('\nš Example 3: Input Required with Handler');
|
|
65
|
+
console.log('ā'.repeat(50));
|
|
66
|
+
|
|
67
|
+
// Handler provides input immediately
|
|
68
|
+
const fieldHandler = createFieldHandler({
|
|
69
|
+
budget: 50000,
|
|
70
|
+
targeting: ['US', 'CA'],
|
|
71
|
+
approval: true,
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
console.log('⢠Server needs input, handler provides it...');
|
|
75
|
+
try {
|
|
76
|
+
const result = await simulateInputRequired(fieldHandler);
|
|
77
|
+
console.log('ā
Handler provided input, task continued!');
|
|
78
|
+
console.log(` Status: ${result.status}`);
|
|
79
|
+
} catch (error) {
|
|
80
|
+
console.log(`ā¹ļø Note: ${error.message} (expected in demo)`);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
console.log('\nš Example 4: Input Required without Handler (ERROR)');
|
|
84
|
+
console.log('ā'.repeat(50));
|
|
85
|
+
|
|
86
|
+
console.log('⢠Server needs input, but no handler provided...');
|
|
87
|
+
try {
|
|
88
|
+
const result = await simulateInputRequired(); // No handler
|
|
89
|
+
console.log('ā This should not happen');
|
|
90
|
+
} catch (error) {
|
|
91
|
+
if (error instanceof InputRequiredError) {
|
|
92
|
+
console.log('ā
Correctly threw InputRequiredError!');
|
|
93
|
+
console.log(` Error: ${error.message}`);
|
|
94
|
+
} else {
|
|
95
|
+
console.log(`ā¹ļø Note: ${error.message} (expected in demo)`);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
console.log('\nš Example 5: Client Deferral (Human-in-the-Loop)');
|
|
100
|
+
console.log('ā'.repeat(50));
|
|
101
|
+
|
|
102
|
+
// Handler chooses to defer for human approval
|
|
103
|
+
const humanApprovalHandler = (context: any) => {
|
|
104
|
+
if (context.inputRequest.field === 'final_approval') {
|
|
105
|
+
console.log('⢠Handler choosing to defer for human approval...');
|
|
106
|
+
return { defer: true, token: `approval-${Date.now()}` };
|
|
107
|
+
}
|
|
108
|
+
return 'auto-approved';
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
try {
|
|
112
|
+
const result = await simulateClientDeferral(humanApprovalHandler);
|
|
113
|
+
|
|
114
|
+
if (result.status === 'deferred' && result.deferred) {
|
|
115
|
+
console.log('ā
Task successfully deferred!');
|
|
116
|
+
console.log(` Token: ${result.deferred.token}`);
|
|
117
|
+
console.log(` Question: ${result.deferred.question}`);
|
|
118
|
+
|
|
119
|
+
// Later, when human provides input...
|
|
120
|
+
console.log('⢠Human provides approval, resuming task...');
|
|
121
|
+
const finalResult = await result.deferred.resume('APPROVED');
|
|
122
|
+
console.log('ā
Task resumed and completed!');
|
|
123
|
+
console.log(` Final status: ${finalResult.status}`);
|
|
124
|
+
}
|
|
125
|
+
} catch (error) {
|
|
126
|
+
console.log(`ā¹ļø Note: ${error.message} (expected in demo)`);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
console.log('\nš Example 6: Server Async (Submitted Status)');
|
|
130
|
+
console.log('ā'.repeat(50));
|
|
131
|
+
|
|
132
|
+
// Server says task will take hours/days
|
|
133
|
+
console.log('⢠Server submitting long-running task...');
|
|
134
|
+
try {
|
|
135
|
+
const result = await simulateSubmittedTask();
|
|
136
|
+
|
|
137
|
+
if (result.status === 'submitted' && result.submitted) {
|
|
138
|
+
console.log('ā
Task submitted for async processing!');
|
|
139
|
+
console.log(` Task ID: ${result.submitted.taskId}`);
|
|
140
|
+
console.log(` Webhook: ${result.submitted.webhookUrl || 'not provided'}`);
|
|
141
|
+
|
|
142
|
+
// User can track progress
|
|
143
|
+
console.log('⢠Tracking task progress...');
|
|
144
|
+
const status = await result.submitted.track();
|
|
145
|
+
console.log(` Current status: ${status.status}`);
|
|
146
|
+
|
|
147
|
+
// Or wait for completion (with polling)
|
|
148
|
+
console.log('⢠Waiting for completion (polling every 30s)...');
|
|
149
|
+
// In real usage: const final = await result.submitted.waitForCompletion(30000);
|
|
150
|
+
console.log(' (Would poll until completed)');
|
|
151
|
+
}
|
|
152
|
+
} catch (error) {
|
|
153
|
+
console.log(`ā¹ļø Note: ${error.message} (expected in demo)`);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
console.log('\nšÆ Key Takeaways:');
|
|
157
|
+
console.log('ā'.repeat(50));
|
|
158
|
+
console.log('⢠ā
Working status: Client keeps connection open (ā¤120s)');
|
|
159
|
+
console.log('⢠ā
Input-required: Handler is MANDATORY');
|
|
160
|
+
console.log('⢠ā
Client deferral: Handler returns { defer: true, token }');
|
|
161
|
+
console.log('⢠ā
Server async: Returns { status: "submitted", submitted: { ... } }');
|
|
162
|
+
console.log('⢠ā
Completed: Returns { success: true, data: ... }');
|
|
163
|
+
console.log('⢠ā
No complex config needed - handler controls the flow!');
|
|
164
|
+
|
|
165
|
+
console.log('\nš Demo Complete!');
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// Demo helper functions (simulate different response patterns)
|
|
169
|
+
|
|
170
|
+
async function simulateTaskResult(status: string, data: any): Promise<TaskResult<any>> {
|
|
171
|
+
// In real usage, this would be a real agent call
|
|
172
|
+
throw new Error('Demo simulation - would call real agent');
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
async function simulateWorkingTask(): Promise<TaskResult<any>> {
|
|
176
|
+
throw new Error('Demo simulation - would poll tasks/get endpoint');
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
async function simulateInputRequired(handler?: any): Promise<TaskResult<any>> {
|
|
180
|
+
if (!handler) {
|
|
181
|
+
throw new InputRequiredError('What is your budget for this campaign?');
|
|
182
|
+
}
|
|
183
|
+
throw new Error('Demo simulation - would call handler and continue');
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
async function simulateClientDeferral(handler: any): Promise<TaskResult<any>> {
|
|
187
|
+
// Simulate the handler being called and choosing to defer
|
|
188
|
+
const mockContext = {
|
|
189
|
+
inputRequest: {
|
|
190
|
+
field: 'final_approval',
|
|
191
|
+
question: 'Do you approve this $50,000 media buy?',
|
|
192
|
+
},
|
|
193
|
+
};
|
|
194
|
+
|
|
195
|
+
const response = handler(mockContext);
|
|
196
|
+
|
|
197
|
+
if (response.defer) {
|
|
198
|
+
return {
|
|
199
|
+
success: false,
|
|
200
|
+
status: 'deferred',
|
|
201
|
+
deferred: {
|
|
202
|
+
token: response.token,
|
|
203
|
+
question: mockContext.inputRequest.question,
|
|
204
|
+
resume: async (input: any) => {
|
|
205
|
+
console.log(` Resumed with input: ${input}`);
|
|
206
|
+
return {
|
|
207
|
+
success: true,
|
|
208
|
+
status: 'completed',
|
|
209
|
+
data: { approved: input === 'APPROVED' },
|
|
210
|
+
};
|
|
211
|
+
},
|
|
212
|
+
},
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
throw new Error('Demo simulation - handler did not defer');
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
async function simulateSubmittedTask(): Promise<TaskResult<any>> {
|
|
220
|
+
return {
|
|
221
|
+
success: false,
|
|
222
|
+
status: 'submitted',
|
|
223
|
+
submitted: {
|
|
224
|
+
taskId: `task-${Date.now()}`,
|
|
225
|
+
webhookUrl: 'https://yourapp.com/webhooks/adcp/xyz123',
|
|
226
|
+
track: async () => ({
|
|
227
|
+
taskId: `task-${Date.now()}`,
|
|
228
|
+
status: 'working',
|
|
229
|
+
taskType: 'create_media_buy',
|
|
230
|
+
createdAt: Date.now(),
|
|
231
|
+
updatedAt: Date.now(),
|
|
232
|
+
}),
|
|
233
|
+
waitForCompletion: async (pollInterval = 60000) => {
|
|
234
|
+
console.log(` Polling every ${pollInterval}ms...`);
|
|
235
|
+
return {
|
|
236
|
+
success: true,
|
|
237
|
+
status: 'completed',
|
|
238
|
+
data: { mediaBuyId: 'mb-12345' },
|
|
239
|
+
};
|
|
240
|
+
},
|
|
241
|
+
},
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
if (require.main === module) {
|
|
246
|
+
main().catch(console.error);
|
|
247
|
+
}
|