@enactprotocol/shared 1.0.14 → 1.2.1
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 +162 -217
- 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/env-loader.js +1 -1
- package/dist/utils/help.js +1 -1
- 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 +232 -269
- 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/env-loader.ts +1 -1
- package/src/utils/help.ts +1 -1
- 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, SecurityConfigManager, 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,69 @@ 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
|
+
const referenceSignature = {
|
|
410
|
+
signature: tool.signatures[0].value,
|
|
411
|
+
publicKey: tool.signatures[0].signer,
|
|
412
|
+
algorithm: tool.signatures[0].algorithm,
|
|
413
|
+
timestamp: new Date(tool.signatures[0].created).getTime()
|
|
414
|
+
};
|
|
415
|
+
|
|
416
|
+
|
|
417
|
+
// Check what canonical document looks like
|
|
418
|
+
const canonicalDoc = SigningService.getCanonicalDocument(documentForVerification, { includeFields: ['command'] });
|
|
419
|
+
// console.log("Canonical document:", JSON.stringify(canonicalDoc));
|
|
420
|
+
|
|
421
|
+
const docString = JSON.stringify(canonicalDoc);
|
|
422
|
+
const messageHash = CryptoUtils.hash(docString);
|
|
423
|
+
// console.log("Document string:", docString);
|
|
424
|
+
// console.log("Message hash:", messageHash);
|
|
425
|
+
|
|
426
|
+
// Test direct crypto verification
|
|
427
|
+
const directVerify = CryptoUtils.verify(
|
|
428
|
+
referenceSignature.publicKey,
|
|
429
|
+
messageHash,
|
|
430
|
+
referenceSignature.signature
|
|
431
|
+
);
|
|
432
|
+
console.log("Direct crypto verification result:", directVerify);
|
|
433
|
+
|
|
434
|
+
// Check trusted keys
|
|
435
|
+
const trustedKeys = KeyManager.getAllTrustedPublicKeys();
|
|
436
|
+
console.log("Trusted keys:", trustedKeys);
|
|
437
|
+
console.log("Is our public key trusted?", trustedKeys.includes(referenceSignature.publicKey));
|
|
438
|
+
|
|
439
|
+
const isValid = SigningService.verifyDocument(
|
|
440
|
+
documentForVerification,
|
|
441
|
+
referenceSignature,
|
|
442
|
+
{ includeFields: ['command'] }
|
|
443
|
+
);
|
|
444
|
+
|
|
445
|
+
console.log("Final verification result:", isValid);
|
|
446
|
+
|
|
447
|
+
if (!isValid) {
|
|
448
|
+
throw new Error(`Tool ${tool.name} has invalid signatures`);
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
logger.info(`Tool ${tool.name} signature verification passed`);
|
|
452
|
+
} catch (error) {
|
|
453
|
+
logger.error(`Signature verification failed for tool ${tool.name}:`, error);
|
|
454
|
+
throw new Error(`Signature verification failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
|
|
352
458
|
/**
|
|
353
459
|
* Execute a tool directly
|
|
354
460
|
*/
|
|
@@ -365,153 +471,50 @@ export class EnactCore {
|
|
|
365
471
|
// Validate tool structure
|
|
366
472
|
validateToolStructure(tool);
|
|
367
473
|
|
|
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
474
|
// Validate inputs
|
|
393
475
|
const validatedInputs = validateInputs(tool, inputs);
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
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
|
-
};
|
|
476
|
+
const config = SecurityConfigManager.loadConfig();
|
|
477
|
+
|
|
478
|
+
if( options.isLocalFile && config.allowLocalUnsigned){
|
|
479
|
+
logger.warn(`Executing local file without signature verification: ${tool.name} (you can disallow in your security config)`);
|
|
414
480
|
}
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
if (safetyCheck.warnings.length > 0) {
|
|
418
|
-
safetyCheck.warnings.forEach((warning: string) => logger.warn(warning));
|
|
481
|
+
if( options.dangerouslySkipVerification) {
|
|
482
|
+
logger.warn(`Skipping signature verification for tool: ${tool.name} because of dangerouslySkipVerification option`);
|
|
419
483
|
}
|
|
484
|
+
const skipVerification = (options.isLocalFile && config.allowLocalUnsigned) || Boolean(options.dangerouslySkipVerification);
|
|
485
|
+
// Verify tool signatures (unless explicitly skipped)
|
|
486
|
+
await this.verifyTool(tool, skipVerification);
|
|
420
487
|
|
|
421
|
-
//
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
success: true,
|
|
425
|
-
output: {
|
|
426
|
-
dryRun: true,
|
|
427
|
-
tool: tool.name,
|
|
428
|
-
command: tool.command,
|
|
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,
|
|
439
|
-
},
|
|
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
|
-
},
|
|
462
|
-
resources: tool.resources,
|
|
463
|
-
namespace: tool.namespace,
|
|
464
|
-
};
|
|
488
|
+
// Resolve environment variables
|
|
489
|
+
const { resolved: envVars } =
|
|
490
|
+
await resolveToolEnvironmentVariables(tool.name, tool.env || {});
|
|
465
491
|
|
|
466
|
-
// Setup execution provider
|
|
467
|
-
await this.executionProvider.setup(tool);
|
|
468
492
|
|
|
469
|
-
// Execute the tool
|
|
470
|
-
|
|
493
|
+
// Execute the tool via the execution provider
|
|
494
|
+
return await this.executionProvider.execute(
|
|
471
495
|
tool,
|
|
472
|
-
validatedInputs,
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
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})`,
|
|
496
|
+
{ ...validatedInputs, ...envVars },
|
|
497
|
+
{
|
|
498
|
+
vars: { ...envVars, ...validatedInputs },
|
|
499
|
+
resources: {
|
|
500
|
+
timeout: options.timeout || tool.timeout || this.options.defaultTimeout,
|
|
501
|
+
},
|
|
502
|
+
},
|
|
487
503
|
);
|
|
488
|
-
return result;
|
|
489
504
|
} catch (error) {
|
|
490
|
-
logger.error(`Error executing tool: ${(error as Error).message}`);
|
|
491
|
-
|
|
492
505
|
return {
|
|
493
506
|
success: false,
|
|
494
507
|
error: {
|
|
495
508
|
message: (error as Error).message,
|
|
496
509
|
code: "EXECUTION_ERROR",
|
|
497
|
-
details: error,
|
|
498
510
|
},
|
|
499
511
|
metadata: {
|
|
500
512
|
executionId,
|
|
501
513
|
toolName: tool.name,
|
|
502
|
-
version: tool.version,
|
|
503
514
|
executedAt: new Date().toISOString(),
|
|
504
515
|
environment: "direct",
|
|
505
|
-
command: tool.command,
|
|
506
516
|
},
|
|
507
517
|
};
|
|
508
|
-
} finally {
|
|
509
|
-
// Cleanup
|
|
510
|
-
try {
|
|
511
|
-
await this.executionProvider.cleanup();
|
|
512
|
-
} catch (cleanupError) {
|
|
513
|
-
logger.error("Error during cleanup:", cleanupError);
|
|
514
|
-
}
|
|
515
518
|
}
|
|
516
519
|
}
|
|
517
520
|
|
|
@@ -562,100 +565,27 @@ export class EnactCore {
|
|
|
562
565
|
}
|
|
563
566
|
}
|
|
564
567
|
|
|
568
|
+
|
|
565
569
|
/**
|
|
566
|
-
*
|
|
570
|
+
* Static method to check if a tool exists
|
|
567
571
|
*/
|
|
568
|
-
async
|
|
572
|
+
static async toolExists(
|
|
569
573
|
name: string,
|
|
570
|
-
|
|
571
|
-
): Promise<{
|
|
572
|
-
verified: boolean;
|
|
573
|
-
signatures: any[];
|
|
574
|
-
policy: string;
|
|
575
|
-
errors?: string[];
|
|
576
|
-
}> {
|
|
574
|
+
coreOptions: Pick<EnactCoreOptions, 'apiUrl' | 'supabaseUrl'> = {}
|
|
575
|
+
): Promise<boolean> {
|
|
577
576
|
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
|
-
};
|
|
577
|
+
const tool = await EnactCore.getToolByName(name, undefined, coreOptions);
|
|
578
|
+
return tool !== null;
|
|
639
579
|
} catch (error) {
|
|
640
|
-
return
|
|
641
|
-
verified: false,
|
|
642
|
-
signatures: [],
|
|
643
|
-
policy: policy || "permissive",
|
|
644
|
-
errors: [`Verification error: ${(error as Error).message}`],
|
|
645
|
-
};
|
|
580
|
+
return false;
|
|
646
581
|
}
|
|
647
582
|
}
|
|
648
583
|
|
|
649
584
|
/**
|
|
650
|
-
*
|
|
585
|
+
* Instance method wrapper for backward compatibility
|
|
651
586
|
*/
|
|
652
587
|
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
|
-
}
|
|
588
|
+
return EnactCore.toolExists(name, this.options);
|
|
659
589
|
}
|
|
660
590
|
|
|
661
591
|
/**
|
|
@@ -665,11 +595,11 @@ export class EnactCore {
|
|
|
665
595
|
tags: string[],
|
|
666
596
|
limit: number = 20,
|
|
667
597
|
): Promise<EnactTool[]> {
|
|
668
|
-
return
|
|
598
|
+
return EnactCore.searchTools({
|
|
669
599
|
query: tags.join(" "),
|
|
670
600
|
tags,
|
|
671
601
|
limit,
|
|
672
|
-
});
|
|
602
|
+
}, this.options);
|
|
673
603
|
}
|
|
674
604
|
|
|
675
605
|
/**
|
|
@@ -679,33 +609,39 @@ export class EnactCore {
|
|
|
679
609
|
author: string,
|
|
680
610
|
limit: number = 20,
|
|
681
611
|
): Promise<EnactTool[]> {
|
|
682
|
-
return
|
|
612
|
+
return EnactCore.searchTools({
|
|
683
613
|
query: `author:${author}`,
|
|
684
614
|
author,
|
|
685
615
|
limit,
|
|
686
|
-
});
|
|
616
|
+
}, this.options);
|
|
687
617
|
}
|
|
688
618
|
|
|
689
619
|
/**
|
|
690
|
-
*
|
|
620
|
+
* Static method to get all tools with filters
|
|
691
621
|
*/
|
|
692
|
-
async getTools(
|
|
622
|
+
static async getTools(
|
|
693
623
|
options: {
|
|
694
624
|
limit?: number;
|
|
695
625
|
offset?: number;
|
|
696
626
|
tags?: string[];
|
|
697
627
|
author?: string;
|
|
698
628
|
} = {},
|
|
629
|
+
coreOptions: Pick<EnactCoreOptions, 'apiUrl' | 'supabaseUrl'> = {}
|
|
699
630
|
): Promise<EnactTool[]> {
|
|
631
|
+
const apiClient = new EnactApiClient(
|
|
632
|
+
coreOptions.apiUrl || "https://enact.tools",
|
|
633
|
+
coreOptions.supabaseUrl || "https://xjnhhxwxovjifdxdwzih.supabase.co"
|
|
634
|
+
);
|
|
635
|
+
|
|
700
636
|
try {
|
|
701
|
-
const apiResults = await
|
|
637
|
+
const apiResults = await apiClient.getTools(options);
|
|
702
638
|
|
|
703
639
|
// Parse and validate results
|
|
704
640
|
const tools: EnactTool[] = [];
|
|
705
641
|
for (const result of apiResults) {
|
|
706
642
|
if (result.name) {
|
|
707
643
|
try {
|
|
708
|
-
const tool = await
|
|
644
|
+
const tool = await EnactCore.getToolByName(result.name, undefined, coreOptions);
|
|
709
645
|
if (tool) {
|
|
710
646
|
tools.push(tool);
|
|
711
647
|
}
|
|
@@ -724,6 +660,20 @@ export class EnactCore {
|
|
|
724
660
|
}
|
|
725
661
|
}
|
|
726
662
|
|
|
663
|
+
/**
|
|
664
|
+
* Instance method wrapper for backward compatibility
|
|
665
|
+
*/
|
|
666
|
+
async getTools(
|
|
667
|
+
options: {
|
|
668
|
+
limit?: number;
|
|
669
|
+
offset?: number;
|
|
670
|
+
tags?: string[];
|
|
671
|
+
author?: string;
|
|
672
|
+
} = {},
|
|
673
|
+
): Promise<EnactTool[]> {
|
|
674
|
+
return EnactCore.getTools(options, this.options);
|
|
675
|
+
}
|
|
676
|
+
|
|
727
677
|
/**
|
|
728
678
|
* Get authentication status (placeholder - would need actual auth implementation)
|
|
729
679
|
*/
|
|
@@ -741,22 +691,22 @@ export class EnactCore {
|
|
|
741
691
|
}
|
|
742
692
|
|
|
743
693
|
/**
|
|
744
|
-
*
|
|
694
|
+
* Static method to publish a tool
|
|
745
695
|
*/
|
|
746
|
-
async publishTool(
|
|
696
|
+
static async publishTool(
|
|
747
697
|
tool: EnactTool,
|
|
698
|
+
authToken: string,
|
|
699
|
+
coreOptions: Pick<EnactCoreOptions, 'apiUrl' | 'supabaseUrl'> = {}
|
|
748
700
|
): Promise<{ success: boolean; message: string }> {
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
};
|
|
754
|
-
}
|
|
701
|
+
const apiClient = new EnactApiClient(
|
|
702
|
+
coreOptions.apiUrl || "https://enact.tools",
|
|
703
|
+
coreOptions.supabaseUrl || "https://xjnhhxwxovjifdxdwzih.supabase.co"
|
|
704
|
+
);
|
|
755
705
|
|
|
756
706
|
try {
|
|
757
707
|
validateToolStructure(tool);
|
|
758
708
|
|
|
759
|
-
await
|
|
709
|
+
await apiClient.publishTool(tool, authToken);
|
|
760
710
|
|
|
761
711
|
return {
|
|
762
712
|
success: true,
|
|
@@ -770,11 +720,27 @@ export class EnactCore {
|
|
|
770
720
|
}
|
|
771
721
|
}
|
|
772
722
|
|
|
723
|
+
/**
|
|
724
|
+
* Instance method wrapper for backward compatibility
|
|
725
|
+
*/
|
|
726
|
+
async publishTool(
|
|
727
|
+
tool: EnactTool,
|
|
728
|
+
): Promise<{ success: boolean; message: string }> {
|
|
729
|
+
if (!this.options.authToken) {
|
|
730
|
+
return {
|
|
731
|
+
success: false,
|
|
732
|
+
message: "Authentication required to publish tools",
|
|
733
|
+
};
|
|
734
|
+
}
|
|
735
|
+
|
|
736
|
+
return EnactCore.publishTool(tool, this.options.authToken, this.options);
|
|
737
|
+
}
|
|
738
|
+
|
|
773
739
|
/**
|
|
774
740
|
* Get tool information (alias for getToolByName for consistency)
|
|
775
741
|
*/
|
|
776
742
|
async getToolInfo(name: string, version?: string): Promise<EnactTool | null> {
|
|
777
|
-
return
|
|
743
|
+
return EnactCore.getToolByName(name, version, this.options);
|
|
778
744
|
}
|
|
779
745
|
|
|
780
746
|
/**
|
|
@@ -783,7 +749,6 @@ export class EnactCore {
|
|
|
783
749
|
async getStatus(): Promise<{
|
|
784
750
|
executionProvider: string;
|
|
785
751
|
apiUrl: string;
|
|
786
|
-
verificationPolicy: string;
|
|
787
752
|
defaultTimeout: string;
|
|
788
753
|
authenticated: boolean;
|
|
789
754
|
}> {
|
|
@@ -792,7 +757,6 @@ export class EnactCore {
|
|
|
792
757
|
return {
|
|
793
758
|
executionProvider: this.options.executionProvider || "direct",
|
|
794
759
|
apiUrl: this.options.apiUrl || "https://enact.tools",
|
|
795
|
-
verificationPolicy: this.options.verificationPolicy || "permissive",
|
|
796
760
|
defaultTimeout: this.options.defaultTimeout || "30s",
|
|
797
761
|
authenticated: authStatus.authenticated,
|
|
798
762
|
};
|
|
@@ -829,5 +793,4 @@ export class EnactCore {
|
|
|
829
793
|
}
|
|
830
794
|
}
|
|
831
795
|
|
|
832
|
-
//
|
|
833
|
-
export const enactCore = new EnactCore();
|
|
796
|
+
// No default instance to avoid eager initialization of Dagger
|