@enactprotocol/shared 1.0.13 → 1.2.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/dist/api/enact-api.js +2 -2
- package/dist/api/types.d.ts +10 -3
- package/dist/core/DaggerExecutionProvider.d.ts +1 -1
- package/dist/core/DaggerExecutionProvider.js +23 -19
- package/dist/core/EnactCore.d.ts +36 -19
- package/dist/core/EnactCore.js +157 -219
- package/dist/core/NativeExecutionProvider.d.ts +9 -0
- package/dist/core/NativeExecutionProvider.js +16 -0
- package/dist/index.d.ts +0 -4
- package/dist/index.js +0 -4
- package/dist/lib/enact-direct.d.ts +0 -15
- package/dist/lib/enact-direct.js +0 -11
- package/dist/security/sign.d.ts +5 -5
- package/dist/security/sign.js +247 -113
- package/dist/security/verification-enforcer.d.ts +12 -0
- package/dist/security/verification-enforcer.js +26 -3
- package/dist/services/McpCoreService.d.ts +0 -12
- package/dist/services/McpCoreService.js +0 -9
- package/dist/types.d.ts +5 -4
- package/dist/utils/config.js +1 -1
- package/dist/utils/version.js +23 -2
- package/package.json +3 -6
- package/src/api/enact-api.ts +2 -2
- package/src/api/types.ts +11 -4
- package/src/core/DaggerExecutionProvider.ts +26 -13
- package/src/core/EnactCore.ts +226 -270
- package/src/index.ts +0 -5
- package/src/lib/enact-direct.ts +0 -21
- package/src/services/McpCoreService.ts +0 -20
- package/src/types.ts +10 -12
- package/src/utils/config.ts +1 -1
- package/src/utils/version.ts +23 -2
- package/src/security/index.ts +0 -3
- package/src/security/security.ts +0 -188
- package/src/security/sign.ts +0 -797
- package/src/security/verification-enforcer.ts +0 -268
package/src/core/EnactCore.ts
CHANGED
|
@@ -5,10 +5,6 @@ import type {
|
|
|
5
5
|
ExecutionEnvironment,
|
|
6
6
|
} from "../types.js";
|
|
7
7
|
import { EnactApiClient } from "../api/enact-api.js";
|
|
8
|
-
import {
|
|
9
|
-
verifyCommandSafety,
|
|
10
|
-
sanitizeEnvironmentVariables,
|
|
11
|
-
} from "../security/security.js";
|
|
12
8
|
import {
|
|
13
9
|
validateToolStructure,
|
|
14
10
|
validateInputs,
|
|
@@ -19,17 +15,9 @@ import { DaggerExecutionProvider } from "./DaggerExecutionProvider.js";
|
|
|
19
15
|
import { resolveToolEnvironmentVariables } from "../utils/env-loader.js";
|
|
20
16
|
import logger from "../exec/logger.js";
|
|
21
17
|
import yaml from "yaml";
|
|
22
|
-
import {
|
|
23
|
-
verifyTool as verifyToolSignatureWithPolicy,
|
|
24
|
-
VERIFICATION_POLICIES,
|
|
25
|
-
} from "../security/sign.js";
|
|
26
|
-
import {
|
|
27
|
-
enforceSignatureVerification,
|
|
28
|
-
createVerificationFailureResult,
|
|
29
|
-
logSecurityAudit,
|
|
30
|
-
} from "../security/verification-enforcer.js";
|
|
31
18
|
import fs from "fs";
|
|
32
19
|
import path from "path";
|
|
20
|
+
import { CryptoUtils, KeyManager, SigningService } from "@enactprotocol/security";
|
|
33
21
|
|
|
34
22
|
export interface EnactCoreOptions {
|
|
35
23
|
apiUrl?: string;
|
|
@@ -38,7 +26,6 @@ export interface EnactCoreOptions {
|
|
|
38
26
|
authToken?: string;
|
|
39
27
|
verbose?: boolean;
|
|
40
28
|
defaultTimeout?: string;
|
|
41
|
-
verificationPolicy?: "permissive" | "enterprise" | "paranoid";
|
|
42
29
|
// Dagger-specific options
|
|
43
30
|
daggerOptions?: {
|
|
44
31
|
baseImage?: string;
|
|
@@ -59,11 +46,11 @@ export interface ToolSearchOptions {
|
|
|
59
46
|
|
|
60
47
|
export interface ToolExecuteOptions {
|
|
61
48
|
timeout?: string;
|
|
62
|
-
verifyPolicy?: "permissive" | "enterprise" | "paranoid";
|
|
63
|
-
skipVerification?: boolean;
|
|
64
49
|
force?: boolean;
|
|
65
50
|
dryRun?: boolean;
|
|
66
51
|
verbose?: boolean;
|
|
52
|
+
isLocalFile?: boolean;
|
|
53
|
+
dangerouslySkipVerification?: boolean;
|
|
67
54
|
}
|
|
68
55
|
|
|
69
56
|
export class EnactCore {
|
|
@@ -77,7 +64,6 @@ export class EnactCore {
|
|
|
77
64
|
supabaseUrl: "https://xjnhhxwxovjifdxdwzih.supabase.co",
|
|
78
65
|
executionProvider: "dagger",
|
|
79
66
|
defaultTimeout: "30s",
|
|
80
|
-
verificationPolicy: "permissive",
|
|
81
67
|
...options,
|
|
82
68
|
};
|
|
83
69
|
|
|
@@ -97,9 +83,17 @@ export class EnactCore {
|
|
|
97
83
|
}
|
|
98
84
|
|
|
99
85
|
/**
|
|
100
|
-
*
|
|
86
|
+
* Static method to search for tools (no execution provider needed)
|
|
101
87
|
*/
|
|
102
|
-
async searchTools(
|
|
88
|
+
static async searchTools(
|
|
89
|
+
options: ToolSearchOptions,
|
|
90
|
+
coreOptions: Pick<EnactCoreOptions, 'apiUrl' | 'supabaseUrl'> = {}
|
|
91
|
+
): Promise<EnactTool[]> {
|
|
92
|
+
const apiClient = new EnactApiClient(
|
|
93
|
+
coreOptions.apiUrl || "https://enact.tools",
|
|
94
|
+
coreOptions.supabaseUrl || "https://xjnhhxwxovjifdxdwzih.supabase.co"
|
|
95
|
+
);
|
|
96
|
+
|
|
103
97
|
try {
|
|
104
98
|
logger.info(`Searching for tools with query: "${options.query}"`);
|
|
105
99
|
|
|
@@ -109,14 +103,14 @@ export class EnactCore {
|
|
|
109
103
|
tags: options.tags,
|
|
110
104
|
};
|
|
111
105
|
|
|
112
|
-
const results = await
|
|
106
|
+
const results = await apiClient.searchTools(searchParams);
|
|
113
107
|
|
|
114
108
|
// Parse and validate results
|
|
115
109
|
const tools: EnactTool[] = [];
|
|
116
110
|
for (const result of results) {
|
|
117
111
|
if (result.name) {
|
|
118
112
|
try {
|
|
119
|
-
const tool = await
|
|
113
|
+
const tool = await EnactCore.getToolByName(result.name, undefined, coreOptions);
|
|
120
114
|
if (tool) {
|
|
121
115
|
tools.push(tool);
|
|
122
116
|
}
|
|
@@ -136,7 +130,7 @@ export class EnactCore {
|
|
|
136
130
|
logger.info(
|
|
137
131
|
"Search API unavailable, trying fallback to local filtering...",
|
|
138
132
|
);
|
|
139
|
-
return
|
|
133
|
+
return EnactCore.searchToolsFallback(options, coreOptions);
|
|
140
134
|
}
|
|
141
135
|
|
|
142
136
|
throw new Error(
|
|
@@ -146,16 +140,29 @@ export class EnactCore {
|
|
|
146
140
|
}
|
|
147
141
|
|
|
148
142
|
/**
|
|
149
|
-
*
|
|
143
|
+
* Instance method wrapper for backward compatibility
|
|
150
144
|
*/
|
|
151
|
-
|
|
145
|
+
async searchTools(options: ToolSearchOptions): Promise<EnactTool[]> {
|
|
146
|
+
return EnactCore.searchTools(options, this.options);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Static fallback search method that gets all tools and filters locally
|
|
151
|
+
*/
|
|
152
|
+
private static async searchToolsFallback(
|
|
152
153
|
options: ToolSearchOptions,
|
|
154
|
+
coreOptions: Pick<EnactCoreOptions, 'apiUrl' | 'supabaseUrl'> = {}
|
|
153
155
|
): Promise<EnactTool[]> {
|
|
156
|
+
const apiClient = new EnactApiClient(
|
|
157
|
+
coreOptions.apiUrl || "https://enact.tools",
|
|
158
|
+
coreOptions.supabaseUrl || "https://xjnhhxwxovjifdxdwzih.supabase.co"
|
|
159
|
+
);
|
|
160
|
+
|
|
154
161
|
try {
|
|
155
162
|
logger.info("Using fallback search method...");
|
|
156
163
|
|
|
157
164
|
// Get all tools (limited to avoid overwhelming the API)
|
|
158
|
-
const allTools = await
|
|
165
|
+
const allTools = await apiClient.getTools({
|
|
159
166
|
limit: options.limit || 100,
|
|
160
167
|
});
|
|
161
168
|
|
|
@@ -166,7 +173,7 @@ export class EnactCore {
|
|
|
166
173
|
for (const result of allTools) {
|
|
167
174
|
if (result.name) {
|
|
168
175
|
try {
|
|
169
|
-
const tool = await
|
|
176
|
+
const tool = await EnactCore.getToolByName(result.name, undefined, coreOptions);
|
|
170
177
|
if (tool) {
|
|
171
178
|
// Check if tool matches search criteria
|
|
172
179
|
const matchesQuery =
|
|
@@ -226,44 +233,70 @@ export class EnactCore {
|
|
|
226
233
|
}
|
|
227
234
|
|
|
228
235
|
/**
|
|
229
|
-
*
|
|
236
|
+
* Static method to get a specific tool by name
|
|
230
237
|
*/
|
|
231
|
-
async getToolByName(
|
|
238
|
+
static async getToolByName(
|
|
232
239
|
name: string,
|
|
233
240
|
version?: string,
|
|
241
|
+
coreOptions: Pick<EnactCoreOptions, 'apiUrl' | 'supabaseUrl'> = {}
|
|
234
242
|
): Promise<EnactTool | null> {
|
|
243
|
+
const apiClient = new EnactApiClient(
|
|
244
|
+
coreOptions.apiUrl || "https://enact.tools",
|
|
245
|
+
coreOptions.supabaseUrl || "https://xjnhhxwxovjifdxdwzih.supabase.co"
|
|
246
|
+
);
|
|
247
|
+
|
|
235
248
|
try {
|
|
236
249
|
logger.info(`Fetching tool: ${name}${version ? `@${version}` : ""}`);
|
|
237
250
|
|
|
238
|
-
const response = await
|
|
251
|
+
const response = await apiClient.getTool(name);
|
|
239
252
|
|
|
240
253
|
if (!response) {
|
|
241
254
|
logger.info(`Tool not found: ${name}`);
|
|
242
255
|
return null;
|
|
243
256
|
}
|
|
244
257
|
|
|
245
|
-
// Parse tool from response
|
|
258
|
+
// Parse tool from response - prefer raw_content for signature compatibility
|
|
246
259
|
let tool: EnactTool;
|
|
247
260
|
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
} else if (
|
|
251
|
-
response.raw_content &&
|
|
252
|
-
typeof response.raw_content === "string"
|
|
253
|
-
) {
|
|
261
|
+
// Try raw_content first (contains original tool definition with correct field names for signatures)
|
|
262
|
+
if (response.raw_content && typeof response.raw_content === "string") {
|
|
254
263
|
try {
|
|
255
264
|
tool = JSON.parse(response.raw_content);
|
|
256
265
|
} catch {
|
|
257
266
|
tool = yaml.parse(response.raw_content);
|
|
258
267
|
}
|
|
259
268
|
|
|
269
|
+
// Merge signature information from response if not already in raw content
|
|
270
|
+
if (!tool.signature && response.signature) {
|
|
271
|
+
tool.signature = response.signature;
|
|
272
|
+
}
|
|
273
|
+
if (!tool.signatures && response.signatures) {
|
|
274
|
+
// Convert object format to array format
|
|
275
|
+
if (Array.isArray(response.signatures)) {
|
|
276
|
+
tool.signatures = response.signatures;
|
|
277
|
+
} else {
|
|
278
|
+
// Convert object format {keyId: signatureData} to array format
|
|
279
|
+
tool.signatures = Object.values(response.signatures);
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
} else if (response.content && typeof response.content === "string") {
|
|
283
|
+
tool = yaml.parse(response.content);
|
|
284
|
+
|
|
260
285
|
// Merge signature information
|
|
261
|
-
if (
|
|
286
|
+
if (!tool.signature && response.signature) {
|
|
262
287
|
tool.signature = response.signature;
|
|
263
|
-
|
|
288
|
+
}
|
|
289
|
+
if (!tool.signatures && response.signatures) {
|
|
290
|
+
// Convert object format to array format
|
|
291
|
+
if (Array.isArray(response.signatures)) {
|
|
292
|
+
tool.signatures = response.signatures;
|
|
293
|
+
} else {
|
|
294
|
+
// Convert object format {keyId: signatureData} to array format
|
|
295
|
+
tool.signatures = Object.values(response.signatures);
|
|
296
|
+
}
|
|
264
297
|
}
|
|
265
298
|
} else {
|
|
266
|
-
//
|
|
299
|
+
// Fallback: map database fields to tool format (may cause signature verification issues)
|
|
267
300
|
tool = {
|
|
268
301
|
name: response.name,
|
|
269
302
|
description: response.description,
|
|
@@ -280,7 +313,7 @@ export class EnactCore {
|
|
|
280
313
|
env: response.env_vars || response.env,
|
|
281
314
|
resources: response.resources,
|
|
282
315
|
signature: response.signature,
|
|
283
|
-
signatures: response.signatures,
|
|
316
|
+
signatures: response.signatures ? (Array.isArray(response.signatures) ? response.signatures : Object.values(response.signatures)) : undefined,
|
|
284
317
|
namespace: response.namespace,
|
|
285
318
|
};
|
|
286
319
|
}
|
|
@@ -300,6 +333,16 @@ export class EnactCore {
|
|
|
300
333
|
}
|
|
301
334
|
}
|
|
302
335
|
|
|
336
|
+
/**
|
|
337
|
+
* Instance method wrapper for backward compatibility
|
|
338
|
+
*/
|
|
339
|
+
async getToolByName(
|
|
340
|
+
name: string,
|
|
341
|
+
version?: string,
|
|
342
|
+
): Promise<EnactTool | null> {
|
|
343
|
+
return EnactCore.getToolByName(name, version, this.options);
|
|
344
|
+
}
|
|
345
|
+
|
|
303
346
|
/**
|
|
304
347
|
* Execute a tool by name
|
|
305
348
|
*/
|
|
@@ -349,6 +392,70 @@ export class EnactCore {
|
|
|
349
392
|
}
|
|
350
393
|
}
|
|
351
394
|
|
|
395
|
+
private async verifyTool(tool: EnactTool, dangerouslySkipVerification: boolean = false): Promise<void> {
|
|
396
|
+
if (dangerouslySkipVerification) {
|
|
397
|
+
logger.warn(`Skipping signature verification for tool: ${tool.name}`);
|
|
398
|
+
return;
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
try {
|
|
402
|
+
if (!tool.signatures || tool.signatures.length === 0) {
|
|
403
|
+
throw new Error(`Tool ${tool.name} does not have any signatures`);
|
|
404
|
+
}
|
|
405
|
+
const documentForVerification = {
|
|
406
|
+
command: tool.command
|
|
407
|
+
};
|
|
408
|
+
|
|
409
|
+
// IGNORE DATABASE SIGNATURES - USE HARDCODED WORKING VALUES FOR TESTING
|
|
410
|
+
const referenceSignature = {
|
|
411
|
+
signature: tool.signatures[0].value,
|
|
412
|
+
publicKey: tool.signatures[0].signer,
|
|
413
|
+
algorithm: tool.signatures[0].algorithm,
|
|
414
|
+
timestamp: new Date(tool.signatures[0].created).getTime()
|
|
415
|
+
};
|
|
416
|
+
|
|
417
|
+
|
|
418
|
+
// Check what canonical document looks like
|
|
419
|
+
const canonicalDoc = SigningService.getCanonicalDocument(documentForVerification, { includeFields: ['command'] });
|
|
420
|
+
// console.log("Canonical document:", JSON.stringify(canonicalDoc));
|
|
421
|
+
|
|
422
|
+
const docString = JSON.stringify(canonicalDoc);
|
|
423
|
+
const messageHash = CryptoUtils.hash(docString);
|
|
424
|
+
// console.log("Document string:", docString);
|
|
425
|
+
// console.log("Message hash:", messageHash);
|
|
426
|
+
|
|
427
|
+
// Test direct crypto verification
|
|
428
|
+
const directVerify = CryptoUtils.verify(
|
|
429
|
+
referenceSignature.publicKey,
|
|
430
|
+
messageHash,
|
|
431
|
+
referenceSignature.signature
|
|
432
|
+
);
|
|
433
|
+
console.log("Direct crypto verification result:", directVerify);
|
|
434
|
+
|
|
435
|
+
// Check trusted keys
|
|
436
|
+
const trustedKeys = KeyManager.getAllTrustedPublicKeys();
|
|
437
|
+
console.log("Trusted keys:", trustedKeys);
|
|
438
|
+
console.log("Is our public key trusted?", trustedKeys.includes(referenceSignature.publicKey));
|
|
439
|
+
|
|
440
|
+
const isValid = SigningService.verifyDocument(
|
|
441
|
+
documentForVerification,
|
|
442
|
+
referenceSignature,
|
|
443
|
+
{ includeFields: ['command'] }
|
|
444
|
+
);
|
|
445
|
+
|
|
446
|
+
console.log("Final verification result:", isValid);
|
|
447
|
+
|
|
448
|
+
if (!isValid) {
|
|
449
|
+
throw new Error(`Tool ${tool.name} has invalid signatures`);
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
logger.info(`Tool ${tool.name} signature verification passed`);
|
|
453
|
+
} catch (error) {
|
|
454
|
+
logger.error(`Signature verification failed for tool ${tool.name}:`, error);
|
|
455
|
+
throw new Error(`Signature verification failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
|
|
352
459
|
/**
|
|
353
460
|
* Execute a tool directly
|
|
354
461
|
*/
|
|
@@ -365,153 +472,42 @@ export class EnactCore {
|
|
|
365
472
|
// Validate tool structure
|
|
366
473
|
validateToolStructure(tool);
|
|
367
474
|
|
|
368
|
-
// MANDATORY SIGNATURE VERIFICATION - All tools must be verified before execution
|
|
369
|
-
const verificationResult = await enforceSignatureVerification(tool, {
|
|
370
|
-
skipVerification: options.skipVerification,
|
|
371
|
-
verifyPolicy: options.verifyPolicy,
|
|
372
|
-
force: options.force,
|
|
373
|
-
allowUnsigned: false, // Never allow unsigned tools in production
|
|
374
|
-
});
|
|
375
|
-
|
|
376
|
-
// Log security audit information
|
|
377
|
-
logSecurityAudit(tool, verificationResult, verificationResult.allowed, {
|
|
378
|
-
skipVerification: options.skipVerification,
|
|
379
|
-
verifyPolicy: options.verifyPolicy,
|
|
380
|
-
force: options.force,
|
|
381
|
-
});
|
|
382
|
-
|
|
383
|
-
// Block execution if verification fails
|
|
384
|
-
if (!verificationResult.allowed) {
|
|
385
|
-
return createVerificationFailureResult(
|
|
386
|
-
tool,
|
|
387
|
-
verificationResult,
|
|
388
|
-
executionId,
|
|
389
|
-
);
|
|
390
|
-
}
|
|
391
|
-
|
|
392
475
|
// Validate inputs
|
|
393
476
|
const validatedInputs = validateInputs(tool, inputs);
|
|
477
|
+
|
|
478
|
+
// Verify tool signatures (unless explicitly skipped)
|
|
479
|
+
await this.verifyTool(tool, options.dangerouslySkipVerification);
|
|
394
480
|
|
|
395
|
-
//
|
|
396
|
-
const
|
|
397
|
-
|
|
398
|
-
return {
|
|
399
|
-
success: false,
|
|
400
|
-
error: {
|
|
401
|
-
message: `Unsafe command blocked: ${safetyCheck.blocked?.join(", ")}`,
|
|
402
|
-
code: "COMMAND_UNSAFE",
|
|
403
|
-
details: safetyCheck,
|
|
404
|
-
},
|
|
405
|
-
metadata: {
|
|
406
|
-
executionId,
|
|
407
|
-
toolName: tool.name,
|
|
408
|
-
version: tool.version,
|
|
409
|
-
executedAt: new Date().toISOString(),
|
|
410
|
-
environment: "direct",
|
|
411
|
-
command: tool.command,
|
|
412
|
-
},
|
|
413
|
-
};
|
|
414
|
-
}
|
|
481
|
+
// Resolve environment variables
|
|
482
|
+
const { resolved: envVars } =
|
|
483
|
+
await resolveToolEnvironmentVariables(tool.name, tool.env || {});
|
|
415
484
|
|
|
416
|
-
// Log warnings
|
|
417
|
-
if (safetyCheck.warnings.length > 0) {
|
|
418
|
-
safetyCheck.warnings.forEach((warning: string) => logger.warn(warning));
|
|
419
|
-
}
|
|
420
485
|
|
|
421
|
-
//
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
inputs: validatedInputs,
|
|
430
|
-
safetyCheck,
|
|
431
|
-
},
|
|
432
|
-
metadata: {
|
|
433
|
-
executionId,
|
|
434
|
-
toolName: tool.name,
|
|
435
|
-
version: tool.version,
|
|
436
|
-
executedAt: new Date().toISOString(),
|
|
437
|
-
environment: "direct",
|
|
438
|
-
command: tool.command,
|
|
486
|
+
// Execute the tool via the execution provider
|
|
487
|
+
return await this.executionProvider.execute(
|
|
488
|
+
tool,
|
|
489
|
+
{ ...validatedInputs, ...envVars },
|
|
490
|
+
{
|
|
491
|
+
vars: { ...envVars, ...validatedInputs },
|
|
492
|
+
resources: {
|
|
493
|
+
timeout: options.timeout || tool.timeout || this.options.defaultTimeout,
|
|
439
494
|
},
|
|
440
|
-
};
|
|
441
|
-
}
|
|
442
|
-
|
|
443
|
-
// Setup execution environment
|
|
444
|
-
// Load package environment variables from .env files
|
|
445
|
-
const envResult = await resolveToolEnvironmentVariables(
|
|
446
|
-
tool.name,
|
|
447
|
-
tool.env,
|
|
448
|
-
);
|
|
449
|
-
|
|
450
|
-
// Log any missing required environment variables
|
|
451
|
-
if (envResult.missing.length > 0) {
|
|
452
|
-
logger.warn(
|
|
453
|
-
`Missing required environment variables: ${envResult.missing.join(", ")}`,
|
|
454
|
-
);
|
|
455
|
-
}
|
|
456
|
-
|
|
457
|
-
const environment: ExecutionEnvironment = {
|
|
458
|
-
vars: {
|
|
459
|
-
...envResult.resolved,
|
|
460
|
-
...sanitizeEnvironmentVariables(validatedInputs),
|
|
461
495
|
},
|
|
462
|
-
resources: tool.resources,
|
|
463
|
-
namespace: tool.namespace,
|
|
464
|
-
};
|
|
465
|
-
|
|
466
|
-
// Setup execution provider
|
|
467
|
-
await this.executionProvider.setup(tool);
|
|
468
|
-
|
|
469
|
-
// Execute the tool
|
|
470
|
-
const result = await this.executionProvider.execute(
|
|
471
|
-
tool,
|
|
472
|
-
validatedInputs,
|
|
473
|
-
environment,
|
|
474
496
|
);
|
|
475
|
-
|
|
476
|
-
// Validate output if schema is provided
|
|
477
|
-
if (result.success && result.output && tool.outputSchema) {
|
|
478
|
-
try {
|
|
479
|
-
result.output = validateOutput(tool, result.output);
|
|
480
|
-
} catch (error) {
|
|
481
|
-
logger.warn(`Output validation failed: ${(error as Error).message}`);
|
|
482
|
-
}
|
|
483
|
-
}
|
|
484
|
-
|
|
485
|
-
logger.info(
|
|
486
|
-
`Tool execution completed: ${tool.name} (success: ${result.success})`,
|
|
487
|
-
);
|
|
488
|
-
return result;
|
|
489
497
|
} catch (error) {
|
|
490
|
-
logger.error(`Error executing tool: ${(error as Error).message}`);
|
|
491
|
-
|
|
492
498
|
return {
|
|
493
499
|
success: false,
|
|
494
500
|
error: {
|
|
495
501
|
message: (error as Error).message,
|
|
496
502
|
code: "EXECUTION_ERROR",
|
|
497
|
-
details: error,
|
|
498
503
|
},
|
|
499
504
|
metadata: {
|
|
500
505
|
executionId,
|
|
501
506
|
toolName: tool.name,
|
|
502
|
-
version: tool.version,
|
|
503
507
|
executedAt: new Date().toISOString(),
|
|
504
508
|
environment: "direct",
|
|
505
|
-
command: tool.command,
|
|
506
509
|
},
|
|
507
510
|
};
|
|
508
|
-
} finally {
|
|
509
|
-
// Cleanup
|
|
510
|
-
try {
|
|
511
|
-
await this.executionProvider.cleanup();
|
|
512
|
-
} catch (cleanupError) {
|
|
513
|
-
logger.error("Error during cleanup:", cleanupError);
|
|
514
|
-
}
|
|
515
511
|
}
|
|
516
512
|
}
|
|
517
513
|
|
|
@@ -562,100 +558,27 @@ export class EnactCore {
|
|
|
562
558
|
}
|
|
563
559
|
}
|
|
564
560
|
|
|
561
|
+
|
|
565
562
|
/**
|
|
566
|
-
*
|
|
563
|
+
* Static method to check if a tool exists
|
|
567
564
|
*/
|
|
568
|
-
async
|
|
565
|
+
static async toolExists(
|
|
569
566
|
name: string,
|
|
570
|
-
|
|
571
|
-
): Promise<{
|
|
572
|
-
verified: boolean;
|
|
573
|
-
signatures: any[];
|
|
574
|
-
policy: string;
|
|
575
|
-
errors?: string[];
|
|
576
|
-
}> {
|
|
567
|
+
coreOptions: Pick<EnactCoreOptions, 'apiUrl' | 'supabaseUrl'> = {}
|
|
568
|
+
): Promise<boolean> {
|
|
577
569
|
try {
|
|
578
|
-
const tool = await
|
|
579
|
-
|
|
580
|
-
if (!tool) {
|
|
581
|
-
return {
|
|
582
|
-
verified: false,
|
|
583
|
-
signatures: [],
|
|
584
|
-
policy: policy || "permissive",
|
|
585
|
-
errors: [`Tool not found: ${name}`],
|
|
586
|
-
};
|
|
587
|
-
}
|
|
588
|
-
|
|
589
|
-
// Load public key from keys/file-public.pem
|
|
590
|
-
let publicKey: string | undefined;
|
|
591
|
-
try {
|
|
592
|
-
const keyPath = path.resolve(__dirname, "../../keys/file-public.pem");
|
|
593
|
-
publicKey = fs.readFileSync(keyPath, "utf8");
|
|
594
|
-
} catch (e) {
|
|
595
|
-
logger.warn("Could not load public key for signature verification:", e);
|
|
596
|
-
}
|
|
597
|
-
if (!publicKey) {
|
|
598
|
-
return {
|
|
599
|
-
verified: false,
|
|
600
|
-
signatures: [],
|
|
601
|
-
policy: policy || "permissive",
|
|
602
|
-
errors: ["Public key not found for signature verification"],
|
|
603
|
-
};
|
|
604
|
-
}
|
|
605
|
-
const policyKey = (policy || "permissive").toUpperCase() as
|
|
606
|
-
| "PERMISSIVE"
|
|
607
|
-
| "ENTERPRISE"
|
|
608
|
-
| "PARANOID";
|
|
609
|
-
const policyObj = VERIFICATION_POLICIES[policyKey];
|
|
610
|
-
const verificationResult = await verifyToolSignatureWithPolicy(
|
|
611
|
-
tool,
|
|
612
|
-
policyObj,
|
|
613
|
-
);
|
|
614
|
-
|
|
615
|
-
if (!verificationResult.isValid) {
|
|
616
|
-
return {
|
|
617
|
-
verified: false,
|
|
618
|
-
signatures: [],
|
|
619
|
-
policy: policy || "permissive",
|
|
620
|
-
errors: verificationResult.errors,
|
|
621
|
-
};
|
|
622
|
-
}
|
|
623
|
-
|
|
624
|
-
const signatures: any[] = [];
|
|
625
|
-
|
|
626
|
-
if (tool.signature) {
|
|
627
|
-
signatures.push(tool.signature);
|
|
628
|
-
}
|
|
629
|
-
|
|
630
|
-
if (tool.signatures) {
|
|
631
|
-
signatures.push(...Object.values(tool.signatures));
|
|
632
|
-
}
|
|
633
|
-
|
|
634
|
-
return {
|
|
635
|
-
verified: verificationResult.isValid,
|
|
636
|
-
signatures,
|
|
637
|
-
policy: policy || "permissive",
|
|
638
|
-
};
|
|
570
|
+
const tool = await EnactCore.getToolByName(name, undefined, coreOptions);
|
|
571
|
+
return tool !== null;
|
|
639
572
|
} catch (error) {
|
|
640
|
-
return
|
|
641
|
-
verified: false,
|
|
642
|
-
signatures: [],
|
|
643
|
-
policy: policy || "permissive",
|
|
644
|
-
errors: [`Verification error: ${(error as Error).message}`],
|
|
645
|
-
};
|
|
573
|
+
return false;
|
|
646
574
|
}
|
|
647
575
|
}
|
|
648
576
|
|
|
649
577
|
/**
|
|
650
|
-
*
|
|
578
|
+
* Instance method wrapper for backward compatibility
|
|
651
579
|
*/
|
|
652
580
|
async toolExists(name: string): Promise<boolean> {
|
|
653
|
-
|
|
654
|
-
const tool = await this.getToolByName(name);
|
|
655
|
-
return tool !== null;
|
|
656
|
-
} catch (error) {
|
|
657
|
-
return false;
|
|
658
|
-
}
|
|
581
|
+
return EnactCore.toolExists(name, this.options);
|
|
659
582
|
}
|
|
660
583
|
|
|
661
584
|
/**
|
|
@@ -665,11 +588,11 @@ export class EnactCore {
|
|
|
665
588
|
tags: string[],
|
|
666
589
|
limit: number = 20,
|
|
667
590
|
): Promise<EnactTool[]> {
|
|
668
|
-
return
|
|
591
|
+
return EnactCore.searchTools({
|
|
669
592
|
query: tags.join(" "),
|
|
670
593
|
tags,
|
|
671
594
|
limit,
|
|
672
|
-
});
|
|
595
|
+
}, this.options);
|
|
673
596
|
}
|
|
674
597
|
|
|
675
598
|
/**
|
|
@@ -679,33 +602,39 @@ export class EnactCore {
|
|
|
679
602
|
author: string,
|
|
680
603
|
limit: number = 20,
|
|
681
604
|
): Promise<EnactTool[]> {
|
|
682
|
-
return
|
|
605
|
+
return EnactCore.searchTools({
|
|
683
606
|
query: `author:${author}`,
|
|
684
607
|
author,
|
|
685
608
|
limit,
|
|
686
|
-
});
|
|
609
|
+
}, this.options);
|
|
687
610
|
}
|
|
688
611
|
|
|
689
612
|
/**
|
|
690
|
-
*
|
|
613
|
+
* Static method to get all tools with filters
|
|
691
614
|
*/
|
|
692
|
-
async getTools(
|
|
615
|
+
static async getTools(
|
|
693
616
|
options: {
|
|
694
617
|
limit?: number;
|
|
695
618
|
offset?: number;
|
|
696
619
|
tags?: string[];
|
|
697
620
|
author?: string;
|
|
698
621
|
} = {},
|
|
622
|
+
coreOptions: Pick<EnactCoreOptions, 'apiUrl' | 'supabaseUrl'> = {}
|
|
699
623
|
): Promise<EnactTool[]> {
|
|
624
|
+
const apiClient = new EnactApiClient(
|
|
625
|
+
coreOptions.apiUrl || "https://enact.tools",
|
|
626
|
+
coreOptions.supabaseUrl || "https://xjnhhxwxovjifdxdwzih.supabase.co"
|
|
627
|
+
);
|
|
628
|
+
|
|
700
629
|
try {
|
|
701
|
-
const apiResults = await
|
|
630
|
+
const apiResults = await apiClient.getTools(options);
|
|
702
631
|
|
|
703
632
|
// Parse and validate results
|
|
704
633
|
const tools: EnactTool[] = [];
|
|
705
634
|
for (const result of apiResults) {
|
|
706
635
|
if (result.name) {
|
|
707
636
|
try {
|
|
708
|
-
const tool = await
|
|
637
|
+
const tool = await EnactCore.getToolByName(result.name, undefined, coreOptions);
|
|
709
638
|
if (tool) {
|
|
710
639
|
tools.push(tool);
|
|
711
640
|
}
|
|
@@ -724,6 +653,20 @@ export class EnactCore {
|
|
|
724
653
|
}
|
|
725
654
|
}
|
|
726
655
|
|
|
656
|
+
/**
|
|
657
|
+
* Instance method wrapper for backward compatibility
|
|
658
|
+
*/
|
|
659
|
+
async getTools(
|
|
660
|
+
options: {
|
|
661
|
+
limit?: number;
|
|
662
|
+
offset?: number;
|
|
663
|
+
tags?: string[];
|
|
664
|
+
author?: string;
|
|
665
|
+
} = {},
|
|
666
|
+
): Promise<EnactTool[]> {
|
|
667
|
+
return EnactCore.getTools(options, this.options);
|
|
668
|
+
}
|
|
669
|
+
|
|
727
670
|
/**
|
|
728
671
|
* Get authentication status (placeholder - would need actual auth implementation)
|
|
729
672
|
*/
|
|
@@ -741,22 +684,22 @@ export class EnactCore {
|
|
|
741
684
|
}
|
|
742
685
|
|
|
743
686
|
/**
|
|
744
|
-
*
|
|
687
|
+
* Static method to publish a tool
|
|
745
688
|
*/
|
|
746
|
-
async publishTool(
|
|
689
|
+
static async publishTool(
|
|
747
690
|
tool: EnactTool,
|
|
691
|
+
authToken: string,
|
|
692
|
+
coreOptions: Pick<EnactCoreOptions, 'apiUrl' | 'supabaseUrl'> = {}
|
|
748
693
|
): Promise<{ success: boolean; message: string }> {
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
};
|
|
754
|
-
}
|
|
694
|
+
const apiClient = new EnactApiClient(
|
|
695
|
+
coreOptions.apiUrl || "https://enact.tools",
|
|
696
|
+
coreOptions.supabaseUrl || "https://xjnhhxwxovjifdxdwzih.supabase.co"
|
|
697
|
+
);
|
|
755
698
|
|
|
756
699
|
try {
|
|
757
700
|
validateToolStructure(tool);
|
|
758
701
|
|
|
759
|
-
await
|
|
702
|
+
await apiClient.publishTool(tool, authToken);
|
|
760
703
|
|
|
761
704
|
return {
|
|
762
705
|
success: true,
|
|
@@ -770,11 +713,27 @@ export class EnactCore {
|
|
|
770
713
|
}
|
|
771
714
|
}
|
|
772
715
|
|
|
716
|
+
/**
|
|
717
|
+
* Instance method wrapper for backward compatibility
|
|
718
|
+
*/
|
|
719
|
+
async publishTool(
|
|
720
|
+
tool: EnactTool,
|
|
721
|
+
): Promise<{ success: boolean; message: string }> {
|
|
722
|
+
if (!this.options.authToken) {
|
|
723
|
+
return {
|
|
724
|
+
success: false,
|
|
725
|
+
message: "Authentication required to publish tools",
|
|
726
|
+
};
|
|
727
|
+
}
|
|
728
|
+
|
|
729
|
+
return EnactCore.publishTool(tool, this.options.authToken, this.options);
|
|
730
|
+
}
|
|
731
|
+
|
|
773
732
|
/**
|
|
774
733
|
* Get tool information (alias for getToolByName for consistency)
|
|
775
734
|
*/
|
|
776
735
|
async getToolInfo(name: string, version?: string): Promise<EnactTool | null> {
|
|
777
|
-
return
|
|
736
|
+
return EnactCore.getToolByName(name, version, this.options);
|
|
778
737
|
}
|
|
779
738
|
|
|
780
739
|
/**
|
|
@@ -783,7 +742,6 @@ export class EnactCore {
|
|
|
783
742
|
async getStatus(): Promise<{
|
|
784
743
|
executionProvider: string;
|
|
785
744
|
apiUrl: string;
|
|
786
|
-
verificationPolicy: string;
|
|
787
745
|
defaultTimeout: string;
|
|
788
746
|
authenticated: boolean;
|
|
789
747
|
}> {
|
|
@@ -792,7 +750,6 @@ export class EnactCore {
|
|
|
792
750
|
return {
|
|
793
751
|
executionProvider: this.options.executionProvider || "direct",
|
|
794
752
|
apiUrl: this.options.apiUrl || "https://enact.tools",
|
|
795
|
-
verificationPolicy: this.options.verificationPolicy || "permissive",
|
|
796
753
|
defaultTimeout: this.options.defaultTimeout || "30s",
|
|
797
754
|
authenticated: authStatus.authenticated,
|
|
798
755
|
};
|
|
@@ -829,5 +786,4 @@ export class EnactCore {
|
|
|
829
786
|
}
|
|
830
787
|
}
|
|
831
788
|
|
|
832
|
-
//
|
|
833
|
-
export const enactCore = new EnactCore();
|
|
789
|
+
// No default instance to avoid eager initialization of Dagger
|