@enactprotocol/shared 1.2.13 → 2.0.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.
Files changed (134) hide show
  1. package/README.md +44 -0
  2. package/package.json +16 -58
  3. package/src/config.ts +476 -0
  4. package/src/constants.ts +36 -0
  5. package/src/execution/command.ts +314 -0
  6. package/src/execution/index.ts +73 -0
  7. package/src/execution/runtime.ts +308 -0
  8. package/src/execution/types.ts +379 -0
  9. package/src/execution/validation.ts +508 -0
  10. package/src/index.ts +237 -30
  11. package/src/manifest/index.ts +36 -0
  12. package/src/manifest/loader.ts +187 -0
  13. package/src/manifest/parser.ts +173 -0
  14. package/src/manifest/validator.ts +309 -0
  15. package/src/paths.ts +108 -0
  16. package/src/registry.ts +219 -0
  17. package/src/resolver.ts +345 -0
  18. package/src/types/index.ts +30 -0
  19. package/src/types/manifest.ts +255 -0
  20. package/src/types.ts +5 -188
  21. package/src/utils/fs.ts +281 -0
  22. package/src/utils/logger.ts +270 -59
  23. package/src/utils/version.ts +304 -36
  24. package/tests/config.test.ts +515 -0
  25. package/tests/execution/command.test.ts +317 -0
  26. package/tests/execution/validation.test.ts +384 -0
  27. package/tests/fixtures/invalid-tool.yaml +4 -0
  28. package/tests/fixtures/valid-tool.md +62 -0
  29. package/tests/fixtures/valid-tool.yaml +40 -0
  30. package/tests/index.test.ts +8 -0
  31. package/tests/manifest/loader.test.ts +291 -0
  32. package/tests/manifest/parser.test.ts +345 -0
  33. package/tests/manifest/validator.test.ts +394 -0
  34. package/tests/manifest-types.test.ts +358 -0
  35. package/tests/paths.test.ts +153 -0
  36. package/tests/registry.test.ts +231 -0
  37. package/tests/resolver.test.ts +272 -0
  38. package/tests/utils/fs.test.ts +388 -0
  39. package/tests/utils/logger.test.ts +480 -0
  40. package/tests/utils/version.test.ts +390 -0
  41. package/tsconfig.json +12 -0
  42. package/tsconfig.tsbuildinfo +1 -0
  43. package/dist/LocalToolResolver.d.ts +0 -84
  44. package/dist/LocalToolResolver.js +0 -353
  45. package/dist/api/enact-api.d.ts +0 -130
  46. package/dist/api/enact-api.js +0 -428
  47. package/dist/api/index.d.ts +0 -2
  48. package/dist/api/index.js +0 -2
  49. package/dist/api/types.d.ts +0 -103
  50. package/dist/api/types.js +0 -1
  51. package/dist/constants.d.ts +0 -7
  52. package/dist/constants.js +0 -10
  53. package/dist/core/DaggerExecutionProvider.d.ts +0 -169
  54. package/dist/core/DaggerExecutionProvider.js +0 -1029
  55. package/dist/core/DirectExecutionProvider.d.ts +0 -23
  56. package/dist/core/DirectExecutionProvider.js +0 -406
  57. package/dist/core/EnactCore.d.ts +0 -162
  58. package/dist/core/EnactCore.js +0 -597
  59. package/dist/core/NativeExecutionProvider.d.ts +0 -9
  60. package/dist/core/NativeExecutionProvider.js +0 -16
  61. package/dist/core/index.d.ts +0 -3
  62. package/dist/core/index.js +0 -3
  63. package/dist/exec/index.d.ts +0 -3
  64. package/dist/exec/index.js +0 -3
  65. package/dist/exec/logger.d.ts +0 -11
  66. package/dist/exec/logger.js +0 -57
  67. package/dist/exec/validate.d.ts +0 -5
  68. package/dist/exec/validate.js +0 -167
  69. package/dist/index.d.ts +0 -21
  70. package/dist/index.js +0 -25
  71. package/dist/lib/enact-direct.d.ts +0 -150
  72. package/dist/lib/enact-direct.js +0 -159
  73. package/dist/lib/index.d.ts +0 -1
  74. package/dist/lib/index.js +0 -1
  75. package/dist/security/index.d.ts +0 -3
  76. package/dist/security/index.js +0 -3
  77. package/dist/security/security.d.ts +0 -23
  78. package/dist/security/security.js +0 -137
  79. package/dist/security/sign.d.ts +0 -103
  80. package/dist/security/sign.js +0 -666
  81. package/dist/security/verification-enforcer.d.ts +0 -53
  82. package/dist/security/verification-enforcer.js +0 -204
  83. package/dist/services/McpCoreService.d.ts +0 -98
  84. package/dist/services/McpCoreService.js +0 -124
  85. package/dist/services/index.d.ts +0 -1
  86. package/dist/services/index.js +0 -1
  87. package/dist/types.d.ts +0 -132
  88. package/dist/types.js +0 -3
  89. package/dist/utils/config.d.ts +0 -111
  90. package/dist/utils/config.js +0 -342
  91. package/dist/utils/env-loader.d.ts +0 -54
  92. package/dist/utils/env-loader.js +0 -270
  93. package/dist/utils/help.d.ts +0 -36
  94. package/dist/utils/help.js +0 -248
  95. package/dist/utils/index.d.ts +0 -7
  96. package/dist/utils/index.js +0 -7
  97. package/dist/utils/logger.d.ts +0 -35
  98. package/dist/utils/logger.js +0 -75
  99. package/dist/utils/silent-monitor.d.ts +0 -67
  100. package/dist/utils/silent-monitor.js +0 -242
  101. package/dist/utils/timeout.d.ts +0 -5
  102. package/dist/utils/timeout.js +0 -23
  103. package/dist/utils/version.d.ts +0 -4
  104. package/dist/utils/version.js +0 -35
  105. package/dist/web/env-manager-server.d.ts +0 -29
  106. package/dist/web/env-manager-server.js +0 -367
  107. package/dist/web/index.d.ts +0 -1
  108. package/dist/web/index.js +0 -1
  109. package/src/LocalToolResolver.ts +0 -424
  110. package/src/api/enact-api.ts +0 -604
  111. package/src/api/index.ts +0 -2
  112. package/src/api/types.ts +0 -114
  113. package/src/core/DaggerExecutionProvider.ts +0 -1357
  114. package/src/core/DirectExecutionProvider.ts +0 -484
  115. package/src/core/EnactCore.ts +0 -847
  116. package/src/core/index.ts +0 -3
  117. package/src/exec/index.ts +0 -3
  118. package/src/exec/logger.ts +0 -63
  119. package/src/exec/validate.ts +0 -238
  120. package/src/lib/enact-direct.ts +0 -254
  121. package/src/lib/index.ts +0 -1
  122. package/src/services/McpCoreService.ts +0 -201
  123. package/src/services/index.ts +0 -1
  124. package/src/utils/config.ts +0 -438
  125. package/src/utils/env-loader.ts +0 -370
  126. package/src/utils/help.ts +0 -257
  127. package/src/utils/index.ts +0 -7
  128. package/src/utils/silent-monitor.ts +0 -328
  129. package/src/utils/timeout.ts +0 -26
  130. package/src/web/env-manager-server.ts +0 -465
  131. package/src/web/index.ts +0 -1
  132. package/src/web/static/app.js +0 -663
  133. package/src/web/static/index.html +0 -117
  134. package/src/web/static/style.css +0 -291
