@anvia/sandbox 0.4.1 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/chunk-FTNNCT6S.js +151 -0
- package/dist/chunk-FTNNCT6S.js.map +1 -0
- package/dist/cli.d.ts +62 -0
- package/dist/cli.js +836 -0
- package/dist/cli.js.map +1 -0
- package/dist/index.d.ts +21 -2
- package/dist/index.js +192 -156
- package/dist/index.js.map +1 -1
- package/package.json +10 -3
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/cli.ts","../src/image-builder.ts"],"sourcesContent":["#!/usr/bin/env node\nimport { lstat, mkdir, readFile, rm, writeFile } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { assertDockerCli } from \"./docker-cli\";\nimport {\n renderSandboxImageContext,\n resolveSandboxImageSpec,\n type SandboxImageFeature,\n type SandboxImageInput,\n type SandboxImageManifest,\n type SandboxImageRuntime,\n type SandboxImageVersions,\n unpinnedSandboxImagePackages,\n} from \"./image-builder\";\n\ninterface CliOptions {\n command?: string;\n name?: string;\n tag?: string;\n output?: string;\n runtimes: SandboxImageRuntime[];\n features: SandboxImageFeature[];\n apt: string[];\n npm: string[];\n uv: string[];\n versions: Partial<SandboxImageVersions>;\n dockerPath: string;\n build: boolean;\n buildExplicit: boolean;\n dryRun: boolean;\n force: boolean;\n help: boolean;\n}\n\nexport interface SandboxImageCliIo {\n log(message: string): void;\n warn(message: string): void;\n error(message: string): void;\n stdout(chunk: Uint8Array): void;\n stderr(chunk: Uint8Array): void;\n}\n\nexport interface SandboxImagePromptResult {\n name: string;\n runtimes: SandboxImageRuntime[];\n features: SandboxImageFeature[];\n apt: string[];\n npm: string[];\n uv: string[];\n tag?: string;\n output?: string;\n build: boolean;\n}\n\nexport interface SandboxImageCliDependencies {\n isTTY?: boolean;\n packageVersion?: string;\n prompt?: (options: Readonly<CliOptions>) => Promise<SandboxImagePromptResult | undefined>;\n buildImage?: (input: {\n contextPath: string;\n tag: string;\n dockerPath: string;\n io: SandboxImageCliIo;\n }) => Promise<void>;\n}\n\nconst generatedFileNames = new Set([\n \".dockerignore\",\n \"Dockerfile\",\n \"anvia-sandbox.json\",\n \"package.json\",\n \"pyproject.toml\",\n]);\n\nconst commonAptPackages = [\n { value: \"git\", label: \"Git\", hint: \"Source control and repository operations\" },\n { value: \"curl\", label: \"curl\", hint: \"HTTP downloads and API debugging\" },\n { value: \"jq\", label: \"jq\", hint: \"JSON processing from the shell\" },\n { value: \"ffmpeg\", label: \"FFmpeg\", hint: \"Audio and video processing\" },\n { value: \"imagemagick\", label: \"ImageMagick\", hint: \"Image conversion and editing\" },\n { value: \"poppler-utils\", label: \"Poppler tools\", hint: \"PDF inspection and conversion\" },\n { value: \"libreoffice\", label: \"LibreOffice\", hint: \"Large; office document conversion\" },\n] as const;\n\nconst commonNpmPackages = [\n { value: \"pdfkit@0.19.1\", label: \"PDFKit\", hint: \"Generate PDF documents\" },\n { value: \"sharp@0.35.3\", label: \"Sharp\", hint: \"Resize and transform images\" },\n { value: \"exceljs@4.4.0\", label: \"ExcelJS\", hint: \"Read and write Excel workbooks\" },\n { value: \"docx@9.7.1\", label: \"docx\", hint: \"Generate Word documents\" },\n { value: \"pptxgenjs@4.0.1\", label: \"PptxGenJS\", hint: \"Generate PowerPoint presentations\" },\n] as const;\n\nconst commonUvPackages = [\n { value: \"httpx==0.28.1\", label: \"HTTPX\", hint: \"Modern HTTP client\" },\n { value: \"beautifulsoup4==4.15.0\", label: \"Beautiful Soup\", hint: \"HTML and XML parsing\" },\n { value: \"scipy==1.18.0\", label: \"SciPy\", hint: \"Scientific computing\" },\n { value: \"scikit-learn==1.9.0\", label: \"scikit-learn\", hint: \"Machine learning utilities\" },\n { value: \"polars==1.42.1\", label: \"Polars\", hint: \"Fast dataframe processing\" },\n] as const;\n\nconst defaultIo: SandboxImageCliIo = {\n log: console.log,\n warn: console.warn,\n error: console.error,\n stdout: (chunk) => process.stdout.write(chunk),\n stderr: (chunk) => process.stderr.write(chunk),\n};\n\nexport async function runCli(\n argv: string[] = process.argv.slice(2),\n cwd = process.cwd(),\n io: SandboxImageCliIo = defaultIo,\n dependencies: SandboxImageCliDependencies = {},\n): Promise<number> {\n let options: CliOptions;\n try {\n options = parseArgs(argv);\n } catch (error) {\n io.error(errorMessage(error));\n io.log(helpText());\n return 1;\n }\n\n if (options.help || options.command === undefined) {\n io.log(helpText());\n return 0;\n }\n if (options.command !== \"create-image\") {\n io.error(`Unknown command: ${options.command}`);\n io.log(helpText());\n return 1;\n }\n\n try {\n const shouldPrompt =\n options.name === undefined ||\n (options.runtimes.length === 0 &&\n options.features.length === 0 &&\n options.npm.length === 0 &&\n options.uv.length === 0);\n if (shouldPrompt) {\n const isTTY = dependencies.isTTY ?? (process.stdin.isTTY && process.stdout.isTTY);\n if (!isTTY) {\n throw new Error(\n \"Non-interactive create-image requires --name and at least one --runtime or --feature.\",\n );\n }\n const prompt = dependencies.prompt ?? promptForImage;\n const result = await prompt(options);\n if (result === undefined) return 130;\n options = applyPromptResult(options, result);\n }\n\n const input: SandboxImageInput = {\n name: required(options.name, \"Image name is required.\"),\n runtimes: options.runtimes,\n features: options.features,\n packages: {\n apt: options.apt,\n npm: options.npm,\n uv: options.uv,\n },\n versions: options.versions,\n };\n if (options.tag !== undefined) input.tag = options.tag;\n\n const spec = resolveSandboxImageSpec(input);\n const packageVersion = dependencies.packageVersion ?? (await readPackageVersion());\n const context = renderSandboxImageContext(spec, packageVersion);\n const outputPath = path.resolve(\n cwd,\n options.output ?? path.join(\".anvia\", \"sandbox-images\", spec.name),\n );\n const displayPath = relativeDisplayPath(cwd, outputPath);\n const unpinned = unpinnedSandboxImagePackages(spec);\n if (unpinned.length > 0) {\n io.warn(`Unpinned custom packages may change on rebuild: ${unpinned.join(\", \")}`);\n }\n\n if (options.dryRun) {\n printDryRun(context.files, displayPath, spec.tag, io);\n return 0;\n }\n\n await writeImageContext(outputPath, context.manifest, context.files, options.force);\n io.log(`Created ${displayPath}`);\n\n if (!options.build) {\n io.log(\"\");\n io.log(\"Build later:\");\n io.log(` ${shellCommand([options.dockerPath, \"build\", \"--tag\", spec.tag, displayPath])}`);\n printUsageSnippet(spec.tag, io);\n return 0;\n }\n\n const buildImage = dependencies.buildImage ?? buildDockerImage;\n await buildImage({\n contextPath: outputPath,\n tag: spec.tag,\n dockerPath: options.dockerPath,\n io,\n });\n io.log(`Built ${spec.tag}`);\n printUsageSnippet(spec.tag, io);\n return 0;\n } catch (error) {\n io.error(errorMessage(error));\n return 1;\n }\n}\n\nasync function promptForImage(\n options: Readonly<CliOptions>,\n): Promise<SandboxImagePromptResult | undefined> {\n assertInteractiveNodeVersion();\n const prompts = await import(\"@clack/prompts\");\n prompts.intro(\"Create an Anvia sandbox image\");\n\n const nameResult =\n options.name ??\n (await prompts.text({\n message: \"Image name\",\n placeholder: \"reports\",\n validate: (value) =>\n /^[a-z0-9][a-z0-9-]{0,62}$/.test(value ?? \"\")\n ? undefined\n : \"Use 1-63 lowercase letters, numbers, or hyphens.\",\n }));\n if (prompts.isCancel(nameResult)) return cancelPrompt(prompts);\n const name = String(nameResult);\n\n let runtimes = [...options.runtimes];\n let features = [...options.features];\n if (\n runtimes.length === 0 &&\n features.length === 0 &&\n options.npm.length === 0 &&\n options.uv.length === 0\n ) {\n const capabilities = await prompts.multiselect<SandboxImageRuntime | SandboxImageFeature>({\n message: \"Select runtimes and features\",\n required: true,\n options: [\n { value: \"node\", label: \"Node.js\", hint: \"Includes npm and pnpm\" },\n { value: \"bun\", label: \"Bun\", hint: \"Includes bun and bunx\" },\n { value: \"python\", label: \"Python\", hint: \"Includes uv and uvx\" },\n { value: \"artifacts\", label: \"Reporting and artifacts\", hint: \"Adds Python automatically\" },\n { value: \"playwright\", label: \"Playwright + Chromium\", hint: \"Adds Node.js automatically\" },\n ],\n });\n if (prompts.isCancel(capabilities)) return cancelPrompt(prompts);\n runtimes = capabilities.filter(isRuntime);\n features = capabilities.filter(isFeature);\n }\n\n const aptResult = await selectCommonPackages(\n prompts,\n \"Select common apt tools (optional)\",\n commonAptPackages,\n options.apt,\n );\n if (aptResult === undefined) return cancelPrompt(prompts);\n const npmResult = await selectCommonPackages(\n prompts,\n \"Select common npm libraries (optional)\",\n commonNpmPackages,\n options.npm,\n );\n if (npmResult === undefined) return cancelPrompt(prompts);\n const uvResult = await selectCommonPackages(\n prompts,\n \"Select common Python libraries with uv (optional)\",\n commonUvPackages,\n options.uv,\n );\n if (uvResult === undefined) return cancelPrompt(prompts);\n\n const tagResult =\n options.tag ??\n (await prompts.text({\n message: \"Docker image tag\",\n initialValue: `anvia-sandbox-${name}:latest`,\n }));\n if (prompts.isCancel(tagResult)) return cancelPrompt(prompts);\n const outputResult =\n options.output ??\n (await prompts.text({\n message: \"Generated source directory\",\n initialValue: path.join(\".anvia\", \"sandbox-images\", name),\n }));\n if (prompts.isCancel(outputResult)) return cancelPrompt(prompts);\n\n const buildResult = options.buildExplicit\n ? options.build\n : await prompts.confirm({ message: \"Build the image now?\", initialValue: true });\n if (prompts.isCancel(buildResult)) return cancelPrompt(prompts);\n const confirmed = await prompts.confirm({\n message: \"Create this sandbox image?\",\n initialValue: true,\n });\n if (prompts.isCancel(confirmed) || !confirmed) return cancelPrompt(prompts);\n\n prompts.outro(\"Configuration ready\");\n return {\n name,\n runtimes,\n features,\n apt: aptResult,\n npm: npmResult,\n uv: uvResult,\n tag: String(tagResult),\n output: String(outputResult),\n build: Boolean(buildResult),\n };\n}\n\nasync function selectCommonPackages(\n prompts: typeof import(\"@clack/prompts\"),\n message: string,\n options: readonly { value: string; label: string; hint: string }[],\n existing: readonly string[],\n): Promise<string[] | undefined> {\n const commonValues = new Set(options.map((option) => option.value));\n const result = await prompts.multiselect<string>({\n message,\n required: false,\n options: [...options],\n initialValues: existing.filter((value) => commonValues.has(value)),\n });\n if (prompts.isCancel(result)) return undefined;\n return [...new Set([...existing, ...result])];\n}\n\nfunction cancelPrompt(prompts: typeof import(\"@clack/prompts\")): undefined {\n prompts.cancel(\"Image creation cancelled.\");\n return undefined;\n}\n\nasync function buildDockerImage(input: {\n contextPath: string;\n tag: string;\n dockerPath: string;\n io: SandboxImageCliIo;\n}): Promise<void> {\n await assertDockerCli([\"build\", \"--tag\", input.tag, input.contextPath], {\n dockerPath: input.dockerPath,\n onStdout: input.io.stdout,\n onStderr: input.io.stderr,\n });\n}\n\nasync function writeImageContext(\n outputPath: string,\n manifest: SandboxImageManifest,\n files: ReadonlyMap<string, string>,\n force: boolean,\n): Promise<void> {\n const outputStat = await safeLstat(outputPath);\n if (outputStat?.isSymbolicLink()) {\n throw new Error(`Refusing to write through symlink: ${outputPath}`);\n }\n\n if (outputStat !== undefined) {\n if (!outputStat.isDirectory()) throw new Error(`Output path is not a directory: ${outputPath}`);\n const previousManifest = await readGeneratedManifest(outputPath);\n if (previousManifest === undefined) {\n throw new Error(\n `Output directory already exists but was not generated by @anvia/sandbox: ${outputPath}`,\n );\n }\n if (!force) throw new Error(`Output directory already exists. Use --force to regenerate it.`);\n\n for (const filename of previousManifest.generatedFiles) {\n const target = path.join(outputPath, filename);\n const targetStat = await safeLstat(target);\n if (targetStat?.isSymbolicLink()) throw new Error(`Refusing to replace symlink: ${target}`);\n if (targetStat !== undefined) await rm(target);\n }\n } else {\n await mkdir(outputPath, { recursive: true });\n }\n\n for (const [filename, content] of files) {\n if (!generatedFileNames.has(filename))\n throw new Error(`Unexpected generated filename: ${filename}`);\n const target = path.join(outputPath, filename);\n const targetStat = await safeLstat(target);\n if (targetStat?.isSymbolicLink()) throw new Error(`Refusing to replace symlink: ${target}`);\n await writeFile(target, content, \"utf8\");\n }\n\n if (!files.has(\"anvia-sandbox.json\") || manifest.generatedFiles.length !== files.size) {\n throw new Error(\"Generated image context manifest is inconsistent.\");\n }\n}\n\nasync function readGeneratedManifest(\n outputPath: string,\n): Promise<SandboxImageManifest | undefined> {\n const manifestPath = path.join(outputPath, \"anvia-sandbox.json\");\n const stat = await safeLstat(manifestPath);\n if (stat === undefined) return undefined;\n if (stat.isSymbolicLink() || !stat.isFile()) {\n throw new Error(`Invalid generated manifest: ${manifestPath}`);\n }\n\n let value: unknown;\n try {\n value = JSON.parse(await readFile(manifestPath, \"utf8\"));\n } catch (error) {\n throw new Error(`Unable to read generated manifest: ${manifestPath}`, { cause: error });\n }\n if (!isGeneratedManifest(value)) {\n throw new Error(\n `Output manifest is not recognized as an @anvia/sandbox manifest: ${manifestPath}`,\n );\n }\n return value;\n}\n\nfunction isGeneratedManifest(value: unknown): value is SandboxImageManifest {\n if (typeof value !== \"object\" || value === null) return false;\n const record = value as Record<string, unknown>;\n const generatedBy = record.generatedBy as Record<string, unknown> | undefined;\n const files = record.generatedFiles;\n return (\n record.schemaVersion === 1 &&\n generatedBy?.package === \"@anvia/sandbox\" &&\n Array.isArray(files) &&\n files.every((file) => typeof file === \"string\" && generatedFileNames.has(file))\n );\n}\n\nasync function safeLstat(target: string) {\n try {\n return await lstat(target);\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === \"ENOENT\") return undefined;\n throw error;\n }\n}\n\nfunction printDryRun(\n files: ReadonlyMap<string, string>,\n outputPath: string,\n tag: string,\n io: SandboxImageCliIo,\n): void {\n io.log(`Would create ${outputPath}`);\n for (const [filename, content] of files) {\n io.log(\"\");\n io.log(`--- ${filename}`);\n io.log(content.trimEnd());\n }\n io.log(\"\");\n io.log(`Would build ${tag}`);\n}\n\nfunction printUsageSnippet(tag: string, io: SandboxImageCliIo): void {\n io.log(\"\");\n io.log(\"Use with @anvia/sandbox:\");\n io.log(\" const sandbox = new DockerSandbox({\");\n io.log(` image: ${JSON.stringify(tag)},`);\n io.log(' pull: \"never\",');\n io.log(\" });\");\n}\n\nfunction applyPromptResult(options: CliOptions, result: SandboxImagePromptResult): CliOptions {\n const next: CliOptions = {\n ...options,\n name: result.name,\n runtimes: result.runtimes,\n features: result.features,\n apt: result.apt,\n npm: result.npm,\n uv: result.uv,\n build: result.build,\n buildExplicit: true,\n };\n if (result.tag !== undefined) next.tag = result.tag;\n if (result.output !== undefined) next.output = result.output;\n return next;\n}\n\nfunction parseArgs(argv: string[]): CliOptions {\n const options: CliOptions = {\n runtimes: [],\n features: [],\n apt: [],\n npm: [],\n uv: [],\n versions: {},\n dockerPath: \"docker\",\n build: true,\n buildExplicit: false,\n dryRun: false,\n force: false,\n help: false,\n };\n\n for (let index = 0; index < argv.length; index += 1) {\n const argument = argv[index] ?? \"\";\n if (!argument.startsWith(\"-\")) {\n if (options.command !== undefined) throw new Error(`Unexpected argument: ${argument}`);\n options.command = argument;\n continue;\n }\n\n const [flag, inlineValue] = splitFlag(argument);\n const value = () => inlineValue ?? required(argv[++index], `${flag} requires a value.`);\n switch (flag) {\n case \"-h\":\n case \"--help\":\n options.help = true;\n break;\n case \"--name\":\n options.name = value();\n break;\n case \"--tag\":\n options.tag = value();\n break;\n case \"--output\":\n options.output = value();\n break;\n case \"--runtime\":\n options.runtimes.push(parseRuntime(value()));\n break;\n case \"--feature\":\n options.features.push(parseFeature(value()));\n break;\n case \"--apt\":\n options.apt.push(value());\n break;\n case \"--npm\":\n options.npm.push(value());\n break;\n case \"--uv\":\n options.uv.push(value());\n break;\n case \"--node-version\":\n options.versions.node = value();\n break;\n case \"--pnpm-version\":\n options.versions.pnpm = value();\n break;\n case \"--bun-version\":\n options.versions.bun = value();\n break;\n case \"--python-version\":\n options.versions.python = value();\n break;\n case \"--uv-version\":\n options.versions.uv = value();\n break;\n case \"--playwright-version\":\n options.versions.playwright = value();\n break;\n case \"--docker-path\":\n options.dockerPath = value();\n break;\n case \"--no-build\":\n rejectInlineValue(flag, inlineValue);\n options.build = false;\n options.buildExplicit = true;\n break;\n case \"--dry-run\":\n rejectInlineValue(flag, inlineValue);\n options.dryRun = true;\n break;\n case \"--force\":\n rejectInlineValue(flag, inlineValue);\n options.force = true;\n break;\n default:\n throw new Error(`Unknown option: ${flag}`);\n }\n }\n return options;\n}\n\nfunction splitFlag(argument: string): [string, string | undefined] {\n const separator = argument.indexOf(\"=\");\n return separator === -1\n ? [argument, undefined]\n : [argument.slice(0, separator), argument.slice(separator + 1)];\n}\n\nfunction rejectInlineValue(flag: string, value: string | undefined): void {\n if (value !== undefined) throw new Error(`${flag} does not accept a value.`);\n}\n\nfunction parseRuntime(value: string): SandboxImageRuntime {\n if (isRuntime(value)) return value;\n throw new Error(`Unknown runtime: ${value}. Expected node, bun, or python.`);\n}\n\nfunction parseFeature(value: string): SandboxImageFeature {\n if (isFeature(value)) return value;\n throw new Error(`Unknown feature: ${value}. Expected artifacts or playwright.`);\n}\n\nfunction isRuntime(value: string): value is SandboxImageRuntime {\n return value === \"node\" || value === \"bun\" || value === \"python\";\n}\n\nfunction isFeature(value: string): value is SandboxImageFeature {\n return value === \"artifacts\" || value === \"playwright\";\n}\n\nfunction assertInteractiveNodeVersion(): void {\n const [major = 0, minor = 0] = process.versions.node.split(\".\").map(Number);\n if (major < 20 || (major === 20 && minor < 12)) {\n throw new Error(\"The interactive create-image wizard requires Node.js 20.12 or newer.\");\n }\n}\n\nasync function readPackageVersion(): Promise<string> {\n const packageJson = JSON.parse(\n await readFile(new URL(\"../package.json\", import.meta.url), \"utf8\"),\n ) as {\n version?: unknown;\n };\n return typeof packageJson.version === \"string\" ? packageJson.version : \"unknown\";\n}\n\nfunction relativeDisplayPath(cwd: string, target: string): string {\n const relative = path.relative(cwd, target);\n return relative && !relative.startsWith(\"..\") ? relative : target;\n}\n\nfunction shellCommand(args: readonly string[]): string {\n return args.map(shellArgument).join(\" \");\n}\n\nfunction shellArgument(value: string): string {\n if (/^[a-zA-Z0-9_./:@+-]+$/.test(value)) return value;\n return `'${value.replaceAll(\"'\", `'\"'\"'`)}'`;\n}\n\nfunction required<T>(value: T | undefined, message: string): T {\n if (value === undefined || value === \"\") throw new Error(message);\n return value;\n}\n\nfunction errorMessage(error: unknown): string {\n return error instanceof Error ? error.message : String(error);\n}\n\nfunction helpText(): string {\n return `Usage:\n pnpm dlx @anvia/sandbox create-image [options]\n\nOptions:\n --name <slug> Image profile name\n --runtime <node|bun|python> Runtime to include; repeatable\n --feature <artifacts|playwright>\n Curated feature to include; repeatable\n --apt <package> Additional apt package; repeatable\n --npm <package[@version]> Additional npm package; repeatable\n --uv <requirement> Additional Python package installed with uv; repeatable\n --tag <tag> Local image tag\n --output <directory> Generated context directory\n --node-version <version> Override Node.js version\n --pnpm-version <version> Override pnpm version\n --bun-version <version> Override Bun version\n --python-version <version> Override Python version\n --uv-version <version> Override uv version\n --playwright-version <version> Override Playwright and browser image version\n --docker-path <path> Docker CLI path (default: docker)\n --no-build Generate files without building\n --dry-run Print generated files without writing or building\n --force Regenerate a context previously created by this CLI\n -h, --help Show this help\n\nExamples:\n pnpm dlx @anvia/sandbox create-image\n pnpm dlx @anvia/sandbox create-image --name reports --feature artifacts\n pnpm dlx @anvia/sandbox create-image --name browser --runtime bun --feature playwright`;\n}\n\nconst invokedPath = process.argv[1] === undefined ? undefined : path.resolve(process.argv[1]);\nif (invokedPath !== undefined && invokedPath === fileURLToPath(import.meta.url)) {\n runCli().then((code) => {\n process.exitCode = code;\n });\n}\n","export type SandboxImageRuntime = \"node\" | \"bun\" | \"python\";\nexport type SandboxImageFeature = \"artifacts\" | \"playwright\";\n\nexport interface SandboxImageVersions {\n node: string;\n pnpm: string;\n bun: string;\n python: string;\n uv: string;\n playwright: string;\n}\n\nexport interface SandboxImagePackages {\n apt: string[];\n npm: string[];\n uv: string[];\n}\n\nexport interface SandboxImageInput {\n name: string;\n tag?: string;\n runtimes?: readonly SandboxImageRuntime[];\n features?: readonly SandboxImageFeature[];\n packages?: Partial<SandboxImagePackages>;\n versions?: Partial<SandboxImageVersions>;\n}\n\nexport interface SandboxImageSpec {\n name: string;\n tag: string;\n runtimes: SandboxImageRuntime[];\n features: SandboxImageFeature[];\n packages: SandboxImagePackages;\n versions: SandboxImageVersions;\n}\n\nexport interface SandboxImageManifest extends SandboxImageSpec {\n schemaVersion: 1;\n generatedBy: {\n package: \"@anvia/sandbox\";\n version: string;\n };\n generatedFiles: string[];\n}\n\nexport interface SandboxImageContext {\n manifest: SandboxImageManifest;\n files: ReadonlyMap<string, string>;\n}\n\nexport const defaultSandboxImageVersions: Readonly<SandboxImageVersions> = {\n node: \"24.18.0\",\n pnpm: \"11.0.4\",\n bun: \"1.3.14\",\n python: \"3.13.14\",\n uv: \"0.11.29\",\n playwright: \"1.61.0\",\n};\n\nexport const artifactPythonPackages = [\n \"matplotlib==3.11.1\",\n \"seaborn==0.13.2\",\n \"Pillow==12.3.0\",\n \"ReportLab==5.0.0\",\n \"pypdf==6.14.2\",\n \"pandas==3.0.3\",\n \"openpyxl==3.1.5\",\n \"XlsxWriter==3.2.9\",\n \"python-docx==1.2.0\",\n] as const;\n\nconst runtimeOrder: SandboxImageRuntime[] = [\"node\", \"bun\", \"python\"];\nconst featureOrder: SandboxImageFeature[] = [\"artifacts\", \"playwright\"];\nconst commonAptPackages = [\"bash\", \"ca-certificates\", \"findutils\", \"libstdc++6\", \"procps\"];\n\nexport function resolveSandboxImageSpec(input: SandboxImageInput): SandboxImageSpec {\n validateName(input.name);\n\n const runtimes = new Set(input.runtimes ?? []);\n const features = new Set(input.features ?? []);\n const packages: SandboxImagePackages = {\n apt: unique(input.packages?.apt ?? []),\n npm: unique(input.packages?.npm ?? []),\n uv: unique(input.packages?.uv ?? []),\n };\n\n for (const runtime of runtimes) validateRuntime(runtime);\n for (const feature of features) validateFeature(feature);\n for (const packageName of packages.apt) validateAptPackage(packageName);\n for (const packageSpec of packages.npm) parseNpmPackageSpec(packageSpec);\n for (const requirement of packages.uv) validateUvRequirement(requirement);\n\n if (features.has(\"artifacts\") || packages.uv.length > 0) runtimes.add(\"python\");\n if (features.has(\"playwright\")) runtimes.add(\"node\");\n if (packages.npm.length > 0 && !runtimes.has(\"node\") && !runtimes.has(\"bun\")) {\n runtimes.add(\"node\");\n }\n\n if (runtimes.size === 0) {\n throw new Error(\"Select at least one runtime or feature.\");\n }\n\n const versions = { ...defaultSandboxImageVersions, ...input.versions };\n for (const [name, version] of Object.entries(versions)) validateVersion(name, version);\n\n const tag = input.tag ?? `anvia-sandbox-${input.name}:latest`;\n validateImageTag(tag);\n\n return {\n name: input.name,\n tag,\n runtimes: runtimeOrder.filter((runtime) => runtimes.has(runtime)),\n features: featureOrder.filter((feature) => features.has(feature)),\n packages,\n versions,\n };\n}\n\nexport function renderSandboxImageContext(\n spec: SandboxImageSpec,\n generatorVersion: string,\n): SandboxImageContext {\n const files = new Map<string, string>();\n const pythonRequirements = [\n ...(spec.features.includes(\"artifacts\") ? artifactPythonPackages : []),\n ...spec.packages.uv,\n ];\n const npmDependencies = npmDependenciesFor(spec);\n\n files.set(\"Dockerfile\", `${renderDockerfile(spec, pythonRequirements, npmDependencies)}\\n`);\n files.set(\n \".dockerignore\",\n renderDockerignore(pythonRequirements.length > 0, npmDependencies.size > 0),\n );\n\n if (pythonRequirements.length > 0) {\n files.set(\"pyproject.toml\", renderPythonProject(spec, unique(pythonRequirements)));\n }\n if (npmDependencies.size > 0) {\n files.set(\n \"package.json\",\n `${JSON.stringify(\n {\n private: true,\n description: `Generated dependencies for ${spec.name}`,\n dependencies: Object.fromEntries(npmDependencies),\n },\n null,\n 2,\n )}\\n`,\n );\n }\n\n const generatedFiles = [...files.keys(), \"anvia-sandbox.json\"].sort();\n const manifest: SandboxImageManifest = {\n schemaVersion: 1,\n generatedBy: {\n package: \"@anvia/sandbox\",\n version: generatorVersion,\n },\n ...spec,\n generatedFiles,\n };\n files.set(\"anvia-sandbox.json\", `${JSON.stringify(manifest, null, 2)}\\n`);\n\n return { manifest, files };\n}\n\nexport function unpinnedSandboxImagePackages(spec: SandboxImageSpec): string[] {\n const unpinned: string[] = [];\n for (const value of spec.packages.npm) {\n if (parseNpmPackageSpec(value).version === \"latest\") unpinned.push(value);\n }\n for (const value of spec.packages.uv) {\n if (!/===|==|@\\s*https?:/i.test(value)) unpinned.push(value);\n }\n return unpinned;\n}\n\nfunction renderDockerfile(\n spec: SandboxImageSpec,\n pythonRequirements: readonly string[],\n npmDependencies: ReadonlyMap<string, string>,\n): string {\n const hasNode = spec.runtimes.includes(\"node\");\n const hasBun = spec.runtimes.includes(\"bun\");\n const hasPython = spec.runtimes.includes(\"python\");\n const hasPlaywright = spec.features.includes(\"playwright\");\n const lines = [\n \"# Generated by @anvia/sandbox. Edit the manifest and regenerate instead of editing this file.\",\n ];\n\n if (hasNode) lines.push(`FROM node:${spec.versions.node}-bookworm-slim AS node-runtime`);\n if (hasPython) lines.push(`FROM python:${spec.versions.python}-slim-bookworm AS python-runtime`);\n if (hasBun) lines.push(`FROM oven/bun:${spec.versions.bun}-slim AS bun-runtime`);\n if (hasPython) lines.push(`FROM ghcr.io/astral-sh/uv:${spec.versions.uv} AS uv-runtime`);\n\n if (hasPlaywright) {\n lines.push(`FROM mcr.microsoft.com/playwright:v${spec.versions.playwright}-noble AS final`);\n } else if (hasPython) {\n lines.push(\"FROM python-runtime AS final\");\n } else if (hasNode) {\n lines.push(\"FROM node-runtime AS final\");\n } else {\n lines.push(\"FROM bun-runtime AS final\");\n }\n\n lines.push(\"\", \"USER root\");\n\n if (hasPython && (hasPlaywright || !isFinalRuntime(spec, \"python\"))) {\n lines.push(\"COPY --from=python-runtime /usr/local/ /usr/local/\");\n }\n if (hasNode && (hasPlaywright || !isFinalRuntime(spec, \"node\"))) {\n lines.push(\"COPY --from=node-runtime /usr/local/bin/ /usr/local/bin/\");\n lines.push(\n \"COPY --from=node-runtime /usr/local/lib/node_modules/ /usr/local/lib/node_modules/\",\n );\n }\n if (hasBun && !isFinalRuntime(spec, \"bun\")) {\n lines.push(\"COPY --from=bun-runtime /usr/local/bin/bun /usr/local/bin/bun\");\n lines.push(\"RUN ln -sf /usr/local/bin/bun /usr/local/bin/bunx\");\n }\n if (hasPython) {\n lines.push(\"COPY --from=uv-runtime /uv /uvx /usr/local/bin/\");\n }\n\n const aptPackages = unique([\n ...commonAptPackages,\n ...(spec.features.includes(\"artifacts\") ? [\"fonts-dejavu-core\"] : []),\n ...spec.packages.apt,\n ]).sort();\n lines.push(\n \"\",\n \"RUN apt-get update \\\\\",\n ` && apt-get install -y --no-install-recommends ${aptPackages.map(shellQuote).join(\" \")} \\\\`,\n \" && rm -rf /var/lib/apt/lists/*\",\n );\n\n if (hasNode) {\n lines.push(\n \"\",\n `RUN npm install --global ${shellQuote(`pnpm@${spec.versions.pnpm}`)} --no-audit --no-fund \\\\`,\n \" && npm cache clean --force\",\n );\n }\n\n if (pythonRequirements.length > 0) {\n lines.push(\n \"\",\n \"COPY pyproject.toml /opt/anvia-python/pyproject.toml\",\n \"RUN cd /opt/anvia-python \\\\\",\n \" && uv sync --no-dev --no-cache --no-install-project\",\n \"ENV VIRTUAL_ENV=/opt/anvia-python/.venv\",\n \"ENV PATH=/opt/anvia-python/.venv/bin:$PATH\",\n );\n }\n\n if (npmDependencies.size > 0) {\n lines.push(\"\", \"COPY package.json /opt/anvia-js/package.json\");\n if (hasNode) {\n lines.push(\n \"RUN npm install --prefix /opt/anvia-js --omit=dev --no-audit --no-fund \\\\\",\n \" && ln -s /opt/anvia-js/node_modules /node_modules \\\\\",\n \" && npm cache clean --force\",\n );\n } else {\n lines.push(\n \"RUN cd /opt/anvia-js \\\\\",\n \" && bun install --production --no-save \\\\\",\n \" && ln -s /opt/anvia-js/node_modules /node_modules\",\n );\n }\n lines.push(\"ENV NODE_PATH=/opt/anvia-js/node_modules\");\n lines.push(\"ENV PATH=/opt/anvia-js/node_modules/.bin:$PATH\");\n }\n\n lines.push(\n \"\",\n ...(hasPlaywright ? [\"ENV PLAYWRIGHT_BROWSERS_PATH=/ms-playwright\"] : []),\n \"RUN mkdir -p /workspace\",\n \"WORKDIR /workspace\",\n \"ENTRYPOINT []\",\n 'CMD [\"sh\", \"-c\", \"trap \\'exit 0\\' TERM INT; while :; do sleep 3600 & wait $!; done\"]',\n );\n\n return lines.join(\"\\n\");\n}\n\nfunction renderDockerignore(hasPython: boolean, hasNpm: boolean): string {\n return [\n \"**\",\n \"!Dockerfile\",\n \"!.dockerignore\",\n \"!anvia-sandbox.json\",\n ...(hasPython ? [\"!pyproject.toml\"] : []),\n ...(hasNpm ? [\"!package.json\"] : []),\n \"\",\n ].join(\"\\n\");\n}\n\nfunction renderPythonProject(spec: SandboxImageSpec, requirements: readonly string[]): string {\n const [major, minor] = spec.versions.python.split(\".\");\n return [\n \"[project]\",\n `name = ${JSON.stringify(`anvia-sandbox-${spec.name}`)}`,\n 'version = \"0.0.0\"',\n `requires-python = ${JSON.stringify(`>=${major}.${minor}`)}`,\n \"dependencies = [\",\n ...requirements.map((requirement) => ` ${JSON.stringify(requirement)},`),\n \"]\",\n \"\",\n ].join(\"\\n\");\n}\n\nfunction npmDependenciesFor(spec: SandboxImageSpec): Map<string, string> {\n const dependencies = new Map<string, string>();\n if (spec.features.includes(\"playwright\")) {\n dependencies.set(\"playwright\", spec.versions.playwright);\n }\n for (const packageSpec of spec.packages.npm) {\n const parsed = parseNpmPackageSpec(packageSpec);\n if (dependencies.has(parsed.name)) {\n throw new Error(`Duplicate npm package: ${parsed.name}`);\n }\n dependencies.set(parsed.name, parsed.version);\n }\n return new Map([...dependencies.entries()].sort(([left], [right]) => left.localeCompare(right)));\n}\n\nfunction isFinalRuntime(spec: SandboxImageSpec, runtime: SandboxImageRuntime): boolean {\n if (spec.features.includes(\"playwright\")) return false;\n if (spec.runtimes.includes(\"python\")) return runtime === \"python\";\n if (spec.runtimes.includes(\"node\")) return runtime === \"node\";\n return runtime === \"bun\";\n}\n\nfunction validateName(name: string): void {\n if (!/^[a-z0-9][a-z0-9-]{0,62}$/.test(name)) {\n throw new Error(\n \"Image name must be 1-63 lowercase letters, numbers, or hyphens and cannot start with a hyphen.\",\n );\n }\n}\n\nfunction validateRuntime(runtime: string): asserts runtime is SandboxImageRuntime {\n if (!runtimeOrder.includes(runtime as SandboxImageRuntime)) {\n throw new Error(`Unknown runtime: ${runtime}. Expected node, bun, or python.`);\n }\n}\n\nfunction validateFeature(feature: string): asserts feature is SandboxImageFeature {\n if (!featureOrder.includes(feature as SandboxImageFeature)) {\n throw new Error(`Unknown feature: ${feature}. Expected artifacts or playwright.`);\n }\n}\n\nfunction validateAptPackage(packageName: string): void {\n if (!/^[a-zA-Z0-9][a-zA-Z0-9.+:~=-]*$/.test(packageName)) {\n throw new Error(`Invalid apt package: ${packageName}`);\n }\n}\n\nfunction parseNpmPackageSpec(packageSpec: string): { name: string; version: string } {\n if (/\\s/.test(packageSpec) || hasControlCharacter(packageSpec)) {\n throw new Error(`Invalid npm package spec: ${packageSpec}`);\n }\n\n const separator = packageSpec.startsWith(\"@\")\n ? packageSpec.indexOf(\"@\", packageSpec.indexOf(\"/\") + 1)\n : packageSpec.indexOf(\"@\");\n const name = separator === -1 ? packageSpec : packageSpec.slice(0, separator);\n const version = separator === -1 ? \"latest\" : packageSpec.slice(separator + 1);\n\n if (!/^(?:@[a-z0-9][a-z0-9._-]*\\/)?[a-z0-9][a-z0-9._-]*$/i.test(name) || !version) {\n throw new Error(`Invalid npm package spec: ${packageSpec}`);\n }\n if (/[\\s'\"`$;&|<>\\\\]/.test(version) || hasControlCharacter(version)) {\n throw new Error(`Invalid npm package version in: ${packageSpec}`);\n }\n return { name, version };\n}\n\nfunction validateUvRequirement(requirement: string): void {\n if (!requirement || requirement.startsWith(\"-\") || hasControlCharacter(requirement)) {\n throw new Error(`Invalid uv package requirement: ${requirement}`);\n }\n}\n\nfunction validateVersion(name: string, version: string): void {\n if (!/^[a-zA-Z0-9][a-zA-Z0-9._-]*$/.test(version)) {\n throw new Error(`Invalid ${name} version: ${version}`);\n }\n}\n\nfunction validateImageTag(tag: string): void {\n if (\n !tag ||\n tag.startsWith(\"-\") ||\n tag.includes(\"@\") ||\n /\\s/.test(tag) ||\n hasControlCharacter(tag)\n ) {\n throw new Error(`Invalid Docker image tag: ${tag}`);\n }\n}\n\nfunction unique(values: readonly string[]): string[] {\n return [...new Set(values)];\n}\n\nfunction hasControlCharacter(value: string): boolean {\n return [...value].some((character) => character.charCodeAt(0) < 32);\n}\n\nfunction shellQuote(value: string): string {\n return `'${value.replaceAll(\"'\", `'\"'\"'`)}'`;\n}\n"],"mappings":"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","names":["commonAptPackages"]}
|
package/dist/index.d.ts
CHANGED
|
@@ -26,11 +26,25 @@ interface SandboxSession {
|
|
|
26
26
|
execStream(options: SandboxExecOptions): AsyncIterable<SandboxExecStreamEvent>;
|
|
27
27
|
readFile(path: string): Promise<Uint8Array>;
|
|
28
28
|
readTextFile(path: string): Promise<string>;
|
|
29
|
+
readTextFilePage?(path: string, options?: SandboxTextFileReadOptions): Promise<SandboxTextFileReadResult>;
|
|
29
30
|
writeFile(path: string, data: string | Uint8Array): Promise<void>;
|
|
30
31
|
writeTextFile(path: string, content: string): Promise<void>;
|
|
31
32
|
listFiles(path?: string): Promise<SandboxFileEntry[]>;
|
|
32
33
|
destroy(): Promise<void>;
|
|
33
34
|
}
|
|
35
|
+
interface SandboxTextFileReadOptions {
|
|
36
|
+
startLine?: number;
|
|
37
|
+
lineCount?: number;
|
|
38
|
+
maxBytes?: number;
|
|
39
|
+
}
|
|
40
|
+
interface SandboxTextFileReadResult {
|
|
41
|
+
content: string;
|
|
42
|
+
startLine: number;
|
|
43
|
+
endLine: number | null;
|
|
44
|
+
nextStartLine: number | null;
|
|
45
|
+
truncated: boolean;
|
|
46
|
+
truncatedBy: "lines" | "bytes" | null;
|
|
47
|
+
}
|
|
34
48
|
interface SandboxPortSession extends SandboxSession {
|
|
35
49
|
readonly publishedPorts: readonly SandboxPublishedPort[];
|
|
36
50
|
waitForPort(containerPort: number, options?: SandboxWaitForPortOptions): Promise<SandboxPublishedPort>;
|
|
@@ -42,6 +56,7 @@ interface SandboxProcessSession extends SandboxSession {
|
|
|
42
56
|
stopProcess(processId: string, options?: SandboxProcessStopOptions): Promise<SandboxProcessInfo>;
|
|
43
57
|
}
|
|
44
58
|
interface DockerSandboxSession extends SandboxPortSession, SandboxProcessSession {
|
|
59
|
+
readTextFilePage(path: string, options?: SandboxTextFileReadOptions): Promise<SandboxTextFileReadResult>;
|
|
45
60
|
}
|
|
46
61
|
interface SandboxCreateSessionOptions {
|
|
47
62
|
id?: string;
|
|
@@ -195,7 +210,7 @@ interface SandboxToolsOptions {
|
|
|
195
210
|
include?: SandboxToolName[];
|
|
196
211
|
execTimeoutMs?: number;
|
|
197
212
|
exec?: SandboxExecToolPolicy;
|
|
198
|
-
readFile?:
|
|
213
|
+
readFile?: SandboxReadFileToolPolicy;
|
|
199
214
|
writeFile?: SandboxFileToolPolicy;
|
|
200
215
|
process?: SandboxProcessToolPolicy;
|
|
201
216
|
}
|
|
@@ -209,6 +224,10 @@ interface SandboxExecToolPolicy {
|
|
|
209
224
|
interface SandboxFileToolPolicy {
|
|
210
225
|
maxBytes?: number;
|
|
211
226
|
}
|
|
227
|
+
interface SandboxReadFileToolPolicy extends SandboxFileToolPolicy {
|
|
228
|
+
defaultLineCount?: number;
|
|
229
|
+
maxLineCount?: number;
|
|
230
|
+
}
|
|
212
231
|
interface SandboxProcessToolPolicy {
|
|
213
232
|
maxLogBytes?: number;
|
|
214
233
|
defaultWaitTimeoutMs?: number;
|
|
@@ -285,4 +304,4 @@ declare class SandboxProcessError extends SandboxError {
|
|
|
285
304
|
|
|
286
305
|
declare function createSandboxTools(session: SandboxSession, options?: SandboxToolsOptions): AnyTool[];
|
|
287
306
|
|
|
288
|
-
export { DockerSandbox, type DockerSandboxCreateSessionOptions, type DockerSandboxNetworkOptions, type DockerSandboxOptions, type DockerSandboxSecurityOptions, type DockerSandboxSession, type Sandbox, type SandboxCreateSessionOptions, SandboxDockerCommandError, SandboxDockerUnavailableError, SandboxError, type SandboxExecEndEvent, type SandboxExecEvent, type SandboxExecOptions, type SandboxExecResult, type SandboxExecStreamEvent, type SandboxExecToolPolicy, type SandboxFileEntry, SandboxFileSizeError, type SandboxFileToolPolicy, type SandboxFileType, type SandboxFileWriteEvent, type SandboxHooks, type SandboxLifecycleOptions, type SandboxLimits, type SandboxManifest, type SandboxNetworkMode, SandboxPathError, SandboxPortError, type SandboxPortSession, SandboxProcessError, type SandboxProcessInfo, type SandboxProcessLogs, type SandboxProcessLogsOptions, type SandboxProcessSession, type SandboxProcessStartOptions, type SandboxProcessStatus, type SandboxProcessStopOptions, type SandboxProcessToolPolicy, type SandboxPublishedPort, type SandboxSession, SandboxSessionDestroyedError, type SandboxSessionEvent, SandboxTimeoutError, type SandboxToolName, SandboxToolPolicyError, type SandboxToolsFactory, type SandboxToolsOptions, type SandboxWaitForPortOptions, type SandboxWorkspaceOptions, createSandboxTools, isSandboxPortSession, isSandboxProcessSession };
|
|
307
|
+
export { DockerSandbox, type DockerSandboxCreateSessionOptions, type DockerSandboxNetworkOptions, type DockerSandboxOptions, type DockerSandboxSecurityOptions, type DockerSandboxSession, type Sandbox, type SandboxCreateSessionOptions, SandboxDockerCommandError, SandboxDockerUnavailableError, SandboxError, type SandboxExecEndEvent, type SandboxExecEvent, type SandboxExecOptions, type SandboxExecResult, type SandboxExecStreamEvent, type SandboxExecToolPolicy, type SandboxFileEntry, SandboxFileSizeError, type SandboxFileToolPolicy, type SandboxFileType, type SandboxFileWriteEvent, type SandboxHooks, type SandboxLifecycleOptions, type SandboxLimits, type SandboxManifest, type SandboxNetworkMode, SandboxPathError, SandboxPortError, type SandboxPortSession, SandboxProcessError, type SandboxProcessInfo, type SandboxProcessLogs, type SandboxProcessLogsOptions, type SandboxProcessSession, type SandboxProcessStartOptions, type SandboxProcessStatus, type SandboxProcessStopOptions, type SandboxProcessToolPolicy, type SandboxPublishedPort, type SandboxReadFileToolPolicy, type SandboxSession, SandboxSessionDestroyedError, type SandboxSessionEvent, type SandboxTextFileReadOptions, type SandboxTextFileReadResult, SandboxTimeoutError, type SandboxToolName, SandboxToolPolicyError, type SandboxToolsFactory, type SandboxToolsOptions, type SandboxWaitForPortOptions, type SandboxWorkspaceOptions, createSandboxTools, isSandboxPortSession, isSandboxProcessSession };
|
package/dist/index.js
CHANGED
|
@@ -1,3 +1,18 @@
|
|
|
1
|
+
import {
|
|
2
|
+
SandboxDockerCommandError,
|
|
3
|
+
SandboxDockerUnavailableError,
|
|
4
|
+
SandboxError,
|
|
5
|
+
SandboxFileSizeError,
|
|
6
|
+
SandboxPathError,
|
|
7
|
+
SandboxPortError,
|
|
8
|
+
SandboxProcessError,
|
|
9
|
+
SandboxSessionDestroyedError,
|
|
10
|
+
SandboxTimeoutError,
|
|
11
|
+
SandboxToolPolicyError,
|
|
12
|
+
assertDockerCli,
|
|
13
|
+
runDockerCli
|
|
14
|
+
} from "./chunk-FTNNCT6S.js";
|
|
15
|
+
|
|
1
16
|
// src/capabilities.ts
|
|
2
17
|
function isSandboxPortSession(session) {
|
|
3
18
|
const candidate = session;
|
|
@@ -14,146 +29,8 @@ import { mkdtemp, rm, writeFile } from "fs/promises";
|
|
|
14
29
|
import os from "os";
|
|
15
30
|
import path2 from "path";
|
|
16
31
|
|
|
17
|
-
// src/docker-cli.ts
|
|
18
|
-
import { spawn } from "child_process";
|
|
19
|
-
|
|
20
|
-
// src/errors.ts
|
|
21
|
-
var SandboxError = class extends Error {
|
|
22
|
-
constructor(message, cause) {
|
|
23
|
-
super(message);
|
|
24
|
-
this.cause = cause;
|
|
25
|
-
this.name = new.target.name;
|
|
26
|
-
}
|
|
27
|
-
cause;
|
|
28
|
-
};
|
|
29
|
-
var SandboxDockerUnavailableError = class extends SandboxError {
|
|
30
|
-
};
|
|
31
|
-
var SandboxDockerCommandError = class extends SandboxError {
|
|
32
|
-
constructor(message, result) {
|
|
33
|
-
super(message);
|
|
34
|
-
this.result = result;
|
|
35
|
-
}
|
|
36
|
-
result;
|
|
37
|
-
};
|
|
38
|
-
var SandboxSessionDestroyedError = class extends SandboxError {
|
|
39
|
-
};
|
|
40
|
-
var SandboxPathError = class extends SandboxError {
|
|
41
|
-
};
|
|
42
|
-
var SandboxTimeoutError = class extends SandboxError {
|
|
43
|
-
};
|
|
44
|
-
var SandboxFileSizeError = class extends SandboxError {
|
|
45
|
-
};
|
|
46
|
-
var SandboxToolPolicyError = class extends SandboxError {
|
|
47
|
-
};
|
|
48
|
-
var SandboxPortError = class extends SandboxError {
|
|
49
|
-
};
|
|
50
|
-
var SandboxProcessError = class extends SandboxError {
|
|
51
|
-
};
|
|
52
|
-
|
|
53
|
-
// src/docker-cli.ts
|
|
54
|
-
var defaultMaxOutputBytes = 1024 * 1024;
|
|
55
|
-
async function runDockerCli(args, options) {
|
|
56
|
-
const startedAt = Date.now();
|
|
57
|
-
const maxOutputBytes = options.maxOutputBytes ?? defaultMaxOutputBytes;
|
|
58
|
-
const stdout = createOutputCollector(maxOutputBytes, options.onStdout);
|
|
59
|
-
const stderr = createOutputCollector(maxOutputBytes, options.onStderr);
|
|
60
|
-
return new Promise((resolve, reject) => {
|
|
61
|
-
const child = spawn(options.dockerPath, args, {
|
|
62
|
-
stdio: ["pipe", "pipe", "pipe"]
|
|
63
|
-
});
|
|
64
|
-
let timedOut = false;
|
|
65
|
-
let aborted = false;
|
|
66
|
-
let settled = false;
|
|
67
|
-
const timeout = options.timeoutMs === void 0 ? void 0 : setTimeout(() => {
|
|
68
|
-
timedOut = true;
|
|
69
|
-
child.kill("SIGKILL");
|
|
70
|
-
}, options.timeoutMs);
|
|
71
|
-
const abort = () => {
|
|
72
|
-
aborted = true;
|
|
73
|
-
child.kill("SIGKILL");
|
|
74
|
-
};
|
|
75
|
-
if (options.signal?.aborted === true) {
|
|
76
|
-
abort();
|
|
77
|
-
} else {
|
|
78
|
-
options.signal?.addEventListener("abort", abort, { once: true });
|
|
79
|
-
}
|
|
80
|
-
child.stdout.on("data", (chunk) => stdout.accept(chunk));
|
|
81
|
-
child.stderr.on("data", (chunk) => stderr.accept(chunk));
|
|
82
|
-
child.on("error", (error) => {
|
|
83
|
-
if (settled) {
|
|
84
|
-
return;
|
|
85
|
-
}
|
|
86
|
-
settled = true;
|
|
87
|
-
clearTimeout(timeout);
|
|
88
|
-
options.signal?.removeEventListener("abort", abort);
|
|
89
|
-
if (error.code === "ENOENT") {
|
|
90
|
-
reject(new SandboxDockerUnavailableError("Docker CLI was not found.", error));
|
|
91
|
-
return;
|
|
92
|
-
}
|
|
93
|
-
reject(error);
|
|
94
|
-
});
|
|
95
|
-
child.on("close", (code) => {
|
|
96
|
-
if (settled) {
|
|
97
|
-
return;
|
|
98
|
-
}
|
|
99
|
-
settled = true;
|
|
100
|
-
clearTimeout(timeout);
|
|
101
|
-
options.signal?.removeEventListener("abort", abort);
|
|
102
|
-
resolve({
|
|
103
|
-
stdout: stdout.text(),
|
|
104
|
-
stderr: stderr.text(),
|
|
105
|
-
exitCode: code ?? 1,
|
|
106
|
-
durationMs: Date.now() - startedAt,
|
|
107
|
-
timedOut,
|
|
108
|
-
aborted,
|
|
109
|
-
stdoutTruncated: stdout.truncated,
|
|
110
|
-
stderrTruncated: stderr.truncated
|
|
111
|
-
});
|
|
112
|
-
});
|
|
113
|
-
if (options.input !== void 0) {
|
|
114
|
-
child.stdin.end(options.input);
|
|
115
|
-
} else {
|
|
116
|
-
child.stdin.end();
|
|
117
|
-
}
|
|
118
|
-
});
|
|
119
|
-
}
|
|
120
|
-
async function assertDockerCli(args, options) {
|
|
121
|
-
const result = await runDockerCli(args, options);
|
|
122
|
-
if (result.exitCode !== 0) {
|
|
123
|
-
throw new SandboxDockerCommandError(`Docker command failed: docker ${args.join(" ")}`, result);
|
|
124
|
-
}
|
|
125
|
-
return result.stdout.trim();
|
|
126
|
-
}
|
|
127
|
-
function createOutputCollector(maxBytes, onChunk) {
|
|
128
|
-
const chunks = [];
|
|
129
|
-
let length = 0;
|
|
130
|
-
let truncated = false;
|
|
131
|
-
return {
|
|
132
|
-
get truncated() {
|
|
133
|
-
return truncated;
|
|
134
|
-
},
|
|
135
|
-
accept(chunk) {
|
|
136
|
-
onChunk?.(chunk);
|
|
137
|
-
if (length >= maxBytes) {
|
|
138
|
-
truncated = true;
|
|
139
|
-
return;
|
|
140
|
-
}
|
|
141
|
-
const remaining = maxBytes - length;
|
|
142
|
-
const next = chunk.length > remaining ? chunk.subarray(0, remaining) : chunk;
|
|
143
|
-
chunks.push(next);
|
|
144
|
-
length += next.length;
|
|
145
|
-
if (next.length < chunk.length) {
|
|
146
|
-
truncated = true;
|
|
147
|
-
}
|
|
148
|
-
},
|
|
149
|
-
text() {
|
|
150
|
-
return Buffer.concat(chunks, length).toString("utf8");
|
|
151
|
-
}
|
|
152
|
-
};
|
|
153
|
-
}
|
|
154
|
-
|
|
155
32
|
// src/docker-process.ts
|
|
156
|
-
import { spawn
|
|
33
|
+
import { spawn } from "child_process";
|
|
157
34
|
import { randomUUID } from "crypto";
|
|
158
35
|
|
|
159
36
|
// src/path.ts
|
|
@@ -242,7 +119,7 @@ var DockerProcessManager = class {
|
|
|
242
119
|
const id = randomUUID();
|
|
243
120
|
const marker = `${processMarkerPrefix}:${id}`;
|
|
244
121
|
const dockerArgs = this.createExecArgs(options, marker);
|
|
245
|
-
const child =
|
|
122
|
+
const child = spawn(this.options.dockerPath, dockerArgs, {
|
|
246
123
|
stdio: ["pipe", "pipe", "pipe"]
|
|
247
124
|
});
|
|
248
125
|
child.stdin.end();
|
|
@@ -663,12 +540,59 @@ async function waitForDelay(timeoutMs) {
|
|
|
663
540
|
});
|
|
664
541
|
}
|
|
665
542
|
|
|
543
|
+
// src/text-file.ts
|
|
544
|
+
function createTextFilePage(text, options) {
|
|
545
|
+
const contentStartLine = options.contentStartLine ?? 1;
|
|
546
|
+
const relativeStart = Math.max(0, options.startLine - contentStartLine);
|
|
547
|
+
const lines = splitLines(text).slice(relativeStart, relativeStart + options.lineCount + 1);
|
|
548
|
+
const hasMoreLines = lines.length > options.lineCount;
|
|
549
|
+
const selectedLines = lines.slice(0, options.lineCount);
|
|
550
|
+
const selectedContent = selectedLines.join("");
|
|
551
|
+
const selectedBytes = Buffer.from(selectedContent);
|
|
552
|
+
if (selectedBytes.byteLength > options.maxBytes) {
|
|
553
|
+
const content = decodeCompleteUtf8(selectedBytes.subarray(0, options.maxBytes));
|
|
554
|
+
const lineBreaks = countLineBreaks(content);
|
|
555
|
+
const endedAtLineBoundary = content.endsWith("\n");
|
|
556
|
+
return {
|
|
557
|
+
content,
|
|
558
|
+
startLine: options.startLine,
|
|
559
|
+
endLine: content.length === 0 ? null : options.startLine + lineBreaks - (endedAtLineBoundary ? 1 : 0),
|
|
560
|
+
nextStartLine: endedAtLineBoundary ? options.startLine + lineBreaks : null,
|
|
561
|
+
truncated: true,
|
|
562
|
+
truncatedBy: "bytes"
|
|
563
|
+
};
|
|
564
|
+
}
|
|
565
|
+
return {
|
|
566
|
+
content: selectedContent,
|
|
567
|
+
startLine: options.startLine,
|
|
568
|
+
endLine: selectedLines.length === 0 ? null : options.startLine + selectedLines.length - 1,
|
|
569
|
+
nextStartLine: hasMoreLines ? options.startLine + selectedLines.length : null,
|
|
570
|
+
truncated: hasMoreLines,
|
|
571
|
+
truncatedBy: hasMoreLines ? "lines" : null
|
|
572
|
+
};
|
|
573
|
+
}
|
|
574
|
+
function splitLines(text) {
|
|
575
|
+
return text.match(/[^\n]*\n|[^\n]+$/g) ?? [];
|
|
576
|
+
}
|
|
577
|
+
function countLineBreaks(text) {
|
|
578
|
+
let count = 0;
|
|
579
|
+
for (const character of text) {
|
|
580
|
+
if (character === "\n") count += 1;
|
|
581
|
+
}
|
|
582
|
+
return count;
|
|
583
|
+
}
|
|
584
|
+
function decodeCompleteUtf8(bytes) {
|
|
585
|
+
return new TextDecoder().decode(bytes, { stream: true });
|
|
586
|
+
}
|
|
587
|
+
|
|
666
588
|
// src/docker-sandbox.ts
|
|
667
589
|
var defaultImage = "node:22-bookworm";
|
|
668
590
|
var defaultWorkdir = "/workspace";
|
|
669
591
|
var defaultTimeoutMs = 3e4;
|
|
670
|
-
var
|
|
592
|
+
var defaultMaxOutputBytes = 1024 * 1024;
|
|
671
593
|
var defaultMaxProcesses = 4;
|
|
594
|
+
var defaultTextFilePageLines = 500;
|
|
595
|
+
var defaultTextFilePageBytes = 64 * 1024;
|
|
672
596
|
var portProbeScript = [
|
|
673
597
|
`port="$(printf '%04X' "$1")"`,
|
|
674
598
|
"for table in /proc/net/tcp /proc/net/tcp6; do",
|
|
@@ -683,6 +607,15 @@ var portProbeScript = [
|
|
|
683
607
|
"done",
|
|
684
608
|
"exit 1"
|
|
685
609
|
].join("\n");
|
|
610
|
+
var textFilePageScript = [
|
|
611
|
+
'start="$1"',
|
|
612
|
+
'count="$2"',
|
|
613
|
+
'max_bytes="$3"',
|
|
614
|
+
'file="$4"',
|
|
615
|
+
'end="$((start + count))"',
|
|
616
|
+
'[ -f "$file" ] || { echo "Not a readable file: $file" >&2; exit 66; }',
|
|
617
|
+
'sed -n "$start,$end p;$end q" "$file" | head -c "$max_bytes"'
|
|
618
|
+
].join("\n");
|
|
686
619
|
var DockerSandbox = class _DockerSandbox {
|
|
687
620
|
provider = "docker";
|
|
688
621
|
image;
|
|
@@ -907,7 +840,7 @@ var DockerSandbox = class _DockerSandbox {
|
|
|
907
840
|
cliOptions() {
|
|
908
841
|
return {
|
|
909
842
|
dockerPath: this.dockerPath,
|
|
910
|
-
maxOutputBytes: this.limits.maxOutputBytes ??
|
|
843
|
+
maxOutputBytes: this.limits.maxOutputBytes ?? defaultMaxOutputBytes
|
|
911
844
|
};
|
|
912
845
|
}
|
|
913
846
|
async cleanup(containerName, volumeName) {
|
|
@@ -954,7 +887,7 @@ var DockerSandboxSessionImpl = class {
|
|
|
954
887
|
dockerPath: this.dockerPath,
|
|
955
888
|
workdir: this.workdir,
|
|
956
889
|
env: this.env,
|
|
957
|
-
maxOutputBytes: this.limits.maxOutputBytes ??
|
|
890
|
+
maxOutputBytes: this.limits.maxOutputBytes ?? defaultMaxOutputBytes,
|
|
958
891
|
maxProcesses: this.limits.maxProcesses ?? defaultMaxProcesses,
|
|
959
892
|
startupTimeoutMs: this.limits.timeoutMs ?? defaultTimeoutMs,
|
|
960
893
|
onStart: async (process) => {
|
|
@@ -1133,6 +1066,48 @@ var DockerSandboxSessionImpl = class {
|
|
|
1133
1066
|
const bytes = await this.readFile(filePath);
|
|
1134
1067
|
return new TextDecoder().decode(bytes);
|
|
1135
1068
|
}
|
|
1069
|
+
async readTextFilePage(filePath, options = {}) {
|
|
1070
|
+
return this.runOperation(async () => {
|
|
1071
|
+
const startLine = options.startLine ?? 1;
|
|
1072
|
+
const lineCount = options.lineCount ?? defaultTextFilePageLines;
|
|
1073
|
+
const maxBytes = options.maxBytes ?? defaultTextFilePageBytes;
|
|
1074
|
+
assertTextFileReadOptions(startLine, lineCount, maxBytes);
|
|
1075
|
+
const normalized = normalizeSandboxPath(filePath);
|
|
1076
|
+
const captureBytes = maxBytes + 4;
|
|
1077
|
+
const result = await runDockerCli(
|
|
1078
|
+
[
|
|
1079
|
+
"exec",
|
|
1080
|
+
"-w",
|
|
1081
|
+
this.workdir,
|
|
1082
|
+
this.containerName,
|
|
1083
|
+
"sh",
|
|
1084
|
+
"-c",
|
|
1085
|
+
textFilePageScript,
|
|
1086
|
+
"anvia-read-file-page",
|
|
1087
|
+
String(startLine),
|
|
1088
|
+
String(lineCount),
|
|
1089
|
+
String(captureBytes),
|
|
1090
|
+
containerPath(this.workdir, normalized)
|
|
1091
|
+
],
|
|
1092
|
+
{
|
|
1093
|
+
...this.cliOptions(),
|
|
1094
|
+
maxOutputBytes: captureBytes
|
|
1095
|
+
}
|
|
1096
|
+
);
|
|
1097
|
+
if (result.exitCode !== 0) {
|
|
1098
|
+
throw new SandboxDockerCommandError(
|
|
1099
|
+
`Unable to read sandbox text file: ${filePath}`,
|
|
1100
|
+
result
|
|
1101
|
+
);
|
|
1102
|
+
}
|
|
1103
|
+
return createTextFilePage(result.stdout, {
|
|
1104
|
+
startLine,
|
|
1105
|
+
lineCount,
|
|
1106
|
+
maxBytes,
|
|
1107
|
+
contentStartLine: startLine
|
|
1108
|
+
});
|
|
1109
|
+
});
|
|
1110
|
+
}
|
|
1136
1111
|
async writeFile(filePath, data) {
|
|
1137
1112
|
await this.runOperation(async () => {
|
|
1138
1113
|
const size = byteLength(data);
|
|
@@ -1218,7 +1193,7 @@ var DockerSandboxSessionImpl = class {
|
|
|
1218
1193
|
cliOptions() {
|
|
1219
1194
|
return {
|
|
1220
1195
|
dockerPath: this.dockerPath,
|
|
1221
|
-
maxOutputBytes: this.limits.maxOutputBytes ??
|
|
1196
|
+
maxOutputBytes: this.limits.maxOutputBytes ?? defaultMaxOutputBytes
|
|
1222
1197
|
};
|
|
1223
1198
|
}
|
|
1224
1199
|
async isPortListening(containerPort, timeoutMs) {
|
|
@@ -1260,7 +1235,7 @@ var DockerSandboxSessionImpl = class {
|
|
|
1260
1235
|
const cliOptions = {
|
|
1261
1236
|
dockerPath: this.dockerPath,
|
|
1262
1237
|
timeoutMs: options.timeoutMs ?? this.limits.timeoutMs ?? defaultTimeoutMs,
|
|
1263
|
-
maxOutputBytes: this.limits.maxOutputBytes ??
|
|
1238
|
+
maxOutputBytes: this.limits.maxOutputBytes ?? defaultMaxOutputBytes
|
|
1264
1239
|
};
|
|
1265
1240
|
if (options.input !== void 0) cliOptions.input = options.input;
|
|
1266
1241
|
if (options.signal !== void 0) cliOptions.signal = options.signal;
|
|
@@ -1402,6 +1377,17 @@ function assertWaitOptions(timeoutMs, intervalMs) {
|
|
|
1402
1377
|
throw new SandboxPortError("Port wait intervalMs must be a positive integer.");
|
|
1403
1378
|
}
|
|
1404
1379
|
}
|
|
1380
|
+
function assertTextFileReadOptions(startLine, lineCount, maxBytes) {
|
|
1381
|
+
if (!Number.isInteger(startLine) || startLine <= 0) {
|
|
1382
|
+
throw new RangeError("Text file startLine must be a positive integer.");
|
|
1383
|
+
}
|
|
1384
|
+
if (!Number.isInteger(lineCount) || lineCount <= 0) {
|
|
1385
|
+
throw new RangeError("Text file lineCount must be a positive integer.");
|
|
1386
|
+
}
|
|
1387
|
+
if (!Number.isInteger(maxBytes) || maxBytes <= 0) {
|
|
1388
|
+
throw new RangeError("Text file maxBytes must be a positive integer.");
|
|
1389
|
+
}
|
|
1390
|
+
}
|
|
1405
1391
|
async function waitWithSignal(timeoutMs, signal) {
|
|
1406
1392
|
if (signal?.aborted === true) throw abortReason(signal);
|
|
1407
1393
|
await new Promise((resolve, reject) => {
|
|
@@ -1432,7 +1418,9 @@ var execCommandInput = z.object({
|
|
|
1432
1418
|
input: z.string().optional().describe("Optional stdin text to pass to the command.")
|
|
1433
1419
|
});
|
|
1434
1420
|
var readFileInput = z.object({
|
|
1435
|
-
path: z.string().min(1).describe("Relative file path inside the sandbox.")
|
|
1421
|
+
path: z.string().min(1).describe("Relative file path inside the sandbox."),
|
|
1422
|
+
startLine: z.number().int().positive().optional().describe("One-based first line to read. Defaults to 1."),
|
|
1423
|
+
lineCount: z.number().int().positive().max(1e4).optional().describe("Maximum lines to return. Defaults to 500.")
|
|
1436
1424
|
});
|
|
1437
1425
|
var writeFileInput = z.object({
|
|
1438
1426
|
path: z.string().min(1).describe("Relative file path inside the sandbox."),
|
|
@@ -1460,6 +1448,19 @@ var waitForPortInput = z.object({
|
|
|
1460
1448
|
});
|
|
1461
1449
|
var textOutput = z.string();
|
|
1462
1450
|
var maxToolLogBytes = 1024 * 1024;
|
|
1451
|
+
var defaultReadFileLineCount = 500;
|
|
1452
|
+
var defaultReadFileMaxLineCount = 2e3;
|
|
1453
|
+
var defaultReadFileMaxBytes = 64 * 1024;
|
|
1454
|
+
var maxToolReadFileBytes = 1024 * 1024;
|
|
1455
|
+
var maxToolReadFileLines = 1e4;
|
|
1456
|
+
var readFileOutput = z.object({
|
|
1457
|
+
content: z.string(),
|
|
1458
|
+
startLine: z.number().int().positive(),
|
|
1459
|
+
endLine: z.number().int().positive().nullable(),
|
|
1460
|
+
nextStartLine: z.number().int().positive().nullable(),
|
|
1461
|
+
truncated: z.boolean(),
|
|
1462
|
+
truncatedBy: z.enum(["lines", "bytes"]).nullable()
|
|
1463
|
+
});
|
|
1463
1464
|
var sandboxToolMetadataKey = /* @__PURE__ */ Symbol.for("anvia.sandbox.tool.metadata");
|
|
1464
1465
|
function createSandboxTools(session, options = {}) {
|
|
1465
1466
|
const include = new Set(
|
|
@@ -1546,13 +1547,30 @@ function createExecCommandTool(session, options) {
|
|
|
1546
1547
|
function createReadFileTool(session, options) {
|
|
1547
1548
|
return createTool({
|
|
1548
1549
|
name: "read_file",
|
|
1549
|
-
description: "Read a text file from the sandbox workspace.",
|
|
1550
|
+
description: "Read a bounded page of a text file from the sandbox workspace. Continue with nextStartLine when provided.",
|
|
1550
1551
|
input: readFileInput,
|
|
1551
|
-
output:
|
|
1552
|
-
execute: async ({ path: path3 }) => {
|
|
1553
|
-
const
|
|
1554
|
-
|
|
1555
|
-
|
|
1552
|
+
output: readFileOutput,
|
|
1553
|
+
execute: async ({ path: path3, startLine, lineCount }) => {
|
|
1554
|
+
const limits = resolveReadFileLimits(options);
|
|
1555
|
+
const effectiveStartLine = startLine ?? 1;
|
|
1556
|
+
const effectiveLineCount = lineCount ?? limits.defaultLineCount;
|
|
1557
|
+
if (effectiveLineCount > limits.maxLineCount) {
|
|
1558
|
+
throw new SandboxToolPolicyError(
|
|
1559
|
+
`File read line count exceeds sandbox tool policy (${effectiveLineCount} > ${limits.maxLineCount}).`
|
|
1560
|
+
);
|
|
1561
|
+
}
|
|
1562
|
+
if (session.readTextFilePage !== void 0) {
|
|
1563
|
+
return session.readTextFilePage(path3, {
|
|
1564
|
+
startLine: effectiveStartLine,
|
|
1565
|
+
lineCount: effectiveLineCount,
|
|
1566
|
+
maxBytes: limits.maxBytes
|
|
1567
|
+
});
|
|
1568
|
+
}
|
|
1569
|
+
return createTextFilePage(await session.readTextFile(path3), {
|
|
1570
|
+
startLine: effectiveStartLine,
|
|
1571
|
+
lineCount: effectiveLineCount,
|
|
1572
|
+
maxBytes: limits.maxBytes
|
|
1573
|
+
});
|
|
1556
1574
|
}
|
|
1557
1575
|
});
|
|
1558
1576
|
}
|
|
@@ -1783,11 +1801,29 @@ function assertContentAllowed(content, options) {
|
|
|
1783
1801
|
throw new SandboxToolPolicyError("File content exceeds sandbox tool policy.");
|
|
1784
1802
|
}
|
|
1785
1803
|
}
|
|
1786
|
-
function
|
|
1787
|
-
const
|
|
1788
|
-
|
|
1789
|
-
|
|
1804
|
+
function resolveReadFileLimits(options) {
|
|
1805
|
+
const defaultLineCount = options.readFile?.defaultLineCount ?? defaultReadFileLineCount;
|
|
1806
|
+
const maxLineCount = options.readFile?.maxLineCount ?? defaultReadFileMaxLineCount;
|
|
1807
|
+
const maxBytes = options.readFile?.maxBytes ?? defaultReadFileMaxBytes;
|
|
1808
|
+
if (!Number.isInteger(defaultLineCount) || defaultLineCount <= 0) {
|
|
1809
|
+
throw new SandboxToolPolicyError("File defaultLineCount must be a positive integer.");
|
|
1810
|
+
}
|
|
1811
|
+
if (!Number.isInteger(maxLineCount) || maxLineCount <= 0 || maxLineCount > maxToolReadFileLines) {
|
|
1812
|
+
throw new SandboxToolPolicyError(
|
|
1813
|
+
`File maxLineCount must be a positive integer no greater than ${maxToolReadFileLines}.`
|
|
1814
|
+
);
|
|
1815
|
+
}
|
|
1816
|
+
if (defaultLineCount > maxLineCount) {
|
|
1817
|
+
throw new SandboxToolPolicyError(
|
|
1818
|
+
`File defaultLineCount exceeds maxLineCount (${defaultLineCount} > ${maxLineCount}).`
|
|
1819
|
+
);
|
|
1820
|
+
}
|
|
1821
|
+
if (!Number.isInteger(maxBytes) || maxBytes <= 0 || maxBytes > maxToolReadFileBytes) {
|
|
1822
|
+
throw new SandboxToolPolicyError(
|
|
1823
|
+
`File maxBytes must be a positive integer no greater than ${maxToolReadFileBytes}.`
|
|
1824
|
+
);
|
|
1790
1825
|
}
|
|
1826
|
+
return { defaultLineCount, maxLineCount, maxBytes };
|
|
1791
1827
|
}
|
|
1792
1828
|
export {
|
|
1793
1829
|
DockerSandbox,
|