@octavus/computer 2.17.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.
@@ -0,0 +1,76 @@
1
+ import { ChildProcess } from 'node:child_process';
2
+ import { ToolProvider, ToolHandler, ToolSchema } from '@octavus/core';
3
+
4
+ /** Stdio transport — spawns an MCP server as a child process, communicates via stdin/stdout. */
5
+ interface StdioConfig {
6
+ type: 'stdio';
7
+ command: string;
8
+ args?: string[];
9
+ env?: Record<string, string>;
10
+ cwd?: string;
11
+ }
12
+ /** HTTP transport — connects to an MCP server over Streamable HTTP. */
13
+ interface HttpConfig {
14
+ type: 'http';
15
+ url: string;
16
+ headers?: Record<string, string>;
17
+ }
18
+ type ShellMode = 'unrestricted' | {
19
+ allowedPatterns?: RegExp[];
20
+ blockedPatterns?: RegExp[];
21
+ };
22
+ /** Built-in shell — executes commands directly via child_process (no MCP subprocess). */
23
+ interface ShellConfig {
24
+ type: 'shell';
25
+ cwd?: string;
26
+ mode: ShellMode;
27
+ timeout?: number;
28
+ }
29
+ type McpEntry = StdioConfig | HttpConfig | ShellConfig;
30
+
31
+ interface ChromeInstance {
32
+ port: number;
33
+ process: ChildProcess;
34
+ pid: number;
35
+ }
36
+ interface ChromeLaunchOptions {
37
+ profileDir: string;
38
+ debuggingPort?: number;
39
+ flags?: string[];
40
+ }
41
+
42
+ interface ManagedProcess {
43
+ process: ChildProcess;
44
+ }
45
+ interface ComputerConfig {
46
+ mcpServers: Record<string, McpEntry>;
47
+ managedProcesses?: ManagedProcess[];
48
+ }
49
+ declare class Computer implements ToolProvider {
50
+ private config;
51
+ private entries;
52
+ private started;
53
+ constructor(config: ComputerConfig);
54
+ static stdio(command: string, args?: string[], options?: {
55
+ env?: Record<string, string>;
56
+ cwd?: string;
57
+ }): StdioConfig;
58
+ static http(url: string, options?: {
59
+ headers?: Record<string, string>;
60
+ }): HttpConfig;
61
+ static shell(options: {
62
+ cwd?: string;
63
+ mode: ShellMode;
64
+ timeout?: number;
65
+ }): ShellConfig;
66
+ static launchChrome(options: ChromeLaunchOptions): Promise<ChromeInstance>;
67
+ start(): Promise<{
68
+ errors: string[];
69
+ }>;
70
+ stop(): Promise<void>;
71
+ toolHandlers(): Record<string, ToolHandler>;
72
+ toolSchemas(): ToolSchema[];
73
+ private connectEntry;
74
+ }
75
+
76
+ export { type ChromeInstance, type ChromeLaunchOptions, Computer, type ComputerConfig, type HttpConfig, type McpEntry, type ShellConfig, type ShellMode, type StdioConfig };
package/dist/index.js ADDED
@@ -0,0 +1,409 @@
1
+ // src/entries.ts
2
+ var NAMESPACE_SEPARATOR = "__";
3
+ function createStdioConfig(command, args, options) {
4
+ return { type: "stdio", command, args, ...options };
5
+ }
6
+ function createHttpConfig(url, options) {
7
+ return { type: "http", url, ...options };
8
+ }
9
+ function createShellConfig(options) {
10
+ return { type: "shell", ...options };
11
+ }
12
+
13
+ // src/mcp-client.ts
14
+ import { Client } from "@modelcontextprotocol/sdk/client/index.js";
15
+ import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
16
+ import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
17
+ var MCP_CLIENT_NAME = "octavus-computer";
18
+ var MCP_CLIENT_VERSION = "1.0.0";
19
+ function namespaceTool(namespace, toolName) {
20
+ return `${namespace}${NAMESPACE_SEPARATOR}${toolName}`;
21
+ }
22
+ function formatContentPart(part) {
23
+ if (part.type === "text" && typeof part.text === "string") {
24
+ return part.text;
25
+ }
26
+ return part;
27
+ }
28
+ function formatCallToolResult(result) {
29
+ if (result.isError) {
30
+ const errorText = result.content.filter((c) => c.type === "text" && typeof c.text === "string").map((c) => c.text).join("\n");
31
+ return { error: errorText || "Tool execution failed" };
32
+ }
33
+ if (result.content.length === 0) {
34
+ return { success: true };
35
+ }
36
+ const textParts = result.content.filter((c) => c.type === "text" && typeof c.text === "string");
37
+ const hasNonText = result.content.some((c) => c.type !== "text");
38
+ if (!hasNonText) {
39
+ if (textParts.length === 1) {
40
+ try {
41
+ return JSON.parse(textParts[0].text);
42
+ } catch {
43
+ return textParts[0].text;
44
+ }
45
+ }
46
+ return textParts.map((p) => p.text).join("\n");
47
+ }
48
+ return result.content.map(formatContentPart);
49
+ }
50
+ async function discoverTools(client, namespace) {
51
+ const { tools } = await client.listTools();
52
+ const handlers = {};
53
+ const schemas = [];
54
+ for (const tool of tools) {
55
+ const nsName = namespaceTool(namespace, tool.name);
56
+ const originalName = tool.name;
57
+ schemas.push({
58
+ name: nsName,
59
+ description: tool.description ?? originalName,
60
+ inputSchema: tool.inputSchema
61
+ });
62
+ handlers[nsName] = async (args) => {
63
+ const result = await client.callTool({ name: originalName, arguments: args });
64
+ return formatCallToolResult(result);
65
+ };
66
+ }
67
+ return { handlers, schemas };
68
+ }
69
+ async function connect(namespace, transport) {
70
+ const client = new Client({ name: MCP_CLIENT_NAME, version: MCP_CLIENT_VERSION });
71
+ await client.connect(transport);
72
+ const { handlers, schemas } = await discoverTools(client, namespace);
73
+ return { client, namespace, handlers, schemas, close: () => client.close() };
74
+ }
75
+ function connectStdio(namespace, config) {
76
+ const transport = new StdioClientTransport({
77
+ command: config.command,
78
+ args: config.args,
79
+ env: config.env ? {
80
+ ...Object.fromEntries(
81
+ Object.entries(process.env).filter((e) => e[1] !== void 0)
82
+ ),
83
+ ...config.env
84
+ } : void 0,
85
+ cwd: config.cwd,
86
+ stderr: "pipe"
87
+ });
88
+ return connect(namespace, transport);
89
+ }
90
+ function connectHttp(namespace, config) {
91
+ const transport = new StreamableHTTPClientTransport(new URL(config.url), {
92
+ requestInit: config.headers ? { headers: config.headers } : void 0
93
+ });
94
+ return connect(namespace, transport);
95
+ }
96
+
97
+ // src/shell.ts
98
+ import { spawn } from "child_process";
99
+ import { platform } from "os";
100
+ var DEFAULT_TIMEOUT_MS = 3e5;
101
+ var MAX_OUTPUT_LENGTH = 1e5;
102
+ function getLoginShell() {
103
+ const os = platform();
104
+ if (os === "win32") {
105
+ return { shell: "cmd.exe", args: ["/c"] };
106
+ }
107
+ const userShell = process.env.SHELL ?? "/bin/bash";
108
+ return { shell: userShell, args: ["-l", "-c"] };
109
+ }
110
+ function isCommandAllowed(command, mode) {
111
+ if (mode === "unrestricted") return true;
112
+ if (mode.blockedPatterns) {
113
+ for (const pattern of mode.blockedPatterns) {
114
+ if (pattern.test(command)) return false;
115
+ }
116
+ }
117
+ if (mode.allowedPatterns) {
118
+ for (const pattern of mode.allowedPatterns) {
119
+ if (pattern.test(command)) return true;
120
+ }
121
+ return false;
122
+ }
123
+ return true;
124
+ }
125
+ function truncateOutput(output) {
126
+ if (output.length <= MAX_OUTPUT_LENGTH) return output;
127
+ const half = Math.floor(MAX_OUTPUT_LENGTH / 2);
128
+ return output.slice(0, half) + `
129
+
130
+ ... truncated ${output.length - MAX_OUTPUT_LENGTH} characters ...
131
+
132
+ ` + output.slice(-half);
133
+ }
134
+ function executeCommand(command, cwd, timeout) {
135
+ return new Promise((resolve) => {
136
+ const { shell, args } = getLoginShell();
137
+ const child = spawn(shell, [...args, command], {
138
+ cwd,
139
+ env: process.env,
140
+ stdio: ["ignore", "pipe", "pipe"],
141
+ timeout
142
+ });
143
+ let stdout = "";
144
+ let stderr = "";
145
+ child.stdout.on("data", (data) => {
146
+ stdout += data.toString();
147
+ });
148
+ child.stderr.on("data", (data) => {
149
+ stderr += data.toString();
150
+ });
151
+ child.on("error", (error) => {
152
+ resolve({
153
+ exitCode: 1,
154
+ stdout: truncateOutput(stdout),
155
+ stderr: truncateOutput(error.message)
156
+ });
157
+ });
158
+ child.on("close", (code) => {
159
+ resolve({
160
+ exitCode: code ?? 1,
161
+ stdout: truncateOutput(stdout),
162
+ stderr: truncateOutput(stderr)
163
+ });
164
+ });
165
+ });
166
+ }
167
+ function createShellTools(namespace, config) {
168
+ const timeout = config.timeout ?? DEFAULT_TIMEOUT_MS;
169
+ const runCommandHandler = async (args) => {
170
+ const command = args.command;
171
+ if (!command) {
172
+ return { error: "command is required" };
173
+ }
174
+ if (!isCommandAllowed(command, config.mode)) {
175
+ return { error: `Command not allowed by safety policy: ${command}` };
176
+ }
177
+ const cwd = args.cwd ?? config.cwd;
178
+ const commandTimeout = args.timeout ?? timeout;
179
+ return await executeCommand(command, cwd, commandTimeout);
180
+ };
181
+ const nsName = `${namespace}${NAMESPACE_SEPARATOR}run_command`;
182
+ return {
183
+ handlers: { [nsName]: runCommandHandler },
184
+ schemas: [
185
+ {
186
+ name: nsName,
187
+ description: "Run a shell command on the local machine. Commands execute in a login shell with the user's full environment.",
188
+ inputSchema: {
189
+ type: "object",
190
+ properties: {
191
+ command: { type: "string", description: "The shell command to execute" },
192
+ cwd: {
193
+ type: "string",
194
+ description: "Working directory (optional, defaults to configured directory)"
195
+ },
196
+ timeout: {
197
+ type: "number",
198
+ description: `Timeout in milliseconds (optional, default ${DEFAULT_TIMEOUT_MS})`
199
+ }
200
+ },
201
+ required: ["command"]
202
+ }
203
+ }
204
+ ]
205
+ };
206
+ }
207
+
208
+ // src/chrome.ts
209
+ import { spawn as spawn2 } from "child_process";
210
+ import { createServer } from "net";
211
+ import { platform as platform2 } from "os";
212
+ import { existsSync } from "fs";
213
+ var CHROME_PATHS = {
214
+ darwin: [
215
+ "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome",
216
+ "/Applications/Google Chrome Canary.app/Contents/MacOS/Google Chrome Canary",
217
+ "/Applications/Chromium.app/Contents/MacOS/Chromium"
218
+ ],
219
+ linux: ["google-chrome", "google-chrome-stable", "chromium-browser", "chromium"],
220
+ win32: [
221
+ "C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe",
222
+ "C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe"
223
+ ]
224
+ };
225
+ function findChromePath() {
226
+ const os = platform2();
227
+ const candidates = CHROME_PATHS[os] ?? CHROME_PATHS.linux;
228
+ for (const candidate of candidates) {
229
+ const isAbsolute = candidate.startsWith("/") || candidate.startsWith("C:\\");
230
+ if (isAbsolute) {
231
+ if (existsSync(candidate)) return candidate;
232
+ } else {
233
+ return candidate;
234
+ }
235
+ }
236
+ throw new Error(
237
+ `Chrome not found. Searched: ${candidates.join(", ")}. Install Google Chrome or set the path explicitly.`
238
+ );
239
+ }
240
+ function findFreePort() {
241
+ return new Promise((resolve, reject) => {
242
+ const server = createServer();
243
+ server.listen(0, "127.0.0.1", () => {
244
+ const address = server.address();
245
+ if (address !== null && typeof address === "object") {
246
+ const port = address.port;
247
+ server.close(() => resolve(port));
248
+ } else {
249
+ server.close(() => reject(new Error("Failed to allocate port")));
250
+ }
251
+ });
252
+ server.on("error", reject);
253
+ });
254
+ }
255
+ function waitForDebuggingPort(port, timeoutMs = 1e4) {
256
+ const start = Date.now();
257
+ return new Promise((resolve, reject) => {
258
+ function attempt() {
259
+ if (Date.now() - start > timeoutMs) {
260
+ reject(new Error(`Chrome debugging port ${port} not ready after ${timeoutMs}ms`));
261
+ return;
262
+ }
263
+ fetch(`http://127.0.0.1:${port}/json/version`).then((res) => {
264
+ if (res.ok) resolve();
265
+ else setTimeout(attempt, 200);
266
+ }).catch(() => {
267
+ setTimeout(attempt, 200);
268
+ });
269
+ }
270
+ attempt();
271
+ });
272
+ }
273
+ async function launchChrome(options) {
274
+ const chromePath = findChromePath();
275
+ const port = options.debuggingPort ?? await findFreePort();
276
+ const args = [
277
+ `--remote-debugging-port=${port}`,
278
+ `--user-data-dir=${options.profileDir}`,
279
+ "--no-first-run",
280
+ "--no-default-browser-check",
281
+ ...options.flags ?? []
282
+ ];
283
+ const child = spawn2(chromePath, args, {
284
+ stdio: "ignore",
285
+ detached: false
286
+ });
287
+ if (child.pid === void 0) {
288
+ throw new Error(`Failed to launch Chrome at ${chromePath}`);
289
+ }
290
+ child.on("error", () => {
291
+ });
292
+ await waitForDebuggingPort(port);
293
+ return { port, process: child, pid: child.pid };
294
+ }
295
+
296
+ // src/computer.ts
297
+ var Computer = class {
298
+ config;
299
+ entries = /* @__PURE__ */ new Map();
300
+ started = false;
301
+ constructor(config) {
302
+ this.config = config;
303
+ }
304
+ // ---------------------------------------------------------------------------
305
+ // Static factories
306
+ // ---------------------------------------------------------------------------
307
+ static stdio(command, args, options) {
308
+ return createStdioConfig(command, args, options);
309
+ }
310
+ static http(url, options) {
311
+ return createHttpConfig(url, options);
312
+ }
313
+ static shell(options) {
314
+ return createShellConfig(options);
315
+ }
316
+ static launchChrome(options) {
317
+ return launchChrome(options);
318
+ }
319
+ // ---------------------------------------------------------------------------
320
+ // Lifecycle
321
+ // ---------------------------------------------------------------------------
322
+ async start() {
323
+ if (this.started) return { errors: [] };
324
+ const entries = Object.entries(this.config.mcpServers);
325
+ const results = await Promise.allSettled(
326
+ entries.map(([namespace, entry]) => this.connectEntry(namespace, entry))
327
+ );
328
+ const errors = [];
329
+ for (let i = 0; i < results.length; i++) {
330
+ const result = results[i];
331
+ if (result.status === "rejected") {
332
+ const namespace = entries[i][0];
333
+ errors.push(
334
+ `${namespace}: ${result.reason instanceof Error ? result.reason.message : String(result.reason)}`
335
+ );
336
+ }
337
+ }
338
+ if (errors.length > 0 && errors.length === entries.length) {
339
+ throw new Error(`All MCP connections failed:
340
+ ${errors.join("\n")}`);
341
+ }
342
+ this.started = true;
343
+ return { errors };
344
+ }
345
+ async stop() {
346
+ const closePromises = [];
347
+ for (const state of this.entries.values()) {
348
+ if (state.connection) {
349
+ closePromises.push(
350
+ state.connection.close().catch(() => {
351
+ })
352
+ );
353
+ }
354
+ }
355
+ await Promise.allSettled(closePromises);
356
+ if (this.config.managedProcesses) {
357
+ for (const managed of this.config.managedProcesses) {
358
+ try {
359
+ managed.process.kill();
360
+ } catch {
361
+ }
362
+ }
363
+ }
364
+ this.entries.clear();
365
+ this.started = false;
366
+ }
367
+ // ---------------------------------------------------------------------------
368
+ // ToolProvider
369
+ // ---------------------------------------------------------------------------
370
+ toolHandlers() {
371
+ const merged = {};
372
+ for (const state of this.entries.values()) {
373
+ Object.assign(merged, state.handlers);
374
+ }
375
+ return merged;
376
+ }
377
+ toolSchemas() {
378
+ const all = [];
379
+ for (const state of this.entries.values()) {
380
+ all.push(...state.schemas);
381
+ }
382
+ return all;
383
+ }
384
+ // ---------------------------------------------------------------------------
385
+ // Internal
386
+ // ---------------------------------------------------------------------------
387
+ async connectEntry(namespace, entry) {
388
+ if (entry.type === "shell") {
389
+ const shell = createShellTools(namespace, entry);
390
+ this.entries.set(namespace, {
391
+ namespace,
392
+ handlers: shell.handlers,
393
+ schemas: shell.schemas
394
+ });
395
+ return;
396
+ }
397
+ const connection = entry.type === "stdio" ? await connectStdio(namespace, entry) : await connectHttp(namespace, entry);
398
+ this.entries.set(namespace, {
399
+ namespace,
400
+ handlers: connection.handlers,
401
+ schemas: connection.schemas,
402
+ connection
403
+ });
404
+ }
405
+ };
406
+ export {
407
+ Computer
408
+ };
409
+ //# sourceMappingURL=index.js.map
@@ -0,0 +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":";AA4BO,IAAM,sBAAsB;AAE5B,SAAS,kBACd,SACA,MACA,SACa;AACb,SAAO,EAAE,MAAM,SAAS,SAAS,MAAM,GAAG,QAAQ;AACpD;AAEO,SAAS,iBACd,KACA,SACY;AACZ,SAAO,EAAE,MAAM,QAAQ,KAAK,GAAG,QAAQ;AACzC;AAEO,SAAS,kBAAkB,SAAiD;AACjF,SAAO,EAAE,MAAM,SAAS,GAAG,QAAQ;AACrC;;;AC/CA,SAAS,cAAc;AACvB,SAAS,4BAA4B;AACrC,SAAS,qCAAqC;AAK9C,IAAM,kBAAkB;AACxB,IAAM,qBAAqB;AAU3B,SAAS,cAAc,WAAmB,UAA0B;AAClE,SAAO,GAAG,SAAS,GAAG,mBAAmB,GAAG,QAAQ;AACtD;AAcA,SAAS,kBAAkB,MAA4B;AACrD,MAAI,KAAK,SAAS,UAAU,OAAO,KAAK,SAAS,UAAU;AACzD,WAAO,KAAK;AAAA,EACd;AAGA,SAAO;AACT;AAEA,SAAS,qBAAqB,QAAiC;AAC7D,MAAI,OAAO,SAAS;AAClB,UAAM,YAAY,OAAO,QACtB,OAAO,CAAC,MAAM,EAAE,SAAS,UAAU,OAAO,EAAE,SAAS,QAAQ,EAC7D,IAAI,CAAC,MAAM,EAAE,IAAK,EAClB,KAAK,IAAI;AACZ,WAAO,EAAE,OAAO,aAAa,wBAAwB;AAAA,EACvD;AAEA,MAAI,OAAO,QAAQ,WAAW,GAAG;AAC/B,WAAO,EAAE,SAAS,KAAK;AAAA,EACzB;AAEA,QAAM,YAAY,OAAO,QAAQ,OAAO,CAAC,MAAM,EAAE,SAAS,UAAU,OAAO,EAAE,SAAS,QAAQ;AAC9F,QAAM,aAAa,OAAO,QAAQ,KAAK,CAAC,MAAM,EAAE,SAAS,MAAM;AAG/D,MAAI,CAAC,YAAY;AACf,QAAI,UAAU,WAAW,GAAG;AAC1B,UAAI;AACF,eAAO,KAAK,MAAM,UAAU,CAAC,EAAG,IAAK;AAAA,MACvC,QAAQ;AACN,eAAO,UAAU,CAAC,EAAG;AAAA,MACvB;AAAA,IACF;AACA,WAAO,UAAU,IAAI,CAAC,MAAM,EAAE,IAAI,EAAE,KAAK,IAAI;AAAA,EAC/C;AAGA,SAAO,OAAO,QAAQ,IAAI,iBAAiB;AAC7C;AAEA,eAAe,cACb,QACA,WACsD;AACtD,QAAM,EAAE,MAAM,IAAI,MAAM,OAAO,UAAU;AAEzC,QAAM,WAAwC,CAAC;AAC/C,QAAM,UAAwB,CAAC;AAE/B,aAAW,QAAQ,OAAO;AACxB,UAAM,SAAS,cAAc,WAAW,KAAK,IAAI;AACjD,UAAM,eAAe,KAAK;AAE1B,YAAQ,KAAK;AAAA,MACX,MAAM;AAAA,MACN,aAAa,KAAK,eAAe;AAAA,MACjC,aAAa,KAAK;AAAA,IACpB,CAAC;AAED,aAAS,MAAM,IAAI,OAAO,SAAkC;AAC1D,YAAM,SAAS,MAAM,OAAO,SAAS,EAAE,MAAM,cAAc,WAAW,KAAK,CAAC;AAC5E,aAAO,qBAAqB,MAAwB;AAAA,IACtD;AAAA,EACF;AAEA,SAAO,EAAE,UAAU,QAAQ;AAC7B;AAEA,eAAe,QAAQ,WAAmB,WAA8C;AACtF,QAAM,SAAS,IAAI,OAAO,EAAE,MAAM,iBAAiB,SAAS,mBAAmB,CAAC;AAChF,QAAM,OAAO,QAAQ,SAAS;AAC9B,QAAM,EAAE,UAAU,QAAQ,IAAI,MAAM,cAAc,QAAQ,SAAS;AACnE,SAAO,EAAE,QAAQ,WAAW,UAAU,SAAS,OAAO,MAAM,OAAO,MAAM,EAAE;AAC7E;AAEO,SAAS,aAAa,WAAmB,QAA6C;AAC3F,QAAM,YAAY,IAAI,qBAAqB;AAAA,IACzC,SAAS,OAAO;AAAA,IAChB,MAAM,OAAO;AAAA,IACb,KAAK,OAAO,MACR;AAAA,MACE,GAAG,OAAO;AAAA,QACR,OAAO,QAAQ,QAAQ,GAAG,EAAE,OAAO,CAAC,MAA6B,EAAE,CAAC,MAAM,MAAS;AAAA,MACrF;AAAA,MACA,GAAG,OAAO;AAAA,IACZ,IACA;AAAA,IACJ,KAAK,OAAO;AAAA,IACZ,QAAQ;AAAA,EACV,CAAC;AAED,SAAO,QAAQ,WAAW,SAAS;AACrC;AAEO,SAAS,YAAY,WAAmB,QAA4C;AACzF,QAAM,YAAY,IAAI,8BAA8B,IAAI,IAAI,OAAO,GAAG,GAAG;AAAA,IACvE,aAAa,OAAO,UAAU,EAAE,SAAS,OAAO,QAAQ,IAAI;AAAA,EAC9D,CAAC;AAED,SAAO,QAAQ,WAAW,SAAS;AACrC;;;ACvIA,SAAS,aAAa;AACtB,SAAS,gBAAgB;AAIzB,IAAM,qBAAqB;AAC3B,IAAM,oBAAoB;AAO1B,SAAS,gBAAmD;AAC1D,QAAM,KAAK,SAAS;AACpB,MAAI,OAAO,SAAS;AAClB,WAAO,EAAE,OAAO,WAAW,MAAM,CAAC,IAAI,EAAE;AAAA,EAC1C;AACA,QAAM,YAAY,QAAQ,IAAI,SAAS;AACvC,SAAO,EAAE,OAAO,WAAW,MAAM,CAAC,MAAM,IAAI,EAAE;AAChD;AAEA,SAAS,iBAAiB,SAAiB,MAA0B;AACnE,MAAI,SAAS,eAAgB,QAAO;AAEpC,MAAI,KAAK,iBAAiB;AACxB,eAAW,WAAW,KAAK,iBAAiB;AAC1C,UAAI,QAAQ,KAAK,OAAO,EAAG,QAAO;AAAA,IACpC;AAAA,EACF;AAEA,MAAI,KAAK,iBAAiB;AACxB,eAAW,WAAW,KAAK,iBAAiB;AAC1C,UAAI,QAAQ,KAAK,OAAO,EAAG,QAAO;AAAA,IACpC;AACA,WAAO;AAAA,EACT;AAEA,SAAO;AACT;AAEA,SAAS,eAAe,QAAwB;AAC9C,MAAI,OAAO,UAAU,kBAAmB,QAAO;AAC/C,QAAM,OAAO,KAAK,MAAM,oBAAoB,CAAC;AAC7C,SACE,OAAO,MAAM,GAAG,IAAI,IACpB;AAAA;AAAA,gBAAqB,OAAO,SAAS,iBAAiB;AAAA;AAAA,IACtD,OAAO,MAAM,CAAC,IAAI;AAEtB;AAEA,SAAS,eACP,SACA,KACA,SAC+D;AAC/D,SAAO,IAAI,QAAQ,CAAC,YAAY;AAC9B,UAAM,EAAE,OAAO,KAAK,IAAI,cAAc;AACtC,UAAM,QAAQ,MAAM,OAAO,CAAC,GAAG,MAAM,OAAO,GAAG;AAAA,MAC7C;AAAA,MACA,KAAK,QAAQ;AAAA,MACb,OAAO,CAAC,UAAU,QAAQ,MAAM;AAAA,MAChC;AAAA,IACF,CAAC;AAED,QAAI,SAAS;AACb,QAAI,SAAS;AAEb,UAAM,OAAO,GAAG,QAAQ,CAAC,SAAiB;AACxC,gBAAU,KAAK,SAAS;AAAA,IAC1B,CAAC;AAED,UAAM,OAAO,GAAG,QAAQ,CAAC,SAAiB;AACxC,gBAAU,KAAK,SAAS;AAAA,IAC1B,CAAC;AAED,UAAM,GAAG,SAAS,CAAC,UAAU;AAC3B,cAAQ;AAAA,QACN,UAAU;AAAA,QACV,QAAQ,eAAe,MAAM;AAAA,QAC7B,QAAQ,eAAe,MAAM,OAAO;AAAA,MACtC,CAAC;AAAA,IACH,CAAC;AAED,UAAM,GAAG,SAAS,CAAC,SAAS;AAC1B,cAAQ;AAAA,QACN,UAAU,QAAQ;AAAA,QAClB,QAAQ,eAAe,MAAM;AAAA,QAC7B,QAAQ,eAAe,MAAM;AAAA,MAC/B,CAAC;AAAA,IACH,CAAC;AAAA,EACH,CAAC;AACH;AAEO,SAAS,iBAAiB,WAAmB,QAAqC;AACvF,QAAM,UAAU,OAAO,WAAW;AAElC,QAAM,oBAAiC,OAAO,SAAkC;AAC9E,UAAM,UAAU,KAAK;AACrB,QAAI,CAAC,SAAS;AACZ,aAAO,EAAE,OAAO,sBAAsB;AAAA,IACxC;AAEA,QAAI,CAAC,iBAAiB,SAAS,OAAO,IAAI,GAAG;AAC3C,aAAO,EAAE,OAAO,yCAAyC,OAAO,GAAG;AAAA,IACrE;AAEA,UAAM,MAAO,KAAK,OAA8B,OAAO;AACvD,UAAM,iBAAkB,KAAK,WAAkC;AAE/D,WAAO,MAAM,eAAe,SAAS,KAAK,cAAc;AAAA,EAC1D;AAEA,QAAM,SAAS,GAAG,SAAS,GAAG,mBAAmB;AAEjD,SAAO;AAAA,IACL,UAAU,EAAE,CAAC,MAAM,GAAG,kBAAkB;AAAA,IACxC,SAAS;AAAA,MACP;AAAA,QACE,MAAM;AAAA,QACN,aACE;AAAA,QACF,aAAa;AAAA,UACX,MAAM;AAAA,UACN,YAAY;AAAA,YACV,SAAS,EAAE,MAAM,UAAU,aAAa,+BAA+B;AAAA,YACvE,KAAK;AAAA,cACH,MAAM;AAAA,cACN,aAAa;AAAA,YACf;AAAA,YACA,SAAS;AAAA,cACP,MAAM;AAAA,cACN,aAAa,8CAA8C,kBAAkB;AAAA,YAC/E;AAAA,UACF;AAAA,UACA,UAAU,CAAC,SAAS;AAAA,QACtB;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF;;;AC5IA,SAAS,SAAAA,cAAgC;AACzC,SAAS,oBAAoB;AAC7B,SAAS,YAAAC,iBAAgB;AACzB,SAAS,kBAAkB;AAc3B,IAAM,eAAyC;AAAA,EAC7C,QAAQ;AAAA,IACN;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAAA,EACA,OAAO,CAAC,iBAAiB,wBAAwB,oBAAoB,UAAU;AAAA,EAC/E,OAAO;AAAA,IACL;AAAA,IACA;AAAA,EACF;AACF;AAEA,SAAS,iBAAyB;AAChC,QAAM,KAAKA,UAAS;AACpB,QAAM,aAAa,aAAa,EAAE,KAAK,aAAa;AAEpD,aAAW,aAAa,YAAY;AAClC,UAAM,aAAa,UAAU,WAAW,GAAG,KAAK,UAAU,WAAW,MAAM;AAC3E,QAAI,YAAY;AACd,UAAI,WAAW,SAAS,EAAG,QAAO;AAAA,IACpC,OAAO;AACL,aAAO;AAAA,IACT;AAAA,EACF;AAEA,QAAM,IAAI;AAAA,IACR,+BAA+B,WAAW,KAAK,IAAI,CAAC;AAAA,EAEtD;AACF;AAEA,SAAS,eAAgC;AACvC,SAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,UAAM,SAAS,aAAa;AAC5B,WAAO,OAAO,GAAG,aAAa,MAAM;AAClC,YAAM,UAAU,OAAO,QAAQ;AAC/B,UAAI,YAAY,QAAQ,OAAO,YAAY,UAAU;AACnD,cAAM,OAAO,QAAQ;AACrB,eAAO,MAAM,MAAM,QAAQ,IAAI,CAAC;AAAA,MAClC,OAAO;AACL,eAAO,MAAM,MAAM,OAAO,IAAI,MAAM,yBAAyB,CAAC,CAAC;AAAA,MACjE;AAAA,IACF,CAAC;AACD,WAAO,GAAG,SAAS,MAAM;AAAA,EAC3B,CAAC;AACH;AAEA,SAAS,qBAAqB,MAAc,YAAY,KAAuB;AAC7E,QAAM,QAAQ,KAAK,IAAI;AACvB,SAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,aAAS,UAAU;AACjB,UAAI,KAAK,IAAI,IAAI,QAAQ,WAAW;AAClC,eAAO,IAAI,MAAM,yBAAyB,IAAI,oBAAoB,SAAS,IAAI,CAAC;AAChF;AAAA,MACF;AAEA,YAAM,oBAAoB,IAAI,eAAe,EAC1C,KAAK,CAAC,QAAQ;AACb,YAAI,IAAI,GAAI,SAAQ;AAAA,YACf,YAAW,SAAS,GAAG;AAAA,MAC9B,CAAC,EACA,MAAM,MAAM;AACX,mBAAW,SAAS,GAAG;AAAA,MACzB,CAAC;AAAA,IACL;AACA,YAAQ;AAAA,EACV,CAAC;AACH;AAEA,eAAsB,aAAa,SAAuD;AACxF,QAAM,aAAa,eAAe;AAClC,QAAM,OAAO,QAAQ,iBAAkB,MAAM,aAAa;AAE1D,QAAM,OAAO;AAAA,IACX,2BAA2B,IAAI;AAAA,IAC/B,mBAAmB,QAAQ,UAAU;AAAA,IACrC;AAAA,IACA;AAAA,IACA,GAAI,QAAQ,SAAS,CAAC;AAAA,EACxB;AAEA,QAAM,QAAQD,OAAM,YAAY,MAAM;AAAA,IACpC,OAAO;AAAA,IACP,UAAU;AAAA,EACZ,CAAC;AAED,MAAI,MAAM,QAAQ,QAAW;AAC3B,UAAM,IAAI,MAAM,8BAA8B,UAAU,EAAE;AAAA,EAC5D;AAEA,QAAM,GAAG,SAAS,MAAM;AAAA,EAExB,CAAC;AAED,QAAM,qBAAqB,IAAI;AAE/B,SAAO,EAAE,MAAM,SAAS,OAAO,KAAK,MAAM,IAAI;AAChD;;;ACjFO,IAAM,WAAN,MAAuC;AAAA,EACpC;AAAA,EACA,UAAU,oBAAI,IAAwB;AAAA,EACtC,UAAU;AAAA,EAElB,YAAY,QAAwB;AAClC,SAAK,SAAS;AAAA,EAChB;AAAA;AAAA;AAAA;AAAA,EAMA,OAAO,MACL,SACA,MACA,SACa;AACb,WAAO,kBAAkB,SAAS,MAAM,OAAO;AAAA,EACjD;AAAA,EAEA,OAAO,KAAK,KAAa,SAA4D;AACnF,WAAO,iBAAiB,KAAK,OAAO;AAAA,EACtC;AAAA,EAEA,OAAO,MAAM,SAA2E;AACtF,WAAO,kBAAkB,OAAO;AAAA,EAClC;AAAA,EAEA,OAAO,aAAa,SAAuD;AACzE,WAAO,aAAa,OAAO;AAAA,EAC7B;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,QAAuC;AAC3C,QAAI,KAAK,QAAS,QAAO,EAAE,QAAQ,CAAC,EAAE;AAEtC,UAAM,UAAU,OAAO,QAAQ,KAAK,OAAO,UAAU;AACrD,UAAM,UAAU,MAAM,QAAQ;AAAA,MAC5B,QAAQ,IAAI,CAAC,CAAC,WAAW,KAAK,MAAM,KAAK,aAAa,WAAW,KAAK,CAAC;AAAA,IACzE;AAEA,UAAM,SAAmB,CAAC;AAC1B,aAAS,IAAI,GAAG,IAAI,QAAQ,QAAQ,KAAK;AACvC,YAAM,SAAS,QAAQ,CAAC;AACxB,UAAI,OAAO,WAAW,YAAY;AAChC,cAAM,YAAY,QAAQ,CAAC,EAAG,CAAC;AAC/B,eAAO;AAAA,UACL,GAAG,SAAS,KAAK,OAAO,kBAAkB,QAAQ,OAAO,OAAO,UAAU,OAAO,OAAO,MAAM,CAAC;AAAA,QACjG;AAAA,MACF;AAAA,IACF;AAEA,QAAI,OAAO,SAAS,KAAK,OAAO,WAAW,QAAQ,QAAQ;AACzD,YAAM,IAAI,MAAM;AAAA,EAAgC,OAAO,KAAK,IAAI,CAAC,EAAE;AAAA,IACrE;AAEA,SAAK,UAAU;AACf,WAAO,EAAE,OAAO;AAAA,EAClB;AAAA,EAEA,MAAM,OAAsB;AAC1B,UAAM,gBAAiC,CAAC;AAExC,eAAW,SAAS,KAAK,QAAQ,OAAO,GAAG;AACzC,UAAI,MAAM,YAAY;AACpB,sBAAc;AAAA,UACZ,MAAM,WAAW,MAAM,EAAE,MAAM,MAAM;AAAA,UAErC,CAAC;AAAA,QACH;AAAA,MACF;AAAA,IACF;AAEA,UAAM,QAAQ,WAAW,aAAa;AAEtC,QAAI,KAAK,OAAO,kBAAkB;AAChC,iBAAW,WAAW,KAAK,OAAO,kBAAkB;AAClD,YAAI;AACF,kBAAQ,QAAQ,KAAK;AAAA,QACvB,QAAQ;AAAA,QAER;AAAA,MACF;AAAA,IACF;AAEA,SAAK,QAAQ,MAAM;AACnB,SAAK,UAAU;AAAA,EACjB;AAAA;AAAA;AAAA;AAAA,EAMA,eAA4C;AAC1C,UAAM,SAAsC,CAAC;AAC7C,eAAW,SAAS,KAAK,QAAQ,OAAO,GAAG;AACzC,aAAO,OAAO,QAAQ,MAAM,QAAQ;AAAA,IACtC;AACA,WAAO;AAAA,EACT;AAAA,EAEA,cAA4B;AAC1B,UAAM,MAAoB,CAAC;AAC3B,eAAW,SAAS,KAAK,QAAQ,OAAO,GAAG;AACzC,UAAI,KAAK,GAAG,MAAM,OAAO;AAAA,IAC3B;AACA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAMA,MAAc,aAAa,WAAmB,OAAgC;AAC5E,QAAI,MAAM,SAAS,SAAS;AAC1B,YAAM,QAAQ,iBAAiB,WAAW,KAAK;AAC/C,WAAK,QAAQ,IAAI,WAAW;AAAA,QAC1B;AAAA,QACA,UAAU,MAAM;AAAA,QAChB,SAAS,MAAM;AAAA,MACjB,CAAC;AACD;AAAA,IACF;AAEA,UAAM,aACJ,MAAM,SAAS,UACX,MAAM,aAAa,WAAW,KAAK,IACnC,MAAM,YAAY,WAAW,KAAK;AAExC,SAAK,QAAQ,IAAI,WAAW;AAAA,MAC1B;AAAA,MACA,UAAU,WAAW;AAAA,MACrB,SAAS,WAAW;AAAA,MACpB;AAAA,IACF,CAAC;AAAA,EACH;AACF;","names":["spawn","platform"]}
package/package.json ADDED
@@ -0,0 +1,59 @@
1
+ {
2
+ "name": "@octavus/computer",
3
+ "version": "2.17.0",
4
+ "description": "Computer capability layer for Octavus agents — browser, filesystem, and shell via MCP",
5
+ "license": "MIT",
6
+ "author": "Octavus AI <dev@octavus.ai>",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "https://github.com/octavus-ai/agent-sdk.git",
10
+ "directory": "packages/computer"
11
+ },
12
+ "homepage": "https://octavus.ai",
13
+ "bugs": {
14
+ "url": "https://github.com/octavus-ai/agent-sdk/issues"
15
+ },
16
+ "keywords": [
17
+ "octavus",
18
+ "ai",
19
+ "agents",
20
+ "sdk",
21
+ "mcp",
22
+ "computer",
23
+ "browser",
24
+ "filesystem",
25
+ "shell"
26
+ ],
27
+ "type": "module",
28
+ "sideEffects": false,
29
+ "main": "./dist/index.js",
30
+ "types": "./dist/index.d.ts",
31
+ "exports": {
32
+ ".": {
33
+ "types": "./dist/index.d.ts",
34
+ "import": "./dist/index.js"
35
+ }
36
+ },
37
+ "files": [
38
+ "dist"
39
+ ],
40
+ "publishConfig": {
41
+ "access": "public"
42
+ },
43
+ "dependencies": {
44
+ "@modelcontextprotocol/sdk": "^1.27.1",
45
+ "@octavus/core": "^2.17.0"
46
+ },
47
+ "devDependencies": {
48
+ "tsup": "^8.3.5",
49
+ "typescript": "^5.6.3"
50
+ },
51
+ "scripts": {
52
+ "build": "tsup",
53
+ "dev": "tsup --watch",
54
+ "lint": "eslint src/ --max-warnings 0",
55
+ "lint:fix": "eslint src/ --max-warnings 0 --fix",
56
+ "type-check": "tsc --noEmit",
57
+ "clean": "rm -rf dist .turbo"
58
+ }
59
+ }