@awi-protocol/sdk 0.1.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/README.md ADDED
@@ -0,0 +1,195 @@
1
+ # @awi-protocol/sdk
2
+
3
+ **AWI (Agent Web Interface) JavaScript SDK**
4
+
5
+ Deterministic web automation for AI agents. Transform any website into a structured, machine-readable API.
6
+
7
+ ## Installation
8
+
9
+ ```bash
10
+ npm install @awi-protocol/sdk
11
+ # or
12
+ yarn add @awi-protocol/sdk
13
+ # or
14
+ pnpm add @awi-protocol/sdk
15
+ ```
16
+
17
+ ## Quick Start
18
+
19
+ ### Proxy Mode (Server Executes Browser)
20
+
21
+ ```typescript
22
+ import { AWIClient } from '@awi-protocol/sdk';
23
+
24
+ const client = new AWIClient({
25
+ endpoint: 'https://awi.example.com',
26
+ certificate: 'your-awi-jwt-token',
27
+ });
28
+
29
+ // Execute a recipe - server runs the browser
30
+ const result = await client.execute({
31
+ target: 'awi://linkedin.com/jobs/search/v1',
32
+ params: {
33
+ query: 'senior rust engineer',
34
+ location: 'remote',
35
+ },
36
+ });
37
+
38
+ if (result.success) {
39
+ console.log(result.data);
40
+ // [{ title: "Senior Rust Engineer", company: "TechCorp", location: "Remote" }, ...]
41
+ }
42
+ ```
43
+
44
+ ### Advisory Mode (Agent Executes Locally)
45
+
46
+ ```typescript
47
+ // Get the recipe blueprint
48
+ const advisory = await client.getAdvisory('awi://linkedin.com/jobs/search/v1');
49
+
50
+ // Execute locally with your own browser automation
51
+ const result = await client.executeAdvisory(
52
+ {
53
+ target: 'awi://linkedin.com/jobs/search/v1',
54
+ params: { query: 'rust' },
55
+ },
56
+ async (blueprint, params) => {
57
+ // Your local execution logic
58
+ // e.g., Puppeteer, Playwright, or DOM manipulation
59
+ return localBrowser.execute(blueprint, params);
60
+ }
61
+ );
62
+ ```
63
+
64
+ ### Explore Unknown Sites
65
+
66
+ ```typescript
67
+ // Automatically explore and generate a recipe
68
+ const recipe = await client.explore('new-site.com', 'search', 'products');
69
+
70
+ if (recipe.success) {
71
+ console.log('Generated recipe:', recipe.data);
72
+ }
73
+ ```
74
+
75
+ ## Features
76
+
77
+ - **Deterministic Execution**: Recipes guarantee consistent extraction
78
+ - **Self-Healing**: AXIR semantic intent regenerates selectors when sites redesign
79
+ - **Multi-Strategy Fallback**: CSS → semantic → text → attribute resolution
80
+ - **Stealth Browser**: Undetectable automation with fingerprint rotation
81
+ - **Agent Identity**: W3C did:key DIDs with tiered rate limiting
82
+ - **OpenTelemetry**: Full distributed tracing
83
+ - **Type-Safe**: Full TypeScript support with generic response types
84
+
85
+ ## API Reference
86
+
87
+ ### `AWIClient`
88
+
89
+ #### Constructor
90
+
91
+ ```typescript
92
+ new AWIClient(options: {
93
+ endpoint: string; // AWI server URL
94
+ certificate: string; // JWT authentication token
95
+ timeout?: number; // Request timeout (ms) - default 30000
96
+ retries?: number; // Retry attempts - default 3
97
+ })
98
+ ```
99
+
100
+ #### Methods
101
+
102
+ | Method | Description |
103
+ |--------|-------------|
104
+ | `execute(request)` | Execute recipe in proxy mode |
105
+ | `getAdvisory(target)` | Get recipe blueprint |
106
+ | `executeAdvisory(request, executor)` | Get blueprint + execute locally |
107
+ | `explore(domain, action, resource?)` | Explore unknown domain |
108
+ | `feedback(request)` | Submit execution feedback |
109
+ | `listRegistry(options?)` | List supported sites |
110
+ | `searchRegistry(query, limit?)` | Search registry |
111
+ | `delegate(request)` | Delegate to another agent |
112
+ | `joinSession(sessionId)` | Join multi-agent session |
113
+ | `health()` | Check server health |
114
+
115
+ ### Error Handling
116
+
117
+ ```typescript
118
+ import { AWIClient, AWIError } from '@awi-protocol/sdk';
119
+
120
+ try {
121
+ const result = await client.execute({...});
122
+ } catch (error) {
123
+ if (error instanceof AWIError) {
124
+ console.log(error.code); // 'RECIPE_NOT_FOUND'
125
+ console.log(error.statusCode); // 404
126
+ console.log(error.details); // { recipe_id: '...' }
127
+ }
128
+ }
129
+ ```
130
+
131
+ ## React Integration
132
+
133
+ ```bash
134
+ npm install @awi-protocol/react
135
+ ```
136
+
137
+ ```tsx
138
+ import { useAWI } from '@awi-protocol/react';
139
+
140
+ function JobSearch() {
141
+ const { execute, loading, data, error, metrics } = useAWI({
142
+ endpoint: 'https://awi.example.com',
143
+ certificate: 'your-jwt',
144
+ });
145
+
146
+ const search = async (query: string) => {
147
+ await execute({
148
+ target: 'awi://linkedin.com/jobs/search/v1',
149
+ params: { query },
150
+ });
151
+ };
152
+
153
+ return (
154
+ <div>
155
+ {loading && <Spinner />}
156
+ {error && <Error message={error.message} />}
157
+ {data && <JobList jobs={data} />}
158
+ {metrics && <div>Latency: {metrics.latency_ms}ms</div>}
159
+ </div>
160
+ );
161
+ }
162
+ ```
163
+
164
+ ## Site SDK (For Website Operators)
165
+
166
+ ```bash
167
+ npm install @awi-protocol/site-sdk
168
+ ```
169
+
170
+ ```typescript
171
+ import { AWISite } from '@awi-protocol/site-sdk';
172
+ import express from 'express';
173
+
174
+ const app = new AWISite();
175
+
176
+ // Define agent-native endpoints
177
+ app.route('/jobs/search', (req, res) => {
178
+ const { query, location } = req.body.params;
179
+ const jobs = database.search(query, location);
180
+
181
+ res.json({
182
+ success: true,
183
+ data: jobs,
184
+ });
185
+ });
186
+
187
+ // Mount on your Express app
188
+ const server = express();
189
+ server.use('/awi', app.middleware());
190
+ server.listen(3000);
191
+ ```
192
+
193
+ ## License
194
+
195
+ MIT
@@ -0,0 +1,382 @@
1
+ /**
2
+ * AWI Protocol Types
3
+ *
4
+ * TypeScript definitions for the Agent Web Interface protocol.
5
+ */
6
+ type ExecutionMode = 'proxy' | 'advisory';
7
+ type RecipeStatus = 'candidate' | 'active' | 'suspended' | 'deprecated';
8
+ type AgentTier = 'free' | 'pro' | 'enterprise';
9
+ interface AgentRequest {
10
+ target: string;
11
+ params: Record<string, unknown>;
12
+ agent_id?: string;
13
+ mode?: ExecutionMode;
14
+ session_id?: string;
15
+ workflow_id?: string;
16
+ delegate_to?: string;
17
+ options?: Record<string, unknown>;
18
+ }
19
+ interface AgentResponse<T = unknown> {
20
+ success: boolean;
21
+ data: T | null;
22
+ errors: Array<{
23
+ code: string;
24
+ message: string;
25
+ details?: Record<string, unknown>;
26
+ }>;
27
+ metadata: {
28
+ execution_id?: string;
29
+ latency_ms?: number;
30
+ recipe_id?: string;
31
+ recipe_version?: string;
32
+ recipe_confidence?: number;
33
+ cache_status?: 'hit' | 'miss' | 'bypass';
34
+ [key: string]: unknown;
35
+ };
36
+ execution_path: string[];
37
+ axir_intent?: {
38
+ intent: string;
39
+ action: string;
40
+ parameters: string[];
41
+ };
42
+ }
43
+ interface Recipe {
44
+ id: string;
45
+ version: string;
46
+ domain: string;
47
+ resource: string;
48
+ action: string;
49
+ status: RecipeStatus;
50
+ confidence: number;
51
+ steps: RecipeStep[];
52
+ selectors: Record<string, SelectorSet>;
53
+ extraction: ExtractionSpec;
54
+ validation: ValidationRules;
55
+ axir?: AXIRIntent;
56
+ }
57
+ interface RecipeStep {
58
+ type: 'navigate' | 'wait' | 'click' | 'type' | 'scroll' | 'extract_list' | 'extract_one';
59
+ name?: string;
60
+ url?: string;
61
+ selector?: string;
62
+ value?: string;
63
+ amount?: number;
64
+ timeout_ms?: number;
65
+ retry?: number;
66
+ }
67
+ interface SelectorSet {
68
+ name: string;
69
+ selectors: Selector[];
70
+ }
71
+ interface Selector {
72
+ type: 'css' | 'semantic' | 'text' | 'attribute';
73
+ value: string;
74
+ priority: number;
75
+ confidence: number;
76
+ }
77
+ interface ExtractionSpec {
78
+ mode: 'list' | 'one';
79
+ container: string;
80
+ fields: ExtractionField[];
81
+ min_items: number;
82
+ }
83
+ interface ExtractionField {
84
+ name: string;
85
+ selector: string;
86
+ transform?: 'strip' | 'strip_currency' | 'number' | 'lower' | 'upper';
87
+ required?: boolean;
88
+ }
89
+ interface ValidationRules {
90
+ required_fields: string[];
91
+ min_items: number;
92
+ custom_checks: ValidationRule[];
93
+ }
94
+ interface ValidationRule {
95
+ field: string;
96
+ rule_type: 'required' | 'type' | 'regex' | 'range';
97
+ value?: string | number;
98
+ }
99
+ interface AXIRIntent {
100
+ intent: string;
101
+ action: string;
102
+ parameters: string[];
103
+ semantic_context?: Record<string, unknown>;
104
+ }
105
+ interface RegistryEntry {
106
+ domain: string;
107
+ display_name: string;
108
+ category: string;
109
+ country: string;
110
+ actions: Array<{
111
+ resource: string;
112
+ action: string;
113
+ version: string;
114
+ confidence: number;
115
+ status: RecipeStatus;
116
+ }>;
117
+ confidence: number;
118
+ certified: boolean;
119
+ recipe_count: number;
120
+ }
121
+ interface FeedbackRequest {
122
+ execution_id: string;
123
+ rating: 'good' | 'bad' | 'neutral';
124
+ notes?: string;
125
+ field_issues?: Array<{
126
+ field: string;
127
+ issue: string;
128
+ }>;
129
+ }
130
+ interface DelegationRequest {
131
+ target: string;
132
+ delegate_to: string;
133
+ session_id?: string;
134
+ workflow_id?: string;
135
+ params?: Record<string, unknown>;
136
+ permissions?: string[];
137
+ }
138
+ interface AWIClientOptions {
139
+ endpoint: string;
140
+ certificate: string;
141
+ timeout?: number;
142
+ retries?: number;
143
+ }
144
+ interface ExecutionMetrics {
145
+ latency_ms: number;
146
+ fallback_count: number;
147
+ selectors_used: string[];
148
+ cache_status: 'hit' | 'miss' | 'bypass';
149
+ }
150
+
151
+ /**
152
+ * AWI Client
153
+ *
154
+ * Main SDK class for interacting with AWI servers.
155
+ * Supports proxy mode (server executes browser) and advisory mode (returns blueprint).
156
+ */
157
+
158
+ declare class AWIError extends Error {
159
+ code: string;
160
+ statusCode: number;
161
+ details?: Record<string, unknown>;
162
+ constructor(code: string, message: string, statusCode: number, details?: Record<string, unknown>);
163
+ }
164
+ declare class AWIClient {
165
+ private endpoint;
166
+ private certificate;
167
+ private timeout;
168
+ private retries;
169
+ constructor(options: AWIClientOptions);
170
+ /**
171
+ * Execute a recipe in proxy mode.
172
+ * The server runs the browser and returns structured data.
173
+ */
174
+ execute<T = unknown>(request: Omit<AgentRequest, 'mode'>): Promise<AgentResponse<T>>;
175
+ /**
176
+ * Get advisory blueprint for agent-side execution.
177
+ * Returns the recipe structure without running the browser.
178
+ */
179
+ getAdvisory(target: string): Promise<AgentResponse<Recipe>>;
180
+ /**
181
+ * Execute in advisory mode - get blueprint then run locally.
182
+ * This is useful when you want to control the browser yourself.
183
+ */
184
+ executeAdvisory<T = unknown>(request: Omit<AgentRequest, 'mode'>, localExecutor: (blueprint: Recipe, params: Record<string, unknown>) => Promise<T>): Promise<AgentResponse<T>>;
185
+ /**
186
+ * Submit feedback on execution quality.
187
+ */
188
+ feedback(request: FeedbackRequest): Promise<AgentResponse<{
189
+ feedback_received: boolean;
190
+ }>>;
191
+ /**
192
+ * Explore an unknown domain and generate a recipe.
193
+ */
194
+ explore(domain: string, action: string, resource?: string): Promise<AgentResponse<Recipe>>;
195
+ /**
196
+ * List supported sites in the registry.
197
+ */
198
+ listRegistry(options?: {
199
+ category?: string;
200
+ certifiedOnly?: boolean;
201
+ minConfidence?: number;
202
+ search?: string;
203
+ limit?: number;
204
+ }): Promise<AgentResponse<{
205
+ sites: RegistryEntry[];
206
+ stats: Record<string, unknown>;
207
+ }>>;
208
+ /**
209
+ * Search registry.
210
+ */
211
+ searchRegistry(query: string, limit?: number): Promise<AgentResponse<{
212
+ results: RegistryEntry[];
213
+ }>>;
214
+ /**
215
+ * Delegate execution to another agent.
216
+ */
217
+ delegate(request: DelegationRequest): Promise<AgentResponse<{
218
+ delegation_id: string;
219
+ }>>;
220
+ /**
221
+ * Join a multi-agent session.
222
+ */
223
+ joinSession(sessionId: string): Promise<AgentResponse<{
224
+ session_id: string;
225
+ participants: string[];
226
+ }>>;
227
+ /**
228
+ * Check server health.
229
+ */
230
+ health(): Promise<{
231
+ status: string;
232
+ version: string;
233
+ }>;
234
+ /**
235
+ * Get execution metrics from a response.
236
+ */
237
+ getMetrics(response: AgentResponse<unknown>): ExecutionMetrics;
238
+ private _request;
239
+ }
240
+
241
+ /**
242
+ * Advisory Mode Executor
243
+ *
244
+ * Executes recipe blueprints locally in a browser or Node.js environment.
245
+ * Useful when you want to control the browser yourself but use AWI selectors.
246
+ */
247
+
248
+ interface LocalExecutionContext {
249
+ document: Document;
250
+ window: Window;
251
+ console: Console;
252
+ }
253
+ declare class AdvisoryExecutor {
254
+ private context;
255
+ private metrics;
256
+ constructor(context: LocalExecutionContext);
257
+ /**
258
+ * Execute a recipe blueprint locally.
259
+ */
260
+ execute<T = unknown>(recipe: Recipe, params: Record<string, unknown>): Promise<{
261
+ success: boolean;
262
+ data: T | null;
263
+ errors: Array<{
264
+ code: string;
265
+ message: string;
266
+ }>;
267
+ metrics: ExecutionMetrics;
268
+ }>;
269
+ private _executeStep;
270
+ private _extractData;
271
+ private _resolveSelector;
272
+ private _resolveSelectorWithin;
273
+ private _trySelector;
274
+ private _waitForSelector;
275
+ private _interpolate;
276
+ private _applyTransform;
277
+ private _sleep;
278
+ }
279
+
280
+ interface SelectorCandidate {
281
+ type: 'css' | 'semantic' | 'text' | 'attribute';
282
+ value: string;
283
+ priority: number;
284
+ confidence?: number;
285
+ }
286
+ interface AXIRNode {
287
+ node_id: string;
288
+ element_type: 'button' | 'link' | 'input' | 'form' | 'navigation' | 'search' | 'filter' | 'sort' | 'pagination' | 'container' | 'list' | 'item' | 'heading' | 'text' | 'image' | 'unknown';
289
+ semantic_role?: string;
290
+ intent?: string;
291
+ tag?: string;
292
+ selector_candidates: SelectorCandidate[];
293
+ parent_id?: string;
294
+ children_ids?: string[];
295
+ aria_label?: string;
296
+ aria_role?: string;
297
+ text_content?: string;
298
+ confidence: number;
299
+ reasoning?: string;
300
+ }
301
+ interface AXIREdge {
302
+ from_node: string;
303
+ to_node: string;
304
+ action: string;
305
+ condition?: string;
306
+ probability: number;
307
+ }
308
+ type PageType = 'landing' | 'search' | 'listing' | 'detail' | 'form' | 'checkout' | 'dashboard' | 'unknown';
309
+ interface AXIRWorkflow {
310
+ nodes: Record<string, AXIRNode>;
311
+ edges: AXIREdge[];
312
+ entry_points: string[];
313
+ exit_points: string[];
314
+ domain: string;
315
+ page_type: PageType;
316
+ structure_hash?: string;
317
+ }
318
+ interface AXIRIntentMapping {
319
+ intent: string;
320
+ action: string;
321
+ parameters: string[];
322
+ context: string;
323
+ }
324
+ interface AXIRField {
325
+ name: string;
326
+ selector: string;
327
+ transform?: string;
328
+ required: boolean;
329
+ }
330
+ interface AXIRCompilationResult {
331
+ workflow: AXIRWorkflow;
332
+ intents: AXIRIntentMapping[];
333
+ selectors: Record<string, SelectorCandidate[]>;
334
+ fields: AXIRField[];
335
+ container?: string;
336
+ model_used: string;
337
+ tokens_used: number;
338
+ compilation_time_ms: number;
339
+ }
340
+ interface AXIRHealingResult {
341
+ selector: string;
342
+ confidence: number;
343
+ reasoning?: string;
344
+ }
345
+
346
+ interface LocalAXIRCompilerOptions {
347
+ modelPath?: string;
348
+ modelUrl?: string;
349
+ contextSize?: number;
350
+ gpuLayers?: number;
351
+ onDownloadProgress?: (downloaded: number, total: number) => void;
352
+ onStatus?: (message: string) => void;
353
+ }
354
+ declare class LocalAXIRCompiler {
355
+ private modelPath;
356
+ private modelUrl;
357
+ private contextSize;
358
+ private gpuLayers;
359
+ private onDownloadProgress?;
360
+ private onStatus?;
361
+ private model;
362
+ private context;
363
+ private grammar;
364
+ private ready;
365
+ constructor(options?: LocalAXIRCompilerOptions);
366
+ compile(domHTML: string, a11yTree: string, intent: string, params?: Record<string, unknown>): Promise<AXIRCompilationResult>;
367
+ heal(domHTML: string, brokenSelector: string, semanticIntent: string): Promise<AXIRHealingResult>;
368
+ isModelCached(): boolean;
369
+ clearCache(): void;
370
+ private _ensureModel;
371
+ private _ensureGrammar;
372
+ private _autoDetectGPULayers;
373
+ private _downloadModel;
374
+ private _complete;
375
+ private _buildCompilePrompt;
376
+ private _buildHealPrompt;
377
+ private _truncate;
378
+ private _estimateTokens;
379
+ private _status;
380
+ }
381
+
382
+ export { AWIClient, type AWIClientOptions, AWIError, type AXIRCompilationResult, type AXIREdge, type AXIRField, type AXIRHealingResult, type AXIRIntent, type AXIRIntentMapping, type AXIRNode, type AXIRWorkflow, AdvisoryExecutor, type AgentRequest, type AgentResponse, type AgentTier, type DelegationRequest, type ExecutionMetrics, type ExecutionMode, type ExtractionField, type ExtractionSpec, type FeedbackRequest, LocalAXIRCompiler, type LocalAXIRCompilerOptions, type PageType, type Recipe, type RecipeStatus, type RecipeStep, type RegistryEntry, type Selector, type SelectorCandidate, type SelectorSet, type ValidationRules, AWIClient as default };