@ruifung/codemode-bridge 1.0.3-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.
Files changed (39) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +378 -0
  3. package/dist/cli/commands.d.ts +70 -0
  4. package/dist/cli/commands.js +436 -0
  5. package/dist/cli/config-manager.d.ts +53 -0
  6. package/dist/cli/config-manager.js +142 -0
  7. package/dist/cli/index.d.ts +19 -0
  8. package/dist/cli/index.js +165 -0
  9. package/dist/executor/container-executor.d.ts +81 -0
  10. package/dist/executor/container-executor.js +351 -0
  11. package/dist/executor/executor-test-suite.d.ts +22 -0
  12. package/dist/executor/executor-test-suite.js +395 -0
  13. package/dist/executor/isolated-vm-executor.d.ts +78 -0
  14. package/dist/executor/isolated-vm-executor.js +368 -0
  15. package/dist/executor/vm2-executor.d.ts +21 -0
  16. package/dist/executor/vm2-executor.js +109 -0
  17. package/dist/executor/wrap-code.d.ts +52 -0
  18. package/dist/executor/wrap-code.js +80 -0
  19. package/dist/index.d.ts +6 -0
  20. package/dist/index.js +6 -0
  21. package/dist/mcp/config.d.ts +44 -0
  22. package/dist/mcp/config.js +102 -0
  23. package/dist/mcp/e2e-bridge-test-suite.d.ts +28 -0
  24. package/dist/mcp/e2e-bridge-test-suite.js +429 -0
  25. package/dist/mcp/executor.d.ts +31 -0
  26. package/dist/mcp/executor.js +121 -0
  27. package/dist/mcp/mcp-adapter.d.ts +12 -0
  28. package/dist/mcp/mcp-adapter.js +49 -0
  29. package/dist/mcp/mcp-client.d.ts +85 -0
  30. package/dist/mcp/mcp-client.js +441 -0
  31. package/dist/mcp/oauth-handler.d.ts +33 -0
  32. package/dist/mcp/oauth-handler.js +138 -0
  33. package/dist/mcp/server.d.ts +25 -0
  34. package/dist/mcp/server.js +322 -0
  35. package/dist/mcp/token-persistence.d.ts +57 -0
  36. package/dist/mcp/token-persistence.js +131 -0
  37. package/dist/utils/logger.d.ts +44 -0
  38. package/dist/utils/logger.js +123 -0
  39. package/package.json +56 -0
