@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.
@@ -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
- * Search for tools
86
+ * Static method to search for tools (no execution provider needed)
101
87
  */
102
- async searchTools(options: ToolSearchOptions): Promise<EnactTool[]> {
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 this.apiClient.searchTools(searchParams);
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 this.getToolByName(result.name);
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 this.searchToolsFallback(options);
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
- * Fallback search method that gets all tools and filters locally
143
+ * Instance method wrapper for backward compatibility
150
144
  */
151
- private async searchToolsFallback(
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 this.apiClient.getTools({
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 this.getToolByName(result.name);
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
- * Get a specific tool by name
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 this.apiClient.getTool(name);
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
- if (response.content && typeof response.content === "string") {
249
- tool = yaml.parse(response.content);
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 (response.signature || response.signatures) {
286
+ if (!tool.signature && response.signature) {
262
287
  tool.signature = response.signature;
263
- tool.signatures = response.signatures;
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
- // Map database fields to tool format
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
- // Check command safety
396
- const safetyCheck = verifyCommandSafety(tool.command, tool);
397
- if (!safetyCheck.isSafe && !options.force) {
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
- // Dry run - just validate and return
422
- if (options.dryRun) {
423
- return {
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,
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
- * Verify a tool's signature
563
+ * Static method to check if a tool exists
567
564
  */
568
- async verifyTool(
565
+ static async toolExists(
569
566
  name: string,
570
- policy?: string,
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 this.getToolByName(name);
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
- * Check if a tool exists
578
+ * Instance method wrapper for backward compatibility
651
579
  */
652
580
  async toolExists(name: string): Promise<boolean> {
653
- try {
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 this.searchTools({
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 this.searchTools({
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
- * Get all tools with filters
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 this.apiClient.getTools(options);
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 this.getToolByName(result.name);
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
- * Publish a tool (requires authentication)
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
- if (!this.options.authToken) {
750
- return {
751
- success: false,
752
- message: "Authentication required to publish tools",
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 this.apiClient.publishTool(tool, this.options.authToken);
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 this.getToolByName(name, version);
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
- // Export a default instance
833
- export const enactCore = new EnactCore();
789
+ // No default instance to avoid eager initialization of Dagger