@octavus/computer 2.17.0 → 2.18.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.
package/dist/index.d.ts CHANGED
@@ -37,6 +37,8 @@ interface ChromeLaunchOptions {
37
37
  profileDir: string;
38
38
  debuggingPort?: number;
39
39
  flags?: string[];
40
+ /** Max time (ms) to wait for the Chrome debugging port to become ready. Default: 30000. */
41
+ debuggingPortTimeoutMs?: number;
40
42
  }
41
43
 
42
44
  interface ManagedProcess {
package/dist/index.js CHANGED
@@ -206,7 +206,7 @@ function createShellTools(namespace, config) {
206
206
  }
207
207
 
208
208
  // src/chrome.ts
209
- import { spawn as spawn2 } from "child_process";
209
+ import { spawn as spawn2, spawnSync } from "child_process";
210
210
  import { createServer } from "net";
211
211
  import { platform as platform2 } from "os";
212
212
  import { existsSync } from "fs";
@@ -229,7 +229,7 @@ function findChromePath() {
229
229
  const isAbsolute = candidate.startsWith("/") || candidate.startsWith("C:\\");
230
230
  if (isAbsolute) {
231
231
  if (existsSync(candidate)) return candidate;
232
- } else {
232
+ } else if (isInPath(candidate)) {
233
233
  return candidate;
234
234
  }
235
235
  }
@@ -237,6 +237,14 @@ function findChromePath() {
237
237
  `Chrome not found. Searched: ${candidates.join(", ")}. Install Google Chrome or set the path explicitly.`
238
238
  );
239
239
  }
