aap-agent-client 2.5.0 → 2.7.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.
Files changed (4) hide show
  1. package/index.js +46 -11
  2. package/package.json +3 -2
  3. package/prover.js +14 -4
  4. package/websocket.js +162 -0
package/index.js CHANGED
@@ -8,6 +8,22 @@
8
8
  import { Prover } from './prover.js';
9
9
  import { Identity } from 'aap-agent-core';
10
10
 
11
+ // Fetch with timeout helper
12
+ async function fetchWithTimeout(url, options = {}, timeoutMs = 30000) {
13
+ const controller = new AbortController();
14
+ const timeout = setTimeout(() => controller.abort(), timeoutMs);
15
+
16
+ try {
17
+ const response = await fetch(url, {
18
+ ...options,
19
+ signal: controller.signal
20
+ });
21
+ return response;
22
+ } finally {
23
+ clearTimeout(timeout);
24
+ }
25
+ }
26
+
11
27
  /**
12
28
  * AAP Client - High-level interface for verification
13
29
  */
@@ -47,11 +63,19 @@ export class AAPClient {
47
63
 
48
64
  const callback = solutionOrCallback || this.llmCallback;
49
65
 
50
- // Step 1: Request challenge
51
- const challengeRes = await fetch(`${baseUrl}/challenge`, {
52
- method: 'POST',
53
- headers: { 'Content-Type': 'application/json' }
54
- });
66
+ // Step 1: Request challenge (with timeout)
67
+ let challengeRes;
68
+ try {
69
+ challengeRes = await fetchWithTimeout(`${baseUrl}/challenge`, {
70
+ method: 'POST',
71
+ headers: { 'Content-Type': 'application/json' }
72
+ }, 10000);
73
+ } catch (error) {
74
+ if (error.name === 'AbortError') {
75
+ throw new Error('Challenge request timed out');
76
+ }
77
+ throw error;
78
+ }
55
79
 
56
80
  if (!challengeRes.ok) {
57
81
  throw new Error(`Failed to get challenge: ${challengeRes.status}`);
@@ -62,12 +86,20 @@ export class AAPClient {
62
86
  // Step 2: Generate proof
63
87
  const proof = await this.prover.generateProof(challenge, callback);
64
88
 
65
- // Step 3: Submit proof
66
- const verifyRes = await fetch(`${baseUrl}/verify`, {
67
- method: 'POST',
68
- headers: { 'Content-Type': 'application/json' },
69
- body: JSON.stringify(proof)
70
- });
89
+ // Step 3: Submit proof (with timeout)
90
+ let verifyRes;
91
+ try {
92
+ verifyRes = await fetchWithTimeout(`${baseUrl}/verify`, {
93
+ method: 'POST',
94
+ headers: { 'Content-Type': 'application/json' },
95
+ body: JSON.stringify(proof)
96
+ }, 15000);
97
+ } catch (error) {
98
+ if (error.name === 'AbortError') {
99
+ throw new Error('Verification request timed out');
100
+ }
101
+ throw error;
102
+ }
71
103
 
72
104
  const result = await verifyRes.json();
73
105
 
@@ -176,4 +208,7 @@ export function createClient(options) {
176
208
  // Re-export Prover
177
209
  export { Prover };
178
210
 
211
+ // WebSocket client (v2.7+)
212
+ export { AAPWebSocketClient, createSolver, verifyWithWebSocket } from './websocket.js';
213
+
179
214
  export default { AAPClient, createClient, Prover };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "aap-agent-client",
3
- "version": "2.5.0",
3
+ "version": "2.7.0",
4
4
  "description": "Client library for Agent Attestation Protocol - prove your AI agent identity",
5
5
  "main": "index.js",
6
6
  "types": "index.d.ts",
@@ -20,7 +20,8 @@
20
20
  },
21
21
  "homepage": "https://github.com/ira-hash/agent-attestation-protocol#readme",
22
22
  "dependencies": {
23
- "aap-agent-core": "^2.5.0"
23
+ "aap-agent-core": "^2.6.0",
24
+ "ws": "^8.16.0"
24
25
  },
25
26
  "engines": {
26
27
  "node": ">=18.0.0"
package/prover.js CHANGED
@@ -47,10 +47,20 @@ export class Prover {
47
47
 
48
48
  if (llmCallback) {
49
49
  // Use LLM to solve all at once (more efficient)
50
- const combinedPrompt = this.createBatchPrompt(challenges);
51
- const llmResponse = await llmCallback(combinedPrompt);
52
- const parsedSolutions = this.parseBatchResponse(llmResponse, challenges.length);
53
- solutions.push(...parsedSolutions);
50
+ try {
51
+ const combinedPrompt = this.createBatchPrompt(challenges);
52
+ const llmResponse = await llmCallback(combinedPrompt);
53
+ const parsedSolutions = this.parseBatchResponse(llmResponse, challenges.length);
54
+ solutions.push(...parsedSolutions);
55
+ } catch (error) {
56
+ console.error('[AAP] LLM callback failed:', error.message);
57
+ // Fallback to placeholder solutions (will fail verification but won't crash)
58
+ for (const c of challenges) {
59
+ const saltMatch = c.challenge_string.match(/\[REQ-([A-Z0-9]+)\]/);
60
+ const salt = saltMatch ? saltMatch[1] : 'ERROR';
61
+ solutions.push(JSON.stringify({ salt, error: 'LLM callback failed' }));
62
+ }
63
+ }
54
64
  } else {
55
65
  // Use built-in solvers
56
66
  for (const challenge of challenges) {
package/websocket.js ADDED
@@ -0,0 +1,162 @@
1
+ /**
2
+ * AAP WebSocket Client v2.7
3
+ *
4
+ * Connects to AAP WebSocket server, receives sequential challenges,
5
+ * and responds in real-time.
6
+ */
7
+
8
+ import WebSocket from 'ws';
9
+ import { Identity } from 'aap-agent-core';
10
+
11
+ const PROTOCOL_VERSION = '2.7.0';
12
+
13
+ /**
14
+ * AAP WebSocket Client
15
+ */
16
+ export class AAPWebSocketClient {
17
+ constructor(options = {}) {
18
+ this.serverUrl = options.serverUrl || 'ws://localhost:3000/aap';
19
+ this.identity = options.identity || new Identity(options);
20
+ this.llmCallback = options.llmCallback || null;
21
+ this.autoSolve = options.autoSolve !== false;
22
+ }
23
+
24
+ /**
25
+ * Get public identity info
26
+ */
27
+ getIdentity() {
28
+ return this.identity.getPublic();
29
+ }
30
+
31
+ /**
32
+ * Connect and verify
33
+ * @param {Function} [llmCallback] - async (challengeString) => solutionObject
34
+ * @returns {Promise<Object>} Verification result
35
+ */
36
+ async verify(llmCallback) {
37
+ const solver = llmCallback || this.llmCallback;
38
+
39
+ return new Promise((resolve, reject) => {
40
+ const ws = new WebSocket(this.serverUrl);
41
+ let sessionId = null;
42
+ let result = null;
43
+
44
+ ws.on('open', () => {
45
+ // Wait for handshake
46
+ });
47
+
48
+ ws.on('message', async (data) => {
49
+ try {
50
+ const msg = JSON.parse(data.toString());
51
+
52
+ switch (msg.type) {
53
+ case 'handshake':
54
+ sessionId = msg.sessionId;
55
+ // Send ready with identity
56
+ const identity = this.identity.getPublic();
57
+ ws.send(JSON.stringify({
58
+ type: 'ready',
59
+ publicKey: identity.publicKey,
60
+ publicId: identity.publicId
61
+ }));
62
+ break;
63
+
64
+ case 'challenge':
65
+ // Solve challenge
66
+ if (!solver) {
67
+ ws.send(JSON.stringify({
68
+ type: 'answer',
69
+ answer: {} // Empty - will fail
70
+ }));
71
+ break;
72
+ }
73
+
74
+ try {
75
+ const answer = await solver(msg.challenge, msg.id);
76
+ ws.send(JSON.stringify({
77
+ type: 'answer',
78
+ answer
79
+ }));
80
+ } catch (e) {
81
+ ws.send(JSON.stringify({
82
+ type: 'answer',
83
+ answer: { error: e.message }
84
+ }));
85
+ }
86
+ break;
87
+
88
+ case 'ack':
89
+ // Challenge acknowledged, wait for next
90
+ break;
91
+
92
+ case 'timeout':
93
+ // Too slow
94
+ break;
95
+
96
+ case 'result':
97
+ result = msg;
98
+ ws.close();
99
+ break;
100
+
101
+ case 'error':
102
+ reject(new Error(msg.message));
103
+ ws.close();
104
+ break;
105
+ }
106
+ } catch (e) {
107
+ reject(e);
108
+ }
109
+ });
110
+
111
+ ws.on('close', () => {
112
+ if (result) {
113
+ resolve(result);
114
+ } else {
115
+ reject(new Error('Connection closed without result'));
116
+ }
117
+ });
118
+
119
+ ws.on('error', (err) => {
120
+ reject(err);
121
+ });
122
+ });
123
+ }
124
+ }
125
+
126
+ /**
127
+ * Create solver function from LLM
128
+ * @param {Function} llm - async (prompt) => response string
129
+ * @returns {Function} Solver function
130
+ */
131
+ export function createSolver(llm) {
132
+ return async (challengeString, challengeId) => {
133
+ const prompt = `Solve this challenge and respond with ONLY valid JSON (no markdown, no explanation):
134
+
135
+ ${challengeString}
136
+
137
+ Important:
138
+ - Include the exact salt from the challenge
139
+ - Follow the response format exactly
140
+ - Return ONLY the JSON object`;
141
+
142
+ const response = await llm(prompt);
143
+
144
+ // Parse JSON from response
145
+ const jsonMatch = response.match(/\{[\s\S]*\}/);
146
+ if (!jsonMatch) {
147
+ throw new Error('No JSON found in response');
148
+ }
149
+
150
+ return JSON.parse(jsonMatch[0]);
151
+ };
152
+ }
153
+
154
+ /**
155
+ * Quick verify helper
156
+ */
157
+ export async function verifyWithWebSocket(serverUrl, llmCallback) {
158
+ const client = new AAPWebSocketClient({ serverUrl, llmCallback });
159
+ return client.verify();
160
+ }
161
+
162
+ export default AAPWebSocketClient;