noumen 0.6.0 → 0.8.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/README.md +237 -93
- package/dist/a2a/index.d.ts +5 -7
- package/dist/a2a/index.js +3 -4
- package/dist/a2a/index.js.map +1 -1
- package/dist/acp/index.d.ts +5 -7
- package/dist/acp/index.js +0 -1
- package/dist/acp/index.js.map +1 -1
- package/dist/{agent-DWE4_P5X.d.ts → agent-D0gl-qYi.d.ts} +89 -34
- package/dist/{chunk-6MMYCGJQ.js → chunk-5HY4IYNT.js} +1529 -2321
- package/dist/chunk-5HY4IYNT.js.map +1 -0
- package/dist/chunk-BC5BLWBC.js +21 -0
- package/dist/chunk-BC5BLWBC.js.map +1 -0
- package/dist/{chunk-XZN4QZLK.js → chunk-CX4BL6PC.js} +25 -15
- package/dist/chunk-CX4BL6PC.js.map +1 -0
- package/dist/{chunk-5GEX6ZSB.js → chunk-HQISH4D7.js} +60 -1
- package/dist/chunk-HQISH4D7.js.map +1 -0
- package/dist/{chunk-Y45R3PQL.js → chunk-NUCJXOUV.js} +32 -18
- package/dist/{chunk-Y45R3PQL.js.map → chunk-NUCJXOUV.js.map} +1 -1
- package/dist/chunk-OPFFLQZL.js +40 -0
- package/dist/chunk-OPFFLQZL.js.map +1 -0
- package/dist/chunk-PDEAJ272.js +660 -0
- package/dist/chunk-PDEAJ272.js.map +1 -0
- package/dist/chunk-PKHLGGEC.js +115 -0
- package/dist/chunk-PKHLGGEC.js.map +1 -0
- package/dist/chunk-XQTNXRE7.js +176 -0
- package/dist/chunk-XQTNXRE7.js.map +1 -0
- package/dist/chunk-XZPAA5TO.js +817 -0
- package/dist/chunk-XZPAA5TO.js.map +1 -0
- package/dist/cli/index.js +77 -42
- package/dist/cli/index.js.map +1 -1
- package/dist/client/index.d.ts +1 -2
- package/dist/client/index.js +0 -2
- package/dist/client/index.js.map +1 -1
- package/dist/client-JJFLE6RT.js +9 -0
- package/dist/{computer-BPdxSo6X.d.ts → computer-DzMR92tK.d.ts} +1 -1
- package/dist/docker.d.ts +2 -2
- package/dist/docker.js +0 -1
- package/dist/docker.js.map +1 -1
- package/dist/e2b.d.ts +2 -2
- package/dist/e2b.js +0 -1
- package/dist/e2b.js.map +1 -1
- package/dist/freestyle.d.ts +2 -2
- package/dist/freestyle.js +0 -1
- package/dist/freestyle.js.map +1 -1
- package/dist/{headless-FFU2DESQ.js → headless-25DU4MJQ.js} +1 -3
- package/dist/{headless-FFU2DESQ.js.map → headless-25DU4MJQ.js.map} +1 -1
- package/dist/{history-snip-64GYP4ZL.js → history-snip-HAWNAYKY.js} +1 -2
- package/dist/index.d.ts +351 -72
- package/dist/index.js +54 -55
- package/dist/jsonrpc/index.js +0 -1
- package/dist/local.d.ts +168 -0
- package/dist/local.js +40 -0
- package/dist/local.js.map +1 -0
- package/dist/lsp/index.d.ts +4 -5
- package/dist/lsp/index.js +0 -1
- package/dist/{lsp-PS3BWIHC.js → lsp-3APWNKB2.js} +1 -2
- package/dist/{manager-DLXK63XC.js → manager-Z5EQ7YYV.js} +1 -2
- package/dist/mcp/index.d.ts +16 -8
- package/dist/mcp/index.js +5 -6
- package/dist/mcp/index.js.map +1 -1
- package/dist/{mcp-auth-AEI2R4ZC.js → mcp-auth-NOIQPF7W.js} +1 -2
- package/dist/{provider-factory-TUHU3DIG.js → provider-factory-KNBSHXJ6.js} +3 -3
- package/dist/{render-GRN4ZSSW.js → render-4VEODRK7.js} +1 -2
- package/dist/{resolve-6KUZNEYW.js → resolve-AGQZFMKD.js} +3 -3
- package/dist/sandbox-DAqQo0Tj.d.ts +49 -0
- package/dist/sandbox-index-ODNREIFA.js +32 -0
- package/dist/sandbox-index-ODNREIFA.js.map +1 -0
- package/dist/server/index.d.ts +18 -7
- package/dist/server/index.js +9 -5
- package/dist/server/index.js.map +1 -1
- package/dist/{server-BzNGKTP6.d.ts → server-DFXdlqyX.d.ts} +1 -1
- package/dist/{spinner-OJNR6NFO.js → spinner-72JEISPK.js} +1 -2
- package/dist/sprites.d.ts +2 -2
- package/dist/sprites.js +0 -1
- package/dist/sprites.js.map +1 -1
- package/dist/ssh.d.ts +2 -2
- package/dist/ssh.js +0 -1
- package/dist/ssh.js.map +1 -1
- package/dist/{types-DhXwOQwD.d.ts → types-BX4ALqoN.d.ts} +76 -4
- package/dist/{types-kiGBF35b.d.ts → types-DLZNyF5t.d.ts} +125 -1
- package/dist/unsandboxed.d.ts +59 -0
- package/dist/unsandboxed.js +32 -0
- package/dist/unsandboxed.js.map +1 -0
- package/dist/{uuid-RVN2T26F.js → uuid-CVTNAPEB.js} +1 -2
- package/dist/{zod-7YXKWYMC.js → zod-VKURGPRT.js} +1 -2
- package/package.json +35 -50
- package/dist/cache-BlBwXXPS.d.ts +0 -38
- package/dist/chunk-5GEX6ZSB.js.map +0 -1
- package/dist/chunk-6MMYCGJQ.js.map +0 -1
- package/dist/chunk-7IQCQI2G.js +0 -94
- package/dist/chunk-7IQCQI2G.js.map +0 -1
- package/dist/chunk-CCM2AXZG.js +0 -16
- package/dist/chunk-CCM2AXZG.js.map +0 -1
- package/dist/chunk-DGUM43GV.js +0 -11
- package/dist/chunk-HEQQQGK5.js +0 -131
- package/dist/chunk-HEQQQGK5.js.map +0 -1
- package/dist/chunk-I3JTUFPK.js +0 -171
- package/dist/chunk-I3JTUFPK.js.map +0 -1
- package/dist/chunk-XZN4QZLK.js.map +0 -1
- package/dist/chunk-ZXSDKBYB.js +0 -474
- package/dist/chunk-ZXSDKBYB.js.map +0 -1
- package/dist/client-CRRO2376.js +0 -10
- package/dist/providers/anthropic.d.ts +0 -19
- package/dist/providers/anthropic.js +0 -36
- package/dist/providers/anthropic.js.map +0 -1
- package/dist/providers/bedrock.d.ts +0 -39
- package/dist/providers/bedrock.js +0 -56
- package/dist/providers/bedrock.js.map +0 -1
- package/dist/providers/gemini.d.ts +0 -17
- package/dist/providers/gemini.js +0 -262
- package/dist/providers/gemini.js.map +0 -1
- package/dist/providers/ollama.d.ts +0 -13
- package/dist/providers/ollama.js +0 -20
- package/dist/providers/ollama.js.map +0 -1
- package/dist/providers/openai.d.ts +0 -21
- package/dist/providers/openai.js +0 -9
- package/dist/providers/openrouter.d.ts +0 -16
- package/dist/providers/openrouter.js +0 -24
- package/dist/providers/openrouter.js.map +0 -1
- package/dist/providers/vertex.d.ts +0 -42
- package/dist/providers/vertex.js +0 -68
- package/dist/providers/vertex.js.map +0 -1
- package/dist/sandbox-9qeMTNrD.d.ts +0 -126
- package/dist/types-CD0rUKKT.d.ts +0 -109
- package/dist/uuid-RVN2T26F.js.map +0 -1
- package/dist/zod-7YXKWYMC.js.map +0 -1
- /package/dist/{chunk-DGUM43GV.js.map → client-JJFLE6RT.js.map} +0 -0
- /package/dist/{client-CRRO2376.js.map → history-snip-HAWNAYKY.js.map} +0 -0
- /package/dist/{history-snip-64GYP4ZL.js.map → lsp-3APWNKB2.js.map} +0 -0
- /package/dist/{lsp-PS3BWIHC.js.map → manager-Z5EQ7YYV.js.map} +0 -0
- /package/dist/{manager-DLXK63XC.js.map → mcp-auth-NOIQPF7W.js.map} +0 -0
- /package/dist/{mcp-auth-AEI2R4ZC.js.map → provider-factory-KNBSHXJ6.js.map} +0 -0
- /package/dist/{provider-factory-TUHU3DIG.js.map → render-4VEODRK7.js.map} +0 -0
- /package/dist/{providers/openai.js.map → resolve-AGQZFMKD.js.map} +0 -0
- /package/dist/{render-GRN4ZSSW.js.map → spinner-72JEISPK.js.map} +0 -0
- /package/dist/{resolve-6KUZNEYW.js.map → uuid-CVTNAPEB.js.map} +0 -0
- /package/dist/{spinner-OJNR6NFO.js.map → zod-VKURGPRT.js.map} +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/presets.ts","../src/swarm/mailbox.ts","../src/swarm/manager.ts","../src/swarm/backends/in-process.ts","../src/permissions/updates.ts","../src/tracing/otel.ts","../src/memory/file-provider.ts"],"sourcesContent":["import { Agent } from \"./agent.js\";\nimport type { AIProvider } from \"./providers/types.js\";\nimport type { Sandbox } from \"./virtual/sandbox.js\";\nimport type { HookDefinition } from \"./hooks/types.js\";\nimport type { McpServerConfig } from \"./mcp/types.js\";\nimport type { AutoTitleConfig } from \"./session/auto-title.js\";\n\nexport interface PresetOptions {\n /** The AI provider instance (e.g. `new AiSdkProvider({ model })`) */\n provider: AIProvider;\n /** Working directory for path resolution. Defaults to `process.cwd()`. */\n cwd?: string;\n /** Model name override. Each preset has a sensible default. */\n model?: string;\n /**\n * Sandbox providing filesystem + shell execution. Required — the root\n * barrel deliberately doesn't pull in a default implementation, so\n * callers must pick a backend explicitly:\n *\n * - `import { UnsandboxedLocal } from \"noumen/unsandboxed\"` — raw host access.\n * - `import { LocalSandbox } from \"noumen/local\"` — OS-level sandboxing.\n * - `import { DockerSandbox } from \"noumen/docker\"`\n * - `import { E2BSandbox } from \"noumen/e2b\"`\n * - `import { FreestyleSandbox } from \"noumen/freestyle\"`\n * - `import { SshSandbox } from \"noumen/ssh\"`\n * - `import { SpritesSandbox } from \"noumen/sprites\"`\n *\n * You can also pass any `{ fs, computer }` pair for custom sandboxes.\n */\n sandbox: Sandbox;\n /** Extra hooks to attach. */\n hooks?: HookDefinition[];\n /** MCP servers to connect to during `init()`. */\n mcpServers?: Record<string, McpServerConfig>;\n /** Custom system prompt prepended to the built-in prompt. */\n systemPrompt?: string;\n /**\n * Opt-in AI-generated session titles. `true` uses the agent's main\n * provider / model; pass a config object to override the model or\n * provider (typically a cheaper one like Haiku) used for title\n * generation only.\n */\n autoTitle?: AutoTitleConfig | boolean;\n}\n\n/**\n * Full-featured coding agent with subagents, tasks, plan mode, auto-compact,\n * retry, cost tracking, and project context enabled out of the box.\n */\nexport function codingAgent(opts: PresetOptions): Agent {\n const cwd = opts.cwd ?? process.cwd();\n return new Agent({\n provider: opts.provider,\n sandbox: opts.sandbox,\n options: {\n cwd,\n model: opts.model,\n systemPrompt: opts.systemPrompt,\n permissions: { mode: \"default\" },\n autoCompact: true,\n enableSubagents: true,\n enableTasks: true,\n enablePlanMode: true,\n projectContext: { cwd },\n costTracking: { enabled: true },\n retry: true,\n hooks: opts.hooks,\n mcpServers: opts.mcpServers,\n autoTitle: opts.autoTitle,\n },\n });\n}\n\n/**\n * Read-only planning agent — can explore the codebase but cannot make changes.\n * Useful for architecture analysis, code review prep, or scoping work.\n */\nexport function planningAgent(opts: PresetOptions): Agent {\n const cwd = opts.cwd ?? process.cwd();\n return new Agent({\n provider: opts.provider,\n sandbox: opts.sandbox,\n options: {\n cwd,\n model: opts.model,\n systemPrompt: opts.systemPrompt,\n permissions: { mode: \"plan\" },\n autoCompact: true,\n enableSubagents: false,\n enableTasks: false,\n enablePlanMode: true,\n projectContext: { cwd },\n costTracking: { enabled: true },\n retry: true,\n hooks: opts.hooks,\n mcpServers: opts.mcpServers,\n autoTitle: opts.autoTitle,\n },\n });\n}\n\n/**\n * Code review agent — read-only with web search enabled for looking up\n * documentation, best practices, and security advisories.\n */\nexport function reviewAgent(opts: PresetOptions): Agent {\n const cwd = opts.cwd ?? process.cwd();\n return new Agent({\n provider: opts.provider,\n sandbox: opts.sandbox,\n options: {\n cwd,\n model: opts.model,\n systemPrompt: opts.systemPrompt,\n permissions: { mode: \"plan\" },\n autoCompact: true,\n enableSubagents: false,\n enableTasks: false,\n enablePlanMode: true,\n projectContext: { cwd },\n costTracking: { enabled: true },\n retry: true,\n hooks: opts.hooks,\n mcpServers: opts.mcpServers,\n autoTitle: opts.autoTitle,\n webSearch: {\n search: async (query: string) => {\n try {\n const res = await fetch(`https://html.duckduckgo.com/html/?q=${encodeURIComponent(query)}`);\n if (!res.ok) return [];\n return [{ title: query, url: res.url, snippet: `Search results for: ${query}` }];\n } catch { return []; }\n },\n },\n },\n });\n}\n","import type { SwarmMessage } from \"./types.js\";\n\n/**\n * In-memory message queue for communication between swarm members.\n */\nexport class Mailbox {\n private messages: SwarmMessage[] = [];\n private listeners = new Map<string, Array<(msg: SwarmMessage) => void>>();\n\n /**\n * Send a message from one member to another.\n */\n send(from: string, to: string, content: string): void {\n const msg: SwarmMessage = {\n from,\n to,\n content,\n timestamp: new Date().toISOString(),\n };\n this.messages.push(msg);\n\n const handlers = this.listeners.get(to);\n if (handlers) {\n for (const handler of handlers) {\n handler(msg);\n }\n }\n }\n\n /**\n * Broadcast a message to all members except the sender.\n */\n broadcast(from: string, content: string, memberNames: string[]): void {\n for (const name of memberNames) {\n if (name !== from) {\n this.send(from, name, content);\n }\n }\n }\n\n /**\n * Get all messages sent to a specific member.\n */\n getMessagesFor(memberName: string): SwarmMessage[] {\n return this.messages.filter((m) => m.to === memberName);\n }\n\n /**\n * Get all unread messages for a member since the last check.\n */\n getNewMessagesFor(memberName: string, since: string): SwarmMessage[] {\n return this.messages.filter(\n (m) => m.to === memberName && m.timestamp > since,\n );\n }\n\n /**\n * Register a listener for incoming messages to a member.\n */\n onMessage(\n memberName: string,\n handler: (msg: SwarmMessage) => void,\n ): () => void {\n const existing = this.listeners.get(memberName) ?? [];\n existing.push(handler);\n this.listeners.set(memberName, existing);\n\n return () => {\n const handlers = this.listeners.get(memberName);\n if (handlers) {\n const idx = handlers.indexOf(handler);\n if (idx !== -1) handlers.splice(idx, 1);\n }\n };\n }\n\n /**\n * Get all messages in the mailbox.\n */\n getAllMessages(): SwarmMessage[] {\n return [...this.messages];\n }\n}\n","import type {\n SwarmConfig,\n SwarmMember,\n SwarmMemberConfig,\n SwarmMemberStatus,\n SwarmStatus,\n SwarmEvents,\n} from \"./types.js\";\nimport type { SwarmBackend } from \"./backends/types.js\";\nimport type { StreamEvent } from \"../session/types.js\";\nimport { Mailbox } from \"./mailbox.js\";\nimport { generateUUID } from \"../utils/uuid.js\";\n\n/**\n * Orchestrates multiple agent threads running in parallel.\n */\nexport class SwarmManager {\n private members = new Map<string, SwarmMember>();\n private backend: SwarmBackend;\n private mailbox = new Mailbox();\n private config: SwarmConfig;\n private runningTasks = new Map<string, Promise<void>>();\n private eventHandlers: Array<(event: SwarmEvents) => void> = [];\n\n constructor(backend: SwarmBackend, config?: SwarmConfig) {\n this.backend = backend;\n this.config = config ?? {};\n }\n\n /**\n * Register a handler for swarm lifecycle events.\n */\n onEvent(handler: (event: SwarmEvents) => void): () => void {\n this.eventHandlers.push(handler);\n return () => {\n const idx = this.eventHandlers.indexOf(handler);\n if (idx !== -1) this.eventHandlers.splice(idx, 1);\n };\n }\n\n private emit(event: SwarmEvents): void {\n for (const handler of this.eventHandlers) {\n handler(event);\n }\n }\n\n /**\n * Spawn a new swarm member. Returns the member ID.\n */\n async spawn(config: SwarmMemberConfig): Promise<string> {\n const id = generateUUID();\n const member: SwarmMember = {\n id,\n name: config.name,\n status: \"pending\",\n };\n this.members.set(id, member);\n\n const maxConcurrent = this.config.maxConcurrent ?? 4;\n const running = Array.from(this.members.values()).filter(\n (m) => m.status === \"running\",\n ).length;\n\n if (running >= maxConcurrent) {\n // Wait for a slot\n await this.waitForSlot(maxConcurrent);\n }\n\n member.status = \"running\";\n this.emit({\n type: \"swarm_member_start\",\n memberId: id,\n memberName: config.name,\n });\n\n const task = this.runMember(config, member);\n this.runningTasks.set(id, task);\n\n return id;\n }\n\n private async runMember(\n config: SwarmMemberConfig,\n member: SwarmMember,\n ): Promise<void> {\n try {\n const gen = this.backend.spawn(config, member);\n let next = await gen.next();\n while (!next.done) {\n next = await gen.next();\n }\n member.result = next.value;\n member.status = \"completed\";\n this.emit({\n type: \"swarm_member_complete\",\n memberId: member.id,\n memberName: member.name,\n content: member.result,\n });\n } catch (err) {\n member.error = err instanceof Error ? err : new Error(String(err));\n member.status = \"failed\";\n this.emit({\n type: \"swarm_member_failed\",\n memberId: member.id,\n memberName: member.name,\n error: member.error,\n });\n } finally {\n this.runningTasks.delete(member.id);\n }\n }\n\n /**\n * Spawn multiple members concurrently. Returns their IDs.\n */\n async spawnAll(configs: SwarmMemberConfig[]): Promise<string[]> {\n const ids: string[] = [];\n for (const config of configs) {\n ids.push(await this.spawn(config));\n }\n return ids;\n }\n\n /**\n * Send a message between swarm members.\n */\n sendMessage(from: string, to: string, content: string): void {\n this.mailbox.send(from, to, content);\n this.emit({\n type: \"swarm_message\",\n memberId: to,\n memberName: this.getMemberName(to),\n content,\n });\n }\n\n /**\n * Kill a running member.\n */\n async kill(memberId: string): Promise<void> {\n const member = this.members.get(memberId);\n if (!member || member.status !== \"running\") return;\n\n await this.backend.kill(memberId);\n member.status = \"killed\";\n this.runningTasks.delete(memberId);\n }\n\n /**\n * Wait for all running members to complete.\n */\n async waitForAll(): Promise<void> {\n while (this.runningTasks.size > 0) {\n await Promise.race(this.runningTasks.values());\n }\n }\n\n /**\n * Get current swarm status.\n */\n getStatus(): SwarmStatus {\n return {\n members: Array.from(this.members.values()),\n messages: this.mailbox.getAllMessages(),\n };\n }\n\n /**\n * Get a specific member.\n */\n getMember(id: string): SwarmMember | undefined {\n return this.members.get(id);\n }\n\n /**\n * Get the mailbox for direct access.\n */\n getMailbox(): Mailbox {\n return this.mailbox;\n }\n\n private getMemberName(id: string): string {\n return this.members.get(id)?.name ?? id;\n }\n\n private async waitForSlot(maxConcurrent: number): Promise<void> {\n while (true) {\n const running = Array.from(this.members.values()).filter(\n (m) => m.status === \"running\",\n ).length;\n if (running < maxConcurrent) return;\n if (this.runningTasks.size > 0) {\n await Promise.race(this.runningTasks.values());\n } else {\n break;\n }\n }\n }\n}\n","import type { SwarmBackend } from \"./types.js\";\nimport type { SwarmMember, SwarmMemberConfig } from \"../types.js\";\nimport type { StreamEvent } from \"../../session/types.js\";\nimport type { ThreadConfig } from \"../../thread.js\";\nimport { Thread } from \"../../thread.js\";\n\n/**\n * In-process backend: runs each swarm member as a concurrent Thread.\n */\nexport class InProcessBackend implements SwarmBackend {\n private threadConfig: Omit<ThreadConfig, \"systemPrompt\" | \"model\">;\n private abortControllers = new Map<string, AbortController>();\n\n constructor(threadConfig: Omit<ThreadConfig, \"systemPrompt\" | \"model\">) {\n this.threadConfig = threadConfig;\n }\n\n async *spawn(\n config: SwarmMemberConfig,\n member: SwarmMember,\n ): AsyncGenerator<StreamEvent, string, unknown> {\n const ac = new AbortController();\n this.abortControllers.set(member.id, ac);\n\n const childTools = config.allowedTools\n ? (this.threadConfig.tools ?? []).filter((t) =>\n config.allowedTools!.includes(t.name),\n )\n : this.threadConfig.tools;\n\n const thread = new Thread(\n {\n ...this.threadConfig,\n tools: childTools,\n systemPrompt: config.systemPrompt,\n model: config.model,\n },\n { cwd: (this.threadConfig as { cwd?: string }).cwd },\n );\n\n member.sessionId = thread.sessionId;\n let resultText = \"\";\n\n for await (const event of thread.run(config.prompt, {\n signal: ac.signal,\n })) {\n yield event;\n\n if (event.type === \"message_complete\" && event.message.content) {\n resultText += event.message.content;\n }\n }\n\n this.abortControllers.delete(member.id);\n return resultText;\n }\n\n async kill(memberId: string): Promise<void> {\n const ac = this.abortControllers.get(memberId);\n if (ac) {\n ac.abort();\n this.abortControllers.delete(memberId);\n }\n }\n}\n","import type { PermissionContext, PermissionUpdate } from \"./types.js\";\n\n/**\n * Apply a permission update to an in-memory context.\n * Returns the mutated context (same reference).\n */\nexport function applyPermissionUpdate(\n ctx: PermissionContext,\n update: PermissionUpdate,\n): PermissionContext {\n switch (update.type) {\n case \"addRules\":\n ctx.rules.push(...update.rules);\n break;\n\n case \"removeRules\":\n ctx.rules = ctx.rules.filter((r) => {\n if (r.toolName !== update.toolName) return true;\n if (update.behavior && r.behavior !== update.behavior) return true;\n return false;\n });\n break;\n\n case \"setMode\":\n ctx.mode = update.mode;\n break;\n\n case \"addDirectories\":\n for (const dir of update.directories) {\n if (!ctx.workingDirectories.includes(dir)) {\n ctx.workingDirectories.push(dir);\n }\n }\n break;\n\n case \"removeDirectories\":\n ctx.workingDirectories = ctx.workingDirectories.filter(\n (d) => !update.directories.includes(d),\n );\n break;\n }\n\n return ctx;\n}\n\n/**\n * Apply multiple permission updates in order.\n */\nexport function applyPermissionUpdates(\n ctx: PermissionContext,\n updates: PermissionUpdate[],\n): PermissionContext {\n for (const update of updates) {\n applyPermissionUpdate(ctx, update);\n }\n return ctx;\n}\n","import {\n SpanStatusCode,\n type Span,\n type SpanAttributeValue,\n type SpanOptions,\n type Tracer,\n} from \"./types.js\";\nimport { NoopSpan, NoopTracer } from \"./noop.js\";\n\ntype OTelApi = typeof import(\"@opentelemetry/api\");\n\nlet otelApi: OTelApi | null = null;\nlet otelLoadFailed = false;\n\nasync function loadOTelApi(): Promise<OTelApi | null> {\n if (otelApi) return otelApi;\n if (otelLoadFailed) return null;\n try {\n otelApi = await import(\"@opentelemetry/api\");\n return otelApi;\n } catch {\n otelLoadFailed = true;\n return null;\n }\n}\n\nclass OTelSpan implements Span {\n readonly name: string;\n private inner: import(\"@opentelemetry/api\").Span;\n\n constructor(name: string, inner: import(\"@opentelemetry/api\").Span) {\n this.name = name;\n this.inner = inner;\n }\n\n setAttribute(key: string, value: SpanAttributeValue): void {\n this.inner.setAttribute(key, value);\n }\n\n addEvent(name: string, attributes?: Record<string, SpanAttributeValue>): void {\n this.inner.addEvent(name, attributes);\n }\n\n setStatus(code: SpanStatusCode, message?: string): void {\n const otelCode = code === SpanStatusCode.ERROR\n ? 2 /* SpanStatusCode.ERROR in OTEL */\n : 1 /* SpanStatusCode.OK in OTEL */;\n this.inner.setStatus({ code: otelCode, message });\n }\n\n end(): void {\n this.inner.end();\n }\n\n /** Access the underlying OTEL span for advanced use cases. */\n getInnerSpan(): import(\"@opentelemetry/api\").Span {\n return this.inner;\n }\n}\n\n/**\n * Adapter that bridges noumen's `Tracer` interface to an OpenTelemetry\n * `TracerProvider`. The `@opentelemetry/api` package is loaded lazily via\n * dynamic `import()` so it remains an optional peer dependency.\n *\n * Call `OTelTracer.create()` (async factory) to obtain an instance.\n * If `@opentelemetry/api` is not installed, the factory returns a `NoopTracer`.\n */\nexport class OTelTracer implements Tracer {\n private otelTracer: import(\"@opentelemetry/api\").Tracer;\n private api: OTelApi;\n\n private constructor(api: OTelApi, otelTracer: import(\"@opentelemetry/api\").Tracer) {\n this.api = api;\n this.otelTracer = otelTracer;\n }\n\n /**\n * Create an `OTelTracer`. Falls back to `NoopTracer` if\n * `@opentelemetry/api` is not available at runtime.\n */\n static async create(\n serviceName: string = \"noumen\",\n version?: string,\n ): Promise<Tracer> {\n const api = await loadOTelApi();\n if (!api) return new NoopTracer();\n const tracer = api.trace.getTracer(serviceName, version);\n return new OTelTracer(api, tracer);\n }\n\n startSpan(name: string, options?: SpanOptions): Span {\n const parentCtx = options?.parent instanceof OTelSpan\n ? this.api.trace.setSpan(this.api.context.active(), options.parent.getInnerSpan())\n : this.api.context.active();\n\n const otelSpan = this.otelTracer.startSpan(\n name,\n options?.attributes ? { attributes: options.attributes as Record<string, import(\"@opentelemetry/api\").AttributeValue> } : undefined,\n parentCtx,\n );\n\n return new OTelSpan(name, otelSpan);\n }\n}\n","import type { VirtualFs } from \"../virtual/fs.js\";\nimport type { MemoryEntry, MemoryProvider, MemoryType } from \"./types.js\";\n\nconst INDEX_NAME = \"MEMORY.md\";\nconst DEFAULT_MAX_LINES = 200;\nconst DEFAULT_MAX_BYTES = 25_000;\n\nconst MEMORY_TYPES: ReadonlySet<string> = new Set([\n \"user\",\n \"project\",\n \"feedback\",\n \"reference\",\n]);\n\n// ---------------------------------------------------------------------------\n// Frontmatter helpers\n// ---------------------------------------------------------------------------\n\ninterface ParsedFrontmatter {\n name?: string;\n description?: string;\n type?: MemoryType;\n rest: string;\n}\n\nfunction parseFrontmatter(raw: string): ParsedFrontmatter {\n const trimmed = raw.trimStart();\n if (!trimmed.startsWith(\"---\")) {\n return { rest: raw };\n }\n\n const endIdx = trimmed.indexOf(\"---\", 3);\n if (endIdx === -1) {\n return { rest: raw };\n }\n\n const fmBlock = trimmed.slice(3, endIdx).trim();\n const rest = trimmed.slice(endIdx + 3).trim();\n\n let name: string | undefined;\n let description: string | undefined;\n let type: MemoryType | undefined;\n\n for (const line of fmBlock.split(\"\\n\")) {\n const colonIdx = line.indexOf(\":\");\n if (colonIdx === -1) continue;\n const key = line.slice(0, colonIdx).trim();\n const value = line.slice(colonIdx + 1).trim();\n if (key === \"name\") name = value;\n else if (key === \"description\") description = value;\n else if (key === \"type\" && MEMORY_TYPES.has(value)) type = value as MemoryType;\n }\n\n return { name, description, type, rest };\n}\n\nfunction serializeEntry(entry: MemoryEntry): string {\n const lines = [\n \"---\",\n `name: ${entry.name}`,\n `description: ${entry.description}`,\n `type: ${entry.type}`,\n \"---\",\n \"\",\n entry.content,\n ];\n return lines.join(\"\\n\");\n}\n\nfunction slugify(name: string): string {\n return name\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, \"_\")\n .replace(/^_+|_+$/g, \"\")\n .slice(0, 60);\n}\n\n// ---------------------------------------------------------------------------\n// Index truncation (ported from claude-code's truncateEntrypointContent)\n// ---------------------------------------------------------------------------\n\nexport interface IndexTruncation {\n content: string;\n lineCount: number;\n byteCount: number;\n wasLineTruncated: boolean;\n wasByteTruncated: boolean;\n}\n\nexport function truncateIndex(\n raw: string,\n maxLines = DEFAULT_MAX_LINES,\n maxBytes = DEFAULT_MAX_BYTES,\n): IndexTruncation {\n const trimmed = raw.trim();\n const contentLines = trimmed.split(\"\\n\");\n const lineCount = contentLines.length;\n const byteCount = trimmed.length;\n\n const wasLineTruncated = lineCount > maxLines;\n const wasByteTruncated = byteCount > maxBytes;\n\n if (!wasLineTruncated && !wasByteTruncated) {\n return { content: trimmed, lineCount, byteCount, wasLineTruncated, wasByteTruncated };\n }\n\n let truncated = wasLineTruncated\n ? contentLines.slice(0, maxLines).join(\"\\n\")\n : trimmed;\n\n if (truncated.length > maxBytes) {\n const cutAt = truncated.lastIndexOf(\"\\n\", maxBytes);\n truncated = truncated.slice(0, cutAt > 0 ? cutAt : maxBytes);\n }\n\n const reason = wasByteTruncated && !wasLineTruncated\n ? `${byteCount} bytes (limit: ${maxBytes})`\n : wasLineTruncated && !wasByteTruncated\n ? `${lineCount} lines (limit: ${maxLines})`\n : `${lineCount} lines and ${byteCount} bytes`;\n\n return {\n content:\n truncated +\n `\\n\\n> WARNING: ${INDEX_NAME} is ${reason}. Only part of it was loaded. Keep index entries to one line under ~200 chars; move detail into topic files.`,\n lineCount,\n byteCount,\n wasLineTruncated,\n wasByteTruncated,\n };\n}\n\n// ---------------------------------------------------------------------------\n// FileMemoryProvider\n// ---------------------------------------------------------------------------\n\n/**\n * Default `MemoryProvider` that stores memories as individual `.md` files\n * with YAML frontmatter, plus a `MEMORY.md` index. All I/O goes through\n * `VirtualFs` so it works with any sandbox backend.\n */\nexport class FileMemoryProvider implements MemoryProvider {\n private fs: VirtualFs;\n private dir: string;\n private maxIndexLines: number;\n\n constructor(fs: VirtualFs, memoryDir: string, maxIndexLines = DEFAULT_MAX_LINES) {\n this.fs = fs;\n this.dir = memoryDir.endsWith(\"/\") ? memoryDir : memoryDir + \"/\";\n this.maxIndexLines = maxIndexLines;\n }\n\n private indexPath(): string {\n return this.dir + INDEX_NAME;\n }\n\n private async ensureDir(): Promise<void> {\n const exists = await this.fs.exists(this.dir);\n if (!exists) {\n await this.fs.mkdir(this.dir, { recursive: true });\n }\n }\n\n async loadIndex(): Promise<string> {\n try {\n const raw = await this.fs.readFile(this.indexPath());\n return truncateIndex(raw, this.maxIndexLines).content;\n } catch {\n return \"\";\n }\n }\n\n async loadEntry(path: string): Promise<MemoryEntry | null> {\n const fullPath = path.startsWith(this.dir) ? path : this.dir + path;\n try {\n const raw = await this.fs.readFile(fullPath);\n const fm = parseFrontmatter(raw);\n const stat = await this.fs.stat(fullPath).catch(() => null);\n return {\n name: fm.name ?? pathToName(path),\n description: fm.description ?? \"\",\n type: fm.type ?? \"project\",\n content: fm.rest,\n path: path.startsWith(this.dir) ? path.slice(this.dir.length) : path,\n updatedAt: stat?.modifiedAt?.toISOString(),\n };\n } catch {\n return null;\n }\n }\n\n async saveEntry(entry: MemoryEntry): Promise<void> {\n await this.ensureDir();\n const relativePath = entry.path ?? slugify(entry.name) + \".md\";\n const fullPath = this.dir + relativePath;\n const content = serializeEntry({ ...entry, path: relativePath });\n await this.fs.writeFile(fullPath, content);\n await this.rebuildIndex();\n }\n\n async removeEntry(path: string): Promise<void> {\n const fullPath = path.startsWith(this.dir) ? path : this.dir + path;\n try {\n await this.fs.deleteFile(fullPath);\n } catch {\n // Already gone\n }\n await this.rebuildIndex();\n }\n\n async listEntries(): Promise<MemoryEntry[]> {\n try {\n const files = await this.fs.readdir(this.dir);\n const entries: MemoryEntry[] = [];\n for (const file of files) {\n if (!file.isFile || !file.name.endsWith(\".md\") || file.name === INDEX_NAME) continue;\n const entry = await this.loadEntry(file.name);\n if (entry) entries.push(entry);\n }\n return entries;\n } catch {\n return [];\n }\n }\n\n async search(query: string): Promise<MemoryEntry[]> {\n const entries = await this.listEntries();\n const lower = query.toLowerCase();\n return entries.filter(\n (e) =>\n e.name.toLowerCase().includes(lower) ||\n e.description.toLowerCase().includes(lower) ||\n e.content.toLowerCase().includes(lower),\n );\n }\n\n private async rebuildIndex(): Promise<void> {\n const entries = await this.listEntries();\n const lines: string[] = [];\n for (const entry of entries) {\n const relativePath = entry.path ?? slugify(entry.name) + \".md\";\n const desc = entry.description ? ` — ${entry.description}` : \"\";\n lines.push(`- [${entry.name}](${relativePath})${desc}`);\n }\n await this.ensureDir();\n await this.fs.writeFile(this.indexPath(), lines.join(\"\\n\") + \"\\n\");\n }\n}\n\nfunction pathToName(p: string): string {\n const base = p.split(\"/\").pop() ?? p;\n return base.replace(/\\.md$/i, \"\").replace(/[_-]/g, \" \");\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import {
|
|
2
|
+
LocalFs
|
|
3
|
+
} from "./chunk-XQTNXRE7.js";
|
|
4
|
+
|
|
5
|
+
// src/virtual/sandboxed-local-computer.ts
|
|
6
|
+
import { exec as execCb } from "child_process";
|
|
7
|
+
import {
|
|
8
|
+
SandboxManager
|
|
9
|
+
} from "@anthropic-ai/sandbox-runtime";
|
|
10
|
+
var SandboxedLocalComputer = class {
|
|
11
|
+
defaultCwd;
|
|
12
|
+
defaultTimeout;
|
|
13
|
+
sandboxConfig;
|
|
14
|
+
initPromise = null;
|
|
15
|
+
initialized = false;
|
|
16
|
+
constructor(opts) {
|
|
17
|
+
this.defaultCwd = opts?.defaultCwd ?? process.cwd();
|
|
18
|
+
this.defaultTimeout = opts?.defaultTimeout ?? 3e4;
|
|
19
|
+
this.sandboxConfig = opts?.sandbox ?? {};
|
|
20
|
+
}
|
|
21
|
+
buildRuntimeConfig() {
|
|
22
|
+
const fs = this.sandboxConfig.filesystem;
|
|
23
|
+
const net = this.sandboxConfig.network;
|
|
24
|
+
return {
|
|
25
|
+
filesystem: {
|
|
26
|
+
allowWrite: fs?.allowWrite ?? [this.defaultCwd],
|
|
27
|
+
denyWrite: fs?.denyWrite ?? [],
|
|
28
|
+
denyRead: fs?.denyRead ?? [],
|
|
29
|
+
allowRead: fs?.allowRead ?? []
|
|
30
|
+
},
|
|
31
|
+
network: {
|
|
32
|
+
allowedDomains: net?.allowedDomains ?? [],
|
|
33
|
+
deniedDomains: net?.deniedDomains ?? []
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
async ensureInitialized() {
|
|
38
|
+
if (this.initialized) return;
|
|
39
|
+
if (!this.initPromise) {
|
|
40
|
+
this.initPromise = (async () => {
|
|
41
|
+
try {
|
|
42
|
+
await SandboxManager.initialize(this.buildRuntimeConfig());
|
|
43
|
+
this.initialized = true;
|
|
44
|
+
} catch (err) {
|
|
45
|
+
this.initPromise = null;
|
|
46
|
+
throw err;
|
|
47
|
+
}
|
|
48
|
+
})();
|
|
49
|
+
}
|
|
50
|
+
await this.initPromise;
|
|
51
|
+
}
|
|
52
|
+
async executeCommand(command, opts) {
|
|
53
|
+
await this.ensureInitialized();
|
|
54
|
+
const wrappedCommand = await SandboxManager.wrapWithSandbox(command);
|
|
55
|
+
return new Promise((resolve) => {
|
|
56
|
+
const child = execCb(
|
|
57
|
+
wrappedCommand,
|
|
58
|
+
{
|
|
59
|
+
cwd: opts?.cwd ?? this.defaultCwd,
|
|
60
|
+
timeout: opts?.timeout ?? this.defaultTimeout,
|
|
61
|
+
env: opts?.env ? { ...process.env, ...opts.env } : process.env,
|
|
62
|
+
maxBuffer: 10 * 1024 * 1024,
|
|
63
|
+
shell: process.env.SHELL || "/bin/sh"
|
|
64
|
+
},
|
|
65
|
+
(error, stdout, stderr) => {
|
|
66
|
+
const result = {
|
|
67
|
+
exitCode: error && "code" in error ? typeof error.code === "number" ? error.code : 1 : child.exitCode ?? 0,
|
|
68
|
+
stdout: stdout ?? "",
|
|
69
|
+
stderr: stderr ?? ""
|
|
70
|
+
};
|
|
71
|
+
Promise.resolve(SandboxManager.cleanupAfterCommand()).then(() => resolve(result)).catch(() => resolve(result));
|
|
72
|
+
}
|
|
73
|
+
);
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Tear down the sandbox runtime. Call when the agent is done.
|
|
78
|
+
*/
|
|
79
|
+
async dispose() {
|
|
80
|
+
if (this.initialized) {
|
|
81
|
+
await SandboxManager.reset();
|
|
82
|
+
this.initialized = false;
|
|
83
|
+
this.initPromise = null;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
// src/virtual/local-sandbox.ts
|
|
89
|
+
function LocalSandbox(opts) {
|
|
90
|
+
const cwd = opts?.cwd ?? process.cwd();
|
|
91
|
+
const computer = new SandboxedLocalComputer({
|
|
92
|
+
defaultCwd: cwd,
|
|
93
|
+
defaultTimeout: opts?.defaultTimeout,
|
|
94
|
+
sandbox: {
|
|
95
|
+
filesystem: {
|
|
96
|
+
allowWrite: [cwd, ...opts?.sandbox?.filesystem?.allowWrite ?? []],
|
|
97
|
+
denyWrite: opts?.sandbox?.filesystem?.denyWrite,
|
|
98
|
+
denyRead: opts?.sandbox?.filesystem?.denyRead,
|
|
99
|
+
allowRead: opts?.sandbox?.filesystem?.allowRead
|
|
100
|
+
},
|
|
101
|
+
network: opts?.sandbox?.network
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
return {
|
|
105
|
+
fs: new LocalFs({ basePath: cwd }),
|
|
106
|
+
computer,
|
|
107
|
+
dispose: () => computer.dispose()
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export {
|
|
112
|
+
SandboxedLocalComputer,
|
|
113
|
+
LocalSandbox
|
|
114
|
+
};
|
|
115
|
+
//# sourceMappingURL=chunk-PKHLGGEC.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/virtual/sandboxed-local-computer.ts","../src/virtual/local-sandbox.ts"],"sourcesContent":["import { exec as execCb } from \"node:child_process\";\nimport type { VirtualComputer, ExecOptions, CommandResult } from \"./computer.js\";\nimport {\n SandboxManager,\n type SandboxRuntimeConfig,\n} from \"@anthropic-ai/sandbox-runtime\";\n\n/**\n * Filesystem and network restriction config passed to `@anthropic-ai/sandbox-runtime`.\n */\nexport interface SandboxConfig {\n filesystem?: {\n /** Paths the agent may write to (default: `[cwd]`). Write is denied everywhere else. */\n allowWrite?: string[];\n /** Paths to explicitly deny writes within allowed regions. */\n denyWrite?: string[];\n /** Paths to deny reading. By default everything is readable. */\n denyRead?: string[];\n /** Paths to re-allow reading within denyRead regions. Takes precedence over denyRead. */\n allowRead?: string[];\n };\n network?: {\n /** Domains the agent may reach via HTTP/HTTPS/SOCKS. */\n allowedDomains?: string[];\n /** Domains to explicitly block. */\n deniedDomains?: string[];\n };\n}\n\nexport interface SandboxedLocalComputerOptions {\n defaultCwd?: string;\n defaultTimeout?: number;\n sandbox?: SandboxConfig;\n}\n\n/**\n * `VirtualComputer` that wraps every command with OS-level sandboxing via\n * `@anthropic-ai/sandbox-runtime`. Uses macOS Seatbelt (`sandbox-exec`) or\n * Linux bubblewrap (`bwrap`) under the hood.\n */\nexport class SandboxedLocalComputer implements VirtualComputer {\n private defaultCwd: string;\n private defaultTimeout: number;\n private sandboxConfig: SandboxConfig;\n private initPromise: Promise<void> | null = null;\n private initialized = false;\n\n constructor(opts?: SandboxedLocalComputerOptions) {\n this.defaultCwd = opts?.defaultCwd ?? process.cwd();\n this.defaultTimeout = opts?.defaultTimeout ?? 30_000;\n this.sandboxConfig = opts?.sandbox ?? {};\n }\n\n private buildRuntimeConfig(): SandboxRuntimeConfig {\n const fs = this.sandboxConfig.filesystem;\n const net = this.sandboxConfig.network;\n return {\n filesystem: {\n allowWrite: fs?.allowWrite ?? [this.defaultCwd],\n denyWrite: fs?.denyWrite ?? [],\n denyRead: fs?.denyRead ?? [],\n allowRead: fs?.allowRead ?? [],\n },\n network: {\n allowedDomains: net?.allowedDomains ?? [],\n deniedDomains: net?.deniedDomains ?? [],\n },\n };\n }\n\n private async ensureInitialized(): Promise<void> {\n if (this.initialized) return;\n\n if (!this.initPromise) {\n this.initPromise = (async () => {\n try {\n await SandboxManager.initialize(this.buildRuntimeConfig());\n this.initialized = true;\n } catch (err) {\n this.initPromise = null;\n throw err;\n }\n })();\n }\n\n await this.initPromise;\n }\n\n async executeCommand(\n command: string,\n opts?: ExecOptions,\n ): Promise<CommandResult> {\n await this.ensureInitialized();\n\n const wrappedCommand = await SandboxManager.wrapWithSandbox(command);\n\n return new Promise((resolve) => {\n const child = execCb(\n wrappedCommand,\n {\n cwd: opts?.cwd ?? this.defaultCwd,\n timeout: opts?.timeout ?? this.defaultTimeout,\n env: opts?.env ? { ...process.env, ...opts.env } : process.env,\n maxBuffer: 10 * 1024 * 1024,\n shell: process.env.SHELL || \"/bin/sh\",\n },\n (error, stdout, stderr) => {\n const result: CommandResult = {\n exitCode:\n error && \"code\" in error\n ? (typeof error.code === \"number\" ? error.code : 1)\n : child.exitCode ?? 0,\n stdout: stdout ?? \"\",\n stderr: stderr ?? \"\",\n };\n Promise.resolve(SandboxManager.cleanupAfterCommand())\n .then(() => resolve(result))\n .catch(() => resolve(result));\n },\n );\n });\n }\n\n /**\n * Tear down the sandbox runtime. Call when the agent is done.\n */\n async dispose(): Promise<void> {\n if (this.initialized) {\n await SandboxManager.reset();\n this.initialized = false;\n this.initPromise = null;\n }\n }\n}\n","import { LocalFs } from \"./local-fs.js\";\nimport { SandboxedLocalComputer, type SandboxConfig } from \"./sandboxed-local-computer.js\";\nimport type { Sandbox } from \"./sandbox.js\";\n\nexport type { SandboxConfig } from \"./sandboxed-local-computer.js\";\n\nexport interface LocalSandboxOptions {\n /** Working directory for both file resolution and command execution. */\n cwd?: string;\n /** Default timeout (ms) for shell commands. */\n defaultTimeout?: number;\n /**\n * Sandbox restrictions. Defaults: writes allowed only in `cwd`,\n * reads allowed everywhere, network unrestricted.\n */\n sandbox?: SandboxConfig;\n}\n\n/**\n * Create a `Sandbox` with OS-level isolation via `@anthropic-ai/sandbox-runtime`.\n *\n * - **macOS**: Seatbelt (`sandbox-exec`) profiles restrict filesystem and network.\n * - **Linux**: bubblewrap (`bwrap`) + socat for namespace-based isolation.\n *\n * Filesystem operations (`VirtualFs`) use the host `node:fs` — the sandbox\n * boundary is enforced on shell commands (`VirtualComputer`), which is where\n * the agent executes arbitrary code.\n *\n * Requires `@anthropic-ai/sandbox-runtime` as a peer dependency.\n */\nexport function LocalSandbox(opts?: LocalSandboxOptions): Sandbox {\n const cwd = opts?.cwd ?? process.cwd();\n const computer = new SandboxedLocalComputer({\n defaultCwd: cwd,\n defaultTimeout: opts?.defaultTimeout,\n sandbox: {\n filesystem: {\n allowWrite: [cwd, ...(opts?.sandbox?.filesystem?.allowWrite ?? [])],\n denyWrite: opts?.sandbox?.filesystem?.denyWrite,\n denyRead: opts?.sandbox?.filesystem?.denyRead,\n allowRead: opts?.sandbox?.filesystem?.allowRead,\n },\n network: opts?.sandbox?.network,\n },\n });\n return {\n fs: new LocalFs({ basePath: cwd }),\n computer,\n dispose: () => computer.dispose(),\n };\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
// src/virtual/local-fs.ts
|
|
2
|
+
import * as fs from "fs/promises";
|
|
3
|
+
import * as path from "path";
|
|
4
|
+
var LocalFs = class {
|
|
5
|
+
basePath;
|
|
6
|
+
resolvedBasePath;
|
|
7
|
+
realBasePathPromise = null;
|
|
8
|
+
constructor(opts) {
|
|
9
|
+
this.basePath = opts?.basePath ?? process.cwd();
|
|
10
|
+
this.resolvedBasePath = path.resolve(this.basePath);
|
|
11
|
+
}
|
|
12
|
+
async getRealBasePath() {
|
|
13
|
+
if (!this.realBasePathPromise) {
|
|
14
|
+
this.realBasePathPromise = (async () => {
|
|
15
|
+
try {
|
|
16
|
+
return await fs.realpath(this.resolvedBasePath);
|
|
17
|
+
} catch {
|
|
18
|
+
const parentReal = await fs.realpath(path.dirname(this.resolvedBasePath)).catch(() => path.dirname(this.resolvedBasePath));
|
|
19
|
+
return path.join(parentReal, path.basename(this.resolvedBasePath));
|
|
20
|
+
}
|
|
21
|
+
})();
|
|
22
|
+
}
|
|
23
|
+
return this.realBasePathPromise;
|
|
24
|
+
}
|
|
25
|
+
async resolve(p) {
|
|
26
|
+
if (p.includes("\0")) {
|
|
27
|
+
throw new Error(`Path contains null bytes`);
|
|
28
|
+
}
|
|
29
|
+
const resolved = path.isAbsolute(p) ? path.normalize(p) : path.resolve(this.basePath, p);
|
|
30
|
+
if (resolved !== this.resolvedBasePath && !resolved.startsWith(this.resolvedBasePath + path.sep)) {
|
|
31
|
+
throw new Error(`Path "${p}" resolves outside base directory "${this.basePath}"`);
|
|
32
|
+
}
|
|
33
|
+
const realBase = await this.getRealBasePath();
|
|
34
|
+
const realTarget = await realpathWalkUp(resolved);
|
|
35
|
+
if (realTarget !== realBase && !realTarget.startsWith(realBase + path.sep)) {
|
|
36
|
+
throw new Error(`Path "${p}" resolves outside base directory via symlink`);
|
|
37
|
+
}
|
|
38
|
+
return realTarget;
|
|
39
|
+
}
|
|
40
|
+
async readFile(filePath, opts) {
|
|
41
|
+
const encoding = opts?.encoding ?? "utf-8";
|
|
42
|
+
const resolved = await this.resolve(filePath);
|
|
43
|
+
if (opts?.maxBytes !== void 0) {
|
|
44
|
+
const fh = await fs.open(resolved, "r");
|
|
45
|
+
try {
|
|
46
|
+
const buf = Buffer.alloc(opts.maxBytes);
|
|
47
|
+
const { bytesRead } = await fh.read(buf, 0, opts.maxBytes, 0);
|
|
48
|
+
return buf.subarray(0, bytesRead).toString(encoding);
|
|
49
|
+
} finally {
|
|
50
|
+
await fh.close();
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return fs.readFile(resolved, { encoding });
|
|
54
|
+
}
|
|
55
|
+
async readFileBytes(filePath, maxBytes) {
|
|
56
|
+
const resolved = await this.resolve(filePath);
|
|
57
|
+
if (maxBytes === void 0) {
|
|
58
|
+
return fs.readFile(resolved);
|
|
59
|
+
}
|
|
60
|
+
const fh = await fs.open(resolved, "r");
|
|
61
|
+
try {
|
|
62
|
+
const buf = Buffer.alloc(maxBytes);
|
|
63
|
+
const { bytesRead } = await fh.read(buf, 0, maxBytes, 0);
|
|
64
|
+
return buf.subarray(0, bytesRead);
|
|
65
|
+
} finally {
|
|
66
|
+
await fh.close();
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
async writeFile(filePath, content) {
|
|
70
|
+
const resolved = await this.resolve(filePath);
|
|
71
|
+
await fs.mkdir(path.dirname(resolved), { recursive: true });
|
|
72
|
+
await fs.writeFile(resolved, content, "utf-8");
|
|
73
|
+
}
|
|
74
|
+
async appendFile(filePath, content) {
|
|
75
|
+
const resolved = await this.resolve(filePath);
|
|
76
|
+
await fs.mkdir(path.dirname(resolved), { recursive: true });
|
|
77
|
+
await fs.appendFile(resolved, content, "utf-8");
|
|
78
|
+
}
|
|
79
|
+
async deleteFile(filePath, opts) {
|
|
80
|
+
await fs.rm(await this.resolve(filePath), {
|
|
81
|
+
recursive: opts?.recursive ?? false,
|
|
82
|
+
force: true
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
async mkdir(dirPath, opts) {
|
|
86
|
+
await fs.mkdir(await this.resolve(dirPath), {
|
|
87
|
+
recursive: opts?.recursive ?? false
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
async readdir(dirPath, opts) {
|
|
91
|
+
const resolved = await this.resolve(dirPath);
|
|
92
|
+
const entries = await fs.readdir(resolved, { withFileTypes: true });
|
|
93
|
+
const results = [];
|
|
94
|
+
for (const entry of entries) {
|
|
95
|
+
const entryPath = path.join(resolved, entry.name);
|
|
96
|
+
results.push({
|
|
97
|
+
name: entry.name,
|
|
98
|
+
path: entryPath,
|
|
99
|
+
isDirectory: entry.isDirectory(),
|
|
100
|
+
isFile: entry.isFile()
|
|
101
|
+
});
|
|
102
|
+
if (opts?.recursive && entry.isDirectory()) {
|
|
103
|
+
const subEntries = await this.readdir(entryPath, { recursive: true });
|
|
104
|
+
results.push(...subEntries);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
return results;
|
|
108
|
+
}
|
|
109
|
+
async exists(filePath) {
|
|
110
|
+
try {
|
|
111
|
+
await fs.access(await this.resolve(filePath));
|
|
112
|
+
return true;
|
|
113
|
+
} catch {
|
|
114
|
+
return false;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
async stat(filePath) {
|
|
118
|
+
const stats = await fs.stat(await this.resolve(filePath));
|
|
119
|
+
return {
|
|
120
|
+
size: stats.size,
|
|
121
|
+
isDirectory: stats.isDirectory(),
|
|
122
|
+
isFile: stats.isFile(),
|
|
123
|
+
createdAt: stats.birthtime,
|
|
124
|
+
modifiedAt: stats.mtime
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
};
|
|
128
|
+
async function realpathWalkUp(target) {
|
|
129
|
+
try {
|
|
130
|
+
return await fs.realpath(target);
|
|
131
|
+
} catch (e) {
|
|
132
|
+
if (e.code !== "ENOENT") throw e;
|
|
133
|
+
}
|
|
134
|
+
const parent = path.dirname(target);
|
|
135
|
+
if (parent === target) return target;
|
|
136
|
+
const resolvedParent = await realpathWalkUp(parent);
|
|
137
|
+
return path.join(resolvedParent, path.basename(target));
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// src/virtual/local-computer.ts
|
|
141
|
+
import { exec as execCb } from "child_process";
|
|
142
|
+
var LocalComputer = class {
|
|
143
|
+
defaultCwd;
|
|
144
|
+
defaultTimeout;
|
|
145
|
+
constructor(opts) {
|
|
146
|
+
this.defaultCwd = opts?.defaultCwd ?? process.cwd();
|
|
147
|
+
this.defaultTimeout = opts?.defaultTimeout ?? 3e4;
|
|
148
|
+
}
|
|
149
|
+
executeCommand(command, opts) {
|
|
150
|
+
return new Promise((resolve2) => {
|
|
151
|
+
const child = execCb(
|
|
152
|
+
command,
|
|
153
|
+
{
|
|
154
|
+
cwd: opts?.cwd ?? this.defaultCwd,
|
|
155
|
+
timeout: opts?.timeout ?? this.defaultTimeout,
|
|
156
|
+
env: opts?.env ? { ...process.env, ...opts.env } : process.env,
|
|
157
|
+
maxBuffer: 10 * 1024 * 1024,
|
|
158
|
+
shell: process.env.SHELL || "/bin/sh"
|
|
159
|
+
},
|
|
160
|
+
(error, stdout, stderr) => {
|
|
161
|
+
resolve2({
|
|
162
|
+
exitCode: error && "code" in error ? typeof error.code === "number" ? error.code : 1 : child.exitCode ?? 0,
|
|
163
|
+
stdout: stdout ?? "",
|
|
164
|
+
stderr: stderr ?? ""
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
);
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
};
|
|
171
|
+
|
|
172
|
+
export {
|
|
173
|
+
LocalFs,
|
|
174
|
+
LocalComputer
|
|
175
|
+
};
|
|
176
|
+
//# sourceMappingURL=chunk-XQTNXRE7.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/virtual/local-fs.ts","../src/virtual/local-computer.ts"],"sourcesContent":["import * as fs from \"node:fs/promises\";\nimport * as path from \"node:path\";\nimport type { VirtualFs, FileEntry, FileStat, ReadOptions } from \"./fs.js\";\n\nexport interface LocalFsOptions {\n basePath?: string;\n}\n\n/**\n * Unsandboxed VirtualFs backed by `node:fs/promises` on the host machine.\n * Paths resolve relative to `basePath`. Suitable for local development and\n * trusted environments. For production or untrusted agents, use a sandboxed\n * implementation like `SpritesFs` (remote container) or a custom\n * Docker/E2B adapter instead.\n */\nexport class LocalFs implements VirtualFs {\n private basePath: string;\n private resolvedBasePath: string;\n private realBasePathPromise: Promise<string> | null = null;\n\n constructor(opts?: LocalFsOptions) {\n this.basePath = opts?.basePath ?? process.cwd();\n this.resolvedBasePath = path.resolve(this.basePath);\n }\n\n private async getRealBasePath(): Promise<string> {\n if (!this.realBasePathPromise) {\n this.realBasePathPromise = (async () => {\n try {\n return await fs.realpath(this.resolvedBasePath);\n } catch {\n // Base path may not exist yet; resolve its closest existing ancestor.\n const parentReal = await fs.realpath(path.dirname(this.resolvedBasePath)).catch(() => path.dirname(this.resolvedBasePath));\n return path.join(parentReal, path.basename(this.resolvedBasePath));\n }\n })();\n }\n return this.realBasePathPromise;\n }\n\n private async resolve(p: string): Promise<string> {\n if (p.includes(\"\\0\")) {\n throw new Error(`Path contains null bytes`);\n }\n const resolved = path.isAbsolute(p) ? path.normalize(p) : path.resolve(this.basePath, p);\n if (resolved !== this.resolvedBasePath && !resolved.startsWith(this.resolvedBasePath + path.sep)) {\n throw new Error(`Path \"${p}\" resolves outside base directory \"${this.basePath}\"`);\n }\n // Resolve symlinks to prevent escaping the base directory via symlink chains.\n // Walk up from the target until we find an existing ancestor, then re-append\n // the non-existent tail so the comparison uses real paths on both sides.\n const realBase = await this.getRealBasePath();\n const realTarget = await realpathWalkUp(resolved);\n if (realTarget !== realBase && !realTarget.startsWith(realBase + path.sep)) {\n throw new Error(`Path \"${p}\" resolves outside base directory via symlink`);\n }\n return realTarget;\n }\n\n async readFile(filePath: string, opts?: ReadOptions): Promise<string> {\n const encoding = opts?.encoding ?? \"utf-8\";\n const resolved = await this.resolve(filePath);\n if (opts?.maxBytes !== undefined) {\n const fh = await fs.open(resolved, \"r\");\n try {\n const buf = Buffer.alloc(opts.maxBytes);\n const { bytesRead } = await fh.read(buf, 0, opts.maxBytes, 0);\n return buf.subarray(0, bytesRead).toString(encoding);\n } finally {\n await fh.close();\n }\n }\n return fs.readFile(resolved, { encoding });\n }\n\n async readFileBytes(filePath: string, maxBytes?: number): Promise<Buffer> {\n const resolved = await this.resolve(filePath);\n if (maxBytes === undefined) {\n return fs.readFile(resolved);\n }\n const fh = await fs.open(resolved, \"r\");\n try {\n const buf = Buffer.alloc(maxBytes);\n const { bytesRead } = await fh.read(buf, 0, maxBytes, 0);\n return buf.subarray(0, bytesRead);\n } finally {\n await fh.close();\n }\n }\n\n async writeFile(filePath: string, content: string): Promise<void> {\n const resolved = await this.resolve(filePath);\n await fs.mkdir(path.dirname(resolved), { recursive: true });\n await fs.writeFile(resolved, content, \"utf-8\");\n }\n\n async appendFile(filePath: string, content: string): Promise<void> {\n const resolved = await this.resolve(filePath);\n await fs.mkdir(path.dirname(resolved), { recursive: true });\n await fs.appendFile(resolved, content, \"utf-8\");\n }\n\n async deleteFile(\n filePath: string,\n opts?: { recursive?: boolean },\n ): Promise<void> {\n await fs.rm(await this.resolve(filePath), {\n recursive: opts?.recursive ?? false,\n force: true,\n });\n }\n\n async mkdir(dirPath: string, opts?: { recursive?: boolean }): Promise<void> {\n await fs.mkdir(await this.resolve(dirPath), {\n recursive: opts?.recursive ?? false,\n });\n }\n\n async readdir(\n dirPath: string,\n opts?: { recursive?: boolean },\n ): Promise<FileEntry[]> {\n const resolved = await this.resolve(dirPath);\n const entries = await fs.readdir(resolved, { withFileTypes: true });\n const results: FileEntry[] = [];\n\n for (const entry of entries) {\n const entryPath = path.join(resolved, entry.name);\n results.push({\n name: entry.name,\n path: entryPath,\n isDirectory: entry.isDirectory(),\n isFile: entry.isFile(),\n });\n\n if (opts?.recursive && entry.isDirectory()) {\n const subEntries = await this.readdir(entryPath, { recursive: true });\n results.push(...subEntries);\n }\n }\n\n return results;\n }\n\n async exists(filePath: string): Promise<boolean> {\n try {\n await fs.access(await this.resolve(filePath));\n return true;\n } catch {\n return false;\n }\n }\n\n async stat(filePath: string): Promise<FileStat> {\n const stats = await fs.stat(await this.resolve(filePath));\n return {\n size: stats.size,\n isDirectory: stats.isDirectory(),\n isFile: stats.isFile(),\n createdAt: stats.birthtime,\n modifiedAt: stats.mtime,\n };\n }\n}\n\n/**\n * Resolve symlinks in a path, walking up to the nearest existing ancestor when\n * the path (or intermediate directories) don't exist yet. Non-existent tail\n * segments are appended to the resolved ancestor.\n */\nasync function realpathWalkUp(target: string): Promise<string> {\n try {\n return await fs.realpath(target);\n } catch (e: unknown) {\n if ((e as NodeJS.ErrnoException).code !== \"ENOENT\") throw e;\n }\n const parent = path.dirname(target);\n if (parent === target) return target;\n const resolvedParent = await realpathWalkUp(parent);\n return path.join(resolvedParent, path.basename(target));\n}\n","import { exec as execCb } from \"node:child_process\";\nimport type { VirtualComputer, ExecOptions, CommandResult } from \"./computer.js\";\n\nexport interface LocalComputerOptions {\n defaultCwd?: string;\n defaultTimeout?: number;\n}\n\n/**\n * Unsandboxed VirtualComputer that runs commands directly on the host via\n * `node:child_process`. Suitable for local development and trusted\n * environments. For production or untrusted agents, use a sandboxed\n * implementation like `SpritesComputer` (remote container) or a custom\n * Docker/E2B adapter instead.\n */\nexport class LocalComputer implements VirtualComputer {\n private defaultCwd: string;\n private defaultTimeout: number;\n\n constructor(opts?: LocalComputerOptions) {\n this.defaultCwd = opts?.defaultCwd ?? process.cwd();\n this.defaultTimeout = opts?.defaultTimeout ?? 30_000;\n }\n\n executeCommand(command: string, opts?: ExecOptions): Promise<CommandResult> {\n return new Promise((resolve) => {\n const child = execCb(\n command,\n {\n cwd: opts?.cwd ?? this.defaultCwd,\n timeout: opts?.timeout ?? this.defaultTimeout,\n env: opts?.env\n ? { ...process.env, ...opts.env }\n : process.env,\n maxBuffer: 10 * 1024 * 1024,\n shell: process.env.SHELL || \"/bin/sh\",\n },\n (error, stdout, stderr) => {\n resolve({\n exitCode:\n error && \"code\" in error\n ? (typeof error.code === \"number\" ? error.code : 1)\n : child.exitCode ?? 0,\n stdout: stdout ?? \"\",\n stderr: stderr ?? \"\",\n });\n },\n );\n });\n }\n}\n"],"mappings":"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","names":["resolve"]}
|