240
+ function isInPath(binary) {
241
+ try {
242
+ const result = spawnSync("which", [binary], { stdio: "ignore" });
243
+ return result.status === 0;
244
+ } catch {
245
+ return false;
246
+ }
247
+ }
240
248
  function findFreePort() {
241
249
  return new Promise((resolve, reject) => {
242
250
  const server = createServer();
@@ -252,7 +260,7 @@ function findFreePort() {
252
260
  server.on("error", reject);
253
261
  });
254
262
  }
255
- function waitForDebuggingPort(port, timeoutMs = 1e4) {
263
+ function waitForDebuggingPort(port, timeoutMs = 3e4) {
256
264
  const start = Date.now();
257
265
  return new Promise((resolve, reject) => {
258
266
  function attempt() {
@@ -289,7 +297,7 @@ async function launchChrome(options) {
289
297
  }
290
298
  child.on("error", () => {
291
299
  });
292
- await waitForDebuggingPort(port);
300
+ await waitForDebuggingPort(port, options.debuggingPortTimeoutMs);
293
301
  return { port, process: child, pid: child.pid };
294
302
  }
295
303
 
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/entries.ts","../src/mcp-client.ts","../src/shell.ts","../src/chrome.ts","../src/computer.ts"],"sourcesContent":["/** Stdio transport — spawns an MCP server as a child process, communicates via stdin/stdout. */\nexport interface StdioConfig {\n type: 'stdio';\n command: string;\n args?: string[];\n env?: Record<string, string>;\n cwd?: string;\n}\n\n/** HTTP transport — connects to an MCP server over Streamable HTTP. */\nexport interface HttpConfig {\n type: 'http';\n url: string;\n headers?: Record<string, string>;\n}\n\nexport type ShellMode = 'unrestricted' | { allowedPatterns?: RegExp[]; blockedPatterns?: RegExp[] };\n\n/** Built-in shell — executes commands directly via child_process (no MCP subprocess). */\nexport interface ShellConfig {\n type: 'shell';\n cwd?: string;\n mode: ShellMode;\n timeout?: number;\n}\n\nexport type McpEntry = StdioConfig | HttpConfig | ShellConfig;\n\nexport const NAMESPACE_SEPARATOR = '__';\n\nexport function createStdioConfig(\n command: string,\n args?: string[],\n options?: { env?: Record<string, string>; cwd?: string },\n): StdioConfig {\n return { type: 'stdio', command, args, ...options };\n}\n\nexport function createHttpConfig(\n url: string,\n options?: { headers?: Record<string, string> },\n): HttpConfig {\n return { type: 'http', url, ...options };\n}\n\nexport function createShellConfig(options: Omit<ShellConfig, 'type'>): ShellConfig {\n return { type: 'shell', ...options };\n}\n","import { Client } from '@modelcontextprotocol/sdk/client/index.js';\nimport { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js';\nimport { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js';\nimport type { Transport } from '@modelcontextprotocol/sdk/shared/transport.js';\nimport type { ToolHandler, ToolSchema } from '@octavus/core';\nimport { NAMESPACE_SEPARATOR, type StdioConfig, type HttpConfig } from './entries';\n\nconst MCP_CLIENT_NAME = 'octavus-computer';\nconst MCP_CLIENT_VERSION = '1.0.0';\n\nexport interface McpConnection {\n client: Client;\n namespace: string;\n handlers: Record<string, ToolHandler>;\n schemas: ToolSchema[];\n close: () => Promise<void>;\n}\n\nfunction namespaceTool(namespace: string, toolName: string): string {\n return `${namespace}${NAMESPACE_SEPARATOR}${toolName}`;\n}\n\ninterface ContentPart {\n type: string;\n text?: string;\n [key: string]: unknown;\n}\n\ninterface CallToolResult {\n content: ContentPart[];\n isError?: boolean;\n [key: string]: unknown;\n}\n\nfunction formatContentPart(part: ContentPart): unknown {\n if (part.type === 'text' && typeof part.text === 'string') {\n return part.text;\n }\n // Preserve non-text content (images, audio, resources) as structured data\n // so downstream consumers can handle them appropriately.\n return part;\n}\n\nfunction formatCallToolResult(result: CallToolResult): unknown {\n if (result.isError) {\n const errorText = result.content\n .filter((c) => c.type === 'text' && typeof c.text === 'string')\n .map((c) => c.text!)\n .join('\\n');\n return { error: errorText || 'Tool execution failed' };\n }\n\n if (result.content.length === 0) {\n return { success: true };\n }\n\n const textParts = result.content.filter((c) => c.type === 'text' && typeof c.text === 'string');\n const hasNonText = result.content.some((c) => c.type !== 'text');\n\n // Pure text results: parse JSON or return as string\n if (!hasNonText) {\n if (textParts.length === 1) {\n try {\n return JSON.parse(textParts[0]!.text!);\n } catch {\n return textParts[0]!.text;\n }\n }\n return textParts.map((p) => p.text).join('\\n');\n }\n\n // Mixed or non-text content: return as structured content array\n return result.content.map(formatContentPart);\n}\n\nasync function discoverTools(\n client: Client,\n namespace: string,\n): Promise<Pick<McpConnection, 'handlers' | 'schemas'>> {\n const { tools } = await client.listTools();\n\n const handlers: Record<string, ToolHandler> = {};\n const schemas: ToolSchema[] = [];\n\n for (const tool of tools) {\n const nsName = namespaceTool(namespace, tool.name);\n const originalName = tool.name;\n\n schemas.push({\n name: nsName,\n description: tool.description ?? originalName,\n inputSchema: tool.inputSchema as Record<string, unknown>,\n });\n\n handlers[nsName] = async (args: Record<string, unknown>) => {\n const result = await client.callTool({ name: originalName, arguments: args });\n return formatCallToolResult(result as CallToolResult);\n };\n }\n\n return { handlers, schemas };\n}\n\nasync function connect(namespace: string, transport: Transport): Promise<McpConnection> {\n const client = new Client({ name: MCP_CLIENT_NAME, version: MCP_CLIENT_VERSION });\n await client.connect(transport);\n const { handlers, schemas } = await discoverTools(client, namespace);\n return { client, namespace, handlers, schemas, close: () => client.close() };\n}\n\nexport function connectStdio(namespace: string, config: StdioConfig): Promise<McpConnection> {\n const transport = new StdioClientTransport({\n command: config.command,\n args: config.args,\n env: config.env\n ? {\n ...Object.fromEntries(\n Object.entries(process.env).filter((e): e is [string, string] => e[1] !== undefined),\n ),\n ...config.env,\n }\n : undefined,\n cwd: config.cwd,\n stderr: 'pipe',\n });\n\n return connect(namespace, transport);\n}\n\nexport function connectHttp(namespace: string, config: HttpConfig): Promise<McpConnection> {\n const transport = new StreamableHTTPClientTransport(new URL(config.url), {\n requestInit: config.headers ? { headers: config.headers } : undefined,\n });\n\n return connect(namespace, transport);\n}\n","import { spawn } from 'node:child_process';\nimport { platform } from 'node:os';\nimport type { ToolHandler, ToolSchema } from '@octavus/core';\nimport { NAMESPACE_SEPARATOR, type ShellConfig, type ShellMode } from './entries';\n\nconst DEFAULT_TIMEOUT_MS = 300_000;\nconst MAX_OUTPUT_LENGTH = 100_000;\n\ninterface ShellToolState {\n handlers: Record<string, ToolHandler>;\n schemas: ToolSchema[];\n}\n\nfunction getLoginShell(): { shell: string; args: string[] } {\n const os = platform();\n if (os === 'win32') {\n return { shell: 'cmd.exe', args: ['/c'] };\n }\n const userShell = process.env.SHELL ?? '/bin/bash';\n return { shell: userShell, args: ['-l', '-c'] };\n}\n\nfunction isCommandAllowed(command: string, mode: ShellMode): boolean {\n if (mode === 'unrestricted') return true;\n\n if (mode.blockedPatterns) {\n for (const pattern of mode.blockedPatterns) {\n if (pattern.test(command)) return false;\n }\n }\n\n if (mode.allowedPatterns) {\n for (const pattern of mode.allowedPatterns) {\n if (pattern.test(command)) return true;\n }\n return false;\n }\n\n return true;\n}\n\nfunction truncateOutput(output: string): string {\n if (output.length <= MAX_OUTPUT_LENGTH) return output;\n const half = Math.floor(MAX_OUTPUT_LENGTH / 2);\n return (\n output.slice(0, half) +\n `\\n\\n... truncated ${output.length - MAX_OUTPUT_LENGTH} characters ...\\n\\n` +\n output.slice(-half)\n );\n}\n\nfunction executeCommand(\n command: string,\n cwd: string | undefined,\n timeout: number,\n): Promise<{ exitCode: number; stdout: string; stderr: string }> {\n return new Promise((resolve) => {\n const { shell, args } = getLoginShell();\n const child = spawn(shell, [...args, command], {\n cwd,\n env: process.env,\n stdio: ['ignore', 'pipe', 'pipe'],\n timeout,\n });\n\n let stdout = '';\n let stderr = '';\n\n child.stdout.on('data', (data: Buffer) => {\n stdout += data.toString();\n });\n\n child.stderr.on('data', (data: Buffer) => {\n stderr += data.toString();\n });\n\n child.on('error', (error) => {\n resolve({\n exitCode: 1,\n stdout: truncateOutput(stdout),\n stderr: truncateOutput(error.message),\n });\n });\n\n child.on('close', (code) => {\n resolve({\n exitCode: code ?? 1,\n stdout: truncateOutput(stdout),\n stderr: truncateOutput(stderr),\n });\n });\n });\n}\n\nexport function createShellTools(namespace: string, config: ShellConfig): ShellToolState {\n const timeout = config.timeout ?? DEFAULT_TIMEOUT_MS;\n\n const runCommandHandler: ToolHandler = async (args: Record<string, unknown>) => {\n const command = args.command as string | undefined;\n if (!command) {\n return { error: 'command is required' };\n }\n\n if (!isCommandAllowed(command, config.mode)) {\n return { error: `Command not allowed by safety policy: ${command}` };\n }\n\n const cwd = (args.cwd as string | undefined) ?? config.cwd;\n const commandTimeout = (args.timeout as number | undefined) ?? timeout;\n\n return await executeCommand(command, cwd, commandTimeout);\n };\n\n const nsName = `${namespace}${NAMESPACE_SEPARATOR}run_command`;\n\n return {\n handlers: { [nsName]: runCommandHandler },\n schemas: [\n {\n name: nsName,\n description:\n \"Run a shell command on the local machine. Commands execute in a login shell with the user's full environment.\",\n inputSchema: {\n type: 'object',\n properties: {\n command: { type: 'string', description: 'The shell command to execute' },\n cwd: {\n type: 'string',\n description: 'Working directory (optional, defaults to configured directory)',\n },\n timeout: {\n type: 'number',\n description: `Timeout in milliseconds (optional, default ${DEFAULT_TIMEOUT_MS})`,\n },\n },\n required: ['command'],\n },\n },\n ],\n };\n}\n","import { spawn, type ChildProcess } from 'node:child_process';\nimport { createServer } from 'node:net';\nimport { platform } from 'node:os';\nimport { existsSync } from 'node:fs';\n\nexport interface ChromeInstance {\n port: number;\n process: ChildProcess;\n pid: number;\n}\n\nexport interface ChromeLaunchOptions {\n profileDir: string;\n debuggingPort?: number;\n flags?: string[];\n}\n\nconst CHROME_PATHS: Record<string, string[]> = {\n darwin: [\n '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome',\n '/Applications/Google Chrome Canary.app/Contents/MacOS/Google Chrome Canary',\n '/Applications/Chromium.app/Contents/MacOS/Chromium',\n ],\n linux: ['google-chrome', 'google-chrome-stable', 'chromium-browser', 'chromium'],\n win32: [\n 'C:\\\\Program Files\\\\Google\\\\Chrome\\\\Application\\\\chrome.exe',\n 'C:\\\\Program Files (x86)\\\\Google\\\\Chrome\\\\Application\\\\chrome.exe',\n ],\n};\n\nfunction findChromePath(): string {\n const os = platform();\n const candidates = CHROME_PATHS[os] ?? CHROME_PATHS.linux!;\n\n for (const candidate of candidates) {\n const isAbsolute = candidate.startsWith('/') || candidate.startsWith('C:\\\\');\n if (isAbsolute) {\n if (existsSync(candidate)) return candidate;\n } else {\n return candidate;\n }\n }\n\n throw new Error(\n `Chrome not found. Searched: ${candidates.join(', ')}. ` +\n 'Install Google Chrome or set the path explicitly.',\n );\n}\n\nfunction findFreePort(): Promise<number> {\n return new Promise((resolve, reject) => {\n const server = createServer();\n server.listen(0, '127.0.0.1', () => {\n const address = server.address();\n if (address !== null && typeof address === 'object') {\n const port = address.port;\n server.close(() => resolve(port));\n } else {\n server.close(() => reject(new Error('Failed to allocate port')));\n }\n });\n server.on('error', reject);\n });\n}\n\nfunction waitForDebuggingPort(port: number, timeoutMs = 10_000): Promise<void> {\n const start = Date.now();\n return new Promise((resolve, reject) => {\n function attempt() {\n if (Date.now() - start > timeoutMs) {\n reject(new Error(`Chrome debugging port ${port} not ready after ${timeoutMs}ms`));\n return;\n }\n\n fetch(`http://127.0.0.1:${port}/json/version`)\n .then((res) => {\n if (res.ok) resolve();\n else setTimeout(attempt, 200);\n })\n .catch(() => {\n setTimeout(attempt, 200);\n });\n }\n attempt();\n });\n}\n\nexport async function launchChrome(options: ChromeLaunchOptions): Promise<ChromeInstance> {\n const chromePath = findChromePath();\n const port = options.debuggingPort ?? (await findFreePort());\n\n const args = [\n `--remote-debugging-port=${port}`,\n `--user-data-dir=${options.profileDir}`,\n '--no-first-run',\n '--no-default-browser-check',\n ...(options.flags ?? []),\n ];\n\n const child = spawn(chromePath, args, {\n stdio: 'ignore',\n detached: false,\n });\n\n if (child.pid === undefined) {\n throw new Error(`Failed to launch Chrome at ${chromePath}`);\n }\n\n child.on('error', () => {\n // Chrome process errors are handled by the caller via the process reference\n });\n\n await waitForDebuggingPort(port);\n\n return { port, process: child, pid: child.pid };\n}\n","import type { ChildProcess } from 'node:child_process';\nimport type { ToolHandler, ToolProvider, ToolSchema } from '@octavus/core';\nimport {\n type McpEntry,\n type StdioConfig,\n type HttpConfig,\n type ShellConfig,\n type ShellMode,\n createStdioConfig,\n createHttpConfig,\n createShellConfig,\n} from './entries';\nimport { connectStdio, connectHttp, type McpConnection } from './mcp-client';\nimport { createShellTools } from './shell';\nimport { launchChrome, type ChromeInstance, type ChromeLaunchOptions } from './chrome';\n\nexport type { ChromeInstance, ChromeLaunchOptions };\n\ninterface ManagedProcess {\n process: ChildProcess;\n}\n\nexport interface ComputerConfig {\n mcpServers: Record<string, McpEntry>;\n managedProcesses?: ManagedProcess[];\n}\n\ninterface EntryState {\n namespace: string;\n handlers: Record<string, ToolHandler>;\n schemas: ToolSchema[];\n connection?: McpConnection;\n}\n\nexport class Computer implements ToolProvider {\n private config: ComputerConfig;\n private entries = new Map<string, EntryState>();\n private started = false;\n\n constructor(config: ComputerConfig) {\n this.config = config;\n }\n\n // ---------------------------------------------------------------------------\n // Static factories\n // ---------------------------------------------------------------------------\n\n static stdio(\n command: string,\n args?: string[],\n options?: { env?: Record<string, string>; cwd?: string },\n ): StdioConfig {\n return createStdioConfig(command, args, options);\n }\n\n static http(url: string, options?: { headers?: Record<string, string> }): HttpConfig {\n return createHttpConfig(url, options);\n }\n\n static shell(options: { cwd?: string; mode: ShellMode; timeout?: number }): ShellConfig {\n return createShellConfig(options);\n }\n\n static launchChrome(options: ChromeLaunchOptions): Promise<ChromeInstance> {\n return launchChrome(options);\n }\n\n // ---------------------------------------------------------------------------\n // Lifecycle\n // ---------------------------------------------------------------------------\n\n async start(): Promise<{ errors: string[] }> {\n if (this.started) return { errors: [] };\n\n const entries = Object.entries(this.config.mcpServers);\n const results = await Promise.allSettled(\n entries.map(([namespace, entry]) => this.connectEntry(namespace, entry)),\n );\n\n const errors: string[] = [];\n for (let i = 0; i < results.length; i++) {\n const result = results[i]!;\n if (result.status === 'rejected') {\n const namespace = entries[i]![0];\n errors.push(\n `${namespace}: ${result.reason instanceof Error ? result.reason.message : String(result.reason)}`,\n );\n }\n }\n\n if (errors.length > 0 && errors.length === entries.length) {\n throw new Error(`All MCP connections failed:\\n${errors.join('\\n')}`);\n }\n\n this.started = true;\n return { errors };\n }\n\n async stop(): Promise<void> {\n const closePromises: Promise<void>[] = [];\n\n for (const state of this.entries.values()) {\n if (state.connection) {\n closePromises.push(\n state.connection.close().catch(() => {\n // Ignore close errors — the process may already be gone\n }),\n );\n }\n }\n\n await Promise.allSettled(closePromises);\n\n if (this.config.managedProcesses) {\n for (const managed of this.config.managedProcesses) {\n try {\n managed.process.kill();\n } catch {\n // Process may already be dead\n }\n }\n }\n\n this.entries.clear();\n this.started = false;\n }\n\n // ---------------------------------------------------------------------------\n // ToolProvider\n // ---------------------------------------------------------------------------\n\n toolHandlers(): Record<string, ToolHandler> {\n const merged: Record<string, ToolHandler> = {};\n for (const state of this.entries.values()) {\n Object.assign(merged, state.handlers);\n }\n return merged;\n }\n\n toolSchemas(): ToolSchema[] {\n const all: ToolSchema[] = [];\n for (const state of this.entries.values()) {\n all.push(...state.schemas);\n }\n return all;\n }\n\n // ---------------------------------------------------------------------------\n // Internal\n // ---------------------------------------------------------------------------\n\n private async connectEntry(namespace: string, entry: McpEntry): Promise<void> {\n if (entry.type === 'shell') {\n const shell = createShellTools(namespace, entry);\n this.entries.set(namespace, {\n namespace,\n handlers: shell.handlers,\n schemas: shell.schemas,\n });\n return;\n }\n\n const connection =\n entry.type === 'stdio'\n ? await connectStdio(namespace, entry)\n : await connectHttp(namespace, entry);\n\n this.entries.set(namespace, {\n namespace,\n handlers: connection.handlers,\n schemas: connection.schemas,\n connection,\n });\n }\n}\n"],"mappings":"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","names":["spawn","platform"]}
1
+ {"version":3,"sources":["../src/entries.ts","../src/mcp-client.ts","../src/shell.ts","../src/chrome.ts","../src/computer.ts"],"sourcesContent":["/** Stdio transport — spawns an MCP server as a child process, communicates via stdin/stdout. */\nexport interface StdioConfig {\n type: 'stdio';\n command: string;\n args?: string[];\n env?: Record<string, string>;\n cwd?: string;\n}\n\n/** HTTP transport — connects to an MCP server over Streamable HTTP. */\nexport interface HttpConfig {\n type: 'http';\n url: string;\n headers?: Record<string, string>;\n}\n\nexport type ShellMode = 'unrestricted' | { allowedPatterns?: RegExp[]; blockedPatterns?: RegExp[] };\n\n/** Built-in shell — executes commands directly via child_process (no MCP subprocess). */\nexport interface ShellConfig {\n type: 'shell';\n cwd?: string;\n mode: ShellMode;\n timeout?: number;\n}\n\nexport type McpEntry = StdioConfig | HttpConfig | ShellConfig;\n\nexport const NAMESPACE_SEPARATOR = '__';\n\nexport function createStdioConfig(\n command: string,\n args?: string[],\n options?: { env?: Record<string, string>; cwd?: string },\n): StdioConfig {\n return { type: 'stdio', command, args, ...options };\n}\n\nexport function createHttpConfig(\n url: string,\n options?: { headers?: Record<string, string> },\n): HttpConfig {\n return { type: 'http', url, ...options };\n}\n\nexport function createShellConfig(options: Omit<ShellConfig, 'type'>): ShellConfig {\n return { type: 'shell', ...options };\n}\n","import { Client } from '@modelcontextprotocol/sdk/client/index.js';\nimport { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js';\nimport { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js';\nimport type { Transport } from '@modelcontextprotocol/sdk/shared/transport.js';\nimport type { ToolHandler, ToolSchema } from '@octavus/core';\nimport { NAMESPACE_SEPARATOR, type StdioConfig, type HttpConfig } from './entries';\n\nconst MCP_CLIENT_NAME = 'octavus-computer';\nconst MCP_CLIENT_VERSION = '1.0.0';\n\nexport interface McpConnection {\n client: Client;\n namespace: string;\n handlers: Record<string, ToolHandler>;\n schemas: ToolSchema[];\n close: () => Promise<void>;\n}\n\nfunction namespaceTool(namespace: string, toolName: string): string {\n return `${namespace}${NAMESPACE_SEPARATOR}${toolName}`;\n}\n\ninterface ContentPart {\n type: string;\n text?: string;\n [key: string]: unknown;\n}\n\ninterface CallToolResult {\n content: ContentPart[];\n isError?: boolean;\n [key: string]: unknown;\n}\n\nfunction formatContentPart(part: ContentPart): unknown {\n if (part.type === 'text' && typeof part.text === 'string') {\n return part.text;\n }\n // Preserve non-text content (images, audio, resources) as structured data\n // so downstream consumers can handle them appropriately.\n return part;\n}\n\nfunction formatCallToolResult(result: CallToolResult): unknown {\n if (result.isError) {\n const errorText = result.content\n .filter((c) => c.type === 'text' && typeof c.text === 'string')\n .map((c) => c.text!)\n .join('\\n');\n return { error: errorText || 'Tool execution failed' };\n }\n\n if (result.content.length === 0) {\n return { success: true };\n }\n\n const textParts = result.content.filter((c) => c.type === 'text' && typeof c.text === 'string');\n const hasNonText = result.content.some((c) => c.type !== 'text');\n\n // Pure text results: parse JSON or return as string\n if (!hasNonText) {\n if (textParts.length === 1) {\n try {\n return JSON.parse(textParts[0]!.text!);\n } catch {\n return textParts[0]!.text;\n }\n }\n return textParts.map((p) => p.text).join('\\n');\n }\n\n // Mixed or non-text content: return as structured content array\n return result.content.map(formatContentPart);\n}\n\nasync function discoverTools(\n client: Client,\n namespace: string,\n): Promise<Pick<McpConnection, 'handlers' | 'schemas'>> {\n const { tools } = await client.listTools();\n\n const handlers: Record<string, ToolHandler> = {};\n const schemas: ToolSchema[] = [];\n\n for (const tool of tools) {\n const nsName = namespaceTool(namespace, tool.name);\n const originalName = tool.name;\n\n schemas.push({\n name: nsName,\n description: tool.description ?? originalName,\n inputSchema: tool.inputSchema as Record<string, unknown>,\n });\n\n handlers[nsName] = async (args: Record<string, unknown>) => {\n const result = await client.callTool({ name: originalName, arguments: args });\n return formatCallToolResult(result as CallToolResult);\n };\n }\n\n return { handlers, schemas };\n}\n\nasync function connect(namespace: string, transport: Transport): Promise<McpConnection> {\n const client = new Client({ name: MCP_CLIENT_NAME, version: MCP_CLIENT_VERSION });\n await client.connect(transport);\n const { handlers, schemas } = await discoverTools(client, namespace);\n return { client, namespace, handlers, schemas, close: () => client.close() };\n}\n\nexport function connectStdio(namespace: string, config: StdioConfig): Promise<McpConnection> {\n const transport = new StdioClientTransport({\n command: config.command,\n args: config.args,\n env: config.env\n ? {\n ...Object.fromEntries(\n Object.entries(process.env).filter((e): e is [string, string] => e[1] !== undefined),\n ),\n ...config.env,\n }\n : undefined,\n cwd: config.cwd,\n stderr: 'pipe',\n });\n\n return connect(namespace, transport);\n}\n\nexport function connectHttp(namespace: string, config: HttpConfig): Promise<McpConnection> {\n const transport = new StreamableHTTPClientTransport(new URL(config.url), {\n requestInit: config.headers ? { headers: config.headers } : undefined,\n });\n\n return connect(namespace, transport);\n}\n","import { spawn } from 'node:child_process';\nimport { platform } from 'node:os';\nimport type { ToolHandler, ToolSchema } from '@octavus/core';\nimport { NAMESPACE_SEPARATOR, type ShellConfig, type ShellMode } from './entries';\n\nconst DEFAULT_TIMEOUT_MS = 300_000;\nconst MAX_OUTPUT_LENGTH = 100_000;\n\ninterface ShellToolState {\n handlers: Record<string, ToolHandler>;\n schemas: ToolSchema[];\n}\n\nfunction getLoginShell(): { shell: string; args: string[] } {\n const os = platform();\n if (os === 'win32') {\n return { shell: 'cmd.exe', args: ['/c'] };\n }\n const userShell = process.env.SHELL ?? '/bin/bash';\n return { shell: userShell, args: ['-l', '-c'] };\n}\n\nfunction isCommandAllowed(command: string, mode: ShellMode): boolean {\n if (mode === 'unrestricted') return true;\n\n if (mode.blockedPatterns) {\n for (const pattern of mode.blockedPatterns) {\n if (pattern.test(command)) return false;\n }\n }\n\n if (mode.allowedPatterns) {\n for (const pattern of mode.allowedPatterns) {\n if (pattern.test(command)) return true;\n }\n return false;\n }\n\n return true;\n}\n\nfunction truncateOutput(output: string): string {\n if (output.length <= MAX_OUTPUT_LENGTH) return output;\n const half = Math.floor(MAX_OUTPUT_LENGTH / 2);\n return (\n output.slice(0, half) +\n `\\n\\n... truncated ${output.length - MAX_OUTPUT_LENGTH} characters ...\\n\\n` +\n output.slice(-half)\n );\n}\n\nfunction executeCommand(\n command: string,\n cwd: string | undefined,\n timeout: number,\n): Promise<{ exitCode: number; stdout: string; stderr: string }> {\n return new Promise((resolve) => {\n const { shell, args } = getLoginShell();\n const child = spawn(shell, [...args, command], {\n cwd,\n env: process.env,\n stdio: ['ignore', 'pipe', 'pipe'],\n timeout,\n });\n\n let stdout = '';\n let stderr = '';\n\n child.stdout.on('data', (data: Buffer) => {\n stdout += data.toString();\n });\n\n child.stderr.on('data', (data: Buffer) => {\n stderr += data.toString();\n });\n\n child.on('error', (error) => {\n resolve({\n exitCode: 1,\n stdout: truncateOutput(stdout),\n stderr: truncateOutput(error.message),\n });\n });\n\n child.on('close', (code) => {\n resolve({\n exitCode: code ?? 1,\n stdout: truncateOutput(stdout),\n stderr: truncateOutput(stderr),\n });\n });\n });\n}\n\nexport function createShellTools(namespace: string, config: ShellConfig): ShellToolState {\n const timeout = config.timeout ?? DEFAULT_TIMEOUT_MS;\n\n const runCommandHandler: ToolHandler = async (args: Record<string, unknown>) => {\n const command = args.command as string | undefined;\n if (!command) {\n return { error: 'command is required' };\n }\n\n if (!isCommandAllowed(command, config.mode)) {\n return { error: `Command not allowed by safety policy: ${command}` };\n }\n\n const cwd = (args.cwd as string | undefined) ?? config.cwd;\n const commandTimeout = (args.timeout as number | undefined) ?? timeout;\n\n return await executeCommand(command, cwd, commandTimeout);\n };\n\n const nsName = `${namespace}${NAMESPACE_SEPARATOR}run_command`;\n\n return {\n handlers: { [nsName]: runCommandHandler },\n schemas: [\n {\n name: nsName,\n description:\n \"Run a shell command on the local machine. Commands execute in a login shell with the user's full environment.\",\n inputSchema: {\n type: 'object',\n properties: {\n command: { type: 'string', description: 'The shell command to execute' },\n cwd: {\n type: 'string',\n description: 'Working directory (optional, defaults to configured directory)',\n },\n timeout: {\n type: 'number',\n description: `Timeout in milliseconds (optional, default ${DEFAULT_TIMEOUT_MS})`,\n },\n },\n required: ['command'],\n },\n },\n ],\n };\n}\n","import { spawn, spawnSync, type ChildProcess } from 'node:child_process';\nimport { createServer } from 'node:net';\nimport { platform } from 'node:os';\nimport { existsSync } from 'node:fs';\n\nexport interface ChromeInstance {\n port: number;\n process: ChildProcess;\n pid: number;\n}\n\nexport interface ChromeLaunchOptions {\n profileDir: string;\n debuggingPort?: number;\n flags?: string[];\n /** Max time (ms) to wait for the Chrome debugging port to become ready. Default: 30000. */\n debuggingPortTimeoutMs?: number;\n}\n\nconst CHROME_PATHS: Record<string, string[]> = {\n darwin: [\n '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome',\n '/Applications/Google Chrome Canary.app/Contents/MacOS/Google Chrome Canary',\n '/Applications/Chromium.app/Contents/MacOS/Chromium',\n ],\n linux: ['google-chrome', 'google-chrome-stable', 'chromium-browser', 'chromium'],\n win32: [\n 'C:\\\\Program Files\\\\Google\\\\Chrome\\\\Application\\\\chrome.exe',\n 'C:\\\\Program Files (x86)\\\\Google\\\\Chrome\\\\Application\\\\chrome.exe',\n ],\n};\n\nfunction findChromePath(): string {\n const os = platform();\n const candidates = CHROME_PATHS[os] ?? CHROME_PATHS.linux!;\n\n for (const candidate of candidates) {\n const isAbsolute = candidate.startsWith('/') || candidate.startsWith('C:\\\\');\n if (isAbsolute) {\n if (existsSync(candidate)) return candidate;\n } else if (isInPath(candidate)) {\n return candidate;\n }\n }\n\n throw new Error(\n `Chrome not found. Searched: ${candidates.join(', ')}. ` +\n 'Install Google Chrome or set the path explicitly.',\n );\n}\n\nfunction isInPath(binary: string): boolean {\n try {\n const result = spawnSync('which', [binary], { stdio: 'ignore' });\n return result.status === 0;\n } catch {\n return false;\n }\n}\n\nfunction findFreePort(): Promise<number> {\n return new Promise((resolve, reject) => {\n const server = createServer();\n server.listen(0, '127.0.0.1', () => {\n const address = server.address();\n if (address !== null && typeof address === 'object') {\n const port = address.port;\n server.close(() => resolve(port));\n } else {\n server.close(() => reject(new Error('Failed to allocate port')));\n }\n });\n server.on('error', reject);\n });\n}\n\nfunction waitForDebuggingPort(port: number, timeoutMs = 30_000): Promise<void> {\n const start = Date.now();\n return new Promise((resolve, reject) => {\n function attempt() {\n if (Date.now() - start > timeoutMs) {\n reject(new Error(`Chrome debugging port ${port} not ready after ${timeoutMs}ms`));\n return;\n }\n\n fetch(`http://127.0.0.1:${port}/json/version`)\n .then((res) => {\n if (res.ok) resolve();\n else setTimeout(attempt, 200);\n })\n .catch(() => {\n setTimeout(attempt, 200);\n });\n }\n attempt();\n });\n}\n\nexport async function launchChrome(options: ChromeLaunchOptions): Promise<ChromeInstance> {\n const chromePath = findChromePath();\n const port = options.debuggingPort ?? (await findFreePort());\n\n const args = [\n `--remote-debugging-port=${port}`,\n `--user-data-dir=${options.profileDir}`,\n '--no-first-run',\n '--no-default-browser-check',\n ...(options.flags ?? []),\n ];\n\n const child = spawn(chromePath, args, {\n stdio: 'ignore',\n detached: false,\n });\n\n if (child.pid === undefined) {\n throw new Error(`Failed to launch Chrome at ${chromePath}`);\n }\n\n child.on('error', () => {\n // Chrome process errors are handled by the caller via the process reference\n });\n\n await waitForDebuggingPort(port, options.debuggingPortTimeoutMs);\n\n return { port, process: child, pid: child.pid };\n}\n","import type { ChildProcess } from 'node:child_process';\nimport type { ToolHandler, ToolProvider, ToolSchema } from '@octavus/core';\nimport {\n type McpEntry,\n type StdioConfig,\n type HttpConfig,\n type ShellConfig,\n type ShellMode,\n createStdioConfig,\n createHttpConfig,\n createShellConfig,\n} from './entries';\nimport { connectStdio, connectHttp, type McpConnection } from './mcp-client';\nimport { createShellTools } from './shell';\nimport { launchChrome, type ChromeInstance, type ChromeLaunchOptions } from './chrome';\n\nexport type { ChromeInstance, ChromeLaunchOptions };\n\ninterface ManagedProcess {\n process: ChildProcess;\n}\n\nexport interface ComputerConfig {\n mcpServers: Record<string, McpEntry>;\n managedProcesses?: ManagedProcess[];\n}\n\ninterface EntryState {\n namespace: string;\n handlers: Record<string, ToolHandler>;\n schemas: ToolSchema[];\n connection?: McpConnection;\n}\n\nexport class Computer implements ToolProvider {\n private config: ComputerConfig;\n private entries = new Map<string, EntryState>();\n private started = false;\n\n constructor(config: ComputerConfig) {\n this.config = config;\n }\n\n // ---------------------------------------------------------------------------\n // Static factories\n // ---------------------------------------------------------------------------\n\n static stdio(\n command: string,\n args?: string[],\n options?: { env?: Record<string, string>; cwd?: string },\n ): StdioConfig {\n return createStdioConfig(command, args, options);\n }\n\n static http(url: string, options?: { headers?: Record<string, string> }): HttpConfig {\n return createHttpConfig(url, options);\n }\n\n static shell(options: { cwd?: string; mode: ShellMode; timeout?: number }): ShellConfig {\n return createShellConfig(options);\n }\n\n static launchChrome(options: ChromeLaunchOptions): Promise<ChromeInstance> {\n return launchChrome(options);\n }\n\n // ---------------------------------------------------------------------------\n // Lifecycle\n // ---------------------------------------------------------------------------\n\n async start(): Promise<{ errors: string[] }> {\n if (this.started) return { errors: [] };\n\n const entries = Object.entries(this.config.mcpServers);\n const results = await Promise.allSettled(\n entries.map(([namespace, entry]) => this.connectEntry(namespace, entry)),\n );\n\n const errors: string[] = [];\n for (let i = 0; i < results.length; i++) {\n const result = results[i]!;\n if (result.status === 'rejected') {\n const namespace = entries[i]![0];\n errors.push(\n `${namespace}: ${result.reason instanceof Error ? result.reason.message : String(result.reason)}`,\n );\n }\n }\n\n if (errors.length > 0 && errors.length === entries.length) {\n throw new Error(`All MCP connections failed:\\n${errors.join('\\n')}`);\n }\n\n this.started = true;\n return { errors };\n }\n\n async stop(): Promise<void> {\n const closePromises: Promise<void>[] = [];\n\n for (const state of this.entries.values()) {\n if (state.connection) {\n closePromises.push(\n state.connection.close().catch(() => {\n // Ignore close errors — the process may already be gone\n }),\n );\n }\n }\n\n await Promise.allSettled(closePromises);\n\n if (this.config.managedProcesses) {\n for (const managed of this.config.managedProcesses) {\n try {\n managed.process.kill();\n } catch {\n // Process may already be dead\n }\n }\n }\n\n this.entries.clear();\n this.started = false;\n }\n\n // ---------------------------------------------------------------------------\n // ToolProvider\n // ---------------------------------------------------------------------------\n\n toolHandlers(): Record<string, ToolHandler> {\n const merged: Record<string, ToolHandler> = {};\n for (const state of this.entries.values()) {\n Object.assign(merged, state.handlers);\n }\n return merged;\n }\n\n toolSchemas(): ToolSchema[] {\n const all: ToolSchema[] = [];\n for (const state of this.entries.values()) {\n all.push(...state.schemas);\n }\n return all;\n }\n\n // ---------------------------------------------------------------------------\n // Internal\n // ---------------------------------------------------------------------------\n\n private async connectEntry(namespace: string, entry: McpEntry): Promise<void> {\n if (entry.type === 'shell') {\n const shell = createShellTools(namespace, entry);\n this.entries.set(namespace, {\n namespace,\n handlers: shell.handlers,\n schemas: shell.schemas,\n });\n return;\n }\n\n const connection =\n entry.type === 'stdio'\n ? await connectStdio(namespace, entry)\n : await connectHttp(namespace, entry);\n\n this.entries.set(namespace, {\n namespace,\n handlers: connection.handlers,\n schemas: connection.schemas,\n connection,\n });\n }\n}\n"],"mappings":"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","names":["spawn","platform"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@octavus/computer",
3
- "version": "2.17.0",
3
+ "version": "2.18.0",
4
4
  "description": "Computer capability layer for Octavus agents — browser, filesystem, and shell via MCP",
5
5
  "license": "MIT",
6
6
  "author": "Octavus AI <dev@octavus.ai>",
@@ -42,7 +42,7 @@
42
42
  },
43
43
  "dependencies": {
44
44
  "@modelcontextprotocol/sdk": "^1.27.1",
45
- "@octavus/core": "^2.17.0"
45
+ "@octavus/core": "^2.18.0"
46
46
  },
47
47
  "devDependencies": {
48
48
  "tsup": "^8.3.5",