@enactprotocol/shared 1.0.12

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 (97) hide show
  1. package/dist/LocalToolResolver.d.ts +84 -0
  2. package/dist/LocalToolResolver.js +353 -0
  3. package/dist/api/enact-api.d.ts +124 -0
  4. package/dist/api/enact-api.js +406 -0
  5. package/dist/api/index.d.ts +2 -0
  6. package/dist/api/index.js +2 -0
  7. package/dist/api/types.d.ts +83 -0
  8. package/dist/api/types.js +1 -0
  9. package/dist/core/DaggerExecutionProvider.d.ts +169 -0
  10. package/dist/core/DaggerExecutionProvider.js +996 -0
  11. package/dist/core/DirectExecutionProvider.d.ts +23 -0
  12. package/dist/core/DirectExecutionProvider.js +406 -0
  13. package/dist/core/EnactCore.d.ts +138 -0
  14. package/dist/core/EnactCore.js +609 -0
  15. package/dist/core/index.d.ts +3 -0
  16. package/dist/core/index.js +3 -0
  17. package/dist/exec/index.d.ts +3 -0
  18. package/dist/exec/index.js +3 -0
  19. package/dist/exec/logger.d.ts +11 -0
  20. package/dist/exec/logger.js +57 -0
  21. package/dist/exec/validate.d.ts +5 -0
  22. package/dist/exec/validate.js +167 -0
  23. package/dist/index.d.ts +25 -0
  24. package/dist/index.js +29 -0
  25. package/dist/lib/enact-direct.d.ts +156 -0
  26. package/dist/lib/enact-direct.js +158 -0
  27. package/dist/lib/index.d.ts +1 -0
  28. package/dist/lib/index.js +1 -0
  29. package/dist/security/index.d.ts +3 -0
  30. package/dist/security/index.js +3 -0
  31. package/dist/security/security.d.ts +23 -0
  32. package/dist/security/security.js +137 -0
  33. package/dist/security/sign.d.ts +103 -0
  34. package/dist/security/sign.js +532 -0
  35. package/dist/security/verification-enforcer.d.ts +41 -0
  36. package/dist/security/verification-enforcer.js +181 -0
  37. package/dist/services/McpCoreService.d.ts +102 -0
  38. package/dist/services/McpCoreService.js +120 -0
  39. package/dist/services/index.d.ts +1 -0
  40. package/dist/services/index.js +1 -0
  41. package/dist/types.d.ts +130 -0
  42. package/dist/types.js +3 -0
  43. package/dist/utils/config.d.ts +32 -0
  44. package/dist/utils/config.js +78 -0
  45. package/dist/utils/env-loader.d.ts +54 -0
  46. package/dist/utils/env-loader.js +270 -0
  47. package/dist/utils/help.d.ts +36 -0
  48. package/dist/utils/help.js +248 -0
  49. package/dist/utils/index.d.ts +7 -0
  50. package/dist/utils/index.js +7 -0
  51. package/dist/utils/logger.d.ts +35 -0
  52. package/dist/utils/logger.js +75 -0
  53. package/dist/utils/silent-monitor.d.ts +67 -0
  54. package/dist/utils/silent-monitor.js +242 -0
  55. package/dist/utils/timeout.d.ts +5 -0
  56. package/dist/utils/timeout.js +23 -0
  57. package/dist/utils/version.d.ts +4 -0
  58. package/dist/utils/version.js +14 -0
  59. package/dist/web/env-manager-server.d.ts +29 -0
  60. package/dist/web/env-manager-server.js +367 -0
  61. package/dist/web/index.d.ts +1 -0
  62. package/dist/web/index.js +1 -0
  63. package/package.json +79 -0
  64. package/src/LocalToolResolver.ts +424 -0
  65. package/src/api/enact-api.ts +569 -0
  66. package/src/api/index.ts +2 -0
  67. package/src/api/types.ts +93 -0
  68. package/src/core/DaggerExecutionProvider.ts +1308 -0
  69. package/src/core/DirectExecutionProvider.ts +484 -0
  70. package/src/core/EnactCore.ts +833 -0
  71. package/src/core/index.ts +3 -0
  72. package/src/exec/index.ts +3 -0
  73. package/src/exec/logger.ts +63 -0
  74. package/src/exec/validate.ts +238 -0
  75. package/src/index.ts +42 -0
  76. package/src/lib/enact-direct.ts +258 -0
  77. package/src/lib/index.ts +1 -0
  78. package/src/security/index.ts +3 -0
  79. package/src/security/security.ts +188 -0
  80. package/src/security/sign.ts +797 -0
  81. package/src/security/verification-enforcer.ts +268 -0
  82. package/src/services/McpCoreService.ts +203 -0
  83. package/src/services/index.ts +1 -0
  84. package/src/types.ts +190 -0
  85. package/src/utils/config.ts +97 -0
  86. package/src/utils/env-loader.ts +370 -0
  87. package/src/utils/help.ts +257 -0
  88. package/src/utils/index.ts +7 -0
  89. package/src/utils/logger.ts +83 -0
  90. package/src/utils/silent-monitor.ts +328 -0
  91. package/src/utils/timeout.ts +26 -0
  92. package/src/utils/version.ts +16 -0
  93. package/src/web/env-manager-server.ts +465 -0
  94. package/src/web/index.ts +1 -0
  95. package/src/web/static/app.js +663 -0
  96. package/src/web/static/index.html +117 -0
  97. package/src/web/static/style.css +291 -0
