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.
- package/index.js +46 -11
- package/package.json +3 -2
- package/prover.js +14 -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
|
-
|
|
52
|
-
|
|
53
|
-
|
|
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
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
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.
|
|
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.
|
|
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
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
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;
|