@kya-os/mcp-i-core 1.3.10-canary.clientinfo.20251126124133 → 1.3.10
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/.turbo/turbo-build.log +1 -1
- package/dist/__tests__/utils/mock-providers.d.ts +2 -1
- package/dist/__tests__/utils/mock-providers.d.ts.map +1 -1
- package/dist/__tests__/utils/mock-providers.js.map +1 -1
- package/dist/config/remote-config.d.ts +51 -0
- package/dist/config/remote-config.d.ts.map +1 -1
- package/dist/config/remote-config.js +74 -0
- package/dist/config/remote-config.js.map +1 -1
- package/dist/config.d.ts +1 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +4 -1
- package/dist/config.js.map +1 -1
- package/dist/delegation/did-key-resolver.d.ts +64 -0
- package/dist/delegation/did-key-resolver.d.ts.map +1 -0
- package/dist/delegation/did-key-resolver.js +159 -0
- package/dist/delegation/did-key-resolver.js.map +1 -0
- package/dist/delegation/utils.d.ts +76 -0
- package/dist/delegation/utils.d.ts.map +1 -1
- package/dist/delegation/utils.js +117 -0
- package/dist/delegation/utils.js.map +1 -1
- package/dist/identity/user-did-manager.d.ts +95 -12
- package/dist/identity/user-did-manager.d.ts.map +1 -1
- package/dist/identity/user-did-manager.js +107 -25
- package/dist/identity/user-did-manager.js.map +1 -1
- package/dist/index.d.ts +5 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +23 -1
- package/dist/index.js.map +1 -1
- package/dist/runtime/base.d.ts +25 -8
- package/dist/runtime/base.d.ts.map +1 -1
- package/dist/runtime/base.js +74 -21
- package/dist/runtime/base.js.map +1 -1
- package/dist/services/session-registration.service.d.ts.map +1 -1
- package/dist/services/session-registration.service.js +10 -90
- package/dist/services/session-registration.service.js.map +1 -1
- package/dist/services/tool-protection.service.d.ts +5 -2
- package/dist/services/tool-protection.service.d.ts.map +1 -1
- package/dist/services/tool-protection.service.js +72 -24
- package/dist/services/tool-protection.service.js.map +1 -1
- package/dist/utils/base58.d.ts +31 -0
- package/dist/utils/base58.d.ts.map +1 -0
- package/dist/utils/base58.js +103 -0
- package/dist/utils/base58.js.map +1 -0
- package/package.json +3 -3
- package/src/__tests__/identity/user-did-manager.test.ts +64 -45
- package/src/__tests__/integration/full-flow.test.ts +23 -10
- package/src/__tests__/runtime/base-extensions.test.ts +23 -21
- package/src/__tests__/runtime/proof-client-did.test.ts +19 -18
- package/src/__tests__/services/agentshield-integration.test.ts +10 -3
- package/src/__tests__/services/tool-protection-merged-config.test.ts +485 -0
- package/src/__tests__/services/tool-protection.service.test.ts +18 -11
- package/src/config/__tests__/merged-config.spec.ts +445 -0
- package/src/config/remote-config.ts +90 -0
- package/src/config.ts +3 -0
- package/src/delegation/__tests__/did-key-resolver.test.ts +265 -0
- package/src/delegation/did-key-resolver.ts +179 -0
- package/src/delegation/utils.ts +179 -0
- package/src/identity/user-did-manager.ts +185 -29
- package/src/index.ts +36 -1
- package/src/runtime/base.ts +84 -21
- package/src/services/session-registration.service.ts +26 -121
- package/src/services/tool-protection.service.ts +125 -56
- package/src/utils/base58.ts +109 -0
- package/coverage/coverage-final.json +0 -57
|
@@ -109,55 +109,9 @@ export class SessionRegistrationService {
|
|
|
109
109
|
sessionId,
|
|
110
110
|
agentDid: request.agent_did,
|
|
111
111
|
clientName: request.client_info.name,
|
|
112
|
-
clientVersion: request.client_info.version,
|
|
113
|
-
hasClientIdentity: !!request.client_identity,
|
|
114
112
|
url,
|
|
115
113
|
});
|
|
116
114
|
|
|
117
|
-
// ✅ EMPIRICAL PROOF: Prepare request headers
|
|
118
|
-
// Try Authorization: Bearer first (same as proofs endpoint)
|
|
119
|
-
// If that fails, AgentShield may need X-AgentShield-Key instead
|
|
120
|
-
const requestHeaders = {
|
|
121
|
-
"Content-Type": "application/json",
|
|
122
|
-
Authorization: `Bearer ${this.config.apiKey}`, // Use same auth as proofs endpoint
|
|
123
|
-
};
|
|
124
|
-
|
|
125
|
-
// ✅ EMPIRICAL PROOF: Log exact request details (sanitized for security)
|
|
126
|
-
const sanitizedHeaders = {
|
|
127
|
-
...requestHeaders,
|
|
128
|
-
Authorization: `Bearer ${this.config.apiKey.slice(0, 8)}...${this.config.apiKey.slice(-4)}`, // Show first 8 and last 4 chars
|
|
129
|
-
};
|
|
130
|
-
|
|
131
|
-
const sanitizedBody = {
|
|
132
|
-
session_id: request.session_id,
|
|
133
|
-
agent_did: request.agent_did,
|
|
134
|
-
project_id: request.project_id,
|
|
135
|
-
created_at: request.created_at,
|
|
136
|
-
client_info: request.client_info,
|
|
137
|
-
client_identity: request.client_identity,
|
|
138
|
-
server_did: request.server_did,
|
|
139
|
-
ttl_minutes: request.ttl_minutes,
|
|
140
|
-
};
|
|
141
|
-
|
|
142
|
-
this.config.logger(
|
|
143
|
-
"[SessionRegistration] 🔍 EMPIRICAL DEBUG - Request details",
|
|
144
|
-
{
|
|
145
|
-
url,
|
|
146
|
-
method: "POST",
|
|
147
|
-
headers: sanitizedHeaders,
|
|
148
|
-
headerKeys: Object.keys(requestHeaders),
|
|
149
|
-
authHeaderPresent: !!requestHeaders["Authorization"],
|
|
150
|
-
authHeaderName: "Authorization",
|
|
151
|
-
authHeaderFormat: requestHeaders["Authorization"]?.startsWith(
|
|
152
|
-
"Bearer "
|
|
153
|
-
)
|
|
154
|
-
? "Bearer"
|
|
155
|
-
: "Other",
|
|
156
|
-
body: sanitizedBody,
|
|
157
|
-
bodySize: JSON.stringify(request).length,
|
|
158
|
-
}
|
|
159
|
-
);
|
|
160
|
-
|
|
161
115
|
// Make the request with timeout
|
|
162
116
|
const controller = new AbortController();
|
|
163
117
|
const timeoutId = setTimeout(
|
|
@@ -168,89 +122,47 @@ export class SessionRegistrationService {
|
|
|
168
122
|
try {
|
|
169
123
|
const response = await this.config.fetchProvider.fetch(url, {
|
|
170
124
|
method: "POST",
|
|
171
|
-
headers:
|
|
125
|
+
headers: {
|
|
126
|
+
"Content-Type": "application/json",
|
|
127
|
+
Authorization: `Bearer ${this.config.apiKey}`,
|
|
128
|
+
},
|
|
172
129
|
body: JSON.stringify(request),
|
|
173
130
|
signal: controller.signal,
|
|
174
131
|
});
|
|
175
132
|
|
|
176
133
|
clearTimeout(timeoutId);
|
|
177
134
|
|
|
178
|
-
// ✅ EMPIRICAL PROOF: Capture exact response details
|
|
179
|
-
const responseHeaders: Record<string, string> = {};
|
|
180
|
-
response.headers.forEach((value, key) => {
|
|
181
|
-
responseHeaders[key] = value;
|
|
182
|
-
});
|
|
183
|
-
|
|
184
|
-
const responseText = await response
|
|
185
|
-
.text()
|
|
186
|
-
.catch(() => "Failed to read response");
|
|
187
|
-
let responseBody: unknown;
|
|
188
|
-
try {
|
|
189
|
-
responseBody = JSON.parse(responseText);
|
|
190
|
-
} catch {
|
|
191
|
-
responseBody = responseText;
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
// ✅ EMPIRICAL PROOF: Log exact response details
|
|
195
|
-
this.config.logger(
|
|
196
|
-
"[SessionRegistration] 🔍 EMPIRICAL DEBUG - Response details",
|
|
197
|
-
{
|
|
198
|
-
status: response.status,
|
|
199
|
-
statusText: response.statusText,
|
|
200
|
-
ok: response.ok,
|
|
201
|
-
headers: responseHeaders,
|
|
202
|
-
body: responseBody,
|
|
203
|
-
bodyLength: responseText.length,
|
|
204
|
-
clientName: request.client_info.name,
|
|
205
|
-
// ✅ DEBUG: Include request ID from response headers for AgentShield support
|
|
206
|
-
requestId:
|
|
207
|
-
responseHeaders["x-request-id"] ||
|
|
208
|
-
responseHeaders["X-Request-ID"] ||
|
|
209
|
-
"not-provided",
|
|
210
|
-
// ✅ DEBUG: Full response text for AgentShield team
|
|
211
|
-
fullResponseText: responseText,
|
|
212
|
-
}
|
|
213
|
-
);
|
|
214
|
-
|
|
215
135
|
if (!response.ok) {
|
|
216
136
|
// Log error but don't throw - this is fire-and-forget
|
|
137
|
+
const errorText = await response.text().catch(() => "Unknown error");
|
|
217
138
|
this.config.logger("[SessionRegistration] Registration failed", {
|
|
218
139
|
sessionId,
|
|
219
140
|
status: response.status,
|
|
220
|
-
error:
|
|
221
|
-
// ✅ DEBUG: EMPIRICAL: Include full response details
|
|
222
|
-
responseHeaders,
|
|
223
|
-
responseBody,
|
|
224
|
-
clientName: request.client_info.name,
|
|
225
|
-
// ✅ DEBUG: Request ID for AgentShield support ticket
|
|
226
|
-
requestId:
|
|
227
|
-
responseHeaders["x-request-id"] ||
|
|
228
|
-
responseHeaders["X-Request-ID"] ||
|
|
229
|
-
"not-provided",
|
|
230
|
-
// ✅ DEBUG: Full request body for AgentShield team
|
|
231
|
-
fullRequestBody: JSON.stringify(request, null, 2),
|
|
141
|
+
error: errorText,
|
|
232
142
|
});
|
|
233
143
|
return {
|
|
234
144
|
success: false,
|
|
235
145
|
sessionId,
|
|
236
|
-
error: `HTTP ${response.status}: ${
|
|
146
|
+
error: `HTTP ${response.status}: ${errorText}`,
|
|
237
147
|
};
|
|
238
148
|
}
|
|
239
149
|
|
|
240
|
-
// Parse response
|
|
241
|
-
const responseData =
|
|
242
|
-
|
|
243
|
-
|
|
150
|
+
// Parse response
|
|
151
|
+
const responseData = (await response.json()) as {
|
|
152
|
+
data?: RegisterSessionResponse;
|
|
153
|
+
} & RegisterSessionResponse;
|
|
244
154
|
const parseResult = registerSessionResponseSchema.safeParse(
|
|
245
|
-
|
|
246
|
-
responseData
|
|
155
|
+
responseData.data || responseData
|
|
247
156
|
);
|
|
248
157
|
|
|
249
158
|
if (!parseResult.success) {
|
|
250
|
-
this.config.logger(
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
159
|
+
this.config.logger(
|
|
160
|
+
"[SessionRegistration] Invalid response format",
|
|
161
|
+
{
|
|
162
|
+
sessionId,
|
|
163
|
+
response: responseData,
|
|
164
|
+
}
|
|
165
|
+
);
|
|
254
166
|
// Still consider it a success if we got a 200 OK
|
|
255
167
|
return { success: true, sessionId };
|
|
256
168
|
}
|
|
@@ -258,12 +170,6 @@ export class SessionRegistrationService {
|
|
|
258
170
|
this.config.logger("[SessionRegistration] Session registered", {
|
|
259
171
|
sessionId,
|
|
260
172
|
registered: parseResult.data.registered,
|
|
261
|
-
// ✅ DEBUG: Include full response for AgentShield debugging
|
|
262
|
-
responseData: parseResult.data,
|
|
263
|
-
requestId:
|
|
264
|
-
responseHeaders["x-request-id"] ||
|
|
265
|
-
responseHeaders["X-Request-ID"] ||
|
|
266
|
-
"not-provided",
|
|
267
173
|
});
|
|
268
174
|
|
|
269
175
|
return { success: true, sessionId };
|
|
@@ -281,7 +187,8 @@ export class SessionRegistrationService {
|
|
|
281
187
|
}
|
|
282
188
|
|
|
283
189
|
// Log any other error
|
|
284
|
-
const errorMsg =
|
|
190
|
+
const errorMsg =
|
|
191
|
+
error instanceof Error ? error.message : "Unknown error";
|
|
285
192
|
this.config.logger("[SessionRegistration] Unexpected error", {
|
|
286
193
|
sessionId,
|
|
287
194
|
error: errorMsg,
|
|
@@ -303,13 +210,10 @@ export class SessionRegistrationService {
|
|
|
303
210
|
this.registerSession(request).catch((error) => {
|
|
304
211
|
// This should never happen since registerSession catches all errors,
|
|
305
212
|
// but just in case
|
|
306
|
-
this.config.logger(
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
error: error instanceof Error ? error.message : "Unknown error",
|
|
311
|
-
}
|
|
312
|
-
);
|
|
213
|
+
this.config.logger("[SessionRegistration] Background registration failed", {
|
|
214
|
+
sessionId: request.session_id,
|
|
215
|
+
error: error instanceof Error ? error.message : "Unknown error",
|
|
216
|
+
});
|
|
313
217
|
});
|
|
314
218
|
}
|
|
315
219
|
}
|
|
@@ -344,3 +248,4 @@ export function createSessionRegistrationService(options: {
|
|
|
344
248
|
logger: options.logger,
|
|
345
249
|
});
|
|
346
250
|
}
|
|
251
|
+
|
|
@@ -46,7 +46,7 @@
|
|
|
46
46
|
* If tool not discovered:
|
|
47
47
|
* - Tool won't appear in dashboard
|
|
48
48
|
* - Protection settings can't be configured
|
|
49
|
-
* - GET /
|
|
49
|
+
* - GET /config returns empty toolProtection.tools object
|
|
50
50
|
*
|
|
51
51
|
* DEBUGGING:
|
|
52
52
|
* ----------
|
|
@@ -87,34 +87,56 @@ import type {
|
|
|
87
87
|
import type { ToolProtectionCache } from "../cache/tool-protection-cache.js";
|
|
88
88
|
import { InMemoryToolProtectionCache } from "../cache/tool-protection-cache.js";
|
|
89
89
|
|
|
90
|
+
/**
|
|
91
|
+
* Tool protection data structure in API responses
|
|
92
|
+
*/
|
|
93
|
+
interface ToolProtectionData {
|
|
94
|
+
requiresDelegation?: boolean;
|
|
95
|
+
requires_delegation?: boolean;
|
|
96
|
+
requiredScopes?: string[];
|
|
97
|
+
required_scopes?: string[];
|
|
98
|
+
scopes?: string[];
|
|
99
|
+
riskLevel?: string;
|
|
100
|
+
risk_level?: string;
|
|
101
|
+
oauthProvider?: string;
|
|
102
|
+
oauth_provider?: string;
|
|
103
|
+
authorization?: {
|
|
104
|
+
type: string;
|
|
105
|
+
provider?: string;
|
|
106
|
+
issuer?: string;
|
|
107
|
+
credentialType?: string;
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
|
|
90
111
|
/**
|
|
91
112
|
* Response from AgentShield API bouncer endpoints
|
|
92
113
|
*
|
|
93
114
|
* Supports multiple endpoint formats:
|
|
94
|
-
* 1.
|
|
95
|
-
* 2.
|
|
96
|
-
* 3.
|
|
115
|
+
* 1. Merged config (/projects/{projectId}/config): { data: { config: { toolProtection: { tools: {...} } } } }
|
|
116
|
+
* 2. Legacy tool-protections endpoint: { data: { toolProtections: { [toolName]: {...} } } }
|
|
117
|
+
* 3. Old config endpoint (/config?agent_did=...): { data: { tools: [{ name: string, ... }] } }
|
|
118
|
+
* 4. Legacy format: { data: { tools: { [toolName]: {...} } } }
|
|
97
119
|
*/
|
|
98
120
|
interface BouncerConfigApiResponse {
|
|
99
121
|
success: boolean;
|
|
100
122
|
data: {
|
|
101
123
|
agent_did?: string;
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
//
|
|
124
|
+
|
|
125
|
+
// NEW: Merged config format (v1.6.0+) - preferred format
|
|
126
|
+
// The entire config is returned with tools embedded at config.toolProtection.tools
|
|
127
|
+
config?: {
|
|
128
|
+
toolProtection?: {
|
|
129
|
+
source?: string;
|
|
130
|
+
tools?: Record<string, ToolProtectionData>;
|
|
131
|
+
};
|
|
132
|
+
// Other config fields we don't need to parse
|
|
133
|
+
[key: string]: unknown;
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
// DEPRECATED: Top-level toolProtections (backward compatibility during transition)
|
|
137
|
+
toolProtections?: Record<string, ToolProtectionData>;
|
|
138
|
+
|
|
139
|
+
// Legacy endpoint formats
|
|
118
140
|
tools?:
|
|
119
141
|
| Array<{
|
|
120
142
|
name: string;
|
|
@@ -122,20 +144,10 @@ interface BouncerConfigApiResponse {
|
|
|
122
144
|
requires_delegation?: boolean;
|
|
123
145
|
scopes?: string[];
|
|
124
146
|
required_scopes?: string[];
|
|
125
|
-
oauthProvider?: string;
|
|
126
|
-
oauth_provider?: string;
|
|
147
|
+
oauthProvider?: string;
|
|
148
|
+
oauth_provider?: string;
|
|
127
149
|
}>
|
|
128
|
-
| Record<
|
|
129
|
-
string,
|
|
130
|
-
{
|
|
131
|
-
requiresDelegation?: boolean;
|
|
132
|
-
requires_delegation?: boolean;
|
|
133
|
-
scopes?: string[];
|
|
134
|
-
required_scopes?: string[];
|
|
135
|
-
oauthProvider?: string; // Phase 2: Tool-specific OAuth provider
|
|
136
|
-
oauth_provider?: string; // Phase 2: snake_case variant
|
|
137
|
-
}
|
|
138
|
-
>;
|
|
150
|
+
| Record<string, ToolProtectionData>;
|
|
139
151
|
reputation_threshold?: number;
|
|
140
152
|
denied_agents?: string[];
|
|
141
153
|
};
|
|
@@ -271,7 +283,7 @@ export class ToolProtectionService {
|
|
|
271
283
|
projectId: this.config.projectId || "none",
|
|
272
284
|
apiUrl: this.config.apiUrl,
|
|
273
285
|
endpoint: this.config.projectId
|
|
274
|
-
? `/api/v1/bouncer/projects/${this.config.projectId}/
|
|
286
|
+
? `/api/v1/bouncer/projects/${this.config.projectId}/config`
|
|
275
287
|
: `/api/v1/bouncer/config?agent_did=${agentDid}`,
|
|
276
288
|
});
|
|
277
289
|
}
|
|
@@ -287,6 +299,8 @@ export class ToolProtectionService {
|
|
|
287
299
|
projectId: this.config.projectId || "none",
|
|
288
300
|
responseKeys: Object.keys(response),
|
|
289
301
|
dataKeys: response.data ? Object.keys(response.data) : [],
|
|
302
|
+
rawConfig: response.data?.config || null,
|
|
303
|
+
rawConfigToolProtection: response.data?.config?.toolProtection || null,
|
|
290
304
|
rawToolProtections: response.data?.toolProtections || null,
|
|
291
305
|
rawTools: response.data?.tools || null,
|
|
292
306
|
responseMetadata: response.metadata || null,
|
|
@@ -294,15 +308,54 @@ export class ToolProtectionService {
|
|
|
294
308
|
}
|
|
295
309
|
|
|
296
310
|
// Transform API response format to internal format
|
|
297
|
-
// Supports multiple response formats:
|
|
298
|
-
// 1.
|
|
299
|
-
// 2.
|
|
300
|
-
// 3. Old endpoint (
|
|
311
|
+
// Supports multiple response formats (in priority order):
|
|
312
|
+
// 1. Merged config endpoint: { data: { config: { toolProtection: { tools: {...} } } } }
|
|
313
|
+
// 2. Legacy toolProtections: { data: { toolProtections: { greet: { requiresDelegation: true, ... } } } }
|
|
314
|
+
// 3. Old endpoint (array): { data: { tools: [{ name: "greet", requiresDelegation: true, ... }] } }
|
|
315
|
+
// 4. Old endpoint (object): { data: { tools: { greet: { requiresDelegation: true, ... } } } }
|
|
301
316
|
const toolProtections: Record<string, ToolProtection> = {};
|
|
302
317
|
|
|
303
|
-
// Check for
|
|
304
|
-
if (response.data.
|
|
305
|
-
//
|
|
318
|
+
// Check for merged config format first (data.config.toolProtection.tools)
|
|
319
|
+
if (response.data.config?.toolProtection?.tools) {
|
|
320
|
+
// Merged config endpoint format: object with tool names as keys
|
|
321
|
+
if (this.config.debug) {
|
|
322
|
+
console.log("[ToolProtectionService] Using merged config format (data.config.toolProtection.tools)");
|
|
323
|
+
}
|
|
324
|
+
for (const [toolName, toolConfig] of Object.entries(
|
|
325
|
+
response.data.config.toolProtection.tools
|
|
326
|
+
)) {
|
|
327
|
+
const requiresDelegation =
|
|
328
|
+
(toolConfig as any).requiresDelegation ??
|
|
329
|
+
(toolConfig as any).requires_delegation ??
|
|
330
|
+
false;
|
|
331
|
+
const requiredScopes =
|
|
332
|
+
(toolConfig as any).requiredScopes ??
|
|
333
|
+
(toolConfig as any).required_scopes ??
|
|
334
|
+
(toolConfig as any).scopes ??
|
|
335
|
+
[];
|
|
336
|
+
|
|
337
|
+
const oauthProvider =
|
|
338
|
+
(toolConfig as any).oauthProvider ??
|
|
339
|
+
(toolConfig as any).oauth_provider ??
|
|
340
|
+
undefined;
|
|
341
|
+
|
|
342
|
+
const riskLevel =
|
|
343
|
+
(toolConfig as any).riskLevel ??
|
|
344
|
+
(toolConfig as any).risk_level ??
|
|
345
|
+
undefined;
|
|
346
|
+
|
|
347
|
+
toolProtections[toolName] = {
|
|
348
|
+
requiresDelegation,
|
|
349
|
+
requiredScopes,
|
|
350
|
+
...(oauthProvider && { oauthProvider }),
|
|
351
|
+
...(riskLevel && { riskLevel }),
|
|
352
|
+
};
|
|
353
|
+
}
|
|
354
|
+
} else if (response.data.toolProtections) {
|
|
355
|
+
// Legacy toolProtections format: object with tool names as keys
|
|
356
|
+
if (this.config.debug) {
|
|
357
|
+
console.log("[ToolProtectionService] Using legacy toolProtections format (data.toolProtections)");
|
|
358
|
+
}
|
|
306
359
|
// Prefer camelCase over snake_case when both present
|
|
307
360
|
for (const [toolName, toolConfig] of Object.entries(
|
|
308
361
|
response.data.toolProtections
|
|
@@ -665,7 +718,10 @@ export class ToolProtectionService {
|
|
|
665
718
|
|
|
666
719
|
/**
|
|
667
720
|
* Fetch tool protection config from AgentShield API
|
|
668
|
-
*
|
|
721
|
+
*
|
|
722
|
+
* Uses the merged /config endpoint which returns tool protections embedded
|
|
723
|
+
* at config.toolProtection.tools. Falls back to legacy formats for backward
|
|
724
|
+
* compatibility.
|
|
669
725
|
*
|
|
670
726
|
* @param agentDid DID of the agent to fetch config for
|
|
671
727
|
* @param options Optional fetch options
|
|
@@ -675,17 +731,17 @@ export class ToolProtectionService {
|
|
|
675
731
|
agentDid: string,
|
|
676
732
|
options?: { bypassCDNCache?: boolean }
|
|
677
733
|
): Promise<BouncerConfigApiResponse> {
|
|
678
|
-
//
|
|
679
|
-
//
|
|
734
|
+
// Use the merged /config endpoint which includes embedded tool protections
|
|
735
|
+
// This endpoint returns config.toolProtection.tools with all tool rules
|
|
680
736
|
let url: string;
|
|
681
|
-
let
|
|
737
|
+
let useMergedEndpoint = false;
|
|
682
738
|
|
|
683
739
|
if (this.config.projectId) {
|
|
684
|
-
// ✅
|
|
685
|
-
url = `${this.config.apiUrl}/api/v1/bouncer/projects/${encodeURIComponent(this.config.projectId)}/
|
|
686
|
-
|
|
740
|
+
// ✅ MERGED CONFIG ENDPOINT: Returns config with embedded toolProtection.tools
|
|
741
|
+
url = `${this.config.apiUrl}/api/v1/bouncer/projects/${encodeURIComponent(this.config.projectId)}/config`;
|
|
742
|
+
useMergedEndpoint = true;
|
|
687
743
|
} else {
|
|
688
|
-
// ⚠️
|
|
744
|
+
// ⚠️ LEGACY ENDPOINT: Agent-scoped, returns tools array (backward compatibility)
|
|
689
745
|
url = `${this.config.apiUrl}/api/v1/bouncer/config?agent_did=${encodeURIComponent(agentDid)}`;
|
|
690
746
|
}
|
|
691
747
|
|
|
@@ -712,9 +768,9 @@ export class ToolProtectionService {
|
|
|
712
768
|
|
|
713
769
|
if (this.config.debug) {
|
|
714
770
|
console.log("[ToolProtectionService] Fetching from API:", url, {
|
|
715
|
-
method:
|
|
716
|
-
? "projects/{projectId}/
|
|
717
|
-
: "config?agent_did (
|
|
771
|
+
method: useMergedEndpoint
|
|
772
|
+
? "projects/{projectId}/config (merged)"
|
|
773
|
+
: "config?agent_did (legacy)",
|
|
718
774
|
projectId: this.config.projectId || "none",
|
|
719
775
|
apiKeyPresent: !!this.config.apiKey,
|
|
720
776
|
apiKeyLength,
|
|
@@ -736,19 +792,19 @@ export class ToolProtectionService {
|
|
|
736
792
|
);
|
|
737
793
|
}
|
|
738
794
|
|
|
739
|
-
// Build headers -
|
|
795
|
+
// Build headers - merged endpoint uses X-API-Key, legacy uses Authorization Bearer
|
|
740
796
|
const headers: Record<string, string> = {
|
|
741
797
|
"Content-Type": "application/json",
|
|
742
798
|
};
|
|
743
799
|
|
|
744
|
-
if (
|
|
745
|
-
// ✅
|
|
800
|
+
if (useMergedEndpoint) {
|
|
801
|
+
// ✅ Merged config endpoint headers
|
|
746
802
|
headers["X-API-Key"] = this.config.apiKey;
|
|
747
803
|
if (this.config.projectId) {
|
|
748
804
|
headers["X-Project-Id"] = this.config.projectId;
|
|
749
805
|
}
|
|
750
806
|
} else {
|
|
751
|
-
// ⚠️
|
|
807
|
+
// ⚠️ Legacy endpoint headers (backward compatibility)
|
|
752
808
|
headers["Authorization"] = `Bearer ${this.config.apiKey}`;
|
|
753
809
|
}
|
|
754
810
|
|
|
@@ -786,6 +842,19 @@ export class ToolProtectionService {
|
|
|
786
842
|
throw new Error("API returned success: false");
|
|
787
843
|
}
|
|
788
844
|
|
|
845
|
+
// Transform merged config format to normalized format
|
|
846
|
+
// If response contains config.toolProtection.tools, extract them to data.toolProtections
|
|
847
|
+
if (useMergedEndpoint && data.data.config?.toolProtection?.tools) {
|
|
848
|
+
// Extract embedded tools to the standard toolProtections field
|
|
849
|
+
data.data.toolProtections = data.data.config.toolProtection.tools;
|
|
850
|
+
if (this.config.debug) {
|
|
851
|
+
console.log("[ToolProtectionService] Extracted tools from merged config", {
|
|
852
|
+
toolCount: Object.keys(data.data.toolProtections).length,
|
|
853
|
+
tools: Object.keys(data.data.toolProtections),
|
|
854
|
+
});
|
|
855
|
+
}
|
|
856
|
+
}
|
|
857
|
+
|
|
789
858
|
return data;
|
|
790
859
|
}
|
|
791
860
|
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Base58 Utilities (Bitcoin alphabet)
|
|
3
|
+
*
|
|
4
|
+
* Encoding and decoding utilities for Base58 (Bitcoin alphabet).
|
|
5
|
+
* Used for did:key multibase encoding (with 'z' prefix for base58btc).
|
|
6
|
+
*
|
|
7
|
+
* The Bitcoin alphabet excludes ambiguous characters (0, O, I, l).
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
const ALPHABET = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz';
|
|
11
|
+
const ALPHABET_MAP = new Map<string, number>();
|
|
12
|
+
|
|
13
|
+
// Build reverse lookup map
|
|
14
|
+
for (let i = 0; i < ALPHABET.length; i++) {
|
|
15
|
+
ALPHABET_MAP.set(ALPHABET[i], i);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Encode bytes to Base58 (Bitcoin alphabet)
|
|
20
|
+
*
|
|
21
|
+
* @param bytes - Bytes to encode
|
|
22
|
+
* @returns Base58-encoded string
|
|
23
|
+
*/
|
|
24
|
+
export function base58Encode(bytes: Uint8Array): string {
|
|
25
|
+
if (bytes.length === 0) return '';
|
|
26
|
+
|
|
27
|
+
// Convert bytes to big integer
|
|
28
|
+
let num = BigInt(0);
|
|
29
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
30
|
+
num = num * BigInt(256) + BigInt(bytes[i]);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Convert to base58
|
|
34
|
+
let result = '';
|
|
35
|
+
while (num > 0) {
|
|
36
|
+
result = ALPHABET[Number(num % BigInt(58))] + result;
|
|
37
|
+
num = num / BigInt(58);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Add leading zeros (encoded as '1' in base58)
|
|
41
|
+
for (let i = 0; i < bytes.length && bytes[i] === 0; i++) {
|
|
42
|
+
result = '1' + result;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return result;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Decode Base58 (Bitcoin alphabet) to bytes
|
|
50
|
+
*
|
|
51
|
+
* @param encoded - Base58-encoded string
|
|
52
|
+
* @returns Decoded bytes
|
|
53
|
+
* @throws Error if input contains invalid characters
|
|
54
|
+
*/
|
|
55
|
+
export function base58Decode(encoded: string): Uint8Array {
|
|
56
|
+
if (encoded.length === 0) return new Uint8Array(0);
|
|
57
|
+
|
|
58
|
+
// Convert base58 to big integer
|
|
59
|
+
let num = BigInt(0);
|
|
60
|
+
for (const char of encoded) {
|
|
61
|
+
const value = ALPHABET_MAP.get(char);
|
|
62
|
+
if (value === undefined) {
|
|
63
|
+
throw new Error(`Invalid base58 character: ${char}`);
|
|
64
|
+
}
|
|
65
|
+
num = num * BigInt(58) + BigInt(value);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Convert big integer to bytes
|
|
69
|
+
const bytes: number[] = [];
|
|
70
|
+
while (num > 0) {
|
|
71
|
+
bytes.unshift(Number(num % BigInt(256)));
|
|
72
|
+
num = num / BigInt(256);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Count leading zeros in input (encoded as '1')
|
|
76
|
+
let leadingZeros = 0;
|
|
77
|
+
for (const char of encoded) {
|
|
78
|
+
if (char === '1') {
|
|
79
|
+
leadingZeros++;
|
|
80
|
+
} else {
|
|
81
|
+
break;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Prepend leading zero bytes
|
|
86
|
+
const result = new Uint8Array(leadingZeros + bytes.length);
|
|
87
|
+
// Leading zeros are already 0 in Uint8Array
|
|
88
|
+
result.set(bytes, leadingZeros);
|
|
89
|
+
|
|
90
|
+
return result;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Validate a Base58 string
|
|
95
|
+
*
|
|
96
|
+
* @param encoded - String to validate
|
|
97
|
+
* @returns true if valid Base58, false otherwise
|
|
98
|
+
*/
|
|
99
|
+
export function isValidBase58(encoded: string): boolean {
|
|
100
|
+
if (encoded.length === 0) return true;
|
|
101
|
+
|
|
102
|
+
for (const char of encoded) {
|
|
103
|
+
if (!ALPHABET_MAP.has(char)) {
|
|
104
|
+
return false;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
return true;
|
|
109
|
+
}
|