@@ -0,0 +1,121 @@
1
+ /**
2
+ * Executor factory and registry for Codemode SDK
3
+ * Selects the best available executor (isolated-vm → container → vm2)
4
+ */
5
+ import { execFileSync } from "node:child_process";
6
+ import { logInfo } from "../utils/logger.js";
7
+ // ── Availability checks (cached) ────────────────────────────────────
8
+ let _isolatedVmAvailable = null;
9
+ async function isIsolatedVmAvailable() {
10
+ if (_isolatedVmAvailable !== null)
11
+ return _isolatedVmAvailable;
12
+ try {
13
+ // @ts-ignore - isolated-vm is an optional dependency
14
+ await import('isolated-vm');
15
+ _isolatedVmAvailable = true;
16
+ }
17
+ catch {
18
+ _isolatedVmAvailable = false;
19
+ }
20
+ return _isolatedVmAvailable;
21
+ }
22
+ let _containerRuntimeAvailable = null;
23
+ function isContainerRuntimeAvailable() {
24
+ if (_containerRuntimeAvailable !== null)
25
+ return _containerRuntimeAvailable;
26
+ for (const cmd of ['docker', 'podman']) {
27
+ try {
28
+ execFileSync(cmd, ['--version'], { stdio: 'ignore', timeout: 5000 });
29
+ _containerRuntimeAvailable = true;
30
+ return true;
31
+ }
32
+ catch {
33
+ // not available
34
+ }
35
+ }
36
+ _containerRuntimeAvailable = false;
37
+ return false;
38
+ }
39
+ // ── Executor registry (sorted by preference, lowest first) ─────────
40
+ const executorRegistry = [
41
+ {
42
+ type: 'isolated-vm',
43
+ preference: 0,
44
+ isAvailable: isIsolatedVmAvailable,
45
+ async create(timeout) {
46
+ const { createIsolatedVmExecutor } = await import('../executor/isolated-vm-executor.js');
47
+ return createIsolatedVmExecutor({ timeout });
48
+ },
49
+ },
50
+ {
51
+ type: 'container',
52
+ preference: 1,
53
+ isAvailable: async () => isContainerRuntimeAvailable(),
54
+ async create(timeout) {
55
+ const { createContainerExecutor } = await import('../executor/container-executor.js');
56
+ return createContainerExecutor({ timeout });
57
+ },
58
+ },
59
+ {
60
+ type: 'vm2',
61
+ preference: 2,
62
+ isAvailable: async () => {
63
+ try {
64
+ await import('vm2');
65
+ return true;
66
+ }
67
+ catch {
68
+ return false;
69
+ }
70
+ },
71
+ async create(timeout) {
72
+ const { createVM2Executor } = await import('../executor/vm2-executor.js');
73
+ return createVM2Executor({ timeout });
74
+ },
75
+ },
76
+ ];
77
+ // ── Factory ─────────────────────────────────────────────────────────
78
+ /**
79
+ * Factory function to create an Executor instance.
80
+ *
81
+ * Selection logic:
82
+ * - If EXECUTOR_TYPE is set, that executor is used (throws if unavailable).
83
+ * - Otherwise, executors are tried in preference order (isolated-vm →
84
+ * container → vm2) and the first available one is selected.
85
+ *
86
+ * Returns both the executor and metadata about the selection.
87
+ */
88
+ export async function createExecutor(timeout = 30000) {
89
+ const requested = process.env.EXECUTOR_TYPE?.toLowerCase();
90
+ // Explicit selection — must succeed or throw
91
+ if (requested) {
92
+ const entry = executorRegistry.find(e => e.type === requested);
93
+ if (!entry) {
94
+ const known = executorRegistry.map(e => e.type).join(', ');
95
+ throw new Error(`Unknown EXECUTOR_TYPE="${requested}". Valid types: ${known}`);
96
+ }
97
+ const available = await entry.isAvailable();
98
+ if (!available) {
99
+ throw new Error(`EXECUTOR_TYPE=${requested} but it is not available in this environment.`);
100
+ }
101
+ logInfo(`Using ${entry.type} executor (EXECUTOR_TYPE=${requested})`, { component: 'Executor' });
102
+ return {
103
+ executor: await entry.create(timeout),
104
+ info: { type: entry.type, reason: 'explicit', timeout },
105
+ };
106
+ }
107
+ // Auto-detect — walk registry in preference order
108
+ const sorted = [...executorRegistry].sort((a, b) => a.preference - b.preference);
109
+ for (const entry of sorted) {
110
+ const available = await entry.isAvailable();
111
+ if (available) {
112
+ logInfo(`Using ${entry.type} executor (auto-detected)`, { component: 'Executor' });
113
+ return {
114
+ executor: await entry.create(timeout),
115
+ info: { type: entry.type, reason: 'auto-detected', timeout },
116
+ };
117
+ }
118
+ }
119
+ // Should never happen since vm2 is always available
120
+ throw new Error('No executor implementation available.');
121
+ }
@@ -0,0 +1,12 @@
1
+ /**
2
+ * MCP Adapter - Shim layer between AI SDK Tools and MCP Protocol
3
+ *
4
+ * Converts AI SDK Tool format (from @cloudflare/codemode) to MCP protocol format
5
+ */
6
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
7
+ import type { Tool } from "ai";
8
+ /**
9
+ * Adapts an AI SDK Tool to MCP protocol format
10
+ * Takes a codemode SDK tool and registers it with the MCP server
11
+ */
12
+ export declare function adaptAISDKToolToMCP(mcp: McpServer, tool: Tool<any, any>): Promise<void>;
@@ -0,0 +1,49 @@
1
+ /**
2
+ * MCP Adapter - Shim layer between AI SDK Tools and MCP Protocol
3
+ *
4
+ * Converts AI SDK Tool format (from @cloudflare/codemode) to MCP protocol format
5
+ */
6
+ import { z } from "zod";
7
+ /**
8
+ * Adapts an AI SDK Tool to MCP protocol format
9
+ * Takes a codemode SDK tool and registers it with the MCP server
10
+ */
11
+ export async function adaptAISDKToolToMCP(mcp, tool) {
12
+ // The tool from createCodeTool has:
13
+ // - tool.description
14
+ // - tool.inputSchema (Zod schema)
15
+ // - tool.execute (async function)
16
+ // Extract tool name from description or use default
17
+ const toolName = "eval";
18
+ // Register the tool with the MCP server
19
+ mcp.registerTool(toolName, {
20
+ description: tool.description || "Execute code with tool orchestration",
21
+ // Use the Zod schema directly - MCP's registerTool handles conversion
22
+ // Cast to any to avoid schema compatibility issues between AI SDK and MCP
23
+ inputSchema: tool.inputSchema || z.object({}).strict(),
24
+ },
25
+ // Handler for MCP tool calls
26
+ async (args) => {
27
+ try {
28
+ // Call the AI SDK tool's execute method
29
+ // The execute function expects the input args
30
+ const result = await tool.execute(args);
31
+ // If result is a string (from fallback tool), use as-is
32
+ // Otherwise format to JSON
33
+ const text = typeof result === 'string' ? result : JSON.stringify(result, null, 2);
34
+ return {
35
+ content: [{ type: "text", text }],
36
+ };
37
+ }
38
+ catch (error) {
39
+ return {
40
+ content: [
41
+ {
42
+ type: "text",
43
+ text: `Tool execution failed: ${error instanceof Error ? error.message : String(error)}`,
44
+ },
45
+ ],
46
+ };
47
+ }
48
+ });
49
+ }
@@ -0,0 +1,85 @@
1
+ /**
2
+ * MCP Client - Wrapper around official MCP SDK for connecting to upstream MCP servers
3
+ *
4
+ * This client:
5
+ * 1. Uses the official @modelcontextprotocol/sdk Client API
6
+ * 2. Supports stdio, HTTP (Streamable HTTP), and SSE transports
7
+ * 3. Returns tools in native MCP format (JSON Schema for inputSchema)
8
+ * 4. Provides tool execution via callTool()
9
+ */
10
+ /**
11
+ * OAuth2 configuration for HTTP/SSE transports
12
+ */
13
+ export interface OAuth2Config {
14
+ /**
15
+ * OAuth client ID (optional for dynamic registration).
16
+ * If not provided, the client will attempt dynamic registration with the server.
17
+ */
18
+ clientId?: string;
19
+ /** OAuth client secret (optional for public clients) */
20
+ clientSecret?: string;
21
+ /**
22
+ * OAuth redirect URL for authorization flow.
23
+ * Defaults to "http://localhost:3000/oauth/callback" if not provided.
24
+ */
25
+ redirectUrl?: string;
26
+ /** OAuth scopes to request */
27
+ scope?: string;
28
+ /** Grant type: 'authorization_code' or 'client_credentials' */
29
+ grantType?: 'authorization_code' | 'client_credentials';
30
+ }
31
+ /**
32
+ * Configuration for an MCP server to connect to upstream
33
+ */
34
+ export interface MCPServerConfig {
35
+ name: string;
36
+ type: "stdio" | "http" | "sse";
37
+ command?: string;
38
+ args?: string[];
39
+ url?: string;
40
+ env?: Record<string, string>;
41
+ /** OAuth2 configuration (only for http/sse transports) */
42
+ oauth?: OAuth2Config;
43
+ }
44
+ /**
45
+ * MCP Tool definition in native format
46
+ */
47
+ export interface MCPTool {
48
+ name: string;
49
+ description?: string;
50
+ inputSchema: any;
51
+ }
52
+ /**
53
+ * MCP Client wrapper using official SDK
54
+ */
55
+ export declare class MCPClient {
56
+ private config;
57
+ private client;
58
+ private transport;
59
+ private connected;
60
+ private oauthProvider?;
61
+ constructor(config: MCPServerConfig);
62
+ /**
63
+ * Authenticate with OAuth server and obtain tokens without connecting the full client.
64
+ * This is useful for CLI auth flows where we just want to get tokens without initializing
65
+ * the full MCP client.
66
+ */
67
+ authenticateOAuth(): Promise<void>;
68
+ /**
69
+ * Connect to the upstream MCP server
70
+ */
71
+ connect(): Promise<void>;
72
+ /**
73
+ * List all tools from the upstream server
74
+ * Returns tools in native MCP format with JSON Schema
75
+ */
76
+ listTools(): Promise<MCPTool[]>;
77
+ /**
78
+ * Call a tool on the upstream server
79
+ */
80
+ callTool(name: string, args: any): Promise<any>;
81
+ /**
82
+ * Close the connection to the upstream server
83
+ */
84
+ close(): Promise<void>;
85
+ }