@@ -0,0 +1,257 @@
1
+ // src/utils/help.ts
2
+ import pc from "picocolors";
3
+ import { readFileSync } from "fs";
4
+ import { join } from "path";
5
+
6
+ /**
7
+ * Get the package version from package.json
8
+ */
9
+ function getVersion(): string {
10
+ try {
11
+ const packageJsonPath = join(__dirname, "../../package.json");
12
+ const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf8"));
13
+ return packageJson.version || "0.1.0";
14
+ } catch (error) {
15
+ // Fallback version if package.json can't be read
16
+ return "0.1.0";
17
+ }
18
+ }
19
+
20
+ /**
21
+ * Show the main help message
22
+ */
23
+ export function showHelp(): void {
24
+ const version = getVersion();
25
+ console.error(`
26
+ ${pc.bold("Enact CLI")} ${pc.dim(`v${version}`)}
27
+ ${pc.dim("A CLI tool for managing and publishing Enact tools")}
28
+
29
+ ${pc.bold("Usage:")}
30
+ ${pc.cyan("enact")} ${pc.green("<command>")} [options]
31
+
32
+ ${pc.bold("Commands:")}
33
+ ${pc.green("auth")} Manage authentication (login, logout, status, token)
34
+ ${pc.green("env")} Manage environment variables (set, get, list, delete)
35
+ ${pc.green("exec")} Execute a tool by fetching and running it
36
+ ${pc.green("init")} Create a new tool definition
37
+ ${pc.green("mcp")} MCP client integration (install, list, status)
38
+ ${pc.green("publish")} Publish a tool to the registry
39
+ ${pc.green("search")} Search for tools in the registry
40
+ ${pc.green("sign")} Sign and verify tools with cryptographic signatures
41
+ ${pc.green("remote")} Manage remote servers (add, list, remove)
42
+ ${pc.green("user")} User operations (get public key)
43
+
44
+ ${pc.bold("Global Options:")}
45
+ ${pc.yellow("--help, -h")} Show help message
46
+ ${pc.yellow("--version, -v")} Show version information
47
+
48
+ ${pc.bold("Examples:")}
49
+ ${pc.cyan("enact")} ${pc.dim("# Interactive mode")}
50
+ ${pc.cyan("enact")} ${pc.green("search")} ${pc.yellow("--tags")} web,api ${pc.dim("# Search tools by tags")}
51
+ ${pc.cyan("enact")} ${pc.green("exec")} enact/text/slugify ${pc.dim("# Execute a tool")}
52
+ ${pc.cyan("enact")} ${pc.green("mcp")} install ${pc.yellow("--client")} claude-desktop ${pc.dim("# Install MCP server")}
53
+ ${pc.cyan("enact")} ${pc.green("mcp")} install ${pc.yellow("--client")} goose ${pc.dim("# Install for Goose AI")}
54
+ ${pc.cyan("enact")} ${pc.green("env")} set API_KEY --encrypt ${pc.dim("# Set encrypted env var")}
55
+ ${pc.cyan("enact")} ${pc.green("sign")} verify my-tool.yaml ${pc.dim("# Verify tool signatures")}
56
+ ${pc.cyan("enact")} ${pc.green("publish")} my-tool.yaml ${pc.dim("# Publish a tool")}
57
+ ${pc.cyan("enact")} ${pc.green("auth")} login ${pc.dim("# Login with OAuth")}
58
+ ${pc.cyan("enact")} ${pc.green("init")} ${pc.yellow("--minimal")} ${pc.dim("# Create minimal tool template")}
59
+
60
+ ${pc.bold("More Help:")}
61
+ ${pc.cyan("enact")} ${pc.green("<command>")} ${pc.yellow("--help")} ${pc.dim("# Show command-specific help")}
62
+ `);
63
+ }
64
+
65
+ /**
66
+ * Show version information
67
+ */
68
+ export function showVersion(): void {
69
+ const version = getVersion();
70
+ console.error(`enact-cli v${version}`);
71
+ }
72
+
73
+ /**
74
+ * Show help for the auth command
75
+ */
76
+ export function showAuthHelp(): void {
77
+ console.error(`
78
+ ${pc.bold("Usage:")} ${pc.cyan("enact")} ${pc.green("auth")} ${pc.blue("<subcommand>")} [options]
79
+
80
+ ${pc.bold("Manage authentication for Enact registry")}
81
+
82
+ ${pc.bold("Subcommands:")}
83
+ ${pc.blue("login")} Login using OAuth
84
+ ${pc.blue("logout")} Logout and clear stored credentials
85
+ ${pc.blue("status")} Check authentication status
86
+ ${pc.blue("token")} Display current authentication token
87
+
88
+ ${pc.bold("Options:")}
89
+ ${pc.yellow("--help, -h")} Show this help message
90
+ ${pc.yellow("--server, -s")} Specify server URL
91
+ ${pc.yellow("--port, -p")} Specify port for OAuth callback
92
+
93
+ ${pc.bold("Examples:")}
94
+ ${pc.cyan("enact")} ${pc.green("auth")} ${pc.blue("login")}
95
+ ${pc.cyan("enact")} ${pc.green("auth")} ${pc.blue("status")}
96
+ ${pc.cyan("enact")} ${pc.green("auth")} ${pc.blue("login")} ${pc.yellow("--server")} https://api.example.com
97
+ `);
98
+ }
99
+
100
+ /**
101
+ * Show help for the init command
102
+ */
103
+ export function showInitHelp(): void {
104
+ console.error(`
105
+ ${pc.bold("Usage:")} ${pc.cyan("enact")} ${pc.green("init")} [options] [name]
106
+
107
+ ${pc.bold("Create a new tool definition file")}
108
+
109
+ ${pc.bold("Arguments:")}
110
+ ${pc.blue("name")} Name for the new tool (optional)
111
+
112
+ ${pc.bold("Options:")}
113
+ ${pc.yellow("--help, -h")} Show this help message
114
+ ${pc.yellow("--minimal, -m")} Create a minimal tool template
115
+
116
+ ${pc.bold("Examples:")}
117
+ ${pc.cyan("enact")} ${pc.green("init")}
118
+ ${pc.cyan("enact")} ${pc.green("init")} my-awesome-tool
119
+ ${pc.cyan("enact")} ${pc.green("init")} ${pc.yellow("--minimal")} simple-tool
120
+ `);
121
+ }
122
+
123
+ /**
124
+ * Show help for the publish command
125
+ */
126
+ export function showPublishHelp(): void {
127
+ console.error(`
128
+ ${pc.bold("Usage:")} ${pc.cyan("enact")} ${pc.green("publish")} [options] [file]
129
+
130
+ ${pc.bold("Publish a tool to the Enact registry")}
131
+
132
+ ${pc.bold("Arguments:")}
133
+ ${pc.blue("file")} The tool definition file to publish (YAML format)
134
+
135
+ ${pc.bold("Options:")}
136
+ ${pc.yellow("--help, -h")} Show this help message
137
+ ${pc.yellow("--url")} Specify the registry URL
138
+ ${pc.yellow("--token, -t")} Specify authentication token
139
+
140
+ ${pc.bold("Examples:")}
141
+ ${pc.cyan("enact")} ${pc.green("publish")} my-tool.yaml
142
+ ${pc.cyan("enact")} ${pc.green("publish")} ${pc.yellow("--url")} https://registry.example.com my-tool.yaml
143
+ ${pc.cyan("enact")} ${pc.green("publish")} ${pc.yellow("--token")} abc123 my-tool.yaml
144
+ `);
145
+ }
146
+
147
+ /**
148
+ * Show help for the search command
149
+ */
150
+ export function showSearchHelp(): void {
151
+ console.error(`
152
+ ${pc.bold("Usage:")} ${pc.cyan("enact")} ${pc.green("search")} [options] [query]
153
+
154
+ ${pc.bold("Search for tools in the Enact registry")}
155
+
156
+ ${pc.bold("Arguments:")}
157
+ ${pc.blue("query")} Search query (optional)
158
+
159
+ ${pc.bold("Options:")}
160
+ ${pc.yellow("--help, -h")} Show this help message
161
+ ${pc.yellow("--limit, -l")} Limit number of results (default: 20)
162
+ ${pc.yellow("--tags")} Filter by tags (comma-separated)
163
+ ${pc.yellow("--author, -a")} Filter by author
164
+ ${pc.yellow("--format, -f")} Output format (table, json, minimal)
165
+
166
+ ${pc.bold("Examples:")}
167
+ ${pc.cyan("enact")} ${pc.green("search")}
168
+ ${pc.cyan("enact")} ${pc.green("search")} "web scraper"
169
+ ${pc.cyan("enact")} ${pc.green("search")} ${pc.yellow("--tags")} web,api ${pc.yellow("--limit")} 10
170
+ ${pc.cyan("enact")} ${pc.green("search")} ${pc.yellow("--author")} johndoe ${pc.yellow("--format")} json
171
+ `);
172
+ }
173
+
174
+ /**
175
+ * Show help for the remote command
176
+ */
177
+ export function showRemoteHelp(): void {
178
+ console.error(`
179
+ ${pc.bold("Usage:")} ${pc.cyan("enact")} ${pc.green("remote")} ${pc.blue("<subcommand>")} [options]
180
+
181
+ ${pc.bold("Manage remote Enact registry servers")}
182
+
183
+ ${pc.bold("Subcommands:")}
184
+ ${pc.blue("add")} Add a new remote server
185
+ ${pc.blue("list")} List all configured remote servers
186
+ ${pc.blue("remove")} Remove a remote server
187
+
188
+ ${pc.bold("Options:")}
189
+ ${pc.yellow("--help, -h")} Show this help message
190
+
191
+ ${pc.bold("Examples:")}
192
+ ${pc.cyan("enact")} ${pc.green("remote")} ${pc.blue("add")}
193
+ ${pc.cyan("enact")} ${pc.green("remote")} ${pc.blue("list")}
194
+ ${pc.cyan("enact")} ${pc.green("remote")} ${pc.blue("remove")} origin
195
+ `);
196
+ }
197
+
198
+ /**
199
+ * Show help for the user command
200
+ */
201
+ export function showUserHelp(): void {
202
+ console.error(`
203
+ ${pc.bold("Usage:")} ${pc.cyan("enact")} ${pc.green("user")} ${pc.blue("<subcommand>")} [options]
204
+
205
+ ${pc.bold("User operations for Enact registry")}
206
+
207
+ ${pc.bold("Subcommands:")}
208
+ ${pc.blue("public-key")} Get user's public key
209
+
210
+ ${pc.bold("Options:")}
211
+ ${pc.yellow("--help, -h")} Show this help message
212
+ ${pc.yellow("--server, -s")} Specify server URL
213
+ ${pc.yellow("--token, -t")} Specify authentication token
214
+ ${pc.yellow("--format, -f")} Output format (default, json)
215
+
216
+ ${pc.bold("Examples:")}
217
+ ${pc.cyan("enact")} ${pc.green("user")} ${pc.blue("public-key")}
218
+ ${pc.cyan("enact")} ${pc.green("user")} ${pc.blue("public-key")} ${pc.yellow("--format")} json
219
+ ${pc.cyan("enact")} ${pc.green("user")} ${pc.blue("public-key")} ${pc.yellow("--server")} https://api.example.com
220
+ `);
221
+ }
222
+
223
+ /**
224
+ * Show help for the env command
225
+ */
226
+ export function showEnvHelp(): void {
227
+ console.error(`
228
+ ${pc.bold("Usage:")} ${pc.cyan("enact")} ${pc.green("env")} ${pc.blue("<subcommand>")} [options]
229
+
230
+ ${pc.bold("Environment variable management for Enact CLI")}
231
+
232
+ ${pc.bold("Subcommands:")}
233
+ ${pc.blue("set")} ${pc.magenta("<name>")} [value] Set an environment variable
234
+ ${pc.blue("get")} ${pc.magenta("<name>")} Get an environment variable
235
+ ${pc.blue("list")} List all environment variables
236
+ ${pc.blue("delete")} ${pc.magenta("<name>")} Delete an environment variable
237
+ ${pc.blue("copy")} ${pc.magenta("<from>")} ${pc.magenta("<to>")} Copy variables between scopes
238
+ ${pc.blue("export")} [format] Export variables (env|json|yaml)
239
+ ${pc.blue("clear")} Clear all environment variables
240
+
241
+ ${pc.bold("Options:")}
242
+ ${pc.yellow("--help, -h")} Show this help message
243
+ ${pc.yellow("--global")} Use global scope (default)
244
+ ${pc.yellow("--project")} Use project scope
245
+ ${pc.yellow("--encrypt")} Encrypt sensitive values
246
+ ${pc.yellow("--format")} Output format (table|json)
247
+ ${pc.yellow("--show")} Show actual values (default: hidden)
248
+
249
+ ${pc.bold("Examples:")}
250
+ ${pc.cyan("enact")} ${pc.green("env")} ${pc.blue("set")} OPENAI_API_KEY ${pc.yellow("--encrypt")}
251
+ ${pc.cyan("enact")} ${pc.green("env")} ${pc.blue("set")} DATABASE_URL postgres://... ${pc.yellow("--project")}
252
+ ${pc.cyan("enact")} ${pc.green("env")} ${pc.blue("get")} OPENAI_API_KEY ${pc.yellow("--show")}
253
+ ${pc.cyan("enact")} ${pc.green("env")} ${pc.blue("list")} ${pc.yellow("--project")} ${pc.yellow("--format")} json
254
+ ${pc.cyan("enact")} ${pc.green("env")} ${pc.blue("copy")} global project
255
+ ${pc.cyan("enact")} ${pc.green("env")} ${pc.blue("export")} env > .env
256
+ `);
257
+ }
@@ -0,0 +1,7 @@
1
+ export * from './config';
2
+ export * from './env-loader';
3
+ export { showHelp } from './help';
4
+ export * from './logger';
5
+ export * from './silent-monitor';
6
+ export * from './timeout';
7
+ export { showVersion } from './version';
@@ -0,0 +1,83 @@
1
+ // src/utils/logger.ts
2
+ import pc from "picocolors";
3
+
4
+ export enum LogLevel {
5
+ DEBUG = 0,
6
+ INFO = 1,
7
+ SUCCESS = 2,
8
+ WARN = 3,
9
+ ERROR = 4,
10
+ }
11
+
12
+ // Default log level
13
+ let currentLogLevel = LogLevel.INFO;
14
+
15
+ /**
16
+ * Sets the current log level
17
+ */
18
+ export function setLogLevel(level: LogLevel): void {
19
+ currentLogLevel = level;
20
+ }
21
+
22
+ /**
23
+ * Debug log - only shown when log level is DEBUG
24
+ */
25
+ export function debug(message: string): void {
26
+ if (currentLogLevel <= LogLevel.DEBUG) {
27
+ console.error(pc.dim(`🔍 ${message}`));
28
+ }
29
+ }
30
+
31
+ /**
32
+ * Info log - general information
33
+ */
34
+ export function info(message: string): void {
35
+ if (currentLogLevel <= LogLevel.INFO) {
36
+ console.error(pc.blue(`ℹ️ ${message}`));
37
+ }
38
+ }
39
+
40
+ /**
41
+ * Success log - operation completed successfully
42
+ */
43
+ export function success(message: string): void {
44
+ if (currentLogLevel <= LogLevel.SUCCESS) {
45
+ console.error(pc.green(`✓ ${message}`));
46
+ }
47
+ }
48
+
49
+ /**
50
+ * Warning log - non-critical issues
51
+ */
52
+ export function warn(message: string): void {
53
+ if (currentLogLevel <= LogLevel.WARN) {
54
+ console.error(pc.yellow(`⚠️ ${message}`));
55
+ }
56
+ }
57
+
58
+ /**
59
+ * Error log - critical issues
60
+ */
61
+ export function error(message: string, details?: any): void {
62
+ if (currentLogLevel <= LogLevel.ERROR) {
63
+ console.error(pc.red(`✗ Error: ${message}`));
64
+
65
+ if (details && currentLogLevel === LogLevel.DEBUG) {
66
+ console.error(pc.dim("Details:"));
67
+ console.error(details);
68
+ }
69
+ }
70
+ }
71
+
72
+ /**
73
+ * Table log - display tabular data
74
+ */
75
+ export function table(data: any[], columns?: string[]): void {
76
+ if (currentLogLevel <= LogLevel.INFO) {
77
+ if (columns) {
78
+ console.table(data, columns);
79
+ } else {
80
+ console.table(data);
81
+ }
82
+ }
83
+ }
@@ -0,0 +1,328 @@
1
+ /**
2
+ * Runtime silent operation monitor for MCP server
3
+ * This utility can be used to ensure the MCP server remains silent during operation
4
+ */
5
+
6
+ interface SilentOperationMonitor {
7
+ startMonitoring(): void;
8
+ stopMonitoring(): SilentOperationReport;
9
+ isCurrentlyMonitoring(): boolean;
10
+ getViolations(): string[];
11
+ }
12
+
13
+ interface SilentOperationReport {
14
+ violations: string[];
15
+ consoleOutputDetected: string[];
16
+ processExitAttempts: number;
17
+ readlineUsageDetected: boolean;
18
+ duration: number;
19
+ timestamp: string;
20
+ }
21
+
22
+ class McpSilentOperationMonitor implements SilentOperationMonitor {
23
+ private isMonitoring = false;
24
+ private violations: string[] = [];
25
+ private consoleOutput: string[] = [];
26
+ private processExitAttempts = 0;
27
+ private readlineUsageDetected = false;
28
+ private startTime = 0;
29
+
30
+ private originalConsoleLog: typeof console.log;
31
+ private originalConsoleError: typeof console.error;
32
+ private originalConsoleWarn: typeof console.warn;
33
+ private originalConsoleInfo: typeof console.info;
34
+ private originalProcessExit: typeof process.exit;
35
+ private originalStdoutWrite: typeof process.stdout.write;
36
+ private originalStderrWrite: typeof process.stderr.write;
37
+
38
+ // Public getters for original methods
39
+ public getOriginalConsoleError(): typeof console.error {
40
+ return this.originalConsoleError;
41
+ }
42
+
43
+ constructor() {
44
+ // Store original methods
45
+ this.originalConsoleLog = console.log;
46
+ this.originalConsoleError = console.error;
47
+ this.originalConsoleWarn = console.warn;
48
+ this.originalConsoleInfo = console.info;
49
+ this.originalProcessExit = process.exit;
50
+ this.originalStdoutWrite = process.stdout.write;
51
+ this.originalStderrWrite = process.stderr.write;
52
+ }
53
+
54
+ startMonitoring(): void {
55
+ if (this.isMonitoring) {
56
+ return;
57
+ }
58
+
59
+ this.isMonitoring = true;
60
+ this.violations = [];
61
+ this.consoleOutput = [];
62
+ this.processExitAttempts = 0;
63
+ this.readlineUsageDetected = false;
64
+ this.startTime = Date.now();
65
+
66
+ // Hook console methods
67
+ console.log = (...args: any[]) => {
68
+ const message = args.join(" ");
69
+ this.consoleOutput.push(`[LOG] ${message}`);
70
+ this.violations.push(`Console.log called: ${message}`);
71
+ };
72
+
73
+ console.error = (...args: any[]) => {
74
+ const message = args.join(" ");
75
+ this.consoleOutput.push(`[ERROR] ${message}`);
76
+ this.violations.push(`Console.error called: ${message}`);
77
+ };
78
+
79
+ console.warn = (...args: any[]) => {
80
+ const message = args.join(" ");
81
+ this.consoleOutput.push(`[WARN] ${message}`);
82
+ this.violations.push(`Console.warn called: ${message}`);
83
+ };
84
+
85
+ console.info = (...args: any[]) => {
86
+ const message = args.join(" ");
87
+ this.consoleOutput.push(`[INFO] ${message}`);
88
+ this.violations.push(`Console.info called: ${message}`);
89
+ };
90
+
91
+ // Hook process.exit
92
+ process.exit = ((code?: number) => {
93
+ this.processExitAttempts++;
94
+ this.violations.push(`Process.exit called with code: ${code}`);
95
+ throw new Error(`Process exit intercepted: ${code}`);
96
+ }) as any;
97
+
98
+ // Hook stdout/stderr
99
+ process.stdout.write = ((chunk: any, ...args: any[]) => {
100
+ if (typeof chunk === "string" && chunk.trim()) {
101
+ this.consoleOutput.push(`[STDOUT] ${chunk}`);
102
+ this.violations.push(
103
+ `Process.stdout.write called: ${chunk.substring(0, 100)}...`,
104
+ );
105
+ }
106
+ return true;
107
+ }) as any;
108
+
109
+ process.stderr.write = ((chunk: any, ...args: any[]) => {
110
+ if (typeof chunk === "string" && chunk.trim()) {
111
+ this.consoleOutput.push(`[STDERR] ${chunk}`);
112
+ this.violations.push(
113
+ `Process.stderr.write called: ${chunk.substring(0, 100)}...`,
114
+ );
115
+ }
116
+ return true;
117
+ }) as any;
118
+
119
+ // Hook readline (if it gets required)
120
+ const originalRequire = require;
121
+ const monitor = this;
122
+ (global as any).require = function (id: string) {
123
+ if (id === "readline" || id.includes("readline")) {
124
+ monitor.readlineUsageDetected = true;
125
+ monitor.violations.push("Readline module usage detected");
126
+ }
127
+ return originalRequire.apply(this, arguments as any);
128
+ };
129
+ }
130
+
131
+ stopMonitoring(): SilentOperationReport {
132
+ if (!this.isMonitoring) {
133
+ throw new Error("Monitor is not currently running");
134
+ }
135
+
136
+ // Restore original methods
137
+ console.log = this.originalConsoleLog;
138
+ console.error = this.originalConsoleError;
139
+ console.warn = this.originalConsoleWarn;
140
+ console.info = this.originalConsoleInfo;
141
+ process.exit = this.originalProcessExit;
142
+ process.stdout.write = this.originalStdoutWrite;
143
+ process.stderr.write = this.originalStderrWrite;
144
+
145
+ // Restore require
146
+ (global as any).require = require;
147
+
148
+ this.isMonitoring = false;
149
+
150
+ return {
151
+ violations: [...this.violations],
152
+ consoleOutputDetected: [...this.consoleOutput],
153
+ processExitAttempts: this.processExitAttempts,
154
+ readlineUsageDetected: this.readlineUsageDetected,
155
+ duration: Date.now() - this.startTime,
156
+ timestamp: new Date().toISOString(),
157
+ };
158
+ }
159
+
160
+ isCurrentlyMonitoring(): boolean {
161
+ return this.isMonitoring;
162
+ }
163
+
164
+ getViolations(): string[] {
165
+ return [...this.violations];
166
+ }
167
+ }
168
+
169
+ /**
170
+ * Global monitor instance
171
+ */
172
+ const globalMonitor = new McpSilentOperationMonitor();
173
+
174
+ /**
175
+ * Decorator to ensure a function operates silently
176
+ */
177
+ export function ensureSilent<T extends (...args: any[]) => any>(fn: T): T {
178
+ return ((...args: any[]) => {
179
+ const wasMonitoring = globalMonitor.isCurrentlyMonitoring();
180
+
181
+ if (!wasMonitoring) {
182
+ globalMonitor.startMonitoring();
183
+ }
184
+
185
+ try {
186
+ const result = fn(...args);
187
+
188
+ // Handle both sync and async functions
189
+ if (result && typeof result.then === "function") {
190
+ return result.finally(() => {
191
+ if (!wasMonitoring) {
192
+ const report = globalMonitor.stopMonitoring();
193
+ if (report.violations.length > 0) {
194
+ throw new Error(
195
+ `Silent operation violated: ${report.violations.join(", ")}`,
196
+ );
197
+ }
198
+ }
199
+ });
200
+ } else {
201
+ if (!wasMonitoring) {
202
+ const report = globalMonitor.stopMonitoring();
203
+ if (report.violations.length > 0) {
204
+ throw new Error(
205
+ `Silent operation violated: ${report.violations.join(", ")}`,
206
+ );
207
+ }
208
+ }
209
+ return result;
210
+ }
211
+ } catch (error) {
212
+ if (!wasMonitoring && globalMonitor.isCurrentlyMonitoring()) {
213
+ globalMonitor.stopMonitoring();
214
+ }
215
+ throw error;
216
+ }
217
+ }) as T;
218
+ }
219
+
220
+ /**
221
+ * Higher-order function to wrap MCP tool handlers for silent operation
222
+ */
223
+ export function silentMcpTool<T extends (...args: any[]) => Promise<any>>(
224
+ handler: T,
225
+ ): T {
226
+ return (async (...args: any[]) => {
227
+ const monitor = new McpSilentOperationMonitor();
228
+ monitor.startMonitoring();
229
+
230
+ try {
231
+ const result = await handler(...args);
232
+ const report = monitor.stopMonitoring();
233
+
234
+ if (report.violations.length > 0) {
235
+ // Log violations using the original console methods for debugging
236
+ globalMonitor.getOriginalConsoleError()(
237
+ "🚨 MCP Tool Handler Violations:",
238
+ report.violations,
239
+ );
240
+
241
+ // In production, you might want to return an error response instead of throwing
242
+ if (process.env.NODE_ENV === "production") {
243
+ return {
244
+ content: [
245
+ {
246
+ type: "text",
247
+ text: "Internal error: Silent operation requirements violated",
248
+ },
249
+ ],
250
+ isError: true,
251
+ };
252
+ }
253
+ }
254
+
255
+ return result;
256
+ } catch (error) {
257
+ const report = monitor.stopMonitoring();
258
+
259
+ if (report.violations.length > 0) {
260
+ globalMonitor.getOriginalConsoleError()(
261
+ "🚨 MCP Tool Handler Violations during error:",
262
+ report.violations,
263
+ );
264
+ }
265
+
266
+ throw error;
267
+ }
268
+ }) as T;
269
+ }
270
+
271
+ /**
272
+ * Utility to check if environment is properly configured for silent operation
273
+ */
274
+ export function validateSilentEnvironment(): {
275
+ valid: boolean;
276
+ issues: string[];
277
+ } {
278
+ const issues: string[] = [];
279
+
280
+ // Check environment variables
281
+ if (process.env.CI !== "true") {
282
+ issues.push('CI environment variable not set to "true"');
283
+ }
284
+
285
+ if (process.env.ENACT_SKIP_INTERACTIVE !== "true") {
286
+ issues.push('ENACT_SKIP_INTERACTIVE not set to "true"');
287
+ }
288
+
289
+ if (process.env.DEBUG === "true" || process.env.VERBOSE === "true") {
290
+ issues.push("DEBUG or VERBOSE environment variables are enabled");
291
+ }
292
+
293
+ // Check if we're in a TTY (which might indicate interactive mode)
294
+ if (process.stdin.isTTY) {
295
+ issues.push("Process is running in TTY mode (potentially interactive)");
296
+ }
297
+
298
+ return {
299
+ valid: issues.length === 0,
300
+ issues,
301
+ };
302
+ }
303
+
304
+ /**
305
+ * Logger specifically for MCP silent operation monitoring
306
+ */
307
+ export const silentLogger = {
308
+ logViolation: (violation: string) => {
309
+ // Use stderr directly to bypass any console mocking
310
+ process.stderr.write(
311
+ `[SILENT-VIOLATION] ${new Date().toISOString()}: ${violation}\n`,
312
+ );
313
+ },
314
+
315
+ logReport: (report: SilentOperationReport) => {
316
+ if (report.violations.length > 0) {
317
+ process.stderr.write(
318
+ `[SILENT-REPORT] ${report.timestamp}: ${report.violations.length} violations in ${report.duration}ms\n`,
319
+ );
320
+ report.violations.forEach((violation) => {
321
+ process.stderr.write(` - ${violation}\n`);
322
+ });
323
+ }
324
+ },
325
+ };
326
+
327
+ export { globalMonitor, McpSilentOperationMonitor };
328
+ export type { SilentOperationMonitor, SilentOperationReport };
@@ -0,0 +1,26 @@
1
+ // src/utils/timeout.ts - Shared timeout parsing utility
2
+
3
+ /**
4
+ * Parse timeout string to milliseconds
5
+ * Supports formats like "30s", "5m", "2h"
6
+ */
7
+ export function parseTimeout(timeout: string): number {
8
+ const match = timeout.match(/^(\d+)([smh])$/);
9
+ if (!match) {
10
+ return 30000; // Default 30 seconds
11
+ }
12
+
13
+ const value = parseInt(match[1]);
14
+ const unit = match[2];
15
+
16
+ switch (unit) {
17
+ case "s":
18
+ return value * 1000;
19
+ case "m":
20
+ return value * 60 * 1000;
21
+ case "h":
22
+ return value * 60 * 60 * 1000;
23
+ default:
24
+ return 30000; // Default fallback
25
+ }
26
+ }