@finalai/agent-provider 0.1.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.

Potentially problematic release.


This version of @finalai/agent-provider might be problematic. Click here for more details.

Files changed (176) hide show
  1. package/README.md +12 -0
  2. package/dist/modules/channel-context.d.mts +102 -0
  3. package/dist/modules/channel-context.mjs +52 -0
  4. package/dist/modules/channel-context.mjs.map +7 -0
  5. package/dist/modules/channel-socket.d.mts +65 -0
  6. package/dist/modules/channel-socket.mjs +188 -0
  7. package/dist/modules/channel-socket.mjs.map +7 -0
  8. package/dist/modules/channel.d.mts +382 -0
  9. package/dist/modules/channel.mjs +146 -0
  10. package/dist/modules/channel.mjs.map +7 -0
  11. package/dist/modules/conformance/agent.d.mts +20 -0
  12. package/dist/modules/conformance/agent.mjs +623 -0
  13. package/dist/modules/conformance/agent.mjs.map +7 -0
  14. package/dist/modules/conformance/fixtures.d.mts +41 -0
  15. package/dist/modules/conformance/fixtures.mjs +190 -0
  16. package/dist/modules/conformance/fixtures.mjs.map +7 -0
  17. package/dist/modules/conformance/harness-kit.d.mts +82 -0
  18. package/dist/modules/conformance/harness-kit.mjs +1303 -0
  19. package/dist/modules/conformance/harness-kit.mjs.map +7 -0
  20. package/dist/modules/conformance/image.d.mts +2 -0
  21. package/dist/modules/conformance/image.mjs +79 -0
  22. package/dist/modules/conformance/image.mjs.map +7 -0
  23. package/dist/modules/conformance/index.d.mts +8 -0
  24. package/dist/modules/conformance/index.mjs +12 -0
  25. package/dist/modules/conformance/index.mjs.map +7 -0
  26. package/dist/modules/conformance/local-harness.d.mts +25 -0
  27. package/dist/modules/conformance/local-harness.mjs +108 -0
  28. package/dist/modules/conformance/local-harness.mjs.map +7 -0
  29. package/dist/modules/conformance/protocol.d.mts +30 -0
  30. package/dist/modules/conformance/protocol.mjs +56 -0
  31. package/dist/modules/conformance/protocol.mjs.map +7 -0
  32. package/dist/modules/conformance/sandbox-kit.d.mts +19 -0
  33. package/dist/modules/conformance/sandbox-kit.mjs +320 -0
  34. package/dist/modules/conformance/sandbox-kit.mjs.map +7 -0
  35. package/dist/modules/conformance/scripted-model/chat-completions.d.mts +30 -0
  36. package/dist/modules/conformance/scripted-model/chat-completions.mjs +125 -0
  37. package/dist/modules/conformance/scripted-model/chat-completions.mjs.map +7 -0
  38. package/dist/modules/conformance/scripted-model/directives.d.mts +84 -0
  39. package/dist/modules/conformance/scripted-model/directives.mjs +304 -0
  40. package/dist/modules/conformance/scripted-model/directives.mjs.map +7 -0
  41. package/dist/modules/conformance/scripted-model/handler.d.mts +14 -0
  42. package/dist/modules/conformance/scripted-model/handler.mjs +133 -0
  43. package/dist/modules/conformance/scripted-model/handler.mjs.map +7 -0
  44. package/dist/modules/conformance/scripted-model/index.d.mts +6 -0
  45. package/dist/modules/conformance/scripted-model/index.mjs +7 -0
  46. package/dist/modules/conformance/scripted-model/index.mjs.map +7 -0
  47. package/dist/modules/conformance/scripted-model/messages.d.mts +39 -0
  48. package/dist/modules/conformance/scripted-model/messages.mjs +139 -0
  49. package/dist/modules/conformance/scripted-model/messages.mjs.map +7 -0
  50. package/dist/modules/conformance/scripted-model/responses.d.mts +62 -0
  51. package/dist/modules/conformance/scripted-model/responses.mjs +273 -0
  52. package/dist/modules/conformance/scripted-model/responses.mjs.map +7 -0
  53. package/dist/modules/conformance/scripted-model/sse.d.mts +15 -0
  54. package/dist/modules/conformance/scripted-model/sse.mjs +75 -0
  55. package/dist/modules/conformance/scripted-model/sse.mjs.map +7 -0
  56. package/dist/modules/conformance/v1-agent.d.mts +24 -0
  57. package/dist/modules/conformance/v1-agent.mjs +40 -0
  58. package/dist/modules/conformance/v1-agent.mjs.map +7 -0
  59. package/dist/modules/credential-routes.d.mts +24 -0
  60. package/dist/modules/credential-routes.mjs +25 -0
  61. package/dist/modules/credential-routes.mjs.map +7 -0
  62. package/dist/modules/credentials.d.mts +206 -0
  63. package/dist/modules/credentials.mjs +207 -0
  64. package/dist/modules/credentials.mjs.map +7 -0
  65. package/dist/modules/dynamic.d.mts +32 -0
  66. package/dist/modules/dynamic.mjs +77 -0
  67. package/dist/modules/dynamic.mjs.map +7 -0
  68. package/dist/modules/harness.d.mts +397 -0
  69. package/dist/modules/harness.mjs +213 -0
  70. package/dist/modules/harness.mjs.map +7 -0
  71. package/dist/modules/index.d.mts +15 -0
  72. package/dist/modules/index.mjs +40 -0
  73. package/dist/modules/index.mjs.map +7 -0
  74. package/dist/modules/json.d.mts +12 -0
  75. package/dist/modules/json.mjs +19 -0
  76. package/dist/modules/json.mjs.map +7 -0
  77. package/dist/modules/local-channel-socket.d.mts +23 -0
  78. package/dist/modules/local-channel-socket.mjs +141 -0
  79. package/dist/modules/local-channel-socket.mjs.map +7 -0
  80. package/dist/modules/manifest.d.mts +558 -0
  81. package/dist/modules/manifest.mjs +266 -0
  82. package/dist/modules/manifest.mjs.map +7 -0
  83. package/dist/modules/mcp-content.d.mts +21 -0
  84. package/dist/modules/mcp-content.mjs +86 -0
  85. package/dist/modules/mcp-content.mjs.map +7 -0
  86. package/dist/modules/provider-ref.d.mts +29 -0
  87. package/dist/modules/provider-ref.mjs +55 -0
  88. package/dist/modules/provider-ref.mjs.map +7 -0
  89. package/dist/modules/sandbox/bytes.d.mts +9 -0
  90. package/dist/modules/sandbox/bytes.mjs +154 -0
  91. package/dist/modules/sandbox/bytes.mjs.map +7 -0
  92. package/dist/modules/sandbox/client.d.mts +51 -0
  93. package/dist/modules/sandbox/client.mjs +500 -0
  94. package/dist/modules/sandbox/client.mjs.map +7 -0
  95. package/dist/modules/sandbox/confined-files.d.mts +22 -0
  96. package/dist/modules/sandbox/confined-files.mjs +142 -0
  97. package/dist/modules/sandbox/confined-files.mjs.map +7 -0
  98. package/dist/modules/sandbox/convenience.d.mts +10 -0
  99. package/dist/modules/sandbox/convenience.mjs +116 -0
  100. package/dist/modules/sandbox/convenience.mjs.map +7 -0
  101. package/dist/modules/sandbox/editor.d.mts +49 -0
  102. package/dist/modules/sandbox/editor.mjs +43 -0
  103. package/dist/modules/sandbox/editor.mjs.map +7 -0
  104. package/dist/modules/sandbox/frame-channel.d.mts +47 -0
  105. package/dist/modules/sandbox/frame-channel.mjs +153 -0
  106. package/dist/modules/sandbox/frame-channel.mjs.map +7 -0
  107. package/dist/modules/sandbox/index.d.mts +18 -0
  108. package/dist/modules/sandbox/index.mjs +70 -0
  109. package/dist/modules/sandbox/index.mjs.map +7 -0
  110. package/dist/modules/sandbox/mcp-channel.d.mts +9 -0
  111. package/dist/modules/sandbox/mcp-channel.mjs +77 -0
  112. package/dist/modules/sandbox/mcp-channel.mjs.map +7 -0
  113. package/dist/modules/sandbox/mcp-hosted.d.mts +46 -0
  114. package/dist/modules/sandbox/mcp-hosted.mjs +221 -0
  115. package/dist/modules/sandbox/mcp-hosted.mjs.map +7 -0
  116. package/dist/modules/sandbox/mcp-search.d.mts +51 -0
  117. package/dist/modules/sandbox/mcp-search.mjs +129 -0
  118. package/dist/modules/sandbox/mcp-search.mjs.map +7 -0
  119. package/dist/modules/sandbox/mcp-shell.d.mts +21 -0
  120. package/dist/modules/sandbox/mcp-shell.mjs +205 -0
  121. package/dist/modules/sandbox/mcp-shell.mjs.map +7 -0
  122. package/dist/modules/sandbox/mcp-testing.d.mts +11 -0
  123. package/dist/modules/sandbox/mcp-testing.mjs +33 -0
  124. package/dist/modules/sandbox/mcp-testing.mjs.map +7 -0
  125. package/dist/modules/sandbox/mcp.d.mts +83 -0
  126. package/dist/modules/sandbox/mcp.mjs +971 -0
  127. package/dist/modules/sandbox/mcp.mjs.map +7 -0
  128. package/dist/modules/sandbox/memory-sandbox.d.mts +32 -0
  129. package/dist/modules/sandbox/memory-sandbox.mjs +416 -0
  130. package/dist/modules/sandbox/memory-sandbox.mjs.map +7 -0
  131. package/dist/modules/sandbox/memory-shell.d.mts +29 -0
  132. package/dist/modules/sandbox/memory-shell.mjs +278 -0
  133. package/dist/modules/sandbox/memory-shell.mjs.map +7 -0
  134. package/dist/modules/sandbox/open-scope-node.d.mts +4 -0
  135. package/dist/modules/sandbox/open-scope-node.mjs +19 -0
  136. package/dist/modules/sandbox/open-scope-node.mjs.map +7 -0
  137. package/dist/modules/sandbox/open-scope.d.mts +16 -0
  138. package/dist/modules/sandbox/open-scope.mjs +66 -0
  139. package/dist/modules/sandbox/open-scope.mjs.map +7 -0
  140. package/dist/modules/sandbox/paths.d.mts +7 -0
  141. package/dist/modules/sandbox/paths.mjs +50 -0
  142. package/dist/modules/sandbox/paths.mjs.map +7 -0
  143. package/dist/modules/sandbox/rpc-peer.d.mts +35 -0
  144. package/dist/modules/sandbox/rpc-peer.mjs +224 -0
  145. package/dist/modules/sandbox/rpc-peer.mjs.map +7 -0
  146. package/dist/modules/sandbox/seeds.d.mts +26 -0
  147. package/dist/modules/sandbox/seeds.mjs +228 -0
  148. package/dist/modules/sandbox/seeds.mjs.map +7 -0
  149. package/dist/modules/sandbox/server.d.mts +66 -0
  150. package/dist/modules/sandbox/server.mjs +567 -0
  151. package/dist/modules/sandbox/server.mjs.map +7 -0
  152. package/dist/modules/sandbox/snapshots.d.mts +49 -0
  153. package/dist/modules/sandbox/snapshots.mjs +55 -0
  154. package/dist/modules/sandbox/snapshots.mjs.map +7 -0
  155. package/dist/modules/sandbox/stream-mux.d.mts +18 -0
  156. package/dist/modules/sandbox/stream-mux.mjs +285 -0
  157. package/dist/modules/sandbox/stream-mux.mjs.map +7 -0
  158. package/dist/modules/sandbox/testing.d.mts +25 -0
  159. package/dist/modules/sandbox/testing.mjs +64 -0
  160. package/dist/modules/sandbox/testing.mjs.map +7 -0
  161. package/dist/modules/sandbox/types.d.mts +352 -0
  162. package/dist/modules/sandbox/types.mjs +86 -0
  163. package/dist/modules/sandbox/types.mjs.map +7 -0
  164. package/dist/modules/sandbox/wire.d.mts +671 -0
  165. package/dist/modules/sandbox/wire.mjs +537 -0
  166. package/dist/modules/sandbox/wire.mjs.map +7 -0
  167. package/dist/modules/sandbox/workspace-root.d.mts +8 -0
  168. package/dist/modules/sandbox/workspace-root.mjs +79 -0
  169. package/dist/modules/sandbox/workspace-root.mjs.map +7 -0
  170. package/dist/modules/schemas.d.mts +121 -0
  171. package/dist/modules/schemas.mjs +146 -0
  172. package/dist/modules/schemas.mjs.map +7 -0
  173. package/dist/modules/semantic-events.d.mts +31 -0
  174. package/dist/modules/semantic-events.mjs +23 -0
  175. package/dist/modules/semantic-events.mjs.map +7 -0
  176. package/package.json +199 -0
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../../src/sandbox/memory-sandbox.ts"],
4
+ "sourcesContent": ["import * as z from 'zod';\nimport type { JsonValue } from \"../json.mjs\";\nimport { encodeSandboxText, readAllBytes, utf8Encode } from \"./bytes.mjs\";\nimport { readTextFileStream, runCollect } from \"./convenience.mjs\";\nimport { runMemoryArgv, runMemoryScript, type MemoryShellFsIface } from \"./memory-shell.mjs\";\nimport { normalizePosixPath, posixDirname, resolvePosixPath } from \"./paths.mjs\";\nimport {\n defineSandboxProvider,\n NetworkPolicy,\n PortProtocol,\n SandboxEntryType,\n type DirEntryIface,\n type NetworkPolicyIface,\n type NetworkSandboxSessionIface,\n type SandboxMcpEndpoint,\n type SandboxProcessIface,\n type SandboxProcessOptionsIface,\n type SandboxProviderHandleIface,\n type StatIface,\n} from \"./types.mjs\";\n\n// An in-memory reference sandbox: a virtual filesystem plus a tiny command\n// interpreter (memory-shell.ts). Used by tests and the conformance kit, and as\n// the smallest complete example of the sandbox contract.\n\nexport interface MemorySandboxIface extends NetworkSandboxSessionIface {\n readonly networkPolicy: NetworkPolicyIface;\n readonly stopped: boolean;\n}\n\nexport interface CreateMemorySandboxArgsIface {\n id?: string;\n files?: Record<string, string | Uint8Array>;\n cwd?: string;\n ports?: number[];\n portUrl?: (args: { port: number; protocol: PortProtocol }) => string;\n // Optional capabilities; present only when given.\n computerUse?: () => Promise<SandboxMcpEndpoint>;\n screen?: NetworkSandboxSessionIface['screen'];\n localMcp?: () => Promise<SandboxMcpEndpoint[]>;\n networkPolicy?: boolean;\n now?: () => Date;\n}\n\ntype MemoryEntry =\n | { type: 'file'; data: Uint8Array; modifiedAt: Date }\n | { type: 'directory'; modifiedAt: Date };\n\ninterface ProcessOutputsIface {\n stdout: ReadableStreamDefaultController<Uint8Array> | null;\n stderr: ReadableStreamDefaultController<Uint8Array> | null;\n}\n\nconst fsError = (code: string, message: string) =>\n Object.assign(new Error(`${code}: ${message}`), { code });\n\nconst abortableSleep = (ms: number, signal: AbortSignal): Promise<void> =>\n new Promise<void>((resolve, reject) => {\n if (signal.aborted) {\n reject(signal.reason);\n return;\n }\n const timer = setTimeout(() => {\n signal.removeEventListener('abort', onAbort);\n resolve();\n }, ms);\n const onAbort = () => {\n clearTimeout(timer);\n reject(signal.reason);\n };\n signal.addEventListener('abort', onAbort, { once: true });\n });\n\nconst signalExitCode = (signal: string | undefined): number => {\n if (signal === 'SIGKILL' || signal === 'KILL' || signal === '9') return 137;\n if (signal === 'SIGINT' || signal === 'INT') return 130;\n return 143;\n};\n\nexport const createMemorySandbox = (\n args: CreateMemorySandboxArgsIface = {},\n): MemorySandboxIface => {\n const id = args.id ?? `memory-${crypto.randomUUID()}`;\n const cwd = normalizePosixPath(args.cwd ?? '/workspace');\n const now = args.now ?? (() => new Date());\n const entries = new Map<string, MemoryEntry>();\n const processes = new Set<SandboxProcessIface>();\n let networkPolicy: NetworkPolicyIface = { mode: NetworkPolicy.AllowAll };\n let stopped = false;\n\n const resolve = (path: string) => resolvePosixPath(cwd, path);\n\n const ensureDirectory = (path: string) => {\n const normalized = normalizePosixPath(path);\n const existing = entries.get(normalized);\n if (existing?.type === 'directory') return;\n if (existing?.type === 'file') throw fsError('ENOTDIR', `${normalized} is a file`);\n if (normalized !== '/') ensureDirectory(posixDirname(normalized));\n entries.set(normalized, { type: 'directory', modifiedAt: now() });\n };\n\n const putFile = (path: string, data: Uint8Array) => {\n const normalized = normalizePosixPath(path);\n if (entries.get(normalized)?.type === 'directory')\n throw fsError('EISDIR', `${normalized} is a directory`);\n ensureDirectory(posixDirname(normalized));\n entries.set(normalized, { type: 'file', data, modifiedAt: now() });\n };\n\n const children = (path: string): string[] => {\n const prefix = path === '/' ? '/' : `${path}/`;\n const names: string[] = [];\n for (const key of entries.keys()) {\n if (key !== path && key.startsWith(prefix) && !key.slice(prefix.length).includes('/')) {\n names.push(key.slice(prefix.length));\n }\n }\n return names.sort();\n };\n\n ensureDirectory('/');\n ensureDirectory(cwd);\n for (const [path, content] of Object.entries(args.files ?? {})) {\n putFile(resolve(path), content instanceof Uint8Array ? content : utf8Encode(content));\n }\n\n const shellFs: MemoryShellFsIface = {\n readFile: (path) => {\n const entry = entries.get(normalizePosixPath(path));\n return entry?.type === 'file' ? entry.data : null;\n },\n writeFile: (path, data, options) => {\n const entry = entries.get(normalizePosixPath(path));\n if (options.append && entry?.type === 'file') {\n const merged = new Uint8Array(entry.data.byteLength + data.byteLength);\n merged.set(entry.data, 0);\n merged.set(data, entry.data.byteLength);\n putFile(path, merged);\n return;\n }\n putFile(path, data);\n },\n kind: (path) => {\n const entry = entries.get(normalizePosixPath(path));\n return entry == null ? null : entry.type;\n },\n list: (path) => {\n const normalized = normalizePosixPath(path);\n return entries.get(normalized)?.type === 'directory' ? children(normalized) : null;\n },\n mkdir: (path, options) => {\n const normalized = normalizePosixPath(path);\n if (!options.parents && entries.get(posixDirname(normalized))?.type !== 'directory') {\n throw fsError('ENOENT', 'No such file or directory');\n }\n if (!options.parents && entries.has(normalized)) throw fsError('EEXIST', 'File exists');\n ensureDirectory(normalized);\n },\n };\n\n const statOf = (path: string): StatIface | null => {\n const entry = entries.get(path);\n if (entry == null) return null;\n return {\n path,\n type: entry.type === 'file' ? SandboxEntryType.File : SandboxEntryType.Directory,\n size: entry.type === 'file' ? entry.data.byteLength : 0,\n modifiedAt: entry.modifiedAt.toISOString(),\n mode: entry.type === 'file' ? 0o644 : 0o755,\n };\n };\n\n const spawn = async (options: SandboxProcessOptionsIface): Promise<SandboxProcessIface> => {\n const controller = new AbortController();\n let killedWith: string | null = null;\n const outputs: ProcessOutputsIface = { stdout: null, stderr: null };\n const stdout = new ReadableStream<Uint8Array>({\n start: (c) => {\n outputs.stdout = c;\n },\n });\n const stderr = new ReadableStream<Uint8Array>({\n start: (c) => {\n outputs.stderr = c;\n },\n });\n let self: SandboxProcessIface | null = null;\n let stdinWriter: WritableStreamDefaultWriter<Uint8Array> | null = null;\n let stdinSource: ReadableStream<Uint8Array>;\n if (options.stdin == null) {\n const pipe = new TransformStream<Uint8Array, Uint8Array>();\n stdinWriter = pipe.writable.getWriter();\n stdinSource = pipe.readable;\n } else if (options.stdin instanceof ReadableStream) {\n stdinSource = options.stdin;\n } else {\n const bytes = utf8Encode(options.stdin);\n stdinSource = new ReadableStream<Uint8Array>({\n start: (c) => {\n if (bytes.byteLength > 0) c.enqueue(bytes);\n c.close();\n },\n });\n }\n const stdinReader = stdinSource.getReader();\n const abortPromise = new Promise<null>((resolveAbort) =>\n controller.signal.addEventListener('abort', () => resolveAbort(null), { once: true }),\n );\n const readStdin = async (): Promise<Uint8Array | null> => {\n const result = await Promise.race([stdinReader.read(), abortPromise]);\n if (result == null) throw controller.signal.reason;\n return result.done ? null : result.value;\n };\n const emit = (\n target: ReadableStreamDefaultController<Uint8Array> | null,\n bytes: Uint8Array,\n ) => {\n if (bytes.byteLength > 0) target?.enqueue(bytes.slice());\n };\n const context = {\n fs: shellFs,\n io: {\n readStdin,\n stdout: (bytes: Uint8Array) => emit(outputs.stdout, bytes),\n stderr: (bytes: Uint8Array) => emit(outputs.stderr, bytes),\n signal: controller.signal,\n },\n cwd: options.cwd == null ? cwd : resolve(options.cwd),\n sleep: abortableSleep,\n };\n const timeout =\n options.timeoutMs == null\n ? null\n : setTimeout(() => {\n killedWith = 'timeout';\n controller.abort(new Error('timed out'));\n }, options.timeoutMs);\n const onAbortSignal = () => {\n killedWith ??= 'SIGTERM';\n controller.abort(new Error('aborted'));\n };\n options.abortSignal?.addEventListener('abort', onAbortSignal, { once: true });\n const exited = (async (): Promise<{ exitCode: number }> => {\n let exitCode: number;\n try {\n if (context.fs.kind(context.cwd) !== 'directory') {\n context.io.stderr(utf8Encode(`cd: ${context.cwd}: No such file or directory\\n`));\n exitCode = 1;\n } else if (options.shell === true) {\n const script = [options.command, ...(options.args ?? [])].join(' ');\n exitCode = await runMemoryScript({ ...context, script });\n } else {\n exitCode = await runMemoryArgv({\n ...context,\n argv: [options.command, ...(options.args ?? [])],\n });\n }\n } catch (error) {\n if (!controller.signal.aborted) {\n context.io.stderr(\n utf8Encode(`${error instanceof Error ? error.message : String(error)}\\n`),\n );\n exitCode = 1;\n } else {\n exitCode = killedWith === 'timeout' ? 124 : signalExitCode(killedWith ?? undefined);\n }\n }\n if (timeout != null) clearTimeout(timeout);\n options.abortSignal?.removeEventListener('abort', onAbortSignal);\n stdinReader.cancel().catch(() => undefined);\n outputs.stdout?.close();\n outputs.stderr?.close();\n if (self != null) processes.delete(self);\n return { exitCode };\n })();\n const child: SandboxProcessIface = {\n pid: null,\n stdout,\n stderr,\n write: async (data, writeOptions) => {\n if (stdinWriter == null) throw new Error('stdin was provided at spawn');\n const bytes = data instanceof Uint8Array ? data : utf8Encode(data);\n if (bytes.byteLength > 0) await stdinWriter.write(bytes);\n if (writeOptions?.end === true) await stdinWriter.close();\n },\n wait: () => exited,\n kill: async (signal) => {\n killedWith ??= signal ?? 'SIGTERM';\n controller.abort(new Error(`killed (${signal ?? 'SIGTERM'})`));\n await exited;\n },\n };\n self = child;\n processes.add(child);\n return child;\n };\n\n const sandbox: MemorySandboxIface = {\n id,\n defaultWorkingDirectory: cwd,\n ports: args.ports ?? [],\n get description() {\n return `In-memory sandbox ${id}. Working directory ${cwd}. Commands: echo, cat, ls, pwd, cd, true, false, sleep, exit, mkdir, sh -c.`;\n },\n get networkPolicy() {\n return networkPolicy;\n },\n get stopped() {\n return stopped;\n },\n readFile: async (options) => {\n const path = resolve(options.path);\n const entry = entries.get(path);\n if (entry == null) return null;\n if (entry.type === 'directory') throw fsError('EISDIR', `${path} is a directory`);\n const data = entry.data;\n const chunkBytes = 64 * 1024;\n let offset = 0;\n return new ReadableStream<Uint8Array>({\n pull: (controller) => {\n if (offset >= data.byteLength) {\n controller.close();\n return;\n }\n const end = Math.min(data.byteLength, offset + chunkBytes);\n controller.enqueue(data.slice(offset, end));\n offset = end;\n },\n });\n },\n readBinaryFile: async (options) => {\n const stream = await sandbox.readFile(options);\n return stream == null ? null : readAllBytes(stream);\n },\n readTextFile: async (options) => {\n const stream = await sandbox.readFile(options);\n return stream == null ? null : readTextFileStream(stream, options);\n },\n writeFile: async (options) => {\n const data = await readAllBytes(options.content);\n putFile(resolve(options.path), data);\n },\n writeBinaryFile: async (options) => {\n putFile(resolve(options.path), options.content.slice());\n },\n writeTextFile: async (options) => {\n putFile(resolve(options.path), encodeSandboxText(options.content, options.encoding));\n },\n spawn,\n run: (options) => runCollect({ spawn }, options),\n stat: async (options) => statOf(resolve(options.path)),\n readDir: async (options): Promise<DirEntryIface[]> => {\n const path = resolve(options.path);\n const entry = entries.get(path);\n if (entry == null) throw fsError('ENOENT', `${path}: No such file or directory`);\n if (entry.type !== 'directory') throw fsError('ENOTDIR', `${path} is not a directory`);\n return children(path).map((name) => {\n const childPath = path === '/' ? `/${name}` : `${path}/${name}`;\n const child = entries.get(childPath);\n return {\n name,\n path: childPath,\n type: child?.type === 'file' ? SandboxEntryType.File : SandboxEntryType.Directory,\n size: child?.type === 'file' ? child.data.byteLength : null,\n };\n });\n },\n resolvePath: resolve,\n removePath: async (options) => {\n const path = resolve(options.path);\n const entry = entries.get(path);\n if (entry == null) {\n if (options.force === true) return;\n throw fsError('ENOENT', `${path}: No such file or directory`);\n }\n if (path === '/') throw fsError('EPERM', 'refusing to remove /');\n if (entry.type === 'directory') {\n const prefix = `${path}/`;\n const nested = [...entries.keys()].filter((key) => key.startsWith(prefix));\n if (nested.length > 0 && options.recursive !== true)\n throw fsError('ENOTEMPTY', `${path}: Directory not empty`);\n for (const key of nested) entries.delete(key);\n }\n entries.delete(path);\n },\n getPortEndpoint: async (options) => {\n const protocol = options.protocol ?? PortProtocol.Http;\n return {\n url:\n args.portUrl?.({ port: options.port, protocol }) ??\n `${protocol}://127.0.0.1:${options.port}`,\n };\n },\n getPortUrl: async (options) => (await sandbox.getPortEndpoint(options)).url,\n stop: async () => {\n stopped = true;\n await Promise.all([...processes].map((child) => child.kill()));\n },\n destroy: async () => {\n await sandbox.stop();\n entries.clear();\n ensureDirectory('/');\n },\n };\n if (args.networkPolicy === true) {\n sandbox.setNetworkPolicy = async (policy) => {\n networkPolicy = policy;\n };\n }\n if (args.computerUse != null) sandbox.computerUse = args.computerUse;\n if (args.screen != null) sandbox.screen = args.screen;\n if (args.localMcp != null) sandbox.localMcp = args.localMcp;\n return sandbox;\n};\n\n// Every file of a memory sandbox as path \u2192 base64-free UTF-8 text (binary\n// files are skipped); handy for assertions.\nexport const memorySandboxTextFiles = async (\n sandbox: NetworkSandboxSessionIface,\n root = '/',\n): Promise<Record<string, string>> => {\n const out: Record<string, string> = {};\n const walk = async (path: string) => {\n for (const entry of await sandbox.readDir({ path })) {\n if (entry.type === SandboxEntryType.Directory) await walk(entry.path);\n else {\n const text = await sandbox.readTextFile({ path: entry.path });\n if (text != null) out[entry.path] = text;\n }\n }\n };\n await walk(root);\n return out;\n};\n\nconst memoryEnvironmentSchema = z\n .object({\n files: z.record(z.string(), z.string()).optional(),\n cwd: z.string().optional(),\n ports: z.array(z.number().int()).optional(),\n })\n .nullable()\n .optional();\n\nexport type MemorySandboxArtifact = { files: Record<string, string>; cwd: string; ports: number[] };\nexport type MemorySandboxState = { id: string };\n\nconst fingerprint = (value: string): string => {\n // FNV-1a, enough for a content key in an example provider.\n let hash = 0x811c9dc5;\n for (let i = 0; i < value.length; i += 1) {\n hash ^= value.charCodeAt(i);\n hash = Math.imul(hash, 0x01000193) >>> 0;\n }\n return hash.toString(16).padStart(8, '0');\n};\n\nconst memoryHandle = (\n sandbox: MemorySandboxIface,\n): SandboxProviderHandleIface<MemorySandboxIface> => ({\n sandbox,\n onSandboxStop: () => sandbox.stop(),\n onSandboxDelete: () => sandbox.destroy(),\n onRuntimeShutdown: () => sandbox.stop(),\n});\n\n// Example provider over the memory sandbox. Ephemeral: `resume` starts again\n// from the prepared artifact, so files written before a park are lost.\nexport const memorySandboxProvider = defineSandboxProvider<\n JsonValue,\n JsonValue,\n MemorySandboxArtifact,\n MemorySandboxState,\n MemorySandboxIface\n>({\n name: 'memory',\n environment: (rawOptions) => {\n const options = memoryEnvironmentSchema.parse(rawOptions) ?? {};\n const artifactFor = (files: Record<string, string>): MemorySandboxArtifact => ({\n files: { ...options.files, ...files },\n cwd: options.cwd ?? '/workspace',\n ports: options.ports ?? [],\n });\n const open = (bindingId: string, artifact: MemorySandboxArtifact) =>\n createMemorySandbox({\n id: bindingId,\n files: artifact.files,\n cwd: artifact.cwd,\n ports: artifact.ports,\n });\n return {\n artifactKey: async (ctx) => `memory:${fingerprint(JSON.stringify(artifactFor(ctx.files)))}`,\n prepare: async (ctx) => artifactFor(ctx.files),\n start: async (ctx, _openOptions, artifact) => ({\n handle: memoryHandle(open(ctx.session.bindingId, artifact)),\n state: { id: ctx.session.bindingId },\n }),\n resume: async (_ctx, artifact, state) => memoryHandle(open(state.id, artifact)),\n park: async (_ctx, _artifact, state) => state,\n };\n },\n});\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }
@@ -0,0 +1,29 @@
1
+ export interface MemoryShellFsIface {
2
+ readFile(path: string): Uint8Array | null;
3
+ writeFile(path: string, data: Uint8Array, options: {
4
+ append: boolean;
5
+ }): void;
6
+ kind(path: string): 'file' | 'directory' | null;
7
+ list(path: string): string[] | null;
8
+ mkdir(path: string, options: {
9
+ parents: boolean;
10
+ }): void;
11
+ }
12
+ export interface MemoryShellIoIface {
13
+ readStdin: () => Promise<Uint8Array | null>;
14
+ stdout: (bytes: Uint8Array) => void;
15
+ stderr: (bytes: Uint8Array) => void;
16
+ signal: AbortSignal;
17
+ }
18
+ export interface MemoryShellContextIface {
19
+ fs: MemoryShellFsIface;
20
+ io: MemoryShellIoIface;
21
+ cwd: string;
22
+ sleep: (ms: number, signal: AbortSignal) => Promise<void>;
23
+ }
24
+ export declare const runMemoryScript: (args: MemoryShellContextIface & {
25
+ script: string;
26
+ }) => Promise<number>;
27
+ export declare const runMemoryArgv: (args: MemoryShellContextIface & {
28
+ argv: string[];
29
+ }) => Promise<number>;
@@ -0,0 +1,278 @@
1
+ import * as z from "zod";
2
+ import { utf8Encode } from "./bytes.mjs";
3
+ import { posixBasename, resolvePosixPath } from "./paths.mjs";
4
+ const EXIT_REQUEST = "MemoryShellExit";
5
+ const exitFieldsSchema = z.object({ code: z.number() });
6
+ const exitRequest = (code) => Object.assign(new Error(`exit ${code}`), { name: EXIT_REQUEST, code });
7
+ const isExitRequest = (cause) => cause instanceof Error && cause.name === EXIT_REQUEST && exitFieldsSchema.safeParse(cause).success;
8
+ const tokenize = (script) => {
9
+ const tokens = [];
10
+ let word = null;
11
+ const flush = () => {
12
+ if (word != null) tokens.push({ kind: "word", value: word });
13
+ word = null;
14
+ };
15
+ for (let i = 0; i < script.length; i += 1) {
16
+ const char = script[i] ?? "";
17
+ if (char === "'") {
18
+ const end = script.indexOf("'", i + 1);
19
+ const stop = end < 0 ? script.length : end;
20
+ word = (word ?? "") + script.slice(i + 1, stop);
21
+ i = stop;
22
+ continue;
23
+ }
24
+ if (char === '"') {
25
+ let value = "";
26
+ let j = i + 1;
27
+ for (; j < script.length && script[j] !== '"'; j += 1) {
28
+ if (script[j] === "\\" && j + 1 < script.length) {
29
+ j += 1;
30
+ }
31
+ value += script[j] ?? "";
32
+ }
33
+ word = (word ?? "") + value;
34
+ i = j;
35
+ continue;
36
+ }
37
+ if (char === "\\" && i + 1 < script.length) {
38
+ word = (word ?? "") + (script[i + 1] ?? "");
39
+ i += 1;
40
+ continue;
41
+ }
42
+ if (char === " " || char === " ") {
43
+ flush();
44
+ continue;
45
+ }
46
+ if (char === "\n" || char === ";") {
47
+ flush();
48
+ tokens.push({ kind: "op", value: ";" });
49
+ continue;
50
+ }
51
+ if (char === "&" && script[i + 1] === "&") {
52
+ flush();
53
+ tokens.push({ kind: "op", value: "&&" });
54
+ i += 1;
55
+ continue;
56
+ }
57
+ if (char === "|" && script[i + 1] === "|") {
58
+ flush();
59
+ tokens.push({ kind: "op", value: "||" });
60
+ i += 1;
61
+ continue;
62
+ }
63
+ if (char === ">") {
64
+ flush();
65
+ if (script.startsWith(">&2", i)) {
66
+ tokens.push({ kind: "op", value: ">&2" });
67
+ i += 2;
68
+ } else if (script[i + 1] === ">") {
69
+ tokens.push({ kind: "op", value: ">>" });
70
+ i += 1;
71
+ } else {
72
+ tokens.push({ kind: "op", value: ">" });
73
+ }
74
+ continue;
75
+ }
76
+ if (char === "#" && word == null) {
77
+ const end = script.indexOf("\n", i);
78
+ i = end < 0 ? script.length : end - 1;
79
+ continue;
80
+ }
81
+ word = (word ?? "") + char;
82
+ }
83
+ flush();
84
+ return tokens;
85
+ };
86
+ const parse = (script) => {
87
+ const steps = [];
88
+ let connector = ";";
89
+ let command = { argv: [], stdoutTo: null };
90
+ const push = () => {
91
+ if (command.argv.length > 0) steps.push({ connector, command });
92
+ command = { argv: [], stdoutTo: null };
93
+ };
94
+ const tokens = tokenize(script);
95
+ for (let i = 0; i < tokens.length; i += 1) {
96
+ const token = tokens[i];
97
+ if (token == null) continue;
98
+ if (token.kind === "word") {
99
+ command.argv.push(token.value);
100
+ continue;
101
+ }
102
+ if (token.value === ">&2") {
103
+ command.stdoutTo = "stderr";
104
+ continue;
105
+ }
106
+ if (token.value === ">" || token.value === ">>") {
107
+ const target = tokens[i + 1];
108
+ if (target?.kind === "word") {
109
+ command.stdoutTo = { path: target.value, append: token.value === ">>" };
110
+ i += 1;
111
+ }
112
+ continue;
113
+ }
114
+ push();
115
+ connector = token.value === "&&" ? "&&" : token.value === "||" ? "||" : ";";
116
+ }
117
+ push();
118
+ return steps;
119
+ };
120
+ const line = (text) => utf8Encode(`${text}
121
+ `);
122
+ const runCommand = async (context, command) => {
123
+ const [name, ...args] = command.argv;
124
+ const captured = [];
125
+ const out = (bytes) => {
126
+ if (command.stdoutTo == null) context.io.stdout(bytes);
127
+ else if (command.stdoutTo === "stderr") context.io.stderr(bytes);
128
+ else captured.push(bytes);
129
+ };
130
+ const err = (text) => context.io.stderr(line(text));
131
+ const status = await (async () => {
132
+ switch (name) {
133
+ case "echo": {
134
+ const noNewline = args[0] === "-n";
135
+ const text = (noNewline ? args.slice(1) : args).join(" ");
136
+ out(utf8Encode(noNewline ? text : `${text}
137
+ `));
138
+ return 0;
139
+ }
140
+ case "cat": {
141
+ if (args.length === 0) {
142
+ for (; ; ) {
143
+ const chunk = await context.io.readStdin();
144
+ if (chunk == null) return 0;
145
+ out(chunk);
146
+ }
147
+ }
148
+ let status2 = 0;
149
+ for (const arg of args) {
150
+ const path = resolvePosixPath(context.cwd, arg);
151
+ const data = context.fs.readFile(path);
152
+ if (data == null) {
153
+ err(
154
+ `cat: ${arg}: ${context.fs.kind(path) === "directory" ? "Is a directory" : "No such file or directory"}`
155
+ );
156
+ status2 = 1;
157
+ } else {
158
+ out(data);
159
+ }
160
+ }
161
+ return status2;
162
+ }
163
+ case "ls": {
164
+ const targets = args.filter((arg) => !arg.startsWith("-"));
165
+ let status2 = 0;
166
+ for (const arg of targets.length === 0 ? ["."] : targets) {
167
+ const path = resolvePosixPath(context.cwd, arg);
168
+ const kind = context.fs.kind(path);
169
+ if (kind == null) {
170
+ err(`ls: cannot access '${arg}': No such file or directory`);
171
+ status2 = 2;
172
+ } else if (kind === "file") {
173
+ out(line(posixBasename(path)));
174
+ } else {
175
+ for (const entry of context.fs.list(path) ?? []) out(line(entry));
176
+ }
177
+ }
178
+ return status2;
179
+ }
180
+ case "pwd":
181
+ out(line(context.cwd));
182
+ return 0;
183
+ case "cd": {
184
+ const path = resolvePosixPath(context.cwd, args[0] ?? "/");
185
+ if (context.fs.kind(path) !== "directory") {
186
+ err(`cd: ${args[0] ?? "/"}: No such file or directory`);
187
+ return 1;
188
+ }
189
+ context.cwd = path;
190
+ return 0;
191
+ }
192
+ case "true":
193
+ return 0;
194
+ case "false":
195
+ return 1;
196
+ case "sleep": {
197
+ const seconds = Number(args[0] ?? "0");
198
+ if (!Number.isFinite(seconds) || seconds < 0) {
199
+ err(`sleep: invalid time interval '${args[0] ?? ""}'`);
200
+ return 1;
201
+ }
202
+ await context.sleep(seconds * 1e3, context.io.signal);
203
+ return 0;
204
+ }
205
+ case "exit": {
206
+ const code = Number(args[0] ?? "0");
207
+ throw exitRequest(Number.isInteger(code) ? code & 255 : 2);
208
+ }
209
+ case "mkdir": {
210
+ const parents = args.includes("-p");
211
+ for (const arg of args.filter((value) => !value.startsWith("-"))) {
212
+ try {
213
+ context.fs.mkdir(resolvePosixPath(context.cwd, arg), { parents });
214
+ } catch (error) {
215
+ err(
216
+ `mkdir: cannot create directory '${arg}': ${error instanceof Error ? error.message : "error"}`
217
+ );
218
+ return 1;
219
+ }
220
+ }
221
+ return 0;
222
+ }
223
+ case "sh":
224
+ case "bash": {
225
+ const flag = args.indexOf("-c");
226
+ const script = flag >= 0 ? args[flag + 1] : void 0;
227
+ if (script == null) {
228
+ err(`${name}: interactive shells are not supported by the memory sandbox`);
229
+ return 127;
230
+ }
231
+ return runMemoryScript({ ...context, script });
232
+ }
233
+ default:
234
+ err(`${name ?? ""}: command not found`);
235
+ return 127;
236
+ }
237
+ })();
238
+ if (command.stdoutTo != null && command.stdoutTo !== "stderr") {
239
+ const path = resolvePosixPath(context.cwd, command.stdoutTo.path);
240
+ const total = captured.reduce((sum, chunk) => sum + chunk.byteLength, 0);
241
+ const data = new Uint8Array(total);
242
+ let offset = 0;
243
+ for (const chunk of captured) {
244
+ data.set(chunk, offset);
245
+ offset += chunk.byteLength;
246
+ }
247
+ context.fs.writeFile(path, data, { append: command.stdoutTo.append });
248
+ }
249
+ return status;
250
+ };
251
+ const runMemoryScript = async (args) => {
252
+ let status = 0;
253
+ try {
254
+ for (const step of parse(args.script)) {
255
+ args.io.signal.throwIfAborted();
256
+ if (step.connector === "&&" && status !== 0) continue;
257
+ if (step.connector === "||" && status === 0) continue;
258
+ status = await runCommand(args, step.command);
259
+ }
260
+ } catch (error) {
261
+ if (isExitRequest(error)) return error.code;
262
+ throw error;
263
+ }
264
+ return status;
265
+ };
266
+ const runMemoryArgv = async (args) => {
267
+ try {
268
+ return await runCommand(args, { argv: args.argv, stdoutTo: null });
269
+ } catch (error) {
270
+ if (isExitRequest(error)) return error.code;
271
+ throw error;
272
+ }
273
+ };
274
+ export {
275
+ runMemoryArgv,
276
+ runMemoryScript
277
+ };
278
+ //# sourceMappingURL=memory-shell.mjs.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../../src/sandbox/memory-shell.ts"],
4
+ "sourcesContent": ["import * as z from 'zod';\nimport { utf8Encode } from \"./bytes.mjs\";\nimport { posixBasename, resolvePosixPath } from \"./paths.mjs\";\n\n// A tiny command interpreter for the in-memory reference sandbox: enough of a\n// shell for tests and the conformance kit. Supports `;`, `&&`, `||`, newlines,\n// single and double quotes, `> file`, `>> file`, `>&2`, and the commands echo,\n// cat, ls, pwd, cd, true, false, sleep, exit, mkdir and `sh -c` / `bash -c`.\n\nexport interface MemoryShellFsIface {\n readFile(path: string): Uint8Array | null;\n writeFile(path: string, data: Uint8Array, options: { append: boolean }): void;\n kind(path: string): 'file' | 'directory' | null;\n list(path: string): string[] | null;\n mkdir(path: string, options: { parents: boolean }): void;\n}\n\nexport interface MemoryShellIoIface {\n // Next stdin chunk, or null at end of input.\n readStdin: () => Promise<Uint8Array | null>;\n stdout: (bytes: Uint8Array) => void;\n stderr: (bytes: Uint8Array) => void;\n signal: AbortSignal;\n}\n\nexport interface MemoryShellContextIface {\n fs: MemoryShellFsIface;\n io: MemoryShellIoIface;\n cwd: string;\n sleep: (ms: number, signal: AbortSignal) => Promise<void>;\n}\n\ntype Token = { kind: 'word'; value: string } | { kind: 'op'; value: string };\n\ninterface CommandIface {\n argv: string[];\n stdoutTo: { path: string; append: boolean } | 'stderr' | null;\n}\n\ninterface StepIface {\n connector: ';' | '&&' | '||';\n command: CommandIface;\n}\n\n// `exit N` unwinds the script as an error carrying the status.\ninterface ExitRequestIface extends Error {\n code: number;\n}\n\nconst EXIT_REQUEST = 'MemoryShellExit';\nconst exitFieldsSchema = z.object({ code: z.number() });\n\nconst exitRequest = (code: number): ExitRequestIface =>\n Object.assign(new Error(`exit ${code}`), { name: EXIT_REQUEST, code });\n\nconst isExitRequest = (cause: unknown): cause is ExitRequestIface =>\n cause instanceof Error &&\n cause.name === EXIT_REQUEST &&\n exitFieldsSchema.safeParse(cause).success;\n\nconst tokenize = (script: string): Token[] => {\n const tokens: Token[] = [];\n let word: string | null = null;\n const flush = () => {\n if (word != null) tokens.push({ kind: 'word', value: word });\n word = null;\n };\n for (let i = 0; i < script.length; i += 1) {\n const char = script[i] ?? '';\n if (char === \"'\") {\n const end = script.indexOf(\"'\", i + 1);\n const stop = end < 0 ? script.length : end;\n word = (word ?? '') + script.slice(i + 1, stop);\n i = stop;\n continue;\n }\n if (char === '\"') {\n let value = '';\n let j = i + 1;\n for (; j < script.length && script[j] !== '\"'; j += 1) {\n if (script[j] === '\\\\' && j + 1 < script.length) {\n j += 1;\n }\n value += script[j] ?? '';\n }\n word = (word ?? '') + value;\n i = j;\n continue;\n }\n if (char === '\\\\' && i + 1 < script.length) {\n word = (word ?? '') + (script[i + 1] ?? '');\n i += 1;\n continue;\n }\n if (char === ' ' || char === '\\t') {\n flush();\n continue;\n }\n if (char === '\\n' || char === ';') {\n flush();\n tokens.push({ kind: 'op', value: ';' });\n continue;\n }\n if (char === '&' && script[i + 1] === '&') {\n flush();\n tokens.push({ kind: 'op', value: '&&' });\n i += 1;\n continue;\n }\n if (char === '|' && script[i + 1] === '|') {\n flush();\n tokens.push({ kind: 'op', value: '||' });\n i += 1;\n continue;\n }\n if (char === '>') {\n flush();\n if (script.startsWith('>&2', i)) {\n tokens.push({ kind: 'op', value: '>&2' });\n i += 2;\n } else if (script[i + 1] === '>') {\n tokens.push({ kind: 'op', value: '>>' });\n i += 1;\n } else {\n tokens.push({ kind: 'op', value: '>' });\n }\n continue;\n }\n if (char === '#' && word == null) {\n const end = script.indexOf('\\n', i);\n i = end < 0 ? script.length : end - 1;\n continue;\n }\n word = (word ?? '') + char;\n }\n flush();\n return tokens;\n};\n\nconst parse = (script: string): StepIface[] => {\n const steps: StepIface[] = [];\n let connector: StepIface['connector'] = ';';\n let command: CommandIface = { argv: [], stdoutTo: null };\n const push = () => {\n if (command.argv.length > 0) steps.push({ connector, command });\n command = { argv: [], stdoutTo: null };\n };\n const tokens = tokenize(script);\n for (let i = 0; i < tokens.length; i += 1) {\n const token = tokens[i];\n if (token == null) continue;\n if (token.kind === 'word') {\n command.argv.push(token.value);\n continue;\n }\n if (token.value === '>&2') {\n command.stdoutTo = 'stderr';\n continue;\n }\n if (token.value === '>' || token.value === '>>') {\n const target = tokens[i + 1];\n if (target?.kind === 'word') {\n command.stdoutTo = { path: target.value, append: token.value === '>>' };\n i += 1;\n }\n continue;\n }\n push();\n connector = token.value === '&&' ? '&&' : token.value === '||' ? '||' : ';';\n }\n push();\n return steps;\n};\n\nconst line = (text: string) => utf8Encode(`${text}\\n`);\n\nconst runCommand = async (\n context: MemoryShellContextIface,\n command: CommandIface,\n): Promise<number> => {\n const [name, ...args] = command.argv;\n const captured: Uint8Array[] = [];\n const out = (bytes: Uint8Array) => {\n if (command.stdoutTo == null) context.io.stdout(bytes);\n else if (command.stdoutTo === 'stderr') context.io.stderr(bytes);\n else captured.push(bytes);\n };\n const err = (text: string) => context.io.stderr(line(text));\n const status = await (async (): Promise<number> => {\n switch (name) {\n case 'echo': {\n const noNewline = args[0] === '-n';\n const text = (noNewline ? args.slice(1) : args).join(' ');\n out(utf8Encode(noNewline ? text : `${text}\\n`));\n return 0;\n }\n case 'cat': {\n if (args.length === 0) {\n for (;;) {\n const chunk = await context.io.readStdin();\n if (chunk == null) return 0;\n out(chunk);\n }\n }\n let status = 0;\n for (const arg of args) {\n const path = resolvePosixPath(context.cwd, arg);\n const data = context.fs.readFile(path);\n if (data == null) {\n err(\n `cat: ${arg}: ${context.fs.kind(path) === 'directory' ? 'Is a directory' : 'No such file or directory'}`,\n );\n status = 1;\n } else {\n out(data);\n }\n }\n return status;\n }\n case 'ls': {\n const targets = args.filter((arg) => !arg.startsWith('-'));\n let status = 0;\n for (const arg of targets.length === 0 ? ['.'] : targets) {\n const path = resolvePosixPath(context.cwd, arg);\n const kind = context.fs.kind(path);\n if (kind == null) {\n err(`ls: cannot access '${arg}': No such file or directory`);\n status = 2;\n } else if (kind === 'file') {\n out(line(posixBasename(path)));\n } else {\n for (const entry of context.fs.list(path) ?? []) out(line(entry));\n }\n }\n return status;\n }\n case 'pwd':\n out(line(context.cwd));\n return 0;\n case 'cd': {\n const path = resolvePosixPath(context.cwd, args[0] ?? '/');\n if (context.fs.kind(path) !== 'directory') {\n err(`cd: ${args[0] ?? '/'}: No such file or directory`);\n return 1;\n }\n context.cwd = path;\n return 0;\n }\n case 'true':\n return 0;\n case 'false':\n return 1;\n case 'sleep': {\n const seconds = Number(args[0] ?? '0');\n if (!Number.isFinite(seconds) || seconds < 0) {\n err(`sleep: invalid time interval '${args[0] ?? ''}'`);\n return 1;\n }\n await context.sleep(seconds * 1000, context.io.signal);\n return 0;\n }\n case 'exit': {\n const code = Number(args[0] ?? '0');\n throw exitRequest(Number.isInteger(code) ? code & 0xff : 2);\n }\n case 'mkdir': {\n const parents = args.includes('-p');\n for (const arg of args.filter((value) => !value.startsWith('-'))) {\n try {\n context.fs.mkdir(resolvePosixPath(context.cwd, arg), { parents });\n } catch (error) {\n err(\n `mkdir: cannot create directory '${arg}': ${error instanceof Error ? error.message : 'error'}`,\n );\n return 1;\n }\n }\n return 0;\n }\n case 'sh':\n case 'bash': {\n const flag = args.indexOf('-c');\n const script = flag >= 0 ? args[flag + 1] : undefined;\n if (script == null) {\n err(`${name}: interactive shells are not supported by the memory sandbox`);\n return 127;\n }\n return runMemoryScript({ ...context, script });\n }\n default:\n err(`${name ?? ''}: command not found`);\n return 127;\n }\n })();\n if (command.stdoutTo != null && command.stdoutTo !== 'stderr') {\n const path = resolvePosixPath(context.cwd, command.stdoutTo.path);\n const total = captured.reduce((sum, chunk) => sum + chunk.byteLength, 0);\n const data = new Uint8Array(total);\n let offset = 0;\n for (const chunk of captured) {\n data.set(chunk, offset);\n offset += chunk.byteLength;\n }\n context.fs.writeFile(path, data, { append: command.stdoutTo.append });\n }\n return status;\n};\n\n// Runs a script; resolves with its exit status. Aborting the signal stops it\n// between commands and inside `sleep` and stdin reads (the caller maps the\n// abort to a signal exit status).\nexport const runMemoryScript = async (\n args: MemoryShellContextIface & { script: string },\n): Promise<number> => {\n let status = 0;\n try {\n for (const step of parse(args.script)) {\n args.io.signal.throwIfAborted();\n if (step.connector === '&&' && status !== 0) continue;\n if (step.connector === '||' && status === 0) continue;\n status = await runCommand(args, step.command);\n }\n } catch (error) {\n if (isExitRequest(error)) return error.code;\n throw error;\n }\n return status;\n};\n\nexport const runMemoryArgv = async (\n args: MemoryShellContextIface & { argv: string[] },\n): Promise<number> => {\n try {\n return await runCommand(args, { argv: args.argv, stdoutTo: null });\n } catch (error) {\n if (isExitRequest(error)) return error.code;\n throw error;\n }\n};\n"],
5
+ "mappings": "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",
6
+ "names": ["status"]
7
+ }
@@ -0,0 +1,4 @@
1
+ import type { SandboxOpener, SandboxOpenScopeGlobalIface, SandboxOpenScopeIface } from "./open-scope.mjs";
2
+ export declare const sandboxOpenScope: SandboxOpenScopeIface;
3
+ export declare const installSandboxOpenScope: (target: SandboxOpenScopeGlobalIface) => void;
4
+ export declare const withSandboxOpener: <T>(opener: SandboxOpener, run: () => T) => Promise<Awaited<T>>;
@@ -0,0 +1,19 @@
1
+ import { AsyncLocalStorage } from "node:async_hooks";
2
+ const openers = new AsyncLocalStorage();
3
+ const sandboxOpenScope = {
4
+ current: () => openers.getStore() ?? null
5
+ };
6
+ const installSandboxOpenScope = (target) => {
7
+ target.__finalSandboxOpenScope = sandboxOpenScope;
8
+ };
9
+ const withSandboxOpener = (opener, run) => {
10
+ if (globalThis.__finalSandboxOpenScope !== sandboxOpenScope) installSandboxOpenScope(globalThis);
11
+ const settle = async () => await run();
12
+ return openers.run(opener, settle);
13
+ };
14
+ export {
15
+ installSandboxOpenScope,
16
+ sandboxOpenScope,
17
+ withSandboxOpener
18
+ };
19
+ //# sourceMappingURL=open-scope-node.mjs.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../../src/sandbox/open-scope-node.ts"],
4
+ "sourcesContent": ["import { AsyncLocalStorage } from 'node:async_hooks';\nimport type {\n SandboxOpener,\n SandboxOpenScopeGlobalIface,\n SandboxOpenScopeIface,\n} from \"./open-scope.mjs\";\n\n// The runtime half of open-scope.ts, for isolate runtimes with\n// AsyncLocalStorage (Node, workerd with nodejs_compat): runs a sandbox.ts\n// selector with its opener as the current one, so concurrent selectors in one\n// isolate each open their own session's target.\n\nconst openers = new AsyncLocalStorage<SandboxOpener>();\n\nexport const sandboxOpenScope: SandboxOpenScopeIface = {\n current: () => openers.getStore() ?? null,\n};\n\n// Installs the scope on a global object (the isolate's, or a local realm's).\nexport const installSandboxOpenScope = (target: SandboxOpenScopeGlobalIface): void => {\n target.__finalSandboxOpenScope = sandboxOpenScope;\n};\n\n// Settles inside the scope, so a selector that returns an open request\n// (`() => computer().open()`) is opened by its own opener too.\nexport const withSandboxOpener = <T>(opener: SandboxOpener, run: () => T): Promise<Awaited<T>> => {\n if (globalThis.__finalSandboxOpenScope !== sandboxOpenScope) installSandboxOpenScope(globalThis);\n const settle = async (): Promise<Awaited<T>> => await run();\n return openers.run(opener, settle);\n};\n"],
5
+ "mappings": "AAAA,SAAS,yBAAyB;AAYlC,MAAM,UAAU,IAAI,kBAAiC;AAE9C,MAAM,mBAA0C;AAAA,EACrD,SAAS,MAAM,QAAQ,SAAS,KAAK;AACvC;AAGO,MAAM,0BAA0B,CAAC,WAA8C;AACpF,SAAO,0BAA0B;AACnC;AAIO,MAAM,oBAAoB,CAAI,QAAuB,QAAsC;AAChG,MAAI,WAAW,4BAA4B,iBAAkB,yBAAwB,UAAU;AAC/F,QAAM,SAAS,YAAiC,MAAM,IAAI;AAC1D,SAAO,QAAQ,IAAI,QAAQ,MAAM;AACnC;",
6
+ "names": []
7
+ }
@@ -0,0 +1,16 @@
1
+ import * as z from 'zod';
2
+ import type { NetworkSandboxSessionIface, SandboxOpenRequestIface } from "./types.mjs";
3
+ export type SandboxOpener = (request: SandboxOpenRequestIface<unknown, unknown>) => Promise<NetworkSandboxSessionIface>;
4
+ export interface SandboxOpenScopeIface {
5
+ current(): SandboxOpener | null;
6
+ }
7
+ declare global {
8
+ var __finalSandboxOpenScope: unknown;
9
+ }
10
+ export interface SandboxOpenScopeGlobalIface {
11
+ __finalSandboxOpenScope?: unknown;
12
+ }
13
+ export type AwaitableSandboxOpenRequestIface<EnvOptions, OpenOptions> = SandboxOpenRequestIface<EnvOptions, OpenOptions> & PromiseLike<NetworkSandboxSessionIface>;
14
+ export declare const unopenedRequestOf: (cause: unknown) => SandboxOpenRequestIface<unknown, unknown> | null;
15
+ export declare const awaitableOpenRequest: <T extends SandboxOpenRequestIface<unknown, unknown>>(request: T) => T & PromiseLike<NetworkSandboxSessionIface>;
16
+ export declare const sandboxSessionSchema: z.ZodCustom<NetworkSandboxSessionIface, NetworkSandboxSessionIface>;
@@ -0,0 +1,66 @@
1
+ import * as z from "zod";
2
+ const openRequestFieldsSchema = z.looseObject({
3
+ providerName: z.string(),
4
+ environmentOptions: z.unknown(),
5
+ openOptions: z.unknown()
6
+ });
7
+ const unopenedSchema = z.object({
8
+ unopenedRequest: openRequestFieldsSchema
9
+ });
10
+ const unopenedRequestOf = (cause) => {
11
+ const parsed = unopenedSchema.safeParse(cause);
12
+ return parsed.success ? parsed.data.unopenedRequest : null;
13
+ };
14
+ const scopeMethodsSchema = z.looseObject({ current: z.function() });
15
+ const sandboxOpenScopeSchema = z.custom(
16
+ (value) => scopeMethodsSchema.safeParse(value).success
17
+ );
18
+ const awaitableOpenRequest = (request) => {
19
+ const then = (onFulfilled, onRejected) => {
20
+ const scope = sandboxOpenScopeSchema.safeParse(globalThis.__finalSandboxOpenScope).data;
21
+ const opener = scope?.current() ?? null;
22
+ const opened = opener == null ? Promise.reject(
23
+ Object.assign(
24
+ new Error(
25
+ `awaiting ${request.providerName}.open() opens a sandbox only inside a defineSandbox selector`
26
+ ),
27
+ { unopenedRequest: { ...request } }
28
+ )
29
+ ) : opener(request);
30
+ return opened.then(onFulfilled, onRejected);
31
+ };
32
+ const awaitable = Object.assign(request, { then });
33
+ Object.defineProperty(awaitable, "then", { enumerable: false });
34
+ return awaitable;
35
+ };
36
+ const sessionMethodsSchema = z.looseObject({
37
+ id: z.string(),
38
+ description: z.string(),
39
+ defaultWorkingDirectory: z.string(),
40
+ ports: z.array(z.number()),
41
+ readFile: z.function(),
42
+ readBinaryFile: z.function(),
43
+ readTextFile: z.function(),
44
+ writeFile: z.function(),
45
+ writeBinaryFile: z.function(),
46
+ writeTextFile: z.function(),
47
+ spawn: z.function(),
48
+ run: z.function(),
49
+ stat: z.function(),
50
+ readDir: z.function(),
51
+ resolvePath: z.function(),
52
+ removePath: z.function(),
53
+ getPortEndpoint: z.function(),
54
+ getPortUrl: z.function(),
55
+ stop: z.function(),
56
+ destroy: z.function()
57
+ });
58
+ const sandboxSessionSchema = z.custom(
59
+ (value) => sessionMethodsSchema.safeParse(value).success
60
+ );
61
+ export {
62
+ awaitableOpenRequest,
63
+ sandboxSessionSchema,
64
+ unopenedRequestOf
65
+ };
66
+ //# sourceMappingURL=open-scope.mjs.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../../src/sandbox/open-scope.ts"],
4
+ "sourcesContent": ["import * as z from 'zod';\nimport type { NetworkSandboxSessionIface, SandboxOpenRequestIface } from \"./types.mjs\";\n\n// sandbox.ts's default `defineSandbox` selector awaits an environment's\n// `open(...)` to get the live sandbox it prepares:\n//\n// export default defineSandbox(async ({ session }) => {\n// const sandbox = await computer.open({ access: 'write' });\n// await sandbox.writeTextFile({ path: '.final/session', content: session.id });\n// return sandbox;\n// });\n//\n// The isolate runtime that calls the selector installs an opener scope on the\n// isolate's global (the authored bundle and the runtime are separate modules\n// that share only the global); a request awaited while a selector runs opens\n// through it. Everywhere else (compiling, dynamic resolvers) an open request\n// is a plain description, not awaited.\n\n// Returns the live session for the request.\nexport type SandboxOpener = (\n request: SandboxOpenRequestIface<unknown, unknown>,\n) => Promise<NetworkSandboxSessionIface>;\n\nexport interface SandboxOpenScopeIface {\n // The opener of the selector running in the current async context.\n current(): SandboxOpener | null;\n}\n\ndeclare global {\n // Installed by the isolate runtime (open-scope-node.ts); absent elsewhere.\n var __finalSandboxOpenScope: unknown;\n}\n\n// A global object the scope is installed on: the isolate's, or a local realm's.\nexport interface SandboxOpenScopeGlobalIface {\n __finalSandboxOpenScope?: unknown;\n}\n\n// What `environment.open(...)` returns: the open request, awaitable inside a\n// selector, where awaiting it opens the sandbox and yields its live session.\nexport type AwaitableSandboxOpenRequestIface<EnvOptions, OpenOptions> = SandboxOpenRequestIface<\n EnvOptions,\n OpenOptions\n> &\n PromiseLike<NetworkSandboxSessionIface>;\n\n// Outside a selector there is nothing to open. Awaiting a request there\n// rejects with this error, which carries the plain request: an async dynamic\n// resolver's `return environment.open(...)` awaits it implicitly, and the\n// resolver runtime takes the request back (`unopenedRequestOf`).\nconst openRequestFieldsSchema = z.looseObject({\n providerName: z.string(),\n environmentOptions: z.unknown(),\n openOptions: z.unknown(),\n});\nconst unopenedSchema = z.object({\n unopenedRequest: openRequestFieldsSchema,\n});\n\nexport const unopenedRequestOf = (\n cause: unknown,\n): SandboxOpenRequestIface<unknown, unknown> | null => {\n const parsed = unopenedSchema.safeParse(cause);\n return parsed.success ? parsed.data.unopenedRequest : null;\n};\n\n// Packed entrypoints each own their declarations; the shared global must not name a private type.\nconst scopeMethodsSchema = z.looseObject({ current: z.function() });\nconst sandboxOpenScopeSchema = z.custom<SandboxOpenScopeIface>(\n (value) => scopeMethodsSchema.safeParse(value).success,\n);\n\nexport const awaitableOpenRequest = <T extends SandboxOpenRequestIface<unknown, unknown>>(\n request: T,\n): T & PromiseLike<NetworkSandboxSessionIface> => {\n const then: PromiseLike<NetworkSandboxSessionIface>['then'] = (onFulfilled, onRejected) => {\n const scope = sandboxOpenScopeSchema.safeParse(globalThis.__finalSandboxOpenScope).data;\n const opener = scope?.current() ?? null;\n const opened: Promise<NetworkSandboxSessionIface> =\n opener == null\n ? Promise.reject(\n Object.assign(\n new Error(\n `awaiting ${request.providerName}.open() opens a sandbox only inside a defineSandbox selector`,\n ),\n { unopenedRequest: { ...request } },\n ),\n )\n : opener(request);\n return opened.then(onFulfilled, onRejected);\n };\n // oxlint-disable-next-line unicorn/no-thenable -- awaiting the request inside a selector is the API.\n const awaitable = Object.assign(request, { then });\n // Non-enumerable: lowering, spreading and JSON never see it.\n // oxlint-disable-next-line unicorn/no-thenable -- the same property, made non-enumerable.\n Object.defineProperty(awaitable, 'then', { enumerable: false });\n return awaitable;\n};\n\n// Runtimes hand selectors their own sandbox clients; enough of the session\n// surface to be sure one is a session before the selector gets it.\nconst sessionMethodsSchema = z.looseObject({\n id: z.string(),\n description: z.string(),\n defaultWorkingDirectory: z.string(),\n ports: z.array(z.number()),\n readFile: z.function(),\n readBinaryFile: z.function(),\n readTextFile: z.function(),\n writeFile: z.function(),\n writeBinaryFile: z.function(),\n writeTextFile: z.function(),\n spawn: z.function(),\n run: z.function(),\n stat: z.function(),\n readDir: z.function(),\n resolvePath: z.function(),\n removePath: z.function(),\n getPortEndpoint: z.function(),\n getPortUrl: z.function(),\n stop: z.function(),\n destroy: z.function(),\n});\n\nexport const sandboxSessionSchema = z.custom<NetworkSandboxSessionIface>(\n (value) => sessionMethodsSchema.safeParse(value).success,\n);\n"],
5
+ "mappings": "AAAA,YAAY,OAAO;AAkDnB,MAAM,0BAA0B,EAAE,YAAY;AAAA,EAC5C,cAAc,EAAE,OAAO;AAAA,EACvB,oBAAoB,EAAE,QAAQ;AAAA,EAC9B,aAAa,EAAE,QAAQ;AACzB,CAAC;AACD,MAAM,iBAAiB,EAAE,OAAO;AAAA,EAC9B,iBAAiB;AACnB,CAAC;AAEM,MAAM,oBAAoB,CAC/B,UACqD;AACrD,QAAM,SAAS,eAAe,UAAU,KAAK;AAC7C,SAAO,OAAO,UAAU,OAAO,KAAK,kBAAkB;AACxD;AAGA,MAAM,qBAAqB,EAAE,YAAY,EAAE,SAAS,EAAE,SAAS,EAAE,CAAC;AAClE,MAAM,yBAAyB,EAAE;AAAA,EAC/B,CAAC,UAAU,mBAAmB,UAAU,KAAK,EAAE;AACjD;AAEO,MAAM,uBAAuB,CAClC,YACgD;AAChD,QAAM,OAAwD,CAAC,aAAa,eAAe;AACzF,UAAM,QAAQ,uBAAuB,UAAU,WAAW,uBAAuB,EAAE;AACnF,UAAM,SAAS,OAAO,QAAQ,KAAK;AACnC,UAAM,SACJ,UAAU,OACN,QAAQ;AAAA,MACN,OAAO;AAAA,QACL,IAAI;AAAA,UACF,YAAY,QAAQ,YAAY;AAAA,QAClC;AAAA,QACA,EAAE,iBAAiB,EAAE,GAAG,QAAQ,EAAE;AAAA,MACpC;AAAA,IACF,IACA,OAAO,OAAO;AACpB,WAAO,OAAO,KAAK,aAAa,UAAU;AAAA,EAC5C;AAEA,QAAM,YAAY,OAAO,OAAO,SAAS,EAAE,KAAK,CAAC;AAGjD,SAAO,eAAe,WAAW,QAAQ,EAAE,YAAY,MAAM,CAAC;AAC9D,SAAO;AACT;AAIA,MAAM,uBAAuB,EAAE,YAAY;AAAA,EACzC,IAAI,EAAE,OAAO;AAAA,EACb,aAAa,EAAE,OAAO;AAAA,EACtB,yBAAyB,EAAE,OAAO;AAAA,EAClC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC;AAAA,EACzB,UAAU,EAAE,SAAS;AAAA,EACrB,gBAAgB,EAAE,SAAS;AAAA,EAC3B,cAAc,EAAE,SAAS;AAAA,EACzB,WAAW,EAAE,SAAS;AAAA,EACtB,iBAAiB,EAAE,SAAS;AAAA,EAC5B,eAAe,EAAE,SAAS;AAAA,EAC1B,OAAO,EAAE,SAAS;AAAA,EAClB,KAAK,EAAE,SAAS;AAAA,EAChB,MAAM,EAAE,SAAS;AAAA,EACjB,SAAS,EAAE,SAAS;AAAA,EACpB,aAAa,EAAE,SAAS;AAAA,EACxB,YAAY,EAAE,SAAS;AAAA,EACvB,iBAAiB,EAAE,SAAS;AAAA,EAC5B,YAAY,EAAE,SAAS;AAAA,EACvB,MAAM,EAAE,SAAS;AAAA,EACjB,SAAS,EAAE,SAAS;AACtB,CAAC;AAEM,MAAM,uBAAuB,EAAE;AAAA,EACpC,CAAC,UAAU,qBAAqB,UAAU,KAAK,EAAE;AACnD;",
6
+ "names": []
7
+ }
@@ -0,0 +1,7 @@
1
+ export declare const normalizePosixPath: (path: string) => string;
2
+ export declare const resolvePosixPath: (cwd: string, path: string) => string;
3
+ export declare const posixDirname: (path: string) => string;
4
+ export declare const posixBasename: (path: string) => string;
5
+ export declare const joinPosixPath: (...parts: string[]) => string;
6
+ export declare const relativeTo: (root: string, path: string) => string | null;
7
+ export declare const shellQuote: (value: string) => string;
@@ -0,0 +1,50 @@
1
+ const normalizePosixPath = (path) => {
2
+ const absolute = path.startsWith("/");
3
+ const parts = [];
4
+ for (const part of path.split("/")) {
5
+ if (part === "" || part === ".") continue;
6
+ if (part === "..") {
7
+ if (parts.length > 0 && parts[parts.length - 1] !== "..") parts.pop();
8
+ else if (!absolute) parts.push("..");
9
+ continue;
10
+ }
11
+ parts.push(part);
12
+ }
13
+ const joined = parts.join("/");
14
+ if (absolute) return `/${joined}`;
15
+ return joined === "" ? "." : joined;
16
+ };
17
+ const resolvePosixPath = (cwd, path) => normalizePosixPath(path.startsWith("/") ? path : `${cwd}/${path}`);
18
+ const posixDirname = (path) => {
19
+ const normalized = normalizePosixPath(path);
20
+ if (normalized === "/") return "/";
21
+ const index = normalized.lastIndexOf("/");
22
+ if (index < 0) return ".";
23
+ if (index === 0) return "/";
24
+ return normalized.slice(0, index);
25
+ };
26
+ const posixBasename = (path) => {
27
+ const normalized = normalizePosixPath(path);
28
+ if (normalized === "/") return "/";
29
+ const index = normalized.lastIndexOf("/");
30
+ return index < 0 ? normalized : normalized.slice(index + 1);
31
+ };
32
+ const joinPosixPath = (...parts) => normalizePosixPath(parts.join("/"));
33
+ const relativeTo = (root, path) => {
34
+ const base = normalizePosixPath(root);
35
+ const target = normalizePosixPath(path);
36
+ if (target === base) return "";
37
+ const prefix = base === "/" ? "/" : `${base}/`;
38
+ return target.startsWith(prefix) ? target.slice(prefix.length) : null;
39
+ };
40
+ const shellQuote = (value) => /^[A-Za-z0-9_./:=@%+-]+$/.test(value) ? value : `'${value.replace(/'/g, `'\\''`)}'`;
41
+ export {
42
+ joinPosixPath,
43
+ normalizePosixPath,
44
+ posixBasename,
45
+ posixDirname,
46
+ relativeTo,
47
+ resolvePosixPath,
48
+ shellQuote
49
+ };
50
+ //# sourceMappingURL=paths.mjs.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../../src/sandbox/paths.ts"],
4
+ "sourcesContent": ["// POSIX path helpers for sandbox paths (no node:path, so they run in Workers).\n\nexport const normalizePosixPath = (path: string): string => {\n const absolute = path.startsWith('/');\n const parts: string[] = [];\n for (const part of path.split('/')) {\n if (part === '' || part === '.') continue;\n if (part === '..') {\n if (parts.length > 0 && parts[parts.length - 1] !== '..') parts.pop();\n else if (!absolute) parts.push('..');\n continue;\n }\n parts.push(part);\n }\n const joined = parts.join('/');\n if (absolute) return `/${joined}`;\n return joined === '' ? '.' : joined;\n};\n\n// Relative paths resolve from `cwd`; absolute paths pass through (normalized).\nexport const resolvePosixPath = (cwd: string, path: string): string =>\n normalizePosixPath(path.startsWith('/') ? path : `${cwd}/${path}`);\n\nexport const posixDirname = (path: string): string => {\n const normalized = normalizePosixPath(path);\n if (normalized === '/') return '/';\n const index = normalized.lastIndexOf('/');\n if (index < 0) return '.';\n if (index === 0) return '/';\n return normalized.slice(0, index);\n};\n\nexport const posixBasename = (path: string): string => {\n const normalized = normalizePosixPath(path);\n if (normalized === '/') return '/';\n const index = normalized.lastIndexOf('/');\n return index < 0 ? normalized : normalized.slice(index + 1);\n};\n\nexport const joinPosixPath = (...parts: string[]): string => normalizePosixPath(parts.join('/'));\n\n// Path of `path` relative to `root` when inside it, otherwise null.\nexport const relativeTo = (root: string, path: string): string | null => {\n const base = normalizePosixPath(root);\n const target = normalizePosixPath(path);\n if (target === base) return '';\n const prefix = base === '/' ? '/' : `${base}/`;\n return target.startsWith(prefix) ? target.slice(prefix.length) : null;\n};\n\n// Shell-quotes one argument with single quotes.\nexport const shellQuote = (value: string): string =>\n /^[A-Za-z0-9_./:=@%+-]+$/.test(value) ? value : `'${value.replace(/'/g, `'\\\\''`)}'`;\n"],
5
+ "mappings": "AAEO,MAAM,qBAAqB,CAAC,SAAyB;AAC1D,QAAM,WAAW,KAAK,WAAW,GAAG;AACpC,QAAM,QAAkB,CAAC;AACzB,aAAW,QAAQ,KAAK,MAAM,GAAG,GAAG;AAClC,QAAI,SAAS,MAAM,SAAS,IAAK;AACjC,QAAI,SAAS,MAAM;AACjB,UAAI,MAAM,SAAS,KAAK,MAAM,MAAM,SAAS,CAAC,MAAM,KAAM,OAAM,IAAI;AAAA,eAC3D,CAAC,SAAU,OAAM,KAAK,IAAI;AACnC;AAAA,IACF;AACA,UAAM,KAAK,IAAI;AAAA,EACjB;AACA,QAAM,SAAS,MAAM,KAAK,GAAG;AAC7B,MAAI,SAAU,QAAO,IAAI,MAAM;AAC/B,SAAO,WAAW,KAAK,MAAM;AAC/B;AAGO,MAAM,mBAAmB,CAAC,KAAa,SAC5C,mBAAmB,KAAK,WAAW,GAAG,IAAI,OAAO,GAAG,GAAG,IAAI,IAAI,EAAE;AAE5D,MAAM,eAAe,CAAC,SAAyB;AACpD,QAAM,aAAa,mBAAmB,IAAI;AAC1C,MAAI,eAAe,IAAK,QAAO;AAC/B,QAAM,QAAQ,WAAW,YAAY,GAAG;AACxC,MAAI,QAAQ,EAAG,QAAO;AACtB,MAAI,UAAU,EAAG,QAAO;AACxB,SAAO,WAAW,MAAM,GAAG,KAAK;AAClC;AAEO,MAAM,gBAAgB,CAAC,SAAyB;AACrD,QAAM,aAAa,mBAAmB,IAAI;AAC1C,MAAI,eAAe,IAAK,QAAO;AAC/B,QAAM,QAAQ,WAAW,YAAY,GAAG;AACxC,SAAO,QAAQ,IAAI,aAAa,WAAW,MAAM,QAAQ,CAAC;AAC5D;AAEO,MAAM,gBAAgB,IAAI,UAA4B,mBAAmB,MAAM,KAAK,GAAG,CAAC;AAGxF,MAAM,aAAa,CAAC,MAAc,SAAgC;AACvE,QAAM,OAAO,mBAAmB,IAAI;AACpC,QAAM,SAAS,mBAAmB,IAAI;AACtC,MAAI,WAAW,KAAM,QAAO;AAC5B,QAAM,SAAS,SAAS,MAAM,MAAM,GAAG,IAAI;AAC3C,SAAO,OAAO,WAAW,MAAM,IAAI,OAAO,MAAM,OAAO,MAAM,IAAI;AACnE;AAGO,MAAM,aAAa,CAAC,UACzB,0BAA0B,KAAK,KAAK,IAAI,QAAQ,IAAI,MAAM,QAAQ,MAAM,OAAO,CAAC;",
6
+ "names": []
7
+ }