@@ -1,484 +0,0 @@
1
- // src/core/DirectExecutionProvider.ts - Direct execution provider that doesn't use external CLI
2
- import { spawn } from "child_process";
3
- import {
4
- ExecutionProvider,
5
- type EnactTool,
6
- type ExecutionEnvironment,
7
- type ExecutionResult,
8
- } from "../types.js";
9
- import logger from "../exec/logger.js";
10
- import { parseTimeout } from "../utils/timeout.js";
11
-
12
- export class DirectExecutionProvider extends ExecutionProvider {
13
- async resolveEnvironmentVariables(
14
- envConfig: Record<string, any>,
15
- namespace?: string,
16
- ): Promise<Record<string, any>> {
17
- const resolved: Record<string, any> = {};
18
-
19
- for (const [key, config] of Object.entries(envConfig)) {
20
- if (typeof config === "object" && config.source) {
21
- // Handle different sources
22
- switch (config.source) {
23
- case "env":
24
- resolved[key] = process.env[key] || config.default;
25
- break;
26
- case "user":
27
- // Could get from user config file
28
- resolved[key] = config.default;
29
- break;
30
- default:
31
- resolved[key] = config.default;
32
- }
33
- } else {
34
- // Direct value
35
- resolved[key] = config;
36
- }
37
- }
38
-
39
- return resolved;
40
- }
41
-
42
- async executeCommand(
43
- command: string,
44
- inputs: Record<string, any>,
45
- environment: ExecutionEnvironment,
46
- timeout?: string,
47
- options?: {
48
- verbose?: boolean;
49
- showSpinner?: boolean;
50
- streamOutput?: boolean;
51
- },
52
- ): Promise<{ stdout: string; stderr: string; exitCode: number }> {
53
- return new Promise((resolve, reject) => {
54
- let stdout = "";
55
- let stderr = "";
56
-
57
- // UI Setup
58
- const verbose = options?.verbose ?? false;
59
- const showSpinner = options?.showSpinner ?? false;
60
- const streamOutput = options?.streamOutput ?? true;
61
-
62
- let spinner: any = null;
63
-
64
- if (showSpinner) {
65
- // Dynamic import to avoid dependency issues when not needed
66
- try {
67
- const p = require("@clack/prompts");
68
- spinner = p.spinner();
69
- spinner.start("Executing tool...");
70
- } catch (e) {
71
- // Fallback if @clack/prompts not available
72
- console.log("Executing tool...");
73
- }
74
- }
75
-
76
- if (verbose) {
77
- try {
78
- const pc = require("picocolors");
79
- console.error(pc.cyan("\n🚀 Executing command:"));
80
- console.error(pc.white(command));
81
- } catch (e) {
82
- console.error("\n🚀 Executing command:");
83
- console.error(command);
84
- }
85
- }
86
-
87
- // Substitute template variables in command with input values
88
- let substitutedCommand = command;
89
- for (const [key, value] of Object.entries(inputs)) {
90
- const templateVar = `\${${key}}`;
91
- // Handle different value types
92
- let substitutionValue: string;
93
- if (typeof value === "string") {
94
- substitutionValue = value;
95
- } else if (typeof value === "object") {
96
- substitutionValue = JSON.stringify(value);
97
- } else {
98
- substitutionValue = String(value);
99
- }
100
- substitutedCommand = substitutedCommand.replace(
101
- new RegExp(`\\$\\{${key}\\}`, "g"),
102
- substitutionValue,
103
- );
104
- }
105
-
106
- // Prepare environment
107
- const env = {
108
- ...process.env,
109
- ...environment.vars,
110
- };
111
-
112
- // Parse command and arguments properly handling quoted strings
113
- const commandParts = this.parseCommand(substitutedCommand);
114
- const cmd = commandParts[0];
115
- const args = commandParts.slice(1);
116
-
117
- logger.info(`Executing command: ${command}`);
118
-
119
- try {
120
- const proc = spawn(cmd, args, {
121
- env,
122
- stdio: ["pipe", "pipe", "pipe"],
123
- // Create a new process group for better cleanup of child processes
124
- detached: process.platform !== "win32",
125
- });
126
-
127
- // Cleanup function to ensure process and children are properly terminated
128
- let isCleanedUp = false;
129
- let cleanupTimer: ReturnType<typeof setTimeout> | null = null;
130
-
131
- const cleanup = () => {
132
- if (isCleanedUp) return;
133
- isCleanedUp = true;
134
-
135
- // Clear any pending cleanup timer
136
- if (cleanupTimer) {
137
- clearTimeout(cleanupTimer);
138
- cleanupTimer = null;
139
- }
140
-
141
- if (proc && !proc.killed) {
142
- try {
143
- logger.debug(`Cleaning up process PID: ${proc.pid}`);
144
- // For Dagger and other tools that may spawn child processes
145
- if (process.platform === "win32") {
146
- proc.kill("SIGKILL");
147
- } else {
148
- // Try graceful termination first
149
- proc.kill("SIGTERM");
150
- // Set a cleanup timer for force kill if needed
151
- cleanupTimer = setTimeout(() => { if (!proc.killed && !isCleanedUp) {
152
- logger.debug(
153
- `Force killing process PID: ${proc.pid}`,
154
- );
155
- try {
156
- proc.kill("SIGKILL");
157
- } catch (killError) {
158
- // Process might already be dead, ignore
159
- logger.debug(
160
- `Force kill error (likely harmless): ${killError}`,
161
- );
162
- }
163
- }
164
- cleanupTimer = null;
165
- }, 1000); // Reduced from 2000ms to 1000ms
166
- }
167
- } catch (killError) {
168
- // Process might already be dead, ignore
169
- logger.debug(
170
- `Process cleanup error (likely harmless): ${killError}`,
171
- );
172
- }
173
- }
174
- };
175
-
176
- // Collect stdout and stream it in real-time
177
- proc.stdout.on("data", (data: Buffer) => {
178
- const chunk = data.toString();
179
- stdout += chunk;
180
- // Stream stdout to console in real-time if enabled
181
- if (streamOutput) {
182
- process.stdout.write(chunk);
183
- }
184
- });
185
-
186
- // Collect stderr and stream it in real-time
187
- proc.stderr.on("data", (data: Buffer) => {
188
- const chunk = data.toString();
189
- stderr += chunk;
190
- // Stream stderr to console in real-time if enabled
191
- if (streamOutput) {
192
- process.stderr.write(chunk);
193
- }
194
- });
195
- // Handle process completion with more robust cleanup
196
- proc.on("close", (code: number) => {
197
- logger.debug(
198
- `Process closed with code: ${code}, PID: ${proc.pid}`,
199
- );
200
- // Force cleanup any remaining resources
201
- cleanup();
202
-
203
- // Handle spinner cleanup and success/error messaging
204
- if (spinner) {
205
- spinner.stop("Execution completed");
206
- }
207
-
208
- if (code === 0) {
209
- if (showSpinner || verbose) {
210
- try {
211
- const pc = require("picocolors");
212
- console.error(pc.green("\n✅ Tool executed successfully"));
213
- if (stdout.trim() && !streamOutput) {
214
- console.error(pc.cyan("\n📤 Output:"));
215
- console.error(stdout.trim());
216
- }
217
- } catch (e) {
218
- console.error("\n✅ Tool executed successfully");
219
- if (stdout.trim() && !streamOutput) {
220
- console.error("\n📤 Output:");
221
- console.error(stdout.trim());
222
- }
223
- }
224
- }
225
- } else {
226
- if (showSpinner || verbose) {
227
- try {
228
- const pc = require("picocolors");
229
- console.error(
230
- pc.red(`\n❌ Tool execution failed (exit code: ${code})`),
231
- );
232
- if (stderr.trim() && !streamOutput) {
233
- console.error(pc.red("\n📤 Error output:"));
234
- console.error(stderr.trim());
235
- }
236
- if (stdout.trim() && !streamOutput) {
237
- console.error(pc.yellow("\n📤 Standard output:"));
238
- console.error(stdout.trim());
239
- }
240
- } catch (e) {
241
- console.error(
242
- `\n❌ Tool execution failed (exit code: ${code})`,
243
- );
244
- if (stderr.trim() && !streamOutput) {
245
- console.error("\n📤 Error output:");
246
- console.error(stderr.trim());
247
- }
248
- if (stdout.trim() && !streamOutput) {
249
- console.error("\n📤 Standard output:");
250
- console.error(stdout.trim());
251
- }
252
- }
253
- }
254
- }
255
-
256
- resolve({
257
- stdout: stdout.trim(),
258
- stderr: stderr.trim(),
259
- exitCode: code || 0,
260
- });
261
- });
262
-
263
- // Handle process errors
264
- proc.on("error", (error: Error) => {
265
- cleanup();
266
- if (spinner) {
267
- spinner.stop("Execution failed");
268
- }
269
-
270
- if (showSpinner || verbose) {
271
- try {
272
- const pc = require("picocolors");
273
- console.error(
274
- pc.red(`\n❌ Failed to execute command: ${error.message}`),
275
- );
276
- } catch (e) {
277
- console.error(`\n❌ Failed to execute command: ${error.message}`);
278
- }
279
- }
280
-
281
- reject(new Error(`Command execution error: ${error.message}`));
282
- });
283
-
284
- // Set timeout if specified
285
- if (timeout) {
286
- const timeoutMs = parseTimeout(timeout);
287
- setTimeout(() => {
288
- cleanup();
289
- if (spinner) {
290
- spinner.stop("Execution failed");
291
- }
292
- reject(new Error(`Command timed out after ${timeout}`));
293
- }, timeoutMs);
294
- }
295
- } catch (spawnError) {
296
- reject(new Error(`Failed to spawn command: ${spawnError}`));
297
- }
298
- });
299
- }
300
-
301
- /**
302
- * Execute command with exec.ts compatible interface
303
- * This method provides the same interface as the exec.ts executeCommand function
304
- */
305
- async executeCommandExecStyle(
306
- command: string,
307
- timeout: string,
308
- verbose: boolean = false,
309
- envVars: Record<string, string> = {},
310
- ): Promise<void> {
311
- const environment: ExecutionEnvironment = {
312
- vars: envVars,
313
- resources: { timeout },
314
- };
315
-
316
- const result = await this.executeCommand(
317
- command,
318
- {}, // No template substitution needed for this interface
319
- environment,
320
- timeout,
321
- {
322
- verbose,
323
- showSpinner: true,
324
- streamOutput: false, // Don't stream since exec.ts shows output at the end
325
- },
326
- );
327
-
328
- if (result.exitCode !== 0) {
329
- throw new Error(`Command failed with exit code ${result.exitCode}`);
330
- }
331
- }
332
-
333
- async setup(tool: EnactTool): Promise<boolean> {
334
- // No special setup needed for direct execution
335
- logger.debug(`Setting up direct execution for tool: ${tool.name}`);
336
- return true;
337
- }
338
-
339
- async execute(
340
- tool: EnactTool,
341
- inputs: Record<string, any>,
342
- environment: ExecutionEnvironment,
343
- ): Promise<ExecutionResult> {
344
- const executionId = this.generateExecutionId();
345
- const timeout = tool.timeout || environment.resources?.timeout;
346
-
347
- // Substitute template variables in command with input values
348
- let substitutedCommand = tool.command;
349
- for (const [key, value] of Object.entries(inputs)) {
350
- const templateVar = `\${${key}}`;
351
- // Handle different value types
352
- let substitutionValue: string;
353
- if (typeof value === "string") {
354
- substitutionValue = value;
355
- } else if (typeof value === "object") {
356
- substitutionValue = JSON.stringify(value);
357
- } else {
358
- substitutionValue = String(value);
359
- }
360
- substitutedCommand = substitutedCommand.replace(
361
- new RegExp(`\\$\\{${key}\\}`, "g"),
362
- substitutionValue,
363
- );
364
- }
365
-
366
- try {
367
- // Execute the command
368
- const result = await this.executeCommand(
369
- substitutedCommand,
370
- inputs,
371
- environment,
372
- timeout,
373
- );
374
-
375
- // Parse output
376
- let parsedOutput: any;
377
- try {
378
- // Try to parse as JSON first
379
- parsedOutput = JSON.parse(result.stdout);
380
- } catch {
381
- // If not JSON, return structured output
382
- parsedOutput = {
383
- stdout: result.stdout,
384
- stderr: result.stderr,
385
- };
386
- }
387
-
388
- return {
389
- success: result.exitCode === 0,
390
- output: parsedOutput,
391
- ...(result.exitCode !== 0 && {
392
- error: {
393
- message: `Command failed with exit code ${result.exitCode}`,
394
- code: "COMMAND_FAILED",
395
- details: {
396
- stdout: result.stdout,
397
- stderr: result.stderr,
398
- command: substitutedCommand, // Show the substituted command
399
- exitCode: result.exitCode,
400
- },
401
- },
402
- }),
403
- metadata: {
404
- executionId,
405
- toolName: tool.name,
406
- version: tool.version,
407
- executedAt: new Date().toISOString(),
408
- environment: "direct",
409
- timeout,
410
- command: substitutedCommand, // Show the substituted command in metadata
411
- },
412
- };
413
- } catch (error) {
414
- return {
415
- success: false,
416
- error: {
417
- message: (error as Error).message,
418
- code: "EXECUTION_ERROR",
419
- details: error,
420
- },
421
- metadata: {
422
- executionId,
423
- toolName: tool.name,
424
- version: tool.version,
425
- executedAt: new Date().toISOString(),
426
- environment: "direct",
427
- },
428
- };
429
- }
430
- }
431
-
432
- async cleanup(): Promise<boolean> {
433
- // No cleanup needed for direct execution
434
- return true;
435
- }
436
-
437
- private generateExecutionId(): string {
438
- return `exec_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;
439
- }
440
-
441
- private parseCommand(command: string): string[] {
442
- const parts: string[] = [];
443
- let current = "";
444
- let inQuotes = false;
445
- let quoteChar = "";
446
- let i = 0;
447
-
448
- while (i < command.length) {
449
- const char = command[i];
450
-
451
- if (!inQuotes && (char === '"' || char === "'")) {
452
- // Start of quoted section
453
- inQuotes = true;
454
- quoteChar = char;
455
- } else if (inQuotes && char === quoteChar) {
456
- // End of quoted section
457
- inQuotes = false;
458
- quoteChar = "";
459
- } else if (!inQuotes && char === " ") {
460
- // Space outside quotes - end current part
461
- if (current.length > 0) {
462
- parts.push(current);
463
- current = "";
464
- }
465
- // Skip whitespace
466
- while (i + 1 < command.length && command[i + 1] === " ") {
467
- i++;
468
- }
469
- } else {
470
- // Regular character or space inside quotes
471
- current += char;
472
- }
473
-
474
- i++;
475
- }
476
-
477
- // Add the last part if it exists
478
- if (current.length > 0) {
479
- parts.push(current);
480
- }
481
-
482
- return parts;
483
- }
484
- }