@geminixiang/mikan 0.3.0-beta.0 → 0.3.1

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.
Files changed (134) hide show
  1. package/CHANGELOG.md +31 -0
  2. package/dist/adapter.d.ts +5 -0
  3. package/dist/adapter.d.ts.map +1 -1
  4. package/dist/adapter.js.map +1 -1
  5. package/dist/adapters/discord/context.d.ts +0 -1
  6. package/dist/adapters/discord/context.d.ts.map +1 -1
  7. package/dist/adapters/discord/context.js +62 -84
  8. package/dist/adapters/discord/context.js.map +1 -1
  9. package/dist/adapters/shared.d.ts +1 -2
  10. package/dist/adapters/shared.d.ts.map +1 -1
  11. package/dist/adapters/shared.js +3 -2
  12. package/dist/adapters/shared.js.map +1 -1
  13. package/dist/adapters/slack/bot.d.ts +9 -34
  14. package/dist/adapters/slack/bot.d.ts.map +1 -1
  15. package/dist/adapters/slack/bot.js +374 -358
  16. package/dist/adapters/slack/bot.js.map +1 -1
  17. package/dist/adapters/slack/context.d.ts +0 -1
  18. package/dist/adapters/slack/context.d.ts.map +1 -1
  19. package/dist/adapters/slack/context.js +110 -113
  20. package/dist/adapters/slack/context.js.map +1 -1
  21. package/dist/adapters/slack/session.d.ts +0 -3
  22. package/dist/adapters/slack/session.d.ts.map +1 -1
  23. package/dist/adapters/slack/session.js +2 -8
  24. package/dist/adapters/slack/session.js.map +1 -1
  25. package/dist/adapters/slack/thread-manager.d.ts +0 -1
  26. package/dist/adapters/slack/thread-manager.d.ts.map +1 -1
  27. package/dist/adapters/slack/thread-manager.js +1 -4
  28. package/dist/adapters/slack/thread-manager.js.map +1 -1
  29. package/dist/adapters/slack/tools/block-kit.d.ts +16 -0
  30. package/dist/adapters/slack/tools/block-kit.d.ts.map +1 -0
  31. package/dist/adapters/slack/tools/block-kit.js +105 -0
  32. package/dist/adapters/slack/tools/block-kit.js.map +1 -0
  33. package/dist/adapters/telegram/context.d.ts +0 -1
  34. package/dist/adapters/telegram/context.d.ts.map +1 -1
  35. package/dist/adapters/telegram/context.js +44 -54
  36. package/dist/adapters/telegram/context.js.map +1 -1
  37. package/dist/admin/portal.d.ts.map +1 -1
  38. package/dist/admin/portal.js +2 -3
  39. package/dist/admin/portal.js.map +1 -1
  40. package/dist/agent.d.ts +0 -1
  41. package/dist/agent.d.ts.map +1 -1
  42. package/dist/agent.js +47 -80
  43. package/dist/agent.js.map +1 -1
  44. package/dist/commands/admin.d.ts +0 -3
  45. package/dist/commands/admin.d.ts.map +1 -1
  46. package/dist/commands/admin.js +5 -30
  47. package/dist/commands/admin.js.map +1 -1
  48. package/dist/commands/session-view.d.ts.map +1 -1
  49. package/dist/commands/session-view.js +4 -17
  50. package/dist/commands/session-view.js.map +1 -1
  51. package/dist/commands/types.d.ts +3 -2
  52. package/dist/commands/types.d.ts.map +1 -1
  53. package/dist/commands/types.js.map +1 -1
  54. package/dist/commands/utils.d.ts +3 -1
  55. package/dist/commands/utils.d.ts.map +1 -1
  56. package/dist/commands/utils.js +15 -5
  57. package/dist/commands/utils.js.map +1 -1
  58. package/dist/context.d.ts +0 -1
  59. package/dist/context.d.ts.map +1 -1
  60. package/dist/context.js +1 -23
  61. package/dist/context.js.map +1 -1
  62. package/dist/html.d.ts +2 -0
  63. package/dist/html.d.ts.map +1 -0
  64. package/dist/html.js +4 -0
  65. package/dist/html.js.map +1 -0
  66. package/dist/login/index.d.ts +2 -1
  67. package/dist/login/index.d.ts.map +1 -1
  68. package/dist/login/index.js.map +1 -1
  69. package/dist/login/portal.d.ts +1 -1
  70. package/dist/login/portal.d.ts.map +1 -1
  71. package/dist/login/portal.js +2 -3
  72. package/dist/login/portal.js.map +1 -1
  73. package/dist/login/{session.d.ts → store.d.ts} +1 -1
  74. package/dist/login/store.d.ts.map +1 -0
  75. package/dist/login/{session.js → store.js} +1 -1
  76. package/dist/login/store.js.map +1 -0
  77. package/dist/main.d.ts.map +1 -1
  78. package/dist/main.js +1 -1
  79. package/dist/main.js.map +1 -1
  80. package/dist/portal-shell.d.ts +2 -2
  81. package/dist/portal-shell.d.ts.map +1 -1
  82. package/dist/portal-shell.js +11 -16
  83. package/dist/portal-shell.js.map +1 -1
  84. package/dist/sandbox/cloudflare.d.ts +0 -2
  85. package/dist/sandbox/cloudflare.d.ts.map +1 -1
  86. package/dist/sandbox/cloudflare.js +2 -2
  87. package/dist/sandbox/cloudflare.js.map +1 -1
  88. package/dist/sandbox/container.d.ts +0 -3
  89. package/dist/sandbox/container.d.ts.map +1 -1
  90. package/dist/sandbox/container.js +3 -3
  91. package/dist/sandbox/container.js.map +1 -1
  92. package/dist/sandbox/firecracker.d.ts +0 -2
  93. package/dist/sandbox/firecracker.d.ts.map +1 -1
  94. package/dist/sandbox/firecracker.js +2 -2
  95. package/dist/sandbox/firecracker.js.map +1 -1
  96. package/dist/sandbox/host.d.ts +0 -2
  97. package/dist/sandbox/host.d.ts.map +1 -1
  98. package/dist/sandbox/host.js +2 -2
  99. package/dist/sandbox/host.js.map +1 -1
  100. package/dist/sandbox/image.d.ts +0 -2
  101. package/dist/sandbox/image.d.ts.map +1 -1
  102. package/dist/sandbox/image.js +2 -2
  103. package/dist/sandbox/image.js.map +1 -1
  104. package/dist/sandbox/index.d.ts +1 -6
  105. package/dist/sandbox/index.d.ts.map +1 -1
  106. package/dist/sandbox/index.js +0 -5
  107. package/dist/sandbox/index.js.map +1 -1
  108. package/dist/sandbox/path-context.d.ts +0 -1
  109. package/dist/sandbox/path-context.d.ts.map +1 -1
  110. package/dist/sandbox/path-context.js +1 -1
  111. package/dist/sandbox/path-context.js.map +1 -1
  112. package/dist/sentry.d.ts +2 -2
  113. package/dist/sentry.d.ts.map +1 -1
  114. package/dist/sentry.js.map +1 -1
  115. package/dist/session-view/portal.d.ts.map +1 -1
  116. package/dist/session-view/portal.js +2 -8
  117. package/dist/session-view/portal.js.map +1 -1
  118. package/dist/sessions/chat-session-manager.d.ts.map +1 -1
  119. package/dist/sessions/chat-session-manager.js +5 -9
  120. package/dist/sessions/chat-session-manager.js.map +1 -1
  121. package/dist/tools/index.d.ts +2 -0
  122. package/dist/tools/index.d.ts.map +1 -1
  123. package/dist/tools/index.js +4 -0
  124. package/dist/tools/index.js.map +1 -1
  125. package/dist/vault-routing.d.ts +0 -1
  126. package/dist/vault-routing.d.ts.map +1 -1
  127. package/dist/vault-routing.js +1 -4
  128. package/dist/vault-routing.js.map +1 -1
  129. package/dist/vault.d.ts +2 -1
  130. package/dist/vault.d.ts.map +1 -1
  131. package/dist/vault.js.map +1 -1
  132. package/package.json +3 -1
  133. package/dist/login/session.d.ts.map +0 -1
  134. package/dist/login/session.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"container.d.ts","sourceRoot":"","sources":["../../src/sandbox/container.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACV,sBAAsB,EACtB,WAAW,EACX,UAAU,EACV,QAAQ,EACR,kBAAkB,EAClB,cAAc,EACf,MAAM,YAAY,CAAC;AASpB,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,MAAM,GAAG,sBAAsB,GAAG,SAAS,CAY1F;AAED,wBAAsB,wBAAwB,CAAC,MAAM,EAAE,sBAAsB,GAAG,OAAO,CAAC,IAAI,CAAC,CA6B5F;AAED,wBAAgB,yBAAyB,CACvC,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,MAAM,EACf,WAAW,CAAC,EAAE,MAAM,GACnB,MAAM,CAGR;AAiBD,qBAAa,iBAAkB,YAAW,QAAQ;IAE9C,OAAO,CAAC,SAAS;IACjB,OAAO,CAAC,GAAG,CAAC;IACZ,OAAO,CAAC,WAAW,CAAC;IAHtB,YACU,SAAS,EAAE,MAAM,EACjB,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,YAAA,EAC5B,WAAW,CAAC,GAAE,MAAM,OAAO,CAAC,IAAI,CAAC,aAAA,EACvC;IAEE,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,UAAU,CAAC,CAmBtE;IAED,gBAAgB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAE1C;IAED,cAAc,CAAC,iBAAiB,EAAE,MAAM,GAAG,kBAAkB,CAE5D;IAED,gBAAgB,IAAI,sBAAsB,CAEzC;CACF;AAED,eAAO,MAAM,uBAAuB,EAAE,cAAc,CAAC,sBAAsB,CAM1E,CAAC","sourcesContent":["import { chmodSync, mkdtempSync, rmSync, writeFileSync } from \"node:fs\";\nimport { tmpdir } from \"node:os\";\nimport { join } from \"node:path\";\nimport type {\n ContainerSandboxConfig,\n ExecOptions,\n ExecResult,\n Executor,\n RuntimePathContext,\n SandboxAdapter,\n} from \"./types.js\";\nimport { SandboxError } from \"./errors.js\";\nimport { execSimple, shellEscape } from \"./utils.js\";\nimport { HostExecutor } from \"./host.js\";\nimport { createMountedRuntimePathContext } from \"./path-context.js\";\n\nconst PRIVATE_DIR_MODE = 0o700;\nconst PRIVATE_FILE_MODE = 0o600;\n\nexport function parseContainerSandboxArg(value: string): ContainerSandboxConfig | undefined {\n if (!value.startsWith(\"container:\")) {\n return undefined;\n }\n\n const container = value.slice(\"container:\".length);\n if (!container) {\n throw new SandboxError(\n \"Error: container sandbox requires container name (e.g., container:mikan-sandbox)\",\n );\n }\n return { type: \"container\", container };\n}\n\nexport async function validateContainerSandbox(config: ContainerSandboxConfig): Promise<void> {\n try {\n await execSimple(\"docker\", [\"--version\"]);\n } catch {\n throw new SandboxError(\"Error: Docker is not installed or not in PATH\");\n }\n\n try {\n const result = await execSimple(\"docker\", [\n \"inspect\",\n \"-f\",\n \"{{.State.Running}}\",\n config.container,\n ]);\n if (result.trim() !== \"true\") {\n throw new SandboxError(`Error: Container '${config.container}' is not running.`, [\n `Start it with: docker start ${config.container}`,\n ]);\n }\n } catch (error) {\n if (error instanceof SandboxError) {\n throw error;\n }\n throw new SandboxError(`Error: Container '${config.container}' does not exist.`, [\n `Create it with: docker run -d --name ${config.container} -v <workspace>:/workspace alpine:latest sleep infinity`,\n ]);\n }\n\n console.log(` Container '${config.container}' is running.`);\n}\n\nexport function buildContainerExecCommand(\n container: string,\n command: string,\n envFilePath?: string,\n): string {\n const envPart = envFilePath ? `--env-file ${shellEscape(envFilePath)} ` : \"\";\n return `docker exec ${envPart}-w /workspace ${container} sh -c ${shellEscape(command)}`;\n}\n\nfunction withRuntimeBootstrap(command: string, env?: Record<string, string>): string {\n if (!hasGitHubToken(env)) {\n return command;\n }\n\n return [\n \"if command -v gh >/dev/null 2>&1 && gh auth status >/dev/null 2>&1; then gh auth setup-git >/dev/null 2>&1 || true; fi\",\n command,\n ].join(\"\\n\");\n}\n\nfunction hasGitHubToken(env?: Record<string, string>): boolean {\n return Boolean(env?.GH_TOKEN || env?.GITHUB_TOKEN || env?.GITHUB_OAUTH_ACCESS_TOKEN);\n}\n\nexport class ContainerExecutor implements Executor {\n constructor(\n private container: string,\n private env?: Record<string, string>,\n private ensureReady?: () => Promise<void>,\n ) {}\n\n async exec(command: string, options?: ExecOptions): Promise<ExecResult> {\n if (this.ensureReady) {\n await this.ensureReady();\n } else {\n await ensureContainerRunning(this.container);\n }\n\n const hostExecutor = new HostExecutor();\n const temp = this.env ? createSecureEnvFile(this.env) : undefined;\n try {\n const dockerCmd = buildContainerExecCommand(\n this.container,\n withRuntimeBootstrap(command, this.env),\n temp?.envFilePath,\n );\n return await hostExecutor.exec(dockerCmd, options);\n } finally {\n temp?.cleanup();\n }\n }\n\n getWorkspacePath(_hostPath: string): string {\n return \"/workspace\";\n }\n\n getPathContext(hostWorkspaceRoot: string): RuntimePathContext {\n return createMountedRuntimePathContext(hostWorkspaceRoot, \"/workspace\");\n }\n\n getSandboxConfig(): ContainerSandboxConfig {\n return { type: \"container\", container: this.container };\n }\n}\n\nexport const containerSandboxAdapter: SandboxAdapter<ContainerSandboxConfig> = {\n type: \"container\",\n parse: parseContainerSandboxArg,\n validate: validateContainerSandbox,\n createExecutor: (config, env, ensureReady) =>\n new ContainerExecutor(config.container, env, ensureReady),\n};\n\nasync function ensureContainerRunning(container: string): Promise<void> {\n try {\n const running = await execSimple(\"docker\", [\"inspect\", \"-f\", \"{{.State.Running}}\", container]);\n if (running.trim() === \"true\") {\n return;\n }\n await execSimple(\"docker\", [\"start\", container]);\n } catch (error) {\n const details = error instanceof Error ? error.message : String(error);\n throw new Error(\n `Container \"${container}\" is not available. ` +\n `Expected a pre-existing container or image provisioning to keep it running.\\n${details}`.trim(),\n { cause: error },\n );\n }\n}\n\nfunction createSecureEnvFile(env: Record<string, string>): {\n envFilePath: string;\n cleanup: () => void;\n} {\n const tempDir = mkdtempSync(join(tmpdir(), \"mikan-docker-env-\"));\n chmodSync(tempDir, PRIVATE_DIR_MODE);\n const envFilePath = join(tempDir, \"env.list\");\n const content =\n Object.entries(env)\n .map(([key, value]) => `${key}=${sanitizeEnvValue(value)}`)\n .join(\"\\n\") + \"\\n\";\n writeFileSync(envFilePath, content, { encoding: \"utf-8\", mode: PRIVATE_FILE_MODE });\n chmodSync(envFilePath, PRIVATE_FILE_MODE);\n\n return {\n envFilePath,\n cleanup: () => {\n rmSync(tempDir, { recursive: true, force: true });\n },\n };\n}\n\nfunction sanitizeEnvValue(value: string): string {\n return value.replace(/\\r?\\n/g, \"\");\n}\n"]}
1
+ {"version":3,"file":"container.d.ts","sourceRoot":"","sources":["../../src/sandbox/container.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACV,sBAAsB,EACtB,WAAW,EACX,UAAU,EACV,QAAQ,EACR,kBAAkB,EAClB,cAAc,EACf,MAAM,YAAY,CAAC;AA8EpB,qBAAa,iBAAkB,YAAW,QAAQ;IAE9C,OAAO,CAAC,SAAS;IACjB,OAAO,CAAC,GAAG,CAAC;IACZ,OAAO,CAAC,WAAW,CAAC;IAHtB,YACU,SAAS,EAAE,MAAM,EACjB,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,YAAA,EAC5B,WAAW,CAAC,GAAE,MAAM,OAAO,CAAC,IAAI,CAAC,aAAA,EACvC;IAEE,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,UAAU,CAAC,CAmBtE;IAED,gBAAgB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAE1C;IAED,cAAc,CAAC,iBAAiB,EAAE,MAAM,GAAG,kBAAkB,CAE5D;IAED,gBAAgB,IAAI,sBAAsB,CAEzC;CACF;AAED,eAAO,MAAM,uBAAuB,EAAE,cAAc,CAAC,sBAAsB,CAM1E,CAAC","sourcesContent":["import { chmodSync, mkdtempSync, rmSync, writeFileSync } from \"node:fs\";\nimport { tmpdir } from \"node:os\";\nimport { join } from \"node:path\";\nimport type {\n ContainerSandboxConfig,\n ExecOptions,\n ExecResult,\n Executor,\n RuntimePathContext,\n SandboxAdapter,\n} from \"./types.js\";\nimport { SandboxError } from \"./errors.js\";\nimport { execSimple, shellEscape } from \"./utils.js\";\nimport { HostExecutor } from \"./host.js\";\nimport { createMountedRuntimePathContext } from \"./path-context.js\";\n\nconst PRIVATE_DIR_MODE = 0o700;\nconst PRIVATE_FILE_MODE = 0o600;\n\nfunction parseContainerSandboxArg(value: string): ContainerSandboxConfig | undefined {\n if (!value.startsWith(\"container:\")) {\n return undefined;\n }\n\n const container = value.slice(\"container:\".length);\n if (!container) {\n throw new SandboxError(\n \"Error: container sandbox requires container name (e.g., container:mikan-sandbox)\",\n );\n }\n return { type: \"container\", container };\n}\n\nasync function validateContainerSandbox(config: ContainerSandboxConfig): Promise<void> {\n try {\n await execSimple(\"docker\", [\"--version\"]);\n } catch {\n throw new SandboxError(\"Error: Docker is not installed or not in PATH\");\n }\n\n try {\n const result = await execSimple(\"docker\", [\n \"inspect\",\n \"-f\",\n \"{{.State.Running}}\",\n config.container,\n ]);\n if (result.trim() !== \"true\") {\n throw new SandboxError(`Error: Container '${config.container}' is not running.`, [\n `Start it with: docker start ${config.container}`,\n ]);\n }\n } catch (error) {\n if (error instanceof SandboxError) {\n throw error;\n }\n throw new SandboxError(`Error: Container '${config.container}' does not exist.`, [\n `Create it with: docker run -d --name ${config.container} -v <workspace>:/workspace alpine:latest sleep infinity`,\n ]);\n }\n\n console.log(` Container '${config.container}' is running.`);\n}\n\nfunction buildContainerExecCommand(\n container: string,\n command: string,\n envFilePath?: string,\n): string {\n const envPart = envFilePath ? `--env-file ${shellEscape(envFilePath)} ` : \"\";\n return `docker exec ${envPart}-w /workspace ${container} sh -c ${shellEscape(command)}`;\n}\n\nfunction withRuntimeBootstrap(command: string, env?: Record<string, string>): string {\n if (!hasGitHubToken(env)) {\n return command;\n }\n\n return [\n \"if command -v gh >/dev/null 2>&1 && gh auth status >/dev/null 2>&1; then gh auth setup-git >/dev/null 2>&1 || true; fi\",\n command,\n ].join(\"\\n\");\n}\n\nfunction hasGitHubToken(env?: Record<string, string>): boolean {\n return Boolean(env?.GH_TOKEN || env?.GITHUB_TOKEN || env?.GITHUB_OAUTH_ACCESS_TOKEN);\n}\n\nexport class ContainerExecutor implements Executor {\n constructor(\n private container: string,\n private env?: Record<string, string>,\n private ensureReady?: () => Promise<void>,\n ) {}\n\n async exec(command: string, options?: ExecOptions): Promise<ExecResult> {\n if (this.ensureReady) {\n await this.ensureReady();\n } else {\n await ensureContainerRunning(this.container);\n }\n\n const hostExecutor = new HostExecutor();\n const temp = this.env ? createSecureEnvFile(this.env) : undefined;\n try {\n const dockerCmd = buildContainerExecCommand(\n this.container,\n withRuntimeBootstrap(command, this.env),\n temp?.envFilePath,\n );\n return await hostExecutor.exec(dockerCmd, options);\n } finally {\n temp?.cleanup();\n }\n }\n\n getWorkspacePath(_hostPath: string): string {\n return \"/workspace\";\n }\n\n getPathContext(hostWorkspaceRoot: string): RuntimePathContext {\n return createMountedRuntimePathContext(hostWorkspaceRoot, \"/workspace\");\n }\n\n getSandboxConfig(): ContainerSandboxConfig {\n return { type: \"container\", container: this.container };\n }\n}\n\nexport const containerSandboxAdapter: SandboxAdapter<ContainerSandboxConfig> = {\n type: \"container\",\n parse: parseContainerSandboxArg,\n validate: validateContainerSandbox,\n createExecutor: (config, env, ensureReady) =>\n new ContainerExecutor(config.container, env, ensureReady),\n};\n\nasync function ensureContainerRunning(container: string): Promise<void> {\n try {\n const running = await execSimple(\"docker\", [\"inspect\", \"-f\", \"{{.State.Running}}\", container]);\n if (running.trim() === \"true\") {\n return;\n }\n await execSimple(\"docker\", [\"start\", container]);\n } catch (error) {\n const details = error instanceof Error ? error.message : String(error);\n throw new Error(\n `Container \"${container}\" is not available. ` +\n `Expected a pre-existing container or image provisioning to keep it running.\\n${details}`.trim(),\n { cause: error },\n );\n }\n}\n\nfunction createSecureEnvFile(env: Record<string, string>): {\n envFilePath: string;\n cleanup: () => void;\n} {\n const tempDir = mkdtempSync(join(tmpdir(), \"mikan-docker-env-\"));\n chmodSync(tempDir, PRIVATE_DIR_MODE);\n const envFilePath = join(tempDir, \"env.list\");\n const content =\n Object.entries(env)\n .map(([key, value]) => `${key}=${sanitizeEnvValue(value)}`)\n .join(\"\\n\") + \"\\n\";\n writeFileSync(envFilePath, content, { encoding: \"utf-8\", mode: PRIVATE_FILE_MODE });\n chmodSync(envFilePath, PRIVATE_FILE_MODE);\n\n return {\n envFilePath,\n cleanup: () => {\n rmSync(tempDir, { recursive: true, force: true });\n },\n };\n}\n\nfunction sanitizeEnvValue(value: string): string {\n return value.replace(/\\r?\\n/g, \"\");\n}\n"]}
@@ -7,7 +7,7 @@ import { HostExecutor } from "./host.js";
7
7
  import { createMountedRuntimePathContext } from "./path-context.js";
8
8
  const PRIVATE_DIR_MODE = 0o700;
9
9
  const PRIVATE_FILE_MODE = 0o600;
10
- export function parseContainerSandboxArg(value) {
10
+ function parseContainerSandboxArg(value) {
11
11
  if (!value.startsWith("container:")) {
12
12
  return undefined;
13
13
  }
@@ -17,7 +17,7 @@ export function parseContainerSandboxArg(value) {
17
17
  }
18
18
  return { type: "container", container };
19
19
  }
20
- export async function validateContainerSandbox(config) {
20
+ async function validateContainerSandbox(config) {
21
21
  try {
22
22
  await execSimple("docker", ["--version"]);
23
23
  }
@@ -47,7 +47,7 @@ export async function validateContainerSandbox(config) {
47
47
  }
48
48
  console.log(` Container '${config.container}' is running.`);
49
49
  }
50
- export function buildContainerExecCommand(container, command, envFilePath) {
50
+ function buildContainerExecCommand(container, command, envFilePath) {
51
51
  const envPart = envFilePath ? `--env-file ${shellEscape(envFilePath)} ` : "";
52
52
  return `docker exec ${envPart}-w /workspace ${container} sh -c ${shellEscape(command)}`;
53
53
  }
@@ -1 +1 @@
1
- {"version":3,"file":"container.js","sourceRoot":"","sources":["../../src/sandbox/container.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AACxE,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACjC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AASjC,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AACrD,OAAO,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AACzC,OAAO,EAAE,+BAA+B,EAAE,MAAM,mBAAmB,CAAC;AAEpE,MAAM,gBAAgB,GAAG,KAAK,CAAC;AAC/B,MAAM,iBAAiB,GAAG,KAAK,CAAC;AAEhC,MAAM,UAAU,wBAAwB,CAAC,KAAa;IACpD,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,YAAY,CAAC,EAAE,CAAC;QACpC,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,MAAM,SAAS,GAAG,KAAK,CAAC,KAAK,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC;IACnD,IAAI,CAAC,SAAS,EAAE,CAAC;QACf,MAAM,IAAI,YAAY,CACpB,kFAAkF,CACnF,CAAC;IACJ,CAAC;IACD,OAAO,EAAE,IAAI,EAAE,WAAW,EAAE,SAAS,EAAE,CAAC;AAC1C,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,wBAAwB,CAAC,MAA8B;IAC3E,IAAI,CAAC;QACH,MAAM,UAAU,CAAC,QAAQ,EAAE,CAAC,WAAW,CAAC,CAAC,CAAC;IAC5C,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,IAAI,YAAY,CAAC,+CAA+C,CAAC,CAAC;IAC1E,CAAC;IAED,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,UAAU,CAAC,QAAQ,EAAE;YACxC,SAAS;YACT,IAAI;YACJ,oBAAoB;YACpB,MAAM,CAAC,SAAS;SACjB,CAAC,CAAC;QACH,IAAI,MAAM,CAAC,IAAI,EAAE,KAAK,MAAM,EAAE,CAAC;YAC7B,MAAM,IAAI,YAAY,CAAC,qBAAqB,MAAM,CAAC,SAAS,mBAAmB,EAAE;gBAC/E,+BAA+B,MAAM,CAAC,SAAS,EAAE;aAClD,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,KAAK,YAAY,YAAY,EAAE,CAAC;YAClC,MAAM,KAAK,CAAC;QACd,CAAC;QACD,MAAM,IAAI,YAAY,CAAC,qBAAqB,MAAM,CAAC,SAAS,mBAAmB,EAAE;YAC/E,wCAAwC,MAAM,CAAC,SAAS,yDAAyD;SAClH,CAAC,CAAC;IACL,CAAC;IAED,OAAO,CAAC,GAAG,CAAC,gBAAgB,MAAM,CAAC,SAAS,eAAe,CAAC,CAAC;AAC/D,CAAC;AAED,MAAM,UAAU,yBAAyB,CACvC,SAAiB,EACjB,OAAe,EACf,WAAoB;IAEpB,MAAM,OAAO,GAAG,WAAW,CAAC,CAAC,CAAC,cAAc,WAAW,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;IAC7E,OAAO,eAAe,OAAO,iBAAiB,SAAS,UAAU,WAAW,CAAC,OAAO,CAAC,EAAE,CAAC;AAC1F,CAAC;AAED,SAAS,oBAAoB,CAAC,OAAe,EAAE,GAA4B;IACzE,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,EAAE,CAAC;QACzB,OAAO,OAAO,CAAC;IACjB,CAAC;IAED,OAAO;QACL,wHAAwH;QACxH,OAAO;KACR,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACf,CAAC;AAED,SAAS,cAAc,CAAC,GAA4B;IAClD,OAAO,OAAO,CAAC,GAAG,EAAE,QAAQ,IAAI,GAAG,EAAE,YAAY,IAAI,GAAG,EAAE,yBAAyB,CAAC,CAAC;AACvF,CAAC;AAED,MAAM,OAAO,iBAAiB;IAC5B,YACU,SAAiB,EACjB,GAA4B,EAC5B,WAAiC;yBAFjC,SAAS;mBACT,GAAG;2BACH,WAAW;IAClB,CAAC;IAEJ,KAAK,CAAC,IAAI,CAAC,OAAe,EAAE,OAAqB;QAC/C,IAAI,IAAI,CAAC,WAAW,EAAE,CAAC;YACrB,MAAM,IAAI,CAAC,WAAW,EAAE,CAAC;QAC3B,CAAC;aAAM,CAAC;YACN,MAAM,sBAAsB,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QAC/C,CAAC;QAED,MAAM,YAAY,GAAG,IAAI,YAAY,EAAE,CAAC;QACxC,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,mBAAmB,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;QAClE,IAAI,CAAC;YACH,MAAM,SAAS,GAAG,yBAAyB,CACzC,IAAI,CAAC,SAAS,EACd,oBAAoB,CAAC,OAAO,EAAE,IAAI,CAAC,GAAG,CAAC,EACvC,IAAI,EAAE,WAAW,CAClB,CAAC;YACF,OAAO,MAAM,YAAY,CAAC,IAAI,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;QACrD,CAAC;gBAAS,CAAC;YACT,IAAI,EAAE,OAAO,EAAE,CAAC;QAClB,CAAC;IACH,CAAC;IAED,gBAAgB,CAAC,SAAiB;QAChC,OAAO,YAAY,CAAC;IACtB,CAAC;IAED,cAAc,CAAC,iBAAyB;QACtC,OAAO,+BAA+B,CAAC,iBAAiB,EAAE,YAAY,CAAC,CAAC;IAC1E,CAAC;IAED,gBAAgB;QACd,OAAO,EAAE,IAAI,EAAE,WAAW,EAAE,SAAS,EAAE,IAAI,CAAC,SAAS,EAAE,CAAC;IAC1D,CAAC;CACF;AAED,MAAM,CAAC,MAAM,uBAAuB,GAA2C;IAC7E,IAAI,EAAE,WAAW;IACjB,KAAK,EAAE,wBAAwB;IAC/B,QAAQ,EAAE,wBAAwB;IAClC,cAAc,EAAE,CAAC,MAAM,EAAE,GAAG,EAAE,WAAW,EAAE,EAAE,CAC3C,IAAI,iBAAiB,CAAC,MAAM,CAAC,SAAS,EAAE,GAAG,EAAE,WAAW,CAAC;CAC5D,CAAC;AAEF,KAAK,UAAU,sBAAsB,CAAC,SAAiB;IACrD,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,MAAM,UAAU,CAAC,QAAQ,EAAE,CAAC,SAAS,EAAE,IAAI,EAAE,oBAAoB,EAAE,SAAS,CAAC,CAAC,CAAC;QAC/F,IAAI,OAAO,CAAC,IAAI,EAAE,KAAK,MAAM,EAAE,CAAC;YAC9B,OAAO;QACT,CAAC;QACD,MAAM,UAAU,CAAC,QAAQ,EAAE,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC,CAAC;IACnD,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,OAAO,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QACvE,MAAM,IAAI,KAAK,CACb,cAAc,SAAS,sBAAsB;YAC3C,gFAAgF,OAAO,EAAE,CAAC,IAAI,EAAE,EAClG,EAAE,KAAK,EAAE,KAAK,EAAE,CACjB,CAAC;IACJ,CAAC;AACH,CAAC;AAED,SAAS,mBAAmB,CAAC,GAA2B;IAItD,MAAM,OAAO,GAAG,WAAW,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,mBAAmB,CAAC,CAAC,CAAC;IACjE,SAAS,CAAC,OAAO,EAAE,gBAAgB,CAAC,CAAC;IACrC,MAAM,WAAW,GAAG,IAAI,CAAC,OAAO,EAAE,UAAU,CAAC,CAAC;IAC9C,MAAM,OAAO,GACX,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC;SAChB,GAAG,CAAC,CAAC,CAAC,GAAG,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC,GAAG,GAAG,IAAI,gBAAgB,CAAC,KAAK,CAAC,EAAE,CAAC;SAC1D,IAAI,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IACvB,aAAa,CAAC,WAAW,EAAE,OAAO,EAAE,EAAE,QAAQ,EAAE,OAAO,EAAE,IAAI,EAAE,iBAAiB,EAAE,CAAC,CAAC;IACpF,SAAS,CAAC,WAAW,EAAE,iBAAiB,CAAC,CAAC;IAE1C,OAAO;QACL,WAAW;QACX,OAAO,EAAE,GAAG,EAAE;YACZ,MAAM,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;QACpD,CAAC;KACF,CAAC;AACJ,CAAC;AAED,SAAS,gBAAgB,CAAC,KAAa;IACrC,OAAO,KAAK,CAAC,OAAO,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC;AACrC,CAAC","sourcesContent":["import { chmodSync, mkdtempSync, rmSync, writeFileSync } from \"node:fs\";\nimport { tmpdir } from \"node:os\";\nimport { join } from \"node:path\";\nimport type {\n ContainerSandboxConfig,\n ExecOptions,\n ExecResult,\n Executor,\n RuntimePathContext,\n SandboxAdapter,\n} from \"./types.js\";\nimport { SandboxError } from \"./errors.js\";\nimport { execSimple, shellEscape } from \"./utils.js\";\nimport { HostExecutor } from \"./host.js\";\nimport { createMountedRuntimePathContext } from \"./path-context.js\";\n\nconst PRIVATE_DIR_MODE = 0o700;\nconst PRIVATE_FILE_MODE = 0o600;\n\nexport function parseContainerSandboxArg(value: string): ContainerSandboxConfig | undefined {\n if (!value.startsWith(\"container:\")) {\n return undefined;\n }\n\n const container = value.slice(\"container:\".length);\n if (!container) {\n throw new SandboxError(\n \"Error: container sandbox requires container name (e.g., container:mikan-sandbox)\",\n );\n }\n return { type: \"container\", container };\n}\n\nexport async function validateContainerSandbox(config: ContainerSandboxConfig): Promise<void> {\n try {\n await execSimple(\"docker\", [\"--version\"]);\n } catch {\n throw new SandboxError(\"Error: Docker is not installed or not in PATH\");\n }\n\n try {\n const result = await execSimple(\"docker\", [\n \"inspect\",\n \"-f\",\n \"{{.State.Running}}\",\n config.container,\n ]);\n if (result.trim() !== \"true\") {\n throw new SandboxError(`Error: Container '${config.container}' is not running.`, [\n `Start it with: docker start ${config.container}`,\n ]);\n }\n } catch (error) {\n if (error instanceof SandboxError) {\n throw error;\n }\n throw new SandboxError(`Error: Container '${config.container}' does not exist.`, [\n `Create it with: docker run -d --name ${config.container} -v <workspace>:/workspace alpine:latest sleep infinity`,\n ]);\n }\n\n console.log(` Container '${config.container}' is running.`);\n}\n\nexport function buildContainerExecCommand(\n container: string,\n command: string,\n envFilePath?: string,\n): string {\n const envPart = envFilePath ? `--env-file ${shellEscape(envFilePath)} ` : \"\";\n return `docker exec ${envPart}-w /workspace ${container} sh -c ${shellEscape(command)}`;\n}\n\nfunction withRuntimeBootstrap(command: string, env?: Record<string, string>): string {\n if (!hasGitHubToken(env)) {\n return command;\n }\n\n return [\n \"if command -v gh >/dev/null 2>&1 && gh auth status >/dev/null 2>&1; then gh auth setup-git >/dev/null 2>&1 || true; fi\",\n command,\n ].join(\"\\n\");\n}\n\nfunction hasGitHubToken(env?: Record<string, string>): boolean {\n return Boolean(env?.GH_TOKEN || env?.GITHUB_TOKEN || env?.GITHUB_OAUTH_ACCESS_TOKEN);\n}\n\nexport class ContainerExecutor implements Executor {\n constructor(\n private container: string,\n private env?: Record<string, string>,\n private ensureReady?: () => Promise<void>,\n ) {}\n\n async exec(command: string, options?: ExecOptions): Promise<ExecResult> {\n if (this.ensureReady) {\n await this.ensureReady();\n } else {\n await ensureContainerRunning(this.container);\n }\n\n const hostExecutor = new HostExecutor();\n const temp = this.env ? createSecureEnvFile(this.env) : undefined;\n try {\n const dockerCmd = buildContainerExecCommand(\n this.container,\n withRuntimeBootstrap(command, this.env),\n temp?.envFilePath,\n );\n return await hostExecutor.exec(dockerCmd, options);\n } finally {\n temp?.cleanup();\n }\n }\n\n getWorkspacePath(_hostPath: string): string {\n return \"/workspace\";\n }\n\n getPathContext(hostWorkspaceRoot: string): RuntimePathContext {\n return createMountedRuntimePathContext(hostWorkspaceRoot, \"/workspace\");\n }\n\n getSandboxConfig(): ContainerSandboxConfig {\n return { type: \"container\", container: this.container };\n }\n}\n\nexport const containerSandboxAdapter: SandboxAdapter<ContainerSandboxConfig> = {\n type: \"container\",\n parse: parseContainerSandboxArg,\n validate: validateContainerSandbox,\n createExecutor: (config, env, ensureReady) =>\n new ContainerExecutor(config.container, env, ensureReady),\n};\n\nasync function ensureContainerRunning(container: string): Promise<void> {\n try {\n const running = await execSimple(\"docker\", [\"inspect\", \"-f\", \"{{.State.Running}}\", container]);\n if (running.trim() === \"true\") {\n return;\n }\n await execSimple(\"docker\", [\"start\", container]);\n } catch (error) {\n const details = error instanceof Error ? error.message : String(error);\n throw new Error(\n `Container \"${container}\" is not available. ` +\n `Expected a pre-existing container or image provisioning to keep it running.\\n${details}`.trim(),\n { cause: error },\n );\n }\n}\n\nfunction createSecureEnvFile(env: Record<string, string>): {\n envFilePath: string;\n cleanup: () => void;\n} {\n const tempDir = mkdtempSync(join(tmpdir(), \"mikan-docker-env-\"));\n chmodSync(tempDir, PRIVATE_DIR_MODE);\n const envFilePath = join(tempDir, \"env.list\");\n const content =\n Object.entries(env)\n .map(([key, value]) => `${key}=${sanitizeEnvValue(value)}`)\n .join(\"\\n\") + \"\\n\";\n writeFileSync(envFilePath, content, { encoding: \"utf-8\", mode: PRIVATE_FILE_MODE });\n chmodSync(envFilePath, PRIVATE_FILE_MODE);\n\n return {\n envFilePath,\n cleanup: () => {\n rmSync(tempDir, { recursive: true, force: true });\n },\n };\n}\n\nfunction sanitizeEnvValue(value: string): string {\n return value.replace(/\\r?\\n/g, \"\");\n}\n"]}
1
+ {"version":3,"file":"container.js","sourceRoot":"","sources":["../../src/sandbox/container.ts"],"names":[],"mappings":"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","sourcesContent":["import { chmodSync, mkdtempSync, rmSync, writeFileSync } from \"node:fs\";\nimport { tmpdir } from \"node:os\";\nimport { join } from \"node:path\";\nimport type {\n ContainerSandboxConfig,\n ExecOptions,\n ExecResult,\n Executor,\n RuntimePathContext,\n SandboxAdapter,\n} from \"./types.js\";\nimport { SandboxError } from \"./errors.js\";\nimport { execSimple, shellEscape } from \"./utils.js\";\nimport { HostExecutor } from \"./host.js\";\nimport { createMountedRuntimePathContext } from \"./path-context.js\";\n\nconst PRIVATE_DIR_MODE = 0o700;\nconst PRIVATE_FILE_MODE = 0o600;\n\nfunction parseContainerSandboxArg(value: string): ContainerSandboxConfig | undefined {\n if (!value.startsWith(\"container:\")) {\n return undefined;\n }\n\n const container = value.slice(\"container:\".length);\n if (!container) {\n throw new SandboxError(\n \"Error: container sandbox requires container name (e.g., container:mikan-sandbox)\",\n );\n }\n return { type: \"container\", container };\n}\n\nasync function validateContainerSandbox(config: ContainerSandboxConfig): Promise<void> {\n try {\n await execSimple(\"docker\", [\"--version\"]);\n } catch {\n throw new SandboxError(\"Error: Docker is not installed or not in PATH\");\n }\n\n try {\n const result = await execSimple(\"docker\", [\n \"inspect\",\n \"-f\",\n \"{{.State.Running}}\",\n config.container,\n ]);\n if (result.trim() !== \"true\") {\n throw new SandboxError(`Error: Container '${config.container}' is not running.`, [\n `Start it with: docker start ${config.container}`,\n ]);\n }\n } catch (error) {\n if (error instanceof SandboxError) {\n throw error;\n }\n throw new SandboxError(`Error: Container '${config.container}' does not exist.`, [\n `Create it with: docker run -d --name ${config.container} -v <workspace>:/workspace alpine:latest sleep infinity`,\n ]);\n }\n\n console.log(` Container '${config.container}' is running.`);\n}\n\nfunction buildContainerExecCommand(\n container: string,\n command: string,\n envFilePath?: string,\n): string {\n const envPart = envFilePath ? `--env-file ${shellEscape(envFilePath)} ` : \"\";\n return `docker exec ${envPart}-w /workspace ${container} sh -c ${shellEscape(command)}`;\n}\n\nfunction withRuntimeBootstrap(command: string, env?: Record<string, string>): string {\n if (!hasGitHubToken(env)) {\n return command;\n }\n\n return [\n \"if command -v gh >/dev/null 2>&1 && gh auth status >/dev/null 2>&1; then gh auth setup-git >/dev/null 2>&1 || true; fi\",\n command,\n ].join(\"\\n\");\n}\n\nfunction hasGitHubToken(env?: Record<string, string>): boolean {\n return Boolean(env?.GH_TOKEN || env?.GITHUB_TOKEN || env?.GITHUB_OAUTH_ACCESS_TOKEN);\n}\n\nexport class ContainerExecutor implements Executor {\n constructor(\n private container: string,\n private env?: Record<string, string>,\n private ensureReady?: () => Promise<void>,\n ) {}\n\n async exec(command: string, options?: ExecOptions): Promise<ExecResult> {\n if (this.ensureReady) {\n await this.ensureReady();\n } else {\n await ensureContainerRunning(this.container);\n }\n\n const hostExecutor = new HostExecutor();\n const temp = this.env ? createSecureEnvFile(this.env) : undefined;\n try {\n const dockerCmd = buildContainerExecCommand(\n this.container,\n withRuntimeBootstrap(command, this.env),\n temp?.envFilePath,\n );\n return await hostExecutor.exec(dockerCmd, options);\n } finally {\n temp?.cleanup();\n }\n }\n\n getWorkspacePath(_hostPath: string): string {\n return \"/workspace\";\n }\n\n getPathContext(hostWorkspaceRoot: string): RuntimePathContext {\n return createMountedRuntimePathContext(hostWorkspaceRoot, \"/workspace\");\n }\n\n getSandboxConfig(): ContainerSandboxConfig {\n return { type: \"container\", container: this.container };\n }\n}\n\nexport const containerSandboxAdapter: SandboxAdapter<ContainerSandboxConfig> = {\n type: \"container\",\n parse: parseContainerSandboxArg,\n validate: validateContainerSandbox,\n createExecutor: (config, env, ensureReady) =>\n new ContainerExecutor(config.container, env, ensureReady),\n};\n\nasync function ensureContainerRunning(container: string): Promise<void> {\n try {\n const running = await execSimple(\"docker\", [\"inspect\", \"-f\", \"{{.State.Running}}\", container]);\n if (running.trim() === \"true\") {\n return;\n }\n await execSimple(\"docker\", [\"start\", container]);\n } catch (error) {\n const details = error instanceof Error ? error.message : String(error);\n throw new Error(\n `Container \"${container}\" is not available. ` +\n `Expected a pre-existing container or image provisioning to keep it running.\\n${details}`.trim(),\n { cause: error },\n );\n }\n}\n\nfunction createSecureEnvFile(env: Record<string, string>): {\n envFilePath: string;\n cleanup: () => void;\n} {\n const tempDir = mkdtempSync(join(tmpdir(), \"mikan-docker-env-\"));\n chmodSync(tempDir, PRIVATE_DIR_MODE);\n const envFilePath = join(tempDir, \"env.list\");\n const content =\n Object.entries(env)\n .map(([key, value]) => `${key}=${sanitizeEnvValue(value)}`)\n .join(\"\\n\") + \"\\n\";\n writeFileSync(envFilePath, content, { encoding: \"utf-8\", mode: PRIVATE_FILE_MODE });\n chmodSync(envFilePath, PRIVATE_FILE_MODE);\n\n return {\n envFilePath,\n cleanup: () => {\n rmSync(tempDir, { recursive: true, force: true });\n },\n };\n}\n\nfunction sanitizeEnvValue(value: string): string {\n return value.replace(/\\r?\\n/g, \"\");\n}\n"]}
@@ -1,6 +1,4 @@
1
1
  import type { ExecOptions, ExecResult, Executor, FirecrackerSandboxConfig, RuntimePathContext, SandboxAdapter } from "./types.js";
2
- export declare function parseFirecrackerSandboxArg(value: string): FirecrackerSandboxConfig | undefined;
3
- export declare function validateFirecrackerSandbox(config: FirecrackerSandboxConfig): Promise<void>;
4
2
  export declare class FirecrackerExecutor implements Executor {
5
3
  private vmId;
6
4
  private hostPath;
@@ -1 +1 @@
1
- {"version":3,"file":"firecracker.d.ts","sourceRoot":"","sources":["../../src/sandbox/firecracker.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,WAAW,EACX,UAAU,EACV,QAAQ,EACR,wBAAwB,EACxB,kBAAkB,EAClB,cAAc,EACf,MAAM,YAAY,CAAC;AAKpB,wBAAgB,0BAA0B,CAAC,KAAK,EAAE,MAAM,GAAG,wBAAwB,GAAG,SAAS,CA8B9F;AAED,wBAAsB,0BAA0B,CAAC,MAAM,EAAE,wBAAwB,GAAG,OAAO,CAAC,IAAI,CAAC,CA6ChG;AAED,qBAAa,mBAAoB,YAAW,QAAQ;IAEhD,OAAO,CAAC,IAAI;IACZ,OAAO,CAAC,QAAQ;IAChB,OAAO,CAAC,OAAO;IACf,OAAO,CAAC,OAAO;IACf,OAAO,CAAC,GAAG,CAAC;IALd,YACU,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,EAChB,OAAO,GAAE,MAAe,EACxB,OAAO,GAAE,MAAW,EACpB,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,YAAA,EAClC;IAEE,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,UAAU,CAAC,CAqGtE;IAED,gBAAgB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAE1C;IAED,cAAc,CAAC,iBAAiB,EAAE,MAAM,GAAG,kBAAkB,CAK5D;IAED,gBAAgB,IAAI,wBAAwB,CAQ3C;CACF;AAgBD,eAAO,MAAM,yBAAyB,EAAE,cAAc,CAAC,wBAAwB,CAM9E,CAAC","sourcesContent":["import { spawn } from \"child_process\";\nimport type {\n ExecOptions,\n ExecResult,\n Executor,\n FirecrackerSandboxConfig,\n RuntimePathContext,\n SandboxAdapter,\n} from \"./types.js\";\nimport { SandboxError } from \"./errors.js\";\nimport { HostExecutor } from \"./host.js\";\nimport { execSimple, killProcessTree, shellEscape } from \"./utils.js\";\n\nexport function parseFirecrackerSandboxArg(value: string): FirecrackerSandboxConfig | undefined {\n if (!value.startsWith(\"firecracker:\")) {\n return undefined;\n }\n\n const arg = value.slice(\"firecracker:\".length);\n // Format: firecracker:<vm-id>:<host-path>[:<ssh-user>[:<ssh-port>]]\n // Example: firecracker:vm1:/home/user/workspace\n // firecracker:vm1:/home/user/workspace:root\n // firecracker:vm1:/home/user/workspace:root:22\n const parts = arg.split(\":\");\n if (parts.length < 2) {\n throw new SandboxError(\n \"Error: firecracker sandbox requires vm-id and host-path\\n\" +\n \"Usage: firecracker:<vm-id>:<host-path>[:<ssh-user>[:<ssh-port>]]\\n\" +\n \"Example: firecracker:vm1:/home/user/workspace\",\n );\n }\n const vmId = parts[0];\n const hostPath = parts[1];\n const sshUser = parts[2] || \"root\";\n const sshPort = parts[3] ? parseInt(parts[3], 10) : 22;\n\n if (!vmId || !hostPath) {\n throw new SandboxError(\"Error: firecracker sandbox requires vm-id and host-path\");\n }\n if (isNaN(sshPort) || sshPort <= 0 || sshPort > 65535) {\n throw new SandboxError(\"Error: invalid SSH port\");\n }\n return { type: \"firecracker\", vmId, hostPath, sshUser, sshPort };\n}\n\nexport async function validateFirecrackerSandbox(config: FirecrackerSandboxConfig): Promise<void> {\n // Check if fc-agent or firecracker CLI is available\n try {\n await execSimple(\"fc-agent\", [\"--version\"]);\n } catch {\n // Try alternative: firecracker\n try {\n await execSimple(\"firecracker\", [\"--version\"]);\n } catch {\n throw new SandboxError(\n \"Error: Firecracker tools (fc-agent or firecracker) not found in PATH\",\n [\"Install firecracker: https://github.com/firecracker-microvm/firecracker\"],\n );\n }\n }\n\n // Check if VM is running using fc-agent\n try {\n const result = await execSimple(\"fc-agent\", [\"status\", config.vmId]);\n if (!result.includes(\"running\") && !result.includes(\"Running\")) {\n throw new SandboxError(`Error: Firecracker VM '${config.vmId}' is not running.`, [\n `Start it with: fc-agent start ${config.vmId}`,\n ]);\n }\n } catch (error) {\n if (error instanceof SandboxError) {\n throw error;\n }\n // Try alternative: firecracker-ctl or direct check\n try {\n await execSimple(\"firecracker-ctl\", [\"status\", config.vmId]);\n } catch {\n console.error(`Warning: Could not verify if VM '${config.vmId}' is running.`);\n console.error(\"Make sure the VM is started before running mikan.\");\n }\n }\n\n // Verify host path exists\n try {\n await execSimple(\"ls\", [\"-d\", config.hostPath]);\n } catch {\n throw new SandboxError(`Error: Host path '${config.hostPath}' does not exist.`);\n }\n\n console.log(` Firecracker VM '${config.vmId}' configured with workspace '${config.hostPath}'.`);\n}\n\nexport class FirecrackerExecutor implements Executor {\n constructor(\n private vmId: string,\n private hostPath: string,\n private sshUser: string = \"root\",\n private sshPort: number = 22,\n private env?: Record<string, string>,\n ) {}\n\n async exec(command: string, options?: ExecOptions): Promise<ExecResult> {\n if (!this.env || Object.keys(this.env).length === 0) {\n const sshCmd =\n this.sshPort === 22\n ? `ssh -o StrictHostKeyChecking=no -o ConnectTimeout=10 ${this.sshUser}@${this.vmId} sh -c ${shellEscape(command)}`\n : `ssh -o StrictHostKeyChecking=no -o ConnectTimeout=10 -p ${this.sshPort} ${this.sshUser}@${this.vmId} sh -c ${shellEscape(command)}`;\n const hostExecutor = new HostExecutor();\n return hostExecutor.exec(sshCmd, options);\n }\n\n return new Promise((resolve, reject) => {\n const sshArgs = [\"-o\", \"StrictHostKeyChecking=no\", \"-o\", \"ConnectTimeout=10\"];\n if (this.sshPort !== 22) {\n sshArgs.push(\"-p\", String(this.sshPort));\n }\n sshArgs.push(`${this.sshUser}@${this.vmId}`, \"sh\", \"-se\");\n\n const child = spawn(\"ssh\", sshArgs, {\n detached: true,\n stdio: [\"pipe\", \"pipe\", \"pipe\"],\n });\n\n let stdout = \"\";\n let stderr = \"\";\n let timedOut = false;\n let settled = false;\n\n const timeoutHandle =\n options?.timeout && options.timeout > 0\n ? setTimeout(() => {\n timedOut = true;\n if (child.pid) killProcessTree(child.pid);\n }, options.timeout * 1000)\n : undefined;\n\n const onAbort = () => {\n if (child.pid) killProcessTree(child.pid);\n };\n\n if (options?.signal) {\n if (options.signal.aborted) {\n onAbort();\n } else {\n options.signal.addEventListener(\"abort\", onAbort, { once: true });\n }\n }\n\n child.on(\"error\", (error) => {\n if (settled) return;\n settled = true;\n if (timeoutHandle) clearTimeout(timeoutHandle);\n if (options?.signal) {\n options.signal.removeEventListener(\"abort\", onAbort);\n }\n reject(error);\n });\n\n child.stdout?.on(\"data\", (data) => {\n stdout += data.toString();\n if (stdout.length > 10 * 1024 * 1024) {\n stdout = stdout.slice(0, 10 * 1024 * 1024);\n }\n });\n\n child.stderr?.on(\"data\", (data) => {\n stderr += data.toString();\n if (stderr.length > 10 * 1024 * 1024) {\n stderr = stderr.slice(0, 10 * 1024 * 1024);\n }\n });\n\n child.stdin?.on(\"error\", (error) => {\n stderr += `${error.message}\\n`;\n });\n child.stdin?.end(buildRemoteScript(command, this.env));\n\n child.on(\"close\", (code) => {\n if (settled) return;\n settled = true;\n if (timeoutHandle) clearTimeout(timeoutHandle);\n if (options?.signal) {\n options.signal.removeEventListener(\"abort\", onAbort);\n }\n\n if (options?.signal?.aborted) {\n reject(new Error(`${stdout}\\n${stderr}\\nCommand aborted`.trim()));\n return;\n }\n\n if (timedOut) {\n reject(\n new Error(\n `${stdout}\\n${stderr}\\nCommand timed out after ${options?.timeout} seconds`.trim(),\n ),\n );\n return;\n }\n\n resolve({ stdout, stderr, code: code ?? 0 });\n });\n });\n }\n\n getWorkspacePath(_hostPath: string): string {\n return \"/workspace\";\n }\n\n getPathContext(hostWorkspaceRoot: string): RuntimePathContext {\n return {\n hostWorkspaceRoot,\n runtimeWorkspaceRoot: \"/workspace\",\n };\n }\n\n getSandboxConfig(): FirecrackerSandboxConfig {\n return {\n type: \"firecracker\",\n vmId: this.vmId,\n hostPath: this.hostPath,\n sshUser: this.sshUser,\n sshPort: this.sshPort,\n };\n }\n}\n\nfunction buildRemoteScript(command: string, env?: Record<string, string>): string {\n const exports = env\n ? Object.entries(env)\n .map(([key, value]) => {\n if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(key)) {\n throw new SandboxError(`Invalid environment variable name for firecracker: ${key}`);\n }\n return `export ${key}=${shellEscape(value)}`;\n })\n .join(\"\\n\") + \"\\n\"\n : \"\";\n return `${exports}${command}\\n`;\n}\n\nexport const firecrackerSandboxAdapter: SandboxAdapter<FirecrackerSandboxConfig> = {\n type: \"firecracker\",\n parse: parseFirecrackerSandboxArg,\n validate: validateFirecrackerSandbox,\n createExecutor: (config, env) =>\n new FirecrackerExecutor(config.vmId, config.hostPath, config.sshUser, config.sshPort, env),\n};\n"]}
1
+ {"version":3,"file":"firecracker.d.ts","sourceRoot":"","sources":["../../src/sandbox/firecracker.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,WAAW,EACX,UAAU,EACV,QAAQ,EACR,wBAAwB,EACxB,kBAAkB,EAClB,cAAc,EACf,MAAM,YAAY,CAAC;AAoFpB,qBAAa,mBAAoB,YAAW,QAAQ;IAEhD,OAAO,CAAC,IAAI;IACZ,OAAO,CAAC,QAAQ;IAChB,OAAO,CAAC,OAAO;IACf,OAAO,CAAC,OAAO;IACf,OAAO,CAAC,GAAG,CAAC;IALd,YACU,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,EAChB,OAAO,GAAE,MAAe,EACxB,OAAO,GAAE,MAAW,EACpB,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,YAAA,EAClC;IAEE,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,UAAU,CAAC,CAqGtE;IAED,gBAAgB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAE1C;IAED,cAAc,CAAC,iBAAiB,EAAE,MAAM,GAAG,kBAAkB,CAK5D;IAED,gBAAgB,IAAI,wBAAwB,CAQ3C;CACF;AAgBD,eAAO,MAAM,yBAAyB,EAAE,cAAc,CAAC,wBAAwB,CAM9E,CAAC","sourcesContent":["import { spawn } from \"child_process\";\nimport type {\n ExecOptions,\n ExecResult,\n Executor,\n FirecrackerSandboxConfig,\n RuntimePathContext,\n SandboxAdapter,\n} from \"./types.js\";\nimport { SandboxError } from \"./errors.js\";\nimport { HostExecutor } from \"./host.js\";\nimport { execSimple, killProcessTree, shellEscape } from \"./utils.js\";\n\nfunction parseFirecrackerSandboxArg(value: string): FirecrackerSandboxConfig | undefined {\n if (!value.startsWith(\"firecracker:\")) {\n return undefined;\n }\n\n const arg = value.slice(\"firecracker:\".length);\n // Format: firecracker:<vm-id>:<host-path>[:<ssh-user>[:<ssh-port>]]\n // Example: firecracker:vm1:/home/user/workspace\n // firecracker:vm1:/home/user/workspace:root\n // firecracker:vm1:/home/user/workspace:root:22\n const parts = arg.split(\":\");\n if (parts.length < 2) {\n throw new SandboxError(\n \"Error: firecracker sandbox requires vm-id and host-path\\n\" +\n \"Usage: firecracker:<vm-id>:<host-path>[:<ssh-user>[:<ssh-port>]]\\n\" +\n \"Example: firecracker:vm1:/home/user/workspace\",\n );\n }\n const vmId = parts[0];\n const hostPath = parts[1];\n const sshUser = parts[2] || \"root\";\n const sshPort = parts[3] ? parseInt(parts[3], 10) : 22;\n\n if (!vmId || !hostPath) {\n throw new SandboxError(\"Error: firecracker sandbox requires vm-id and host-path\");\n }\n if (isNaN(sshPort) || sshPort <= 0 || sshPort > 65535) {\n throw new SandboxError(\"Error: invalid SSH port\");\n }\n return { type: \"firecracker\", vmId, hostPath, sshUser, sshPort };\n}\n\nasync function validateFirecrackerSandbox(config: FirecrackerSandboxConfig): Promise<void> {\n // Check if fc-agent or firecracker CLI is available\n try {\n await execSimple(\"fc-agent\", [\"--version\"]);\n } catch {\n // Try alternative: firecracker\n try {\n await execSimple(\"firecracker\", [\"--version\"]);\n } catch {\n throw new SandboxError(\n \"Error: Firecracker tools (fc-agent or firecracker) not found in PATH\",\n [\"Install firecracker: https://github.com/firecracker-microvm/firecracker\"],\n );\n }\n }\n\n // Check if VM is running using fc-agent\n try {\n const result = await execSimple(\"fc-agent\", [\"status\", config.vmId]);\n if (!result.includes(\"running\") && !result.includes(\"Running\")) {\n throw new SandboxError(`Error: Firecracker VM '${config.vmId}' is not running.`, [\n `Start it with: fc-agent start ${config.vmId}`,\n ]);\n }\n } catch (error) {\n if (error instanceof SandboxError) {\n throw error;\n }\n // Try alternative: firecracker-ctl or direct check\n try {\n await execSimple(\"firecracker-ctl\", [\"status\", config.vmId]);\n } catch {\n console.error(`Warning: Could not verify if VM '${config.vmId}' is running.`);\n console.error(\"Make sure the VM is started before running mikan.\");\n }\n }\n\n // Verify host path exists\n try {\n await execSimple(\"ls\", [\"-d\", config.hostPath]);\n } catch {\n throw new SandboxError(`Error: Host path '${config.hostPath}' does not exist.`);\n }\n\n console.log(` Firecracker VM '${config.vmId}' configured with workspace '${config.hostPath}'.`);\n}\n\nexport class FirecrackerExecutor implements Executor {\n constructor(\n private vmId: string,\n private hostPath: string,\n private sshUser: string = \"root\",\n private sshPort: number = 22,\n private env?: Record<string, string>,\n ) {}\n\n async exec(command: string, options?: ExecOptions): Promise<ExecResult> {\n if (!this.env || Object.keys(this.env).length === 0) {\n const sshCmd =\n this.sshPort === 22\n ? `ssh -o StrictHostKeyChecking=no -o ConnectTimeout=10 ${this.sshUser}@${this.vmId} sh -c ${shellEscape(command)}`\n : `ssh -o StrictHostKeyChecking=no -o ConnectTimeout=10 -p ${this.sshPort} ${this.sshUser}@${this.vmId} sh -c ${shellEscape(command)}`;\n const hostExecutor = new HostExecutor();\n return hostExecutor.exec(sshCmd, options);\n }\n\n return new Promise((resolve, reject) => {\n const sshArgs = [\"-o\", \"StrictHostKeyChecking=no\", \"-o\", \"ConnectTimeout=10\"];\n if (this.sshPort !== 22) {\n sshArgs.push(\"-p\", String(this.sshPort));\n }\n sshArgs.push(`${this.sshUser}@${this.vmId}`, \"sh\", \"-se\");\n\n const child = spawn(\"ssh\", sshArgs, {\n detached: true,\n stdio: [\"pipe\", \"pipe\", \"pipe\"],\n });\n\n let stdout = \"\";\n let stderr = \"\";\n let timedOut = false;\n let settled = false;\n\n const timeoutHandle =\n options?.timeout && options.timeout > 0\n ? setTimeout(() => {\n timedOut = true;\n if (child.pid) killProcessTree(child.pid);\n }, options.timeout * 1000)\n : undefined;\n\n const onAbort = () => {\n if (child.pid) killProcessTree(child.pid);\n };\n\n if (options?.signal) {\n if (options.signal.aborted) {\n onAbort();\n } else {\n options.signal.addEventListener(\"abort\", onAbort, { once: true });\n }\n }\n\n child.on(\"error\", (error) => {\n if (settled) return;\n settled = true;\n if (timeoutHandle) clearTimeout(timeoutHandle);\n if (options?.signal) {\n options.signal.removeEventListener(\"abort\", onAbort);\n }\n reject(error);\n });\n\n child.stdout?.on(\"data\", (data) => {\n stdout += data.toString();\n if (stdout.length > 10 * 1024 * 1024) {\n stdout = stdout.slice(0, 10 * 1024 * 1024);\n }\n });\n\n child.stderr?.on(\"data\", (data) => {\n stderr += data.toString();\n if (stderr.length > 10 * 1024 * 1024) {\n stderr = stderr.slice(0, 10 * 1024 * 1024);\n }\n });\n\n child.stdin?.on(\"error\", (error) => {\n stderr += `${error.message}\\n`;\n });\n child.stdin?.end(buildRemoteScript(command, this.env));\n\n child.on(\"close\", (code) => {\n if (settled) return;\n settled = true;\n if (timeoutHandle) clearTimeout(timeoutHandle);\n if (options?.signal) {\n options.signal.removeEventListener(\"abort\", onAbort);\n }\n\n if (options?.signal?.aborted) {\n reject(new Error(`${stdout}\\n${stderr}\\nCommand aborted`.trim()));\n return;\n }\n\n if (timedOut) {\n reject(\n new Error(\n `${stdout}\\n${stderr}\\nCommand timed out after ${options?.timeout} seconds`.trim(),\n ),\n );\n return;\n }\n\n resolve({ stdout, stderr, code: code ?? 0 });\n });\n });\n }\n\n getWorkspacePath(_hostPath: string): string {\n return \"/workspace\";\n }\n\n getPathContext(hostWorkspaceRoot: string): RuntimePathContext {\n return {\n hostWorkspaceRoot,\n runtimeWorkspaceRoot: \"/workspace\",\n };\n }\n\n getSandboxConfig(): FirecrackerSandboxConfig {\n return {\n type: \"firecracker\",\n vmId: this.vmId,\n hostPath: this.hostPath,\n sshUser: this.sshUser,\n sshPort: this.sshPort,\n };\n }\n}\n\nfunction buildRemoteScript(command: string, env?: Record<string, string>): string {\n const exports = env\n ? Object.entries(env)\n .map(([key, value]) => {\n if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(key)) {\n throw new SandboxError(`Invalid environment variable name for firecracker: ${key}`);\n }\n return `export ${key}=${shellEscape(value)}`;\n })\n .join(\"\\n\") + \"\\n\"\n : \"\";\n return `${exports}${command}\\n`;\n}\n\nexport const firecrackerSandboxAdapter: SandboxAdapter<FirecrackerSandboxConfig> = {\n type: \"firecracker\",\n parse: parseFirecrackerSandboxArg,\n validate: validateFirecrackerSandbox,\n createExecutor: (config, env) =>\n new FirecrackerExecutor(config.vmId, config.hostPath, config.sshUser, config.sshPort, env),\n};\n"]}
@@ -2,7 +2,7 @@ import { spawn } from "child_process";
2
2
  import { SandboxError } from "./errors.js";
3
3
  import { HostExecutor } from "./host.js";
4
4
  import { execSimple, killProcessTree, shellEscape } from "./utils.js";
5
- export function parseFirecrackerSandboxArg(value) {
5
+ function parseFirecrackerSandboxArg(value) {
6
6
  if (!value.startsWith("firecracker:")) {
7
7
  return undefined;
8
8
  }
@@ -29,7 +29,7 @@ export function parseFirecrackerSandboxArg(value) {
29
29
  }
30
30
  return { type: "firecracker", vmId, hostPath, sshUser, sshPort };
31
31
  }
32
- export async function validateFirecrackerSandbox(config) {
32
+ async function validateFirecrackerSandbox(config) {
33
33
  // Check if fc-agent or firecracker CLI is available
34
34
  try {
35
35
  await execSimple("fc-agent", ["--version"]);
@@ -1 +1 @@
1
- {"version":3,"file":"firecracker.js","sourceRoot":"","sources":["../../src/sandbox/firecracker.ts"],"names":[],"mappings":"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","sourcesContent":["import { spawn } from \"child_process\";\nimport type {\n ExecOptions,\n ExecResult,\n Executor,\n FirecrackerSandboxConfig,\n RuntimePathContext,\n SandboxAdapter,\n} from \"./types.js\";\nimport { SandboxError } from \"./errors.js\";\nimport { HostExecutor } from \"./host.js\";\nimport { execSimple, killProcessTree, shellEscape } from \"./utils.js\";\n\nexport function parseFirecrackerSandboxArg(value: string): FirecrackerSandboxConfig | undefined {\n if (!value.startsWith(\"firecracker:\")) {\n return undefined;\n }\n\n const arg = value.slice(\"firecracker:\".length);\n // Format: firecracker:<vm-id>:<host-path>[:<ssh-user>[:<ssh-port>]]\n // Example: firecracker:vm1:/home/user/workspace\n // firecracker:vm1:/home/user/workspace:root\n // firecracker:vm1:/home/user/workspace:root:22\n const parts = arg.split(\":\");\n if (parts.length < 2) {\n throw new SandboxError(\n \"Error: firecracker sandbox requires vm-id and host-path\\n\" +\n \"Usage: firecracker:<vm-id>:<host-path>[:<ssh-user>[:<ssh-port>]]\\n\" +\n \"Example: firecracker:vm1:/home/user/workspace\",\n );\n }\n const vmId = parts[0];\n const hostPath = parts[1];\n const sshUser = parts[2] || \"root\";\n const sshPort = parts[3] ? parseInt(parts[3], 10) : 22;\n\n if (!vmId || !hostPath) {\n throw new SandboxError(\"Error: firecracker sandbox requires vm-id and host-path\");\n }\n if (isNaN(sshPort) || sshPort <= 0 || sshPort > 65535) {\n throw new SandboxError(\"Error: invalid SSH port\");\n }\n return { type: \"firecracker\", vmId, hostPath, sshUser, sshPort };\n}\n\nexport async function validateFirecrackerSandbox(config: FirecrackerSandboxConfig): Promise<void> {\n // Check if fc-agent or firecracker CLI is available\n try {\n await execSimple(\"fc-agent\", [\"--version\"]);\n } catch {\n // Try alternative: firecracker\n try {\n await execSimple(\"firecracker\", [\"--version\"]);\n } catch {\n throw new SandboxError(\n \"Error: Firecracker tools (fc-agent or firecracker) not found in PATH\",\n [\"Install firecracker: https://github.com/firecracker-microvm/firecracker\"],\n );\n }\n }\n\n // Check if VM is running using fc-agent\n try {\n const result = await execSimple(\"fc-agent\", [\"status\", config.vmId]);\n if (!result.includes(\"running\") && !result.includes(\"Running\")) {\n throw new SandboxError(`Error: Firecracker VM '${config.vmId}' is not running.`, [\n `Start it with: fc-agent start ${config.vmId}`,\n ]);\n }\n } catch (error) {\n if (error instanceof SandboxError) {\n throw error;\n }\n // Try alternative: firecracker-ctl or direct check\n try {\n await execSimple(\"firecracker-ctl\", [\"status\", config.vmId]);\n } catch {\n console.error(`Warning: Could not verify if VM '${config.vmId}' is running.`);\n console.error(\"Make sure the VM is started before running mikan.\");\n }\n }\n\n // Verify host path exists\n try {\n await execSimple(\"ls\", [\"-d\", config.hostPath]);\n } catch {\n throw new SandboxError(`Error: Host path '${config.hostPath}' does not exist.`);\n }\n\n console.log(` Firecracker VM '${config.vmId}' configured with workspace '${config.hostPath}'.`);\n}\n\nexport class FirecrackerExecutor implements Executor {\n constructor(\n private vmId: string,\n private hostPath: string,\n private sshUser: string = \"root\",\n private sshPort: number = 22,\n private env?: Record<string, string>,\n ) {}\n\n async exec(command: string, options?: ExecOptions): Promise<ExecResult> {\n if (!this.env || Object.keys(this.env).length === 0) {\n const sshCmd =\n this.sshPort === 22\n ? `ssh -o StrictHostKeyChecking=no -o ConnectTimeout=10 ${this.sshUser}@${this.vmId} sh -c ${shellEscape(command)}`\n : `ssh -o StrictHostKeyChecking=no -o ConnectTimeout=10 -p ${this.sshPort} ${this.sshUser}@${this.vmId} sh -c ${shellEscape(command)}`;\n const hostExecutor = new HostExecutor();\n return hostExecutor.exec(sshCmd, options);\n }\n\n return new Promise((resolve, reject) => {\n const sshArgs = [\"-o\", \"StrictHostKeyChecking=no\", \"-o\", \"ConnectTimeout=10\"];\n if (this.sshPort !== 22) {\n sshArgs.push(\"-p\", String(this.sshPort));\n }\n sshArgs.push(`${this.sshUser}@${this.vmId}`, \"sh\", \"-se\");\n\n const child = spawn(\"ssh\", sshArgs, {\n detached: true,\n stdio: [\"pipe\", \"pipe\", \"pipe\"],\n });\n\n let stdout = \"\";\n let stderr = \"\";\n let timedOut = false;\n let settled = false;\n\n const timeoutHandle =\n options?.timeout && options.timeout > 0\n ? setTimeout(() => {\n timedOut = true;\n if (child.pid) killProcessTree(child.pid);\n }, options.timeout * 1000)\n : undefined;\n\n const onAbort = () => {\n if (child.pid) killProcessTree(child.pid);\n };\n\n if (options?.signal) {\n if (options.signal.aborted) {\n onAbort();\n } else {\n options.signal.addEventListener(\"abort\", onAbort, { once: true });\n }\n }\n\n child.on(\"error\", (error) => {\n if (settled) return;\n settled = true;\n if (timeoutHandle) clearTimeout(timeoutHandle);\n if (options?.signal) {\n options.signal.removeEventListener(\"abort\", onAbort);\n }\n reject(error);\n });\n\n child.stdout?.on(\"data\", (data) => {\n stdout += data.toString();\n if (stdout.length > 10 * 1024 * 1024) {\n stdout = stdout.slice(0, 10 * 1024 * 1024);\n }\n });\n\n child.stderr?.on(\"data\", (data) => {\n stderr += data.toString();\n if (stderr.length > 10 * 1024 * 1024) {\n stderr = stderr.slice(0, 10 * 1024 * 1024);\n }\n });\n\n child.stdin?.on(\"error\", (error) => {\n stderr += `${error.message}\\n`;\n });\n child.stdin?.end(buildRemoteScript(command, this.env));\n\n child.on(\"close\", (code) => {\n if (settled) return;\n settled = true;\n if (timeoutHandle) clearTimeout(timeoutHandle);\n if (options?.signal) {\n options.signal.removeEventListener(\"abort\", onAbort);\n }\n\n if (options?.signal?.aborted) {\n reject(new Error(`${stdout}\\n${stderr}\\nCommand aborted`.trim()));\n return;\n }\n\n if (timedOut) {\n reject(\n new Error(\n `${stdout}\\n${stderr}\\nCommand timed out after ${options?.timeout} seconds`.trim(),\n ),\n );\n return;\n }\n\n resolve({ stdout, stderr, code: code ?? 0 });\n });\n });\n }\n\n getWorkspacePath(_hostPath: string): string {\n return \"/workspace\";\n }\n\n getPathContext(hostWorkspaceRoot: string): RuntimePathContext {\n return {\n hostWorkspaceRoot,\n runtimeWorkspaceRoot: \"/workspace\",\n };\n }\n\n getSandboxConfig(): FirecrackerSandboxConfig {\n return {\n type: \"firecracker\",\n vmId: this.vmId,\n hostPath: this.hostPath,\n sshUser: this.sshUser,\n sshPort: this.sshPort,\n };\n }\n}\n\nfunction buildRemoteScript(command: string, env?: Record<string, string>): string {\n const exports = env\n ? Object.entries(env)\n .map(([key, value]) => {\n if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(key)) {\n throw new SandboxError(`Invalid environment variable name for firecracker: ${key}`);\n }\n return `export ${key}=${shellEscape(value)}`;\n })\n .join(\"\\n\") + \"\\n\"\n : \"\";\n return `${exports}${command}\\n`;\n}\n\nexport const firecrackerSandboxAdapter: SandboxAdapter<FirecrackerSandboxConfig> = {\n type: \"firecracker\",\n parse: parseFirecrackerSandboxArg,\n validate: validateFirecrackerSandbox,\n createExecutor: (config, env) =>\n new FirecrackerExecutor(config.vmId, config.hostPath, config.sshUser, config.sshPort, env),\n};\n"]}
1
+ {"version":3,"file":"firecracker.js","sourceRoot":"","sources":["../../src/sandbox/firecracker.ts"],"names":[],"mappings":"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","sourcesContent":["import { spawn } from \"child_process\";\nimport type {\n ExecOptions,\n ExecResult,\n Executor,\n FirecrackerSandboxConfig,\n RuntimePathContext,\n SandboxAdapter,\n} from \"./types.js\";\nimport { SandboxError } from \"./errors.js\";\nimport { HostExecutor } from \"./host.js\";\nimport { execSimple, killProcessTree, shellEscape } from \"./utils.js\";\n\nfunction parseFirecrackerSandboxArg(value: string): FirecrackerSandboxConfig | undefined {\n if (!value.startsWith(\"firecracker:\")) {\n return undefined;\n }\n\n const arg = value.slice(\"firecracker:\".length);\n // Format: firecracker:<vm-id>:<host-path>[:<ssh-user>[:<ssh-port>]]\n // Example: firecracker:vm1:/home/user/workspace\n // firecracker:vm1:/home/user/workspace:root\n // firecracker:vm1:/home/user/workspace:root:22\n const parts = arg.split(\":\");\n if (parts.length < 2) {\n throw new SandboxError(\n \"Error: firecracker sandbox requires vm-id and host-path\\n\" +\n \"Usage: firecracker:<vm-id>:<host-path>[:<ssh-user>[:<ssh-port>]]\\n\" +\n \"Example: firecracker:vm1:/home/user/workspace\",\n );\n }\n const vmId = parts[0];\n const hostPath = parts[1];\n const sshUser = parts[2] || \"root\";\n const sshPort = parts[3] ? parseInt(parts[3], 10) : 22;\n\n if (!vmId || !hostPath) {\n throw new SandboxError(\"Error: firecracker sandbox requires vm-id and host-path\");\n }\n if (isNaN(sshPort) || sshPort <= 0 || sshPort > 65535) {\n throw new SandboxError(\"Error: invalid SSH port\");\n }\n return { type: \"firecracker\", vmId, hostPath, sshUser, sshPort };\n}\n\nasync function validateFirecrackerSandbox(config: FirecrackerSandboxConfig): Promise<void> {\n // Check if fc-agent or firecracker CLI is available\n try {\n await execSimple(\"fc-agent\", [\"--version\"]);\n } catch {\n // Try alternative: firecracker\n try {\n await execSimple(\"firecracker\", [\"--version\"]);\n } catch {\n throw new SandboxError(\n \"Error: Firecracker tools (fc-agent or firecracker) not found in PATH\",\n [\"Install firecracker: https://github.com/firecracker-microvm/firecracker\"],\n );\n }\n }\n\n // Check if VM is running using fc-agent\n try {\n const result = await execSimple(\"fc-agent\", [\"status\", config.vmId]);\n if (!result.includes(\"running\") && !result.includes(\"Running\")) {\n throw new SandboxError(`Error: Firecracker VM '${config.vmId}' is not running.`, [\n `Start it with: fc-agent start ${config.vmId}`,\n ]);\n }\n } catch (error) {\n if (error instanceof SandboxError) {\n throw error;\n }\n // Try alternative: firecracker-ctl or direct check\n try {\n await execSimple(\"firecracker-ctl\", [\"status\", config.vmId]);\n } catch {\n console.error(`Warning: Could not verify if VM '${config.vmId}' is running.`);\n console.error(\"Make sure the VM is started before running mikan.\");\n }\n }\n\n // Verify host path exists\n try {\n await execSimple(\"ls\", [\"-d\", config.hostPath]);\n } catch {\n throw new SandboxError(`Error: Host path '${config.hostPath}' does not exist.`);\n }\n\n console.log(` Firecracker VM '${config.vmId}' configured with workspace '${config.hostPath}'.`);\n}\n\nexport class FirecrackerExecutor implements Executor {\n constructor(\n private vmId: string,\n private hostPath: string,\n private sshUser: string = \"root\",\n private sshPort: number = 22,\n private env?: Record<string, string>,\n ) {}\n\n async exec(command: string, options?: ExecOptions): Promise<ExecResult> {\n if (!this.env || Object.keys(this.env).length === 0) {\n const sshCmd =\n this.sshPort === 22\n ? `ssh -o StrictHostKeyChecking=no -o ConnectTimeout=10 ${this.sshUser}@${this.vmId} sh -c ${shellEscape(command)}`\n : `ssh -o StrictHostKeyChecking=no -o ConnectTimeout=10 -p ${this.sshPort} ${this.sshUser}@${this.vmId} sh -c ${shellEscape(command)}`;\n const hostExecutor = new HostExecutor();\n return hostExecutor.exec(sshCmd, options);\n }\n\n return new Promise((resolve, reject) => {\n const sshArgs = [\"-o\", \"StrictHostKeyChecking=no\", \"-o\", \"ConnectTimeout=10\"];\n if (this.sshPort !== 22) {\n sshArgs.push(\"-p\", String(this.sshPort));\n }\n sshArgs.push(`${this.sshUser}@${this.vmId}`, \"sh\", \"-se\");\n\n const child = spawn(\"ssh\", sshArgs, {\n detached: true,\n stdio: [\"pipe\", \"pipe\", \"pipe\"],\n });\n\n let stdout = \"\";\n let stderr = \"\";\n let timedOut = false;\n let settled = false;\n\n const timeoutHandle =\n options?.timeout && options.timeout > 0\n ? setTimeout(() => {\n timedOut = true;\n if (child.pid) killProcessTree(child.pid);\n }, options.timeout * 1000)\n : undefined;\n\n const onAbort = () => {\n if (child.pid) killProcessTree(child.pid);\n };\n\n if (options?.signal) {\n if (options.signal.aborted) {\n onAbort();\n } else {\n options.signal.addEventListener(\"abort\", onAbort, { once: true });\n }\n }\n\n child.on(\"error\", (error) => {\n if (settled) return;\n settled = true;\n if (timeoutHandle) clearTimeout(timeoutHandle);\n if (options?.signal) {\n options.signal.removeEventListener(\"abort\", onAbort);\n }\n reject(error);\n });\n\n child.stdout?.on(\"data\", (data) => {\n stdout += data.toString();\n if (stdout.length > 10 * 1024 * 1024) {\n stdout = stdout.slice(0, 10 * 1024 * 1024);\n }\n });\n\n child.stderr?.on(\"data\", (data) => {\n stderr += data.toString();\n if (stderr.length > 10 * 1024 * 1024) {\n stderr = stderr.slice(0, 10 * 1024 * 1024);\n }\n });\n\n child.stdin?.on(\"error\", (error) => {\n stderr += `${error.message}\\n`;\n });\n child.stdin?.end(buildRemoteScript(command, this.env));\n\n child.on(\"close\", (code) => {\n if (settled) return;\n settled = true;\n if (timeoutHandle) clearTimeout(timeoutHandle);\n if (options?.signal) {\n options.signal.removeEventListener(\"abort\", onAbort);\n }\n\n if (options?.signal?.aborted) {\n reject(new Error(`${stdout}\\n${stderr}\\nCommand aborted`.trim()));\n return;\n }\n\n if (timedOut) {\n reject(\n new Error(\n `${stdout}\\n${stderr}\\nCommand timed out after ${options?.timeout} seconds`.trim(),\n ),\n );\n return;\n }\n\n resolve({ stdout, stderr, code: code ?? 0 });\n });\n });\n }\n\n getWorkspacePath(_hostPath: string): string {\n return \"/workspace\";\n }\n\n getPathContext(hostWorkspaceRoot: string): RuntimePathContext {\n return {\n hostWorkspaceRoot,\n runtimeWorkspaceRoot: \"/workspace\",\n };\n }\n\n getSandboxConfig(): FirecrackerSandboxConfig {\n return {\n type: \"firecracker\",\n vmId: this.vmId,\n hostPath: this.hostPath,\n sshUser: this.sshUser,\n sshPort: this.sshPort,\n };\n }\n}\n\nfunction buildRemoteScript(command: string, env?: Record<string, string>): string {\n const exports = env\n ? Object.entries(env)\n .map(([key, value]) => {\n if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(key)) {\n throw new SandboxError(`Invalid environment variable name for firecracker: ${key}`);\n }\n return `export ${key}=${shellEscape(value)}`;\n })\n .join(\"\\n\") + \"\\n\"\n : \"\";\n return `${exports}${command}\\n`;\n}\n\nexport const firecrackerSandboxAdapter: SandboxAdapter<FirecrackerSandboxConfig> = {\n type: \"firecracker\",\n parse: parseFirecrackerSandboxArg,\n validate: validateFirecrackerSandbox,\n createExecutor: (config, env) =>\n new FirecrackerExecutor(config.vmId, config.hostPath, config.sshUser, config.sshPort, env),\n};\n"]}
@@ -1,6 +1,4 @@
1
1
  import type { ExecOptions, ExecResult, Executor, HostSandboxConfig, RuntimePathContext, SandboxAdapter } from "./types.js";
2
- export declare function parseHostSandboxArg(value: string): HostSandboxConfig | undefined;
3
- export declare function validateHostSandbox(_config: HostSandboxConfig): Promise<void>;
4
2
  export declare class HostExecutor implements Executor {
5
3
  exec(command: string, options?: ExecOptions): Promise<ExecResult>;
6
4
  getWorkspacePath(hostPath: string): string;
@@ -1 +1 @@
1
- {"version":3,"file":"host.d.ts","sourceRoot":"","sources":["../../src/sandbox/host.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,WAAW,EACX,UAAU,EACV,QAAQ,EACR,iBAAiB,EACjB,kBAAkB,EAClB,cAAc,EACf,MAAM,YAAY,CAAC;AAIpB,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,iBAAiB,GAAG,SAAS,CAKhF;AAED,wBAAsB,mBAAmB,CAAC,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC,CAEnF;AAED,qBAAa,YAAa,YAAW,QAAQ;IACrC,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,UAAU,CAAC,CAuEtE;IAED,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAEzC;IAED,cAAc,CAAC,iBAAiB,EAAE,MAAM,GAAG,kBAAkB,CAE5D;IAED,gBAAgB,IAAI,iBAAiB,CAEpC;CACF;AAED,eAAO,MAAM,kBAAkB,EAAE,cAAc,CAAC,iBAAiB,CAKhE,CAAC","sourcesContent":["import { spawn } from \"child_process\";\nimport type {\n ExecOptions,\n ExecResult,\n Executor,\n HostSandboxConfig,\n RuntimePathContext,\n SandboxAdapter,\n} from \"./types.js\";\nimport { killProcessTree } from \"./utils.js\";\nimport { createMountedRuntimePathContext } from \"./path-context.js\";\n\nexport function parseHostSandboxArg(value: string): HostSandboxConfig | undefined {\n if (value === \"host\") {\n return { type: \"host\" };\n }\n return undefined;\n}\n\nexport async function validateHostSandbox(_config: HostSandboxConfig): Promise<void> {\n return;\n}\n\nexport class HostExecutor implements Executor {\n async exec(command: string, options?: ExecOptions): Promise<ExecResult> {\n return new Promise((resolve, reject) => {\n const shell = process.platform === \"win32\" ? \"cmd\" : \"sh\";\n const shellArgs = process.platform === \"win32\" ? [\"/c\"] : [\"-c\"];\n\n const child = spawn(shell, [...shellArgs, command], {\n detached: true,\n stdio: [\"ignore\", \"pipe\", \"pipe\"],\n });\n\n let stdout = \"\";\n let stderr = \"\";\n let timedOut = false;\n\n const timeoutHandle =\n options?.timeout && options.timeout > 0\n ? setTimeout(() => {\n timedOut = true;\n killProcessTree(child.pid!);\n }, options.timeout * 1000)\n : undefined;\n\n const onAbort = () => {\n if (child.pid) killProcessTree(child.pid);\n };\n\n if (options?.signal) {\n if (options.signal.aborted) {\n onAbort();\n } else {\n options.signal.addEventListener(\"abort\", onAbort, { once: true });\n }\n }\n\n child.stdout?.on(\"data\", (data) => {\n stdout += data.toString();\n if (stdout.length > 10 * 1024 * 1024) {\n stdout = stdout.slice(0, 10 * 1024 * 1024);\n }\n });\n\n child.stderr?.on(\"data\", (data) => {\n stderr += data.toString();\n if (stderr.length > 10 * 1024 * 1024) {\n stderr = stderr.slice(0, 10 * 1024 * 1024);\n }\n });\n\n child.on(\"close\", (code) => {\n if (timeoutHandle) clearTimeout(timeoutHandle);\n if (options?.signal) {\n options.signal.removeEventListener(\"abort\", onAbort);\n }\n\n if (options?.signal?.aborted) {\n reject(new Error(`${stdout}\\n${stderr}\\nCommand aborted`.trim()));\n return;\n }\n\n if (timedOut) {\n reject(\n new Error(\n `${stdout}\\n${stderr}\\nCommand timed out after ${options?.timeout} seconds`.trim(),\n ),\n );\n return;\n }\n\n resolve({ stdout, stderr, code: code ?? 0 });\n });\n });\n }\n\n getWorkspacePath(hostPath: string): string {\n return hostPath;\n }\n\n getPathContext(hostWorkspaceRoot: string): RuntimePathContext {\n return createMountedRuntimePathContext(hostWorkspaceRoot, hostWorkspaceRoot);\n }\n\n getSandboxConfig(): HostSandboxConfig {\n return { type: \"host\" };\n }\n}\n\nexport const hostSandboxAdapter: SandboxAdapter<HostSandboxConfig> = {\n type: \"host\",\n parse: parseHostSandboxArg,\n validate: validateHostSandbox,\n createExecutor: () => new HostExecutor(),\n};\n"]}
1
+ {"version":3,"file":"host.d.ts","sourceRoot":"","sources":["../../src/sandbox/host.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,WAAW,EACX,UAAU,EACV,QAAQ,EACR,iBAAiB,EACjB,kBAAkB,EAClB,cAAc,EACf,MAAM,YAAY,CAAC;AAepB,qBAAa,YAAa,YAAW,QAAQ;IACrC,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,UAAU,CAAC,CAuEtE;IAED,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAEzC;IAED,cAAc,CAAC,iBAAiB,EAAE,MAAM,GAAG,kBAAkB,CAE5D;IAED,gBAAgB,IAAI,iBAAiB,CAEpC;CACF;AAED,eAAO,MAAM,kBAAkB,EAAE,cAAc,CAAC,iBAAiB,CAKhE,CAAC","sourcesContent":["import { spawn } from \"child_process\";\nimport type {\n ExecOptions,\n ExecResult,\n Executor,\n HostSandboxConfig,\n RuntimePathContext,\n SandboxAdapter,\n} from \"./types.js\";\nimport { killProcessTree } from \"./utils.js\";\nimport { createMountedRuntimePathContext } from \"./path-context.js\";\n\nfunction parseHostSandboxArg(value: string): HostSandboxConfig | undefined {\n if (value === \"host\") {\n return { type: \"host\" };\n }\n return undefined;\n}\n\nasync function validateHostSandbox(_config: HostSandboxConfig): Promise<void> {\n return;\n}\n\nexport class HostExecutor implements Executor {\n async exec(command: string, options?: ExecOptions): Promise<ExecResult> {\n return new Promise((resolve, reject) => {\n const shell = process.platform === \"win32\" ? \"cmd\" : \"sh\";\n const shellArgs = process.platform === \"win32\" ? [\"/c\"] : [\"-c\"];\n\n const child = spawn(shell, [...shellArgs, command], {\n detached: true,\n stdio: [\"ignore\", \"pipe\", \"pipe\"],\n });\n\n let stdout = \"\";\n let stderr = \"\";\n let timedOut = false;\n\n const timeoutHandle =\n options?.timeout && options.timeout > 0\n ? setTimeout(() => {\n timedOut = true;\n killProcessTree(child.pid!);\n }, options.timeout * 1000)\n : undefined;\n\n const onAbort = () => {\n if (child.pid) killProcessTree(child.pid);\n };\n\n if (options?.signal) {\n if (options.signal.aborted) {\n onAbort();\n } else {\n options.signal.addEventListener(\"abort\", onAbort, { once: true });\n }\n }\n\n child.stdout?.on(\"data\", (data) => {\n stdout += data.toString();\n if (stdout.length > 10 * 1024 * 1024) {\n stdout = stdout.slice(0, 10 * 1024 * 1024);\n }\n });\n\n child.stderr?.on(\"data\", (data) => {\n stderr += data.toString();\n if (stderr.length > 10 * 1024 * 1024) {\n stderr = stderr.slice(0, 10 * 1024 * 1024);\n }\n });\n\n child.on(\"close\", (code) => {\n if (timeoutHandle) clearTimeout(timeoutHandle);\n if (options?.signal) {\n options.signal.removeEventListener(\"abort\", onAbort);\n }\n\n if (options?.signal?.aborted) {\n reject(new Error(`${stdout}\\n${stderr}\\nCommand aborted`.trim()));\n return;\n }\n\n if (timedOut) {\n reject(\n new Error(\n `${stdout}\\n${stderr}\\nCommand timed out after ${options?.timeout} seconds`.trim(),\n ),\n );\n return;\n }\n\n resolve({ stdout, stderr, code: code ?? 0 });\n });\n });\n }\n\n getWorkspacePath(hostPath: string): string {\n return hostPath;\n }\n\n getPathContext(hostWorkspaceRoot: string): RuntimePathContext {\n return createMountedRuntimePathContext(hostWorkspaceRoot, hostWorkspaceRoot);\n }\n\n getSandboxConfig(): HostSandboxConfig {\n return { type: \"host\" };\n }\n}\n\nexport const hostSandboxAdapter: SandboxAdapter<HostSandboxConfig> = {\n type: \"host\",\n parse: parseHostSandboxArg,\n validate: validateHostSandbox,\n createExecutor: () => new HostExecutor(),\n};\n"]}
@@ -1,13 +1,13 @@
1
1
  import { spawn } from "child_process";
2
2
  import { killProcessTree } from "./utils.js";
3
3
  import { createMountedRuntimePathContext } from "./path-context.js";
4
- export function parseHostSandboxArg(value) {
4
+ function parseHostSandboxArg(value) {
5
5
  if (value === "host") {
6
6
  return { type: "host" };
7
7
  }
8
8
  return undefined;
9
9
  }
10
- export async function validateHostSandbox(_config) {
10
+ async function validateHostSandbox(_config) {
11
11
  return;
12
12
  }
13
13
  export class HostExecutor {
@@ -1 +1 @@
1
- {"version":3,"file":"host.js","sourceRoot":"","sources":["../../src/sandbox/host.ts"],"names":[],"mappings":"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","sourcesContent":["import { spawn } from \"child_process\";\nimport type {\n ExecOptions,\n ExecResult,\n Executor,\n HostSandboxConfig,\n RuntimePathContext,\n SandboxAdapter,\n} from \"./types.js\";\nimport { killProcessTree } from \"./utils.js\";\nimport { createMountedRuntimePathContext } from \"./path-context.js\";\n\nexport function parseHostSandboxArg(value: string): HostSandboxConfig | undefined {\n if (value === \"host\") {\n return { type: \"host\" };\n }\n return undefined;\n}\n\nexport async function validateHostSandbox(_config: HostSandboxConfig): Promise<void> {\n return;\n}\n\nexport class HostExecutor implements Executor {\n async exec(command: string, options?: ExecOptions): Promise<ExecResult> {\n return new Promise((resolve, reject) => {\n const shell = process.platform === \"win32\" ? \"cmd\" : \"sh\";\n const shellArgs = process.platform === \"win32\" ? [\"/c\"] : [\"-c\"];\n\n const child = spawn(shell, [...shellArgs, command], {\n detached: true,\n stdio: [\"ignore\", \"pipe\", \"pipe\"],\n });\n\n let stdout = \"\";\n let stderr = \"\";\n let timedOut = false;\n\n const timeoutHandle =\n options?.timeout && options.timeout > 0\n ? setTimeout(() => {\n timedOut = true;\n killProcessTree(child.pid!);\n }, options.timeout * 1000)\n : undefined;\n\n const onAbort = () => {\n if (child.pid) killProcessTree(child.pid);\n };\n\n if (options?.signal) {\n if (options.signal.aborted) {\n onAbort();\n } else {\n options.signal.addEventListener(\"abort\", onAbort, { once: true });\n }\n }\n\n child.stdout?.on(\"data\", (data) => {\n stdout += data.toString();\n if (stdout.length > 10 * 1024 * 1024) {\n stdout = stdout.slice(0, 10 * 1024 * 1024);\n }\n });\n\n child.stderr?.on(\"data\", (data) => {\n stderr += data.toString();\n if (stderr.length > 10 * 1024 * 1024) {\n stderr = stderr.slice(0, 10 * 1024 * 1024);\n }\n });\n\n child.on(\"close\", (code) => {\n if (timeoutHandle) clearTimeout(timeoutHandle);\n if (options?.signal) {\n options.signal.removeEventListener(\"abort\", onAbort);\n }\n\n if (options?.signal?.aborted) {\n reject(new Error(`${stdout}\\n${stderr}\\nCommand aborted`.trim()));\n return;\n }\n\n if (timedOut) {\n reject(\n new Error(\n `${stdout}\\n${stderr}\\nCommand timed out after ${options?.timeout} seconds`.trim(),\n ),\n );\n return;\n }\n\n resolve({ stdout, stderr, code: code ?? 0 });\n });\n });\n }\n\n getWorkspacePath(hostPath: string): string {\n return hostPath;\n }\n\n getPathContext(hostWorkspaceRoot: string): RuntimePathContext {\n return createMountedRuntimePathContext(hostWorkspaceRoot, hostWorkspaceRoot);\n }\n\n getSandboxConfig(): HostSandboxConfig {\n return { type: \"host\" };\n }\n}\n\nexport const hostSandboxAdapter: SandboxAdapter<HostSandboxConfig> = {\n type: \"host\",\n parse: parseHostSandboxArg,\n validate: validateHostSandbox,\n createExecutor: () => new HostExecutor(),\n};\n"]}
1
+ {"version":3,"file":"host.js","sourceRoot":"","sources":["../../src/sandbox/host.ts"],"names":[],"mappings":"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","sourcesContent":["import { spawn } from \"child_process\";\nimport type {\n ExecOptions,\n ExecResult,\n Executor,\n HostSandboxConfig,\n RuntimePathContext,\n SandboxAdapter,\n} from \"./types.js\";\nimport { killProcessTree } from \"./utils.js\";\nimport { createMountedRuntimePathContext } from \"./path-context.js\";\n\nfunction parseHostSandboxArg(value: string): HostSandboxConfig | undefined {\n if (value === \"host\") {\n return { type: \"host\" };\n }\n return undefined;\n}\n\nasync function validateHostSandbox(_config: HostSandboxConfig): Promise<void> {\n return;\n}\n\nexport class HostExecutor implements Executor {\n async exec(command: string, options?: ExecOptions): Promise<ExecResult> {\n return new Promise((resolve, reject) => {\n const shell = process.platform === \"win32\" ? \"cmd\" : \"sh\";\n const shellArgs = process.platform === \"win32\" ? [\"/c\"] : [\"-c\"];\n\n const child = spawn(shell, [...shellArgs, command], {\n detached: true,\n stdio: [\"ignore\", \"pipe\", \"pipe\"],\n });\n\n let stdout = \"\";\n let stderr = \"\";\n let timedOut = false;\n\n const timeoutHandle =\n options?.timeout && options.timeout > 0\n ? setTimeout(() => {\n timedOut = true;\n killProcessTree(child.pid!);\n }, options.timeout * 1000)\n : undefined;\n\n const onAbort = () => {\n if (child.pid) killProcessTree(child.pid);\n };\n\n if (options?.signal) {\n if (options.signal.aborted) {\n onAbort();\n } else {\n options.signal.addEventListener(\"abort\", onAbort, { once: true });\n }\n }\n\n child.stdout?.on(\"data\", (data) => {\n stdout += data.toString();\n if (stdout.length > 10 * 1024 * 1024) {\n stdout = stdout.slice(0, 10 * 1024 * 1024);\n }\n });\n\n child.stderr?.on(\"data\", (data) => {\n stderr += data.toString();\n if (stderr.length > 10 * 1024 * 1024) {\n stderr = stderr.slice(0, 10 * 1024 * 1024);\n }\n });\n\n child.on(\"close\", (code) => {\n if (timeoutHandle) clearTimeout(timeoutHandle);\n if (options?.signal) {\n options.signal.removeEventListener(\"abort\", onAbort);\n }\n\n if (options?.signal?.aborted) {\n reject(new Error(`${stdout}\\n${stderr}\\nCommand aborted`.trim()));\n return;\n }\n\n if (timedOut) {\n reject(\n new Error(\n `${stdout}\\n${stderr}\\nCommand timed out after ${options?.timeout} seconds`.trim(),\n ),\n );\n return;\n }\n\n resolve({ stdout, stderr, code: code ?? 0 });\n });\n });\n }\n\n getWorkspacePath(hostPath: string): string {\n return hostPath;\n }\n\n getPathContext(hostWorkspaceRoot: string): RuntimePathContext {\n return createMountedRuntimePathContext(hostWorkspaceRoot, hostWorkspaceRoot);\n }\n\n getSandboxConfig(): HostSandboxConfig {\n return { type: \"host\" };\n }\n}\n\nexport const hostSandboxAdapter: SandboxAdapter<HostSandboxConfig> = {\n type: \"host\",\n parse: parseHostSandboxArg,\n validate: validateHostSandbox,\n createExecutor: () => new HostExecutor(),\n};\n"]}
@@ -1,5 +1,3 @@
1
1
  import type { ImageSandboxConfig, SandboxAdapter } from "./types.js";
2
- export declare function parseImageSandboxArg(value: string): ImageSandboxConfig | undefined;
3
- export declare function validateImageSandbox(config: ImageSandboxConfig): Promise<void>;
4
2
  export declare const imageSandboxAdapter: SandboxAdapter<ImageSandboxConfig>;
5
3
  //# sourceMappingURL=image.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"image.d.ts","sourceRoot":"","sources":["../../src/sandbox/image.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAIrE,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,MAAM,GAAG,kBAAkB,GAAG,SAAS,CAUlF;AAED,wBAAsB,oBAAoB,CAAC,MAAM,EAAE,kBAAkB,GAAG,OAAO,CAAC,IAAI,CAAC,CAOpF;AAED,eAAO,MAAM,mBAAmB,EAAE,cAAc,CAAC,kBAAkB,CAOlE,CAAC","sourcesContent":["import type { ImageSandboxConfig, SandboxAdapter } from \"./types.js\";\nimport { SandboxError } from \"./errors.js\";\nimport { execSimple } from \"./utils.js\";\n\nexport function parseImageSandboxArg(value: string): ImageSandboxConfig | undefined {\n if (!value.startsWith(\"image:\")) {\n return undefined;\n }\n\n const image = value.slice(\"image:\".length);\n if (!image) {\n throw new SandboxError(\"Error: image sandbox requires image name (e.g., image:ubuntu:24.04)\");\n }\n return { type: \"image\", image };\n}\n\nexport async function validateImageSandbox(config: ImageSandboxConfig): Promise<void> {\n try {\n await execSimple(\"docker\", [\"--version\"]);\n } catch {\n throw new SandboxError(\"Error: Docker is not installed or not in PATH\");\n }\n console.log(` Image auto-provisioning enabled. Image: ${config.image}`);\n}\n\nexport const imageSandboxAdapter: SandboxAdapter<ImageSandboxConfig> = {\n type: \"image\",\n parse: parseImageSandboxArg,\n validate: validateImageSandbox,\n createExecutor: () => {\n throw new SandboxError(\"Error: image sandbox must resolve to a concrete container executor\");\n },\n};\n"]}
1
+ {"version":3,"file":"image.d.ts","sourceRoot":"","sources":["../../src/sandbox/image.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAyBrE,eAAO,MAAM,mBAAmB,EAAE,cAAc,CAAC,kBAAkB,CAOlE,CAAC","sourcesContent":["import type { ImageSandboxConfig, SandboxAdapter } from \"./types.js\";\nimport { SandboxError } from \"./errors.js\";\nimport { execSimple } from \"./utils.js\";\n\nfunction parseImageSandboxArg(value: string): ImageSandboxConfig | undefined {\n if (!value.startsWith(\"image:\")) {\n return undefined;\n }\n\n const image = value.slice(\"image:\".length);\n if (!image) {\n throw new SandboxError(\"Error: image sandbox requires image name (e.g., image:ubuntu:24.04)\");\n }\n return { type: \"image\", image };\n}\n\nasync function validateImageSandbox(config: ImageSandboxConfig): Promise<void> {\n try {\n await execSimple(\"docker\", [\"--version\"]);\n } catch {\n throw new SandboxError(\"Error: Docker is not installed or not in PATH\");\n }\n console.log(` Image auto-provisioning enabled. Image: ${config.image}`);\n}\n\nexport const imageSandboxAdapter: SandboxAdapter<ImageSandboxConfig> = {\n type: \"image\",\n parse: parseImageSandboxArg,\n validate: validateImageSandbox,\n createExecutor: () => {\n throw new SandboxError(\"Error: image sandbox must resolve to a concrete container executor\");\n },\n};\n"]}
@@ -1,6 +1,6 @@
1
1
  import { SandboxError } from "./errors.js";
2
2
  import { execSimple } from "./utils.js";
3
- export function parseImageSandboxArg(value) {
3
+ function parseImageSandboxArg(value) {
4
4
  if (!value.startsWith("image:")) {
5
5
  return undefined;
6
6
  }
@@ -10,7 +10,7 @@ export function parseImageSandboxArg(value) {
10
10
  }
11
11
  return { type: "image", image };
12
12
  }
13
- export async function validateImageSandbox(config) {
13
+ async function validateImageSandbox(config) {
14
14
  try {
15
15
  await execSimple("docker", ["--version"]);
16
16
  }
@@ -1 +1 @@
1
- {"version":3,"file":"image.js","sourceRoot":"","sources":["../../src/sandbox/image.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAExC,MAAM,UAAU,oBAAoB,CAAC,KAAa;IAChD,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;QAChC,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;IAC3C,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,MAAM,IAAI,YAAY,CAAC,qEAAqE,CAAC,CAAC;IAChG,CAAC;IACD,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;AAClC,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,oBAAoB,CAAC,MAA0B;IACnE,IAAI,CAAC;QACH,MAAM,UAAU,CAAC,QAAQ,EAAE,CAAC,WAAW,CAAC,CAAC,CAAC;IAC5C,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,IAAI,YAAY,CAAC,+CAA+C,CAAC,CAAC;IAC1E,CAAC;IACD,OAAO,CAAC,GAAG,CAAC,6CAA6C,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC;AAC3E,CAAC;AAED,MAAM,CAAC,MAAM,mBAAmB,GAAuC;IACrE,IAAI,EAAE,OAAO;IACb,KAAK,EAAE,oBAAoB;IAC3B,QAAQ,EAAE,oBAAoB;IAC9B,cAAc,EAAE,GAAG,EAAE;QACnB,MAAM,IAAI,YAAY,CAAC,oEAAoE,CAAC,CAAC;IAC/F,CAAC;CACF,CAAC","sourcesContent":["import type { ImageSandboxConfig, SandboxAdapter } from \"./types.js\";\nimport { SandboxError } from \"./errors.js\";\nimport { execSimple } from \"./utils.js\";\n\nexport function parseImageSandboxArg(value: string): ImageSandboxConfig | undefined {\n if (!value.startsWith(\"image:\")) {\n return undefined;\n }\n\n const image = value.slice(\"image:\".length);\n if (!image) {\n throw new SandboxError(\"Error: image sandbox requires image name (e.g., image:ubuntu:24.04)\");\n }\n return { type: \"image\", image };\n}\n\nexport async function validateImageSandbox(config: ImageSandboxConfig): Promise<void> {\n try {\n await execSimple(\"docker\", [\"--version\"]);\n } catch {\n throw new SandboxError(\"Error: Docker is not installed or not in PATH\");\n }\n console.log(` Image auto-provisioning enabled. Image: ${config.image}`);\n}\n\nexport const imageSandboxAdapter: SandboxAdapter<ImageSandboxConfig> = {\n type: \"image\",\n parse: parseImageSandboxArg,\n validate: validateImageSandbox,\n createExecutor: () => {\n throw new SandboxError(\"Error: image sandbox must resolve to a concrete container executor\");\n },\n};\n"]}
1
+ {"version":3,"file":"image.js","sourceRoot":"","sources":["../../src/sandbox/image.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAExC,SAAS,oBAAoB,CAAC,KAAa;IACzC,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;QAChC,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;IAC3C,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,MAAM,IAAI,YAAY,CAAC,qEAAqE,CAAC,CAAC;IAChG,CAAC;IACD,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;AAClC,CAAC;AAED,KAAK,UAAU,oBAAoB,CAAC,MAA0B;IAC5D,IAAI,CAAC;QACH,MAAM,UAAU,CAAC,QAAQ,EAAE,CAAC,WAAW,CAAC,CAAC,CAAC;IAC5C,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,IAAI,YAAY,CAAC,+CAA+C,CAAC,CAAC;IAC1E,CAAC;IACD,OAAO,CAAC,GAAG,CAAC,6CAA6C,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC;AAC3E,CAAC;AAED,MAAM,CAAC,MAAM,mBAAmB,GAAuC;IACrE,IAAI,EAAE,OAAO;IACb,KAAK,EAAE,oBAAoB;IAC3B,QAAQ,EAAE,oBAAoB;IAC9B,cAAc,EAAE,GAAG,EAAE;QACnB,MAAM,IAAI,YAAY,CAAC,oEAAoE,CAAC,CAAC;IAC/F,CAAC;CACF,CAAC","sourcesContent":["import type { ImageSandboxConfig, SandboxAdapter } from \"./types.js\";\nimport { SandboxError } from \"./errors.js\";\nimport { execSimple } from \"./utils.js\";\n\nfunction parseImageSandboxArg(value: string): ImageSandboxConfig | undefined {\n if (!value.startsWith(\"image:\")) {\n return undefined;\n }\n\n const image = value.slice(\"image:\".length);\n if (!image) {\n throw new SandboxError(\"Error: image sandbox requires image name (e.g., image:ubuntu:24.04)\");\n }\n return { type: \"image\", image };\n}\n\nasync function validateImageSandbox(config: ImageSandboxConfig): Promise<void> {\n try {\n await execSimple(\"docker\", [\"--version\"]);\n } catch {\n throw new SandboxError(\"Error: Docker is not installed or not in PATH\");\n }\n console.log(` Image auto-provisioning enabled. Image: ${config.image}`);\n}\n\nexport const imageSandboxAdapter: SandboxAdapter<ImageSandboxConfig> = {\n type: \"image\",\n parse: parseImageSandboxArg,\n validate: validateImageSandbox,\n createExecutor: () => {\n throw new SandboxError(\"Error: image sandbox must resolve to a concrete container executor\");\n },\n};\n"]}
@@ -3,14 +3,9 @@ import { FirecrackerExecutor } from "./firecracker.js";
3
3
  import { CloudflareSandboxExecutor } from "./cloudflare.js";
4
4
  import { HostExecutor } from "./host.js";
5
5
  import type { Executor, SandboxAdapter, SandboxConfig } from "./types.js";
6
- export type { ContainerSandboxConfig, CloudflareSandboxConfig, ExecOptions, ExecResult, Executor, FirecrackerSandboxConfig, HostSandboxConfig, ImageSandboxConfig, RuntimePathContext, SandboxAdapter, SandboxConfig, } from "./types.js";
6
+ export type { CloudflareSandboxConfig, ExecOptions, ExecResult, Executor, RuntimePathContext, SandboxAdapter, SandboxConfig, } from "./types.js";
7
7
  export { CloudflareSandboxExecutor, ContainerExecutor, FirecrackerExecutor, HostExecutor };
8
8
  export { SandboxError } from "./errors.js";
9
- export { buildContainerExecCommand, containerSandboxAdapter, parseContainerSandboxArg, validateContainerSandbox, } from "./container.js";
10
- export { firecrackerSandboxAdapter, parseFirecrackerSandboxArg, validateFirecrackerSandbox, } from "./firecracker.js";
11
- export { cloudflareSandboxAdapter, parseCloudflareSandboxArg, validateCloudflareSandbox, } from "./cloudflare.js";
12
- export { hostSandboxAdapter, parseHostSandboxArg, validateHostSandbox } from "./host.js";
13
- export { imageSandboxAdapter, parseImageSandboxArg, validateImageSandbox } from "./image.js";
14
9
  declare const sandboxAdapters: readonly [SandboxAdapter<import("./types.js").HostSandboxConfig>, SandboxAdapter<import("./types.js").ContainerSandboxConfig>, SandboxAdapter<import("./types.js").ImageSandboxConfig>, SandboxAdapter<import("./types.js").FirecrackerSandboxConfig>, SandboxAdapter<import("./types.js").CloudflareSandboxConfig>];
15
10
  export declare function getSandboxAdapters(): readonly [...typeof sandboxAdapters];
16
11
  export declare function parseSandboxArg(value: string): SandboxConfig;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/sandbox/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAA2B,MAAM,gBAAgB,CAAC;AAC5E,OAAO,EAAE,mBAAmB,EAA6B,MAAM,kBAAkB,CAAC;AAClF,OAAO,EAAE,yBAAyB,EAA4B,MAAM,iBAAiB,CAAC;AACtF,OAAO,EAAE,YAAY,EAAsB,MAAM,WAAW,CAAC;AAG7D,OAAO,KAAK,EAAE,QAAQ,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAE1E,YAAY,EACV,sBAAsB,EACtB,uBAAuB,EACvB,WAAW,EACX,UAAU,EACV,QAAQ,EACR,wBAAwB,EACxB,iBAAiB,EACjB,kBAAkB,EAClB,kBAAkB,EAClB,cAAc,EACd,aAAa,GACd,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,yBAAyB,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,YAAY,EAAE,CAAC;AAC3F,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EACL,yBAAyB,EACzB,uBAAuB,EACvB,wBAAwB,EACxB,wBAAwB,GACzB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EACL,yBAAyB,EACzB,0BAA0B,EAC1B,0BAA0B,GAC3B,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EACL,wBAAwB,EACxB,yBAAyB,EACzB,yBAAyB,GAC1B,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,MAAM,WAAW,CAAC;AACzF,OAAO,EAAE,mBAAmB,EAAE,oBAAoB,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAE7F,QAAA,MAAM,eAAe,sTAMX,CAAC;AAKX,wBAAgB,kBAAkB,IAAI,SAAS,CAAC,GAAG,OAAO,eAAe,CAAC,CAEzE;AAED,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,aAAa,CAiB5D;AAED,wBAAsB,eAAe,CAAC,MAAM,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CAO1E;AAED;;GAEG;AACH,wBAAgB,cAAc,CAC5B,MAAM,EAAE,aAAa,EACrB,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAC5B,WAAW,CAAC,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,GAChC,QAAQ,CAMV","sourcesContent":["import { ContainerExecutor, containerSandboxAdapter } from \"./container.js\";\nimport { FirecrackerExecutor, firecrackerSandboxAdapter } from \"./firecracker.js\";\nimport { CloudflareSandboxExecutor, cloudflareSandboxAdapter } from \"./cloudflare.js\";\nimport { HostExecutor, hostSandboxAdapter } from \"./host.js\";\nimport { imageSandboxAdapter } from \"./image.js\";\nimport { SandboxError } from \"./errors.js\";\nimport type { Executor, SandboxAdapter, SandboxConfig } from \"./types.js\";\n\nexport type {\n ContainerSandboxConfig,\n CloudflareSandboxConfig,\n ExecOptions,\n ExecResult,\n Executor,\n FirecrackerSandboxConfig,\n HostSandboxConfig,\n ImageSandboxConfig,\n RuntimePathContext,\n SandboxAdapter,\n SandboxConfig,\n} from \"./types.js\";\nexport { CloudflareSandboxExecutor, ContainerExecutor, FirecrackerExecutor, HostExecutor };\nexport { SandboxError } from \"./errors.js\";\nexport {\n buildContainerExecCommand,\n containerSandboxAdapter,\n parseContainerSandboxArg,\n validateContainerSandbox,\n} from \"./container.js\";\nexport {\n firecrackerSandboxAdapter,\n parseFirecrackerSandboxArg,\n validateFirecrackerSandbox,\n} from \"./firecracker.js\";\nexport {\n cloudflareSandboxAdapter,\n parseCloudflareSandboxArg,\n validateCloudflareSandbox,\n} from \"./cloudflare.js\";\nexport { hostSandboxAdapter, parseHostSandboxArg, validateHostSandbox } from \"./host.js\";\nexport { imageSandboxAdapter, parseImageSandboxArg, validateImageSandbox } from \"./image.js\";\n\nconst sandboxAdapters = [\n hostSandboxAdapter,\n containerSandboxAdapter,\n imageSandboxAdapter,\n firecrackerSandboxAdapter,\n cloudflareSandboxAdapter,\n] as const;\nconst sandboxAdapterByType = new Map(\n sandboxAdapters.map((adapter) => [adapter.type, adapter]),\n) as Map<SandboxConfig[\"type\"], SandboxAdapter>;\n\nexport function getSandboxAdapters(): readonly [...typeof sandboxAdapters] {\n return sandboxAdapters;\n}\n\nexport function parseSandboxArg(value: string): SandboxConfig {\n for (const adapter of sandboxAdapters) {\n const config = adapter.parse(value);\n if (config) {\n return config;\n }\n }\n\n if (value.startsWith(\"docker:\")) {\n throw new SandboxError(\n `Error: '${value}' is not supported. Use 'container:<container-name>' for the shared-container mode or 'image:<image-name>' for mikan-managed per-user containers.`,\n );\n }\n\n throw new SandboxError(\n `Error: Invalid sandbox type '${value}'. Use 'host', 'container:<container-name>', 'image:<image-name>', 'firecracker:<vm-id>:<host-path>', or 'cloudflare:<sandbox-id>'`,\n );\n}\n\nexport async function validateSandbox(config: SandboxConfig): Promise<void> {\n const adapter = sandboxAdapterByType.get(config.type);\n if (!adapter) {\n throw new SandboxError(`Error: Unsupported sandbox type '${config.type}'`);\n }\n\n await adapter.validate(config);\n}\n\n/**\n * Create an executor that runs commands on host, in Docker, in a Firecracker VM, or through a Cloudflare sandbox bridge.\n */\nexport function createExecutor(\n config: SandboxConfig,\n env?: Record<string, string>,\n ensureReady?: () => Promise<void>,\n): Executor {\n const adapter = sandboxAdapterByType.get(config.type);\n if (!adapter) {\n throw new SandboxError(`Error: Unsupported sandbox type '${config.type}'`);\n }\n return adapter.createExecutor(config, env, ensureReady);\n}\n"]}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/sandbox/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAA2B,MAAM,gBAAgB,CAAC;AAC5E,OAAO,EAAE,mBAAmB,EAA6B,MAAM,kBAAkB,CAAC;AAClF,OAAO,EAAE,yBAAyB,EAA4B,MAAM,iBAAiB,CAAC;AACtF,OAAO,EAAE,YAAY,EAAsB,MAAM,WAAW,CAAC;AAG7D,OAAO,KAAK,EAAE,QAAQ,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAE1E,YAAY,EACV,uBAAuB,EACvB,WAAW,EACX,UAAU,EACV,QAAQ,EACR,kBAAkB,EAClB,cAAc,EACd,aAAa,GACd,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,yBAAyB,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,YAAY,EAAE,CAAC;AAC3F,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE3C,QAAA,MAAM,eAAe,sTAMX,CAAC;AAKX,wBAAgB,kBAAkB,IAAI,SAAS,CAAC,GAAG,OAAO,eAAe,CAAC,CAEzE;AAED,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,aAAa,CAiB5D;AAED,wBAAsB,eAAe,CAAC,MAAM,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CAO1E;AAED;;GAEG;AACH,wBAAgB,cAAc,CAC5B,MAAM,EAAE,aAAa,EACrB,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAC5B,WAAW,CAAC,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,GAChC,QAAQ,CAMV","sourcesContent":["import { ContainerExecutor, containerSandboxAdapter } from \"./container.js\";\nimport { FirecrackerExecutor, firecrackerSandboxAdapter } from \"./firecracker.js\";\nimport { CloudflareSandboxExecutor, cloudflareSandboxAdapter } from \"./cloudflare.js\";\nimport { HostExecutor, hostSandboxAdapter } from \"./host.js\";\nimport { imageSandboxAdapter } from \"./image.js\";\nimport { SandboxError } from \"./errors.js\";\nimport type { Executor, SandboxAdapter, SandboxConfig } from \"./types.js\";\n\nexport type {\n CloudflareSandboxConfig,\n ExecOptions,\n ExecResult,\n Executor,\n RuntimePathContext,\n SandboxAdapter,\n SandboxConfig,\n} from \"./types.js\";\nexport { CloudflareSandboxExecutor, ContainerExecutor, FirecrackerExecutor, HostExecutor };\nexport { SandboxError } from \"./errors.js\";\n\nconst sandboxAdapters = [\n hostSandboxAdapter,\n containerSandboxAdapter,\n imageSandboxAdapter,\n firecrackerSandboxAdapter,\n cloudflareSandboxAdapter,\n] as const;\nconst sandboxAdapterByType = new Map(\n sandboxAdapters.map((adapter) => [adapter.type, adapter]),\n) as Map<SandboxConfig[\"type\"], SandboxAdapter>;\n\nexport function getSandboxAdapters(): readonly [...typeof sandboxAdapters] {\n return sandboxAdapters;\n}\n\nexport function parseSandboxArg(value: string): SandboxConfig {\n for (const adapter of sandboxAdapters) {\n const config = adapter.parse(value);\n if (config) {\n return config;\n }\n }\n\n if (value.startsWith(\"docker:\")) {\n throw new SandboxError(\n `Error: '${value}' is not supported. Use 'container:<container-name>' for the shared-container mode or 'image:<image-name>' for mikan-managed per-user containers.`,\n );\n }\n\n throw new SandboxError(\n `Error: Invalid sandbox type '${value}'. Use 'host', 'container:<container-name>', 'image:<image-name>', 'firecracker:<vm-id>:<host-path>', or 'cloudflare:<sandbox-id>'`,\n );\n}\n\nexport async function validateSandbox(config: SandboxConfig): Promise<void> {\n const adapter = sandboxAdapterByType.get(config.type);\n if (!adapter) {\n throw new SandboxError(`Error: Unsupported sandbox type '${config.type}'`);\n }\n\n await adapter.validate(config);\n}\n\n/**\n * Create an executor that runs commands on host, in Docker, in a Firecracker VM, or through a Cloudflare sandbox bridge.\n */\nexport function createExecutor(\n config: SandboxConfig,\n env?: Record<string, string>,\n ensureReady?: () => Promise<void>,\n): Executor {\n const adapter = sandboxAdapterByType.get(config.type);\n if (!adapter) {\n throw new SandboxError(`Error: Unsupported sandbox type '${config.type}'`);\n }\n return adapter.createExecutor(config, env, ensureReady);\n}\n"]}
@@ -6,11 +6,6 @@ import { imageSandboxAdapter } from "./image.js";
6
6
  import { SandboxError } from "./errors.js";
7
7
  export { CloudflareSandboxExecutor, ContainerExecutor, FirecrackerExecutor, HostExecutor };
8
8
  export { SandboxError } from "./errors.js";
9
- export { buildContainerExecCommand, containerSandboxAdapter, parseContainerSandboxArg, validateContainerSandbox, } from "./container.js";
10
- export { firecrackerSandboxAdapter, parseFirecrackerSandboxArg, validateFirecrackerSandbox, } from "./firecracker.js";
11
- export { cloudflareSandboxAdapter, parseCloudflareSandboxArg, validateCloudflareSandbox, } from "./cloudflare.js";
12
- export { hostSandboxAdapter, parseHostSandboxArg, validateHostSandbox } from "./host.js";
13
- export { imageSandboxAdapter, parseImageSandboxArg, validateImageSandbox } from "./image.js";
14
9
  const sandboxAdapters = [
15
10
  hostSandboxAdapter,
16
11
  containerSandboxAdapter,
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/sandbox/index.ts"],"names":[],"mappings":"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","sourcesContent":["import { ContainerExecutor, containerSandboxAdapter } from \"./container.js\";\nimport { FirecrackerExecutor, firecrackerSandboxAdapter } from \"./firecracker.js\";\nimport { CloudflareSandboxExecutor, cloudflareSandboxAdapter } from \"./cloudflare.js\";\nimport { HostExecutor, hostSandboxAdapter } from \"./host.js\";\nimport { imageSandboxAdapter } from \"./image.js\";\nimport { SandboxError } from \"./errors.js\";\nimport type { Executor, SandboxAdapter, SandboxConfig } from \"./types.js\";\n\nexport type {\n ContainerSandboxConfig,\n CloudflareSandboxConfig,\n ExecOptions,\n ExecResult,\n Executor,\n FirecrackerSandboxConfig,\n HostSandboxConfig,\n ImageSandboxConfig,\n RuntimePathContext,\n SandboxAdapter,\n SandboxConfig,\n} from \"./types.js\";\nexport { CloudflareSandboxExecutor, ContainerExecutor, FirecrackerExecutor, HostExecutor };\nexport { SandboxError } from \"./errors.js\";\nexport {\n buildContainerExecCommand,\n containerSandboxAdapter,\n parseContainerSandboxArg,\n validateContainerSandbox,\n} from \"./container.js\";\nexport {\n firecrackerSandboxAdapter,\n parseFirecrackerSandboxArg,\n validateFirecrackerSandbox,\n} from \"./firecracker.js\";\nexport {\n cloudflareSandboxAdapter,\n parseCloudflareSandboxArg,\n validateCloudflareSandbox,\n} from \"./cloudflare.js\";\nexport { hostSandboxAdapter, parseHostSandboxArg, validateHostSandbox } from \"./host.js\";\nexport { imageSandboxAdapter, parseImageSandboxArg, validateImageSandbox } from \"./image.js\";\n\nconst sandboxAdapters = [\n hostSandboxAdapter,\n containerSandboxAdapter,\n imageSandboxAdapter,\n firecrackerSandboxAdapter,\n cloudflareSandboxAdapter,\n] as const;\nconst sandboxAdapterByType = new Map(\n sandboxAdapters.map((adapter) => [adapter.type, adapter]),\n) as Map<SandboxConfig[\"type\"], SandboxAdapter>;\n\nexport function getSandboxAdapters(): readonly [...typeof sandboxAdapters] {\n return sandboxAdapters;\n}\n\nexport function parseSandboxArg(value: string): SandboxConfig {\n for (const adapter of sandboxAdapters) {\n const config = adapter.parse(value);\n if (config) {\n return config;\n }\n }\n\n if (value.startsWith(\"docker:\")) {\n throw new SandboxError(\n `Error: '${value}' is not supported. Use 'container:<container-name>' for the shared-container mode or 'image:<image-name>' for mikan-managed per-user containers.`,\n );\n }\n\n throw new SandboxError(\n `Error: Invalid sandbox type '${value}'. Use 'host', 'container:<container-name>', 'image:<image-name>', 'firecracker:<vm-id>:<host-path>', or 'cloudflare:<sandbox-id>'`,\n );\n}\n\nexport async function validateSandbox(config: SandboxConfig): Promise<void> {\n const adapter = sandboxAdapterByType.get(config.type);\n if (!adapter) {\n throw new SandboxError(`Error: Unsupported sandbox type '${config.type}'`);\n }\n\n await adapter.validate(config);\n}\n\n/**\n * Create an executor that runs commands on host, in Docker, in a Firecracker VM, or through a Cloudflare sandbox bridge.\n */\nexport function createExecutor(\n config: SandboxConfig,\n env?: Record<string, string>,\n ensureReady?: () => Promise<void>,\n): Executor {\n const adapter = sandboxAdapterByType.get(config.type);\n if (!adapter) {\n throw new SandboxError(`Error: Unsupported sandbox type '${config.type}'`);\n }\n return adapter.createExecutor(config, env, ensureReady);\n}\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/sandbox/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,uBAAuB,EAAE,MAAM,gBAAgB,CAAC;AAC5E,OAAO,EAAE,mBAAmB,EAAE,yBAAyB,EAAE,MAAM,kBAAkB,CAAC;AAClF,OAAO,EAAE,yBAAyB,EAAE,wBAAwB,EAAE,MAAM,iBAAiB,CAAC;AACtF,OAAO,EAAE,YAAY,EAAE,kBAAkB,EAAE,MAAM,WAAW,CAAC;AAC7D,OAAO,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AACjD,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAY3C,OAAO,EAAE,yBAAyB,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,YAAY,EAAE,CAAC;AAC3F,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE3C,MAAM,eAAe,GAAG;IACtB,kBAAkB;IAClB,uBAAuB;IACvB,mBAAmB;IACnB,yBAAyB;IACzB,wBAAwB;CAChB,CAAC;AACX,MAAM,oBAAoB,GAAG,IAAI,GAAG,CAClC,eAAe,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,CACZ,CAAC;AAEhD,MAAM,UAAU,kBAAkB;IAChC,OAAO,eAAe,CAAC;AACzB,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,KAAa;IAC3C,KAAK,MAAM,OAAO,IAAI,eAAe,EAAE,CAAC;QACtC,MAAM,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;QACpC,IAAI,MAAM,EAAE,CAAC;YACX,OAAO,MAAM,CAAC;QAChB,CAAC;IACH,CAAC;IAED,IAAI,KAAK,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;QAChC,MAAM,IAAI,YAAY,CACpB,WAAW,KAAK,mJAAmJ,CACpK,CAAC;IACJ,CAAC;IAED,MAAM,IAAI,YAAY,CACpB,gCAAgC,KAAK,oIAAoI,CAC1K,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,MAAqB;IACzD,MAAM,OAAO,GAAG,oBAAoB,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IACtD,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,MAAM,IAAI,YAAY,CAAC,oCAAoC,MAAM,CAAC,IAAI,GAAG,CAAC,CAAC;IAC7E,CAAC;IAED,MAAM,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;AACjC,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,cAAc,CAC5B,MAAqB,EACrB,GAA4B,EAC5B,WAAiC;IAEjC,MAAM,OAAO,GAAG,oBAAoB,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IACtD,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,MAAM,IAAI,YAAY,CAAC,oCAAoC,MAAM,CAAC,IAAI,GAAG,CAAC,CAAC;IAC7E,CAAC;IACD,OAAO,OAAO,CAAC,cAAc,CAAC,MAAM,EAAE,GAAG,EAAE,WAAW,CAAC,CAAC;AAC1D,CAAC","sourcesContent":["import { ContainerExecutor, containerSandboxAdapter } from \"./container.js\";\nimport { FirecrackerExecutor, firecrackerSandboxAdapter } from \"./firecracker.js\";\nimport { CloudflareSandboxExecutor, cloudflareSandboxAdapter } from \"./cloudflare.js\";\nimport { HostExecutor, hostSandboxAdapter } from \"./host.js\";\nimport { imageSandboxAdapter } from \"./image.js\";\nimport { SandboxError } from \"./errors.js\";\nimport type { Executor, SandboxAdapter, SandboxConfig } from \"./types.js\";\n\nexport type {\n CloudflareSandboxConfig,\n ExecOptions,\n ExecResult,\n Executor,\n RuntimePathContext,\n SandboxAdapter,\n SandboxConfig,\n} from \"./types.js\";\nexport { CloudflareSandboxExecutor, ContainerExecutor, FirecrackerExecutor, HostExecutor };\nexport { SandboxError } from \"./errors.js\";\n\nconst sandboxAdapters = [\n hostSandboxAdapter,\n containerSandboxAdapter,\n imageSandboxAdapter,\n firecrackerSandboxAdapter,\n cloudflareSandboxAdapter,\n] as const;\nconst sandboxAdapterByType = new Map(\n sandboxAdapters.map((adapter) => [adapter.type, adapter]),\n) as Map<SandboxConfig[\"type\"], SandboxAdapter>;\n\nexport function getSandboxAdapters(): readonly [...typeof sandboxAdapters] {\n return sandboxAdapters;\n}\n\nexport function parseSandboxArg(value: string): SandboxConfig {\n for (const adapter of sandboxAdapters) {\n const config = adapter.parse(value);\n if (config) {\n return config;\n }\n }\n\n if (value.startsWith(\"docker:\")) {\n throw new SandboxError(\n `Error: '${value}' is not supported. Use 'container:<container-name>' for the shared-container mode or 'image:<image-name>' for mikan-managed per-user containers.`,\n );\n }\n\n throw new SandboxError(\n `Error: Invalid sandbox type '${value}'. Use 'host', 'container:<container-name>', 'image:<image-name>', 'firecracker:<vm-id>:<host-path>', or 'cloudflare:<sandbox-id>'`,\n );\n}\n\nexport async function validateSandbox(config: SandboxConfig): Promise<void> {\n const adapter = sandboxAdapterByType.get(config.type);\n if (!adapter) {\n throw new SandboxError(`Error: Unsupported sandbox type '${config.type}'`);\n }\n\n await adapter.validate(config);\n}\n\n/**\n * Create an executor that runs commands on host, in Docker, in a Firecracker VM, or through a Cloudflare sandbox bridge.\n */\nexport function createExecutor(\n config: SandboxConfig,\n env?: Record<string, string>,\n ensureReady?: () => Promise<void>,\n): Executor {\n const adapter = sandboxAdapterByType.get(config.type);\n if (!adapter) {\n throw new SandboxError(`Error: Unsupported sandbox type '${config.type}'`);\n }\n return adapter.createExecutor(config, env, ensureReady);\n}\n"]}
@@ -1,4 +1,3 @@
1
1
  import type { RuntimePathContext } from "./types.js";
2
2
  export declare function createMountedRuntimePathContext(hostWorkspaceRoot: string, runtimeWorkspaceRoot: string): RuntimePathContext;
3
- export declare function translateMountedRuntimePathToHost(runtimePath: string, runtimeWorkspaceRoot: string, hostWorkspaceRoot: string): string;
4
3
  //# sourceMappingURL=path-context.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"path-context.d.ts","sourceRoot":"","sources":["../../src/sandbox/path-context.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAErD,wBAAgB,+BAA+B,CAC7C,iBAAiB,EAAE,MAAM,EACzB,oBAAoB,EAAE,MAAM,GAC3B,kBAAkB,CAOpB;AAED,wBAAgB,iCAAiC,CAC/C,WAAW,EAAE,MAAM,EACnB,oBAAoB,EAAE,MAAM,EAC5B,iBAAiB,EAAE,MAAM,GACxB,MAAM,CAYR","sourcesContent":["import { join } from \"node:path\";\nimport type { RuntimePathContext } from \"./types.js\";\n\nexport function createMountedRuntimePathContext(\n hostWorkspaceRoot: string,\n runtimeWorkspaceRoot: string,\n): RuntimePathContext {\n return {\n hostWorkspaceRoot,\n runtimeWorkspaceRoot,\n runtimeToHostPath: (runtimePath) =>\n translateMountedRuntimePathToHost(runtimePath, runtimeWorkspaceRoot, hostWorkspaceRoot),\n };\n}\n\nexport function translateMountedRuntimePathToHost(\n runtimePath: string,\n runtimeWorkspaceRoot: string,\n hostWorkspaceRoot: string,\n): string {\n const runtimeRoot = runtimeWorkspaceRoot.replace(/\\/+$/, \"\");\n if (runtimePath === runtimeRoot) {\n return hostWorkspaceRoot;\n }\n\n const workspacePrefix = `${runtimeRoot}/`;\n if (runtimePath.startsWith(workspacePrefix)) {\n return join(hostWorkspaceRoot, runtimePath.slice(workspacePrefix.length));\n }\n\n return runtimePath;\n}\n"]}
1
+ {"version":3,"file":"path-context.d.ts","sourceRoot":"","sources":["../../src/sandbox/path-context.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAErD,wBAAgB,+BAA+B,CAC7C,iBAAiB,EAAE,MAAM,EACzB,oBAAoB,EAAE,MAAM,GAC3B,kBAAkB,CAOpB","sourcesContent":["import { join } from \"node:path\";\nimport type { RuntimePathContext } from \"./types.js\";\n\nexport function createMountedRuntimePathContext(\n hostWorkspaceRoot: string,\n runtimeWorkspaceRoot: string,\n): RuntimePathContext {\n return {\n hostWorkspaceRoot,\n runtimeWorkspaceRoot,\n runtimeToHostPath: (runtimePath) =>\n translateMountedRuntimePathToHost(runtimePath, runtimeWorkspaceRoot, hostWorkspaceRoot),\n };\n}\n\nfunction translateMountedRuntimePathToHost(\n runtimePath: string,\n runtimeWorkspaceRoot: string,\n hostWorkspaceRoot: string,\n): string {\n const runtimeRoot = runtimeWorkspaceRoot.replace(/\\/+$/, \"\");\n if (runtimePath === runtimeRoot) {\n return hostWorkspaceRoot;\n }\n\n const workspacePrefix = `${runtimeRoot}/`;\n if (runtimePath.startsWith(workspacePrefix)) {\n return join(hostWorkspaceRoot, runtimePath.slice(workspacePrefix.length));\n }\n\n return runtimePath;\n}\n"]}
@@ -6,7 +6,7 @@ export function createMountedRuntimePathContext(hostWorkspaceRoot, runtimeWorksp
6
6
  runtimeToHostPath: (runtimePath) => translateMountedRuntimePathToHost(runtimePath, runtimeWorkspaceRoot, hostWorkspaceRoot),
7
7
  };
8
8
  }
9
- export function translateMountedRuntimePathToHost(runtimePath, runtimeWorkspaceRoot, hostWorkspaceRoot) {
9
+ function translateMountedRuntimePathToHost(runtimePath, runtimeWorkspaceRoot, hostWorkspaceRoot) {
10
10
  const runtimeRoot = runtimeWorkspaceRoot.replace(/\/+$/, "");
11
11
  if (runtimePath === runtimeRoot) {
12
12
  return hostWorkspaceRoot;
@@ -1 +1 @@
1
- {"version":3,"file":"path-context.js","sourceRoot":"","sources":["../../src/sandbox/path-context.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAGjC,MAAM,UAAU,+BAA+B,CAC7C,iBAAyB,EACzB,oBAA4B;IAE5B,OAAO;QACL,iBAAiB;QACjB,oBAAoB;QACpB,iBAAiB,EAAE,CAAC,WAAW,EAAE,EAAE,CACjC,iCAAiC,CAAC,WAAW,EAAE,oBAAoB,EAAE,iBAAiB,CAAC;KAC1F,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,iCAAiC,CAC/C,WAAmB,EACnB,oBAA4B,EAC5B,iBAAyB;IAEzB,MAAM,WAAW,GAAG,oBAAoB,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;IAC7D,IAAI,WAAW,KAAK,WAAW,EAAE,CAAC;QAChC,OAAO,iBAAiB,CAAC;IAC3B,CAAC;IAED,MAAM,eAAe,GAAG,GAAG,WAAW,GAAG,CAAC;IAC1C,IAAI,WAAW,CAAC,UAAU,CAAC,eAAe,CAAC,EAAE,CAAC;QAC5C,OAAO,IAAI,CAAC,iBAAiB,EAAE,WAAW,CAAC,KAAK,CAAC,eAAe,CAAC,MAAM,CAAC,CAAC,CAAC;IAC5E,CAAC;IAED,OAAO,WAAW,CAAC;AACrB,CAAC","sourcesContent":["import { join } from \"node:path\";\nimport type { RuntimePathContext } from \"./types.js\";\n\nexport function createMountedRuntimePathContext(\n hostWorkspaceRoot: string,\n runtimeWorkspaceRoot: string,\n): RuntimePathContext {\n return {\n hostWorkspaceRoot,\n runtimeWorkspaceRoot,\n runtimeToHostPath: (runtimePath) =>\n translateMountedRuntimePathToHost(runtimePath, runtimeWorkspaceRoot, hostWorkspaceRoot),\n };\n}\n\nexport function translateMountedRuntimePathToHost(\n runtimePath: string,\n runtimeWorkspaceRoot: string,\n hostWorkspaceRoot: string,\n): string {\n const runtimeRoot = runtimeWorkspaceRoot.replace(/\\/+$/, \"\");\n if (runtimePath === runtimeRoot) {\n return hostWorkspaceRoot;\n }\n\n const workspacePrefix = `${runtimeRoot}/`;\n if (runtimePath.startsWith(workspacePrefix)) {\n return join(hostWorkspaceRoot, runtimePath.slice(workspacePrefix.length));\n }\n\n return runtimePath;\n}\n"]}
1
+ {"version":3,"file":"path-context.js","sourceRoot":"","sources":["../../src/sandbox/path-context.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAGjC,MAAM,UAAU,+BAA+B,CAC7C,iBAAyB,EACzB,oBAA4B;IAE5B,OAAO;QACL,iBAAiB;QACjB,oBAAoB;QACpB,iBAAiB,EAAE,CAAC,WAAW,EAAE,EAAE,CACjC,iCAAiC,CAAC,WAAW,EAAE,oBAAoB,EAAE,iBAAiB,CAAC;KAC1F,CAAC;AACJ,CAAC;AAED,SAAS,iCAAiC,CACxC,WAAmB,EACnB,oBAA4B,EAC5B,iBAAyB;IAEzB,MAAM,WAAW,GAAG,oBAAoB,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;IAC7D,IAAI,WAAW,KAAK,WAAW,EAAE,CAAC;QAChC,OAAO,iBAAiB,CAAC;IAC3B,CAAC;IAED,MAAM,eAAe,GAAG,GAAG,WAAW,GAAG,CAAC;IAC1C,IAAI,WAAW,CAAC,UAAU,CAAC,eAAe,CAAC,EAAE,CAAC;QAC5C,OAAO,IAAI,CAAC,iBAAiB,EAAE,WAAW,CAAC,KAAK,CAAC,eAAe,CAAC,MAAM,CAAC,CAAC,CAAC;IAC5E,CAAC;IAED,OAAO,WAAW,CAAC;AACrB,CAAC","sourcesContent":["import { join } from \"node:path\";\nimport type { RuntimePathContext } from \"./types.js\";\n\nexport function createMountedRuntimePathContext(\n hostWorkspaceRoot: string,\n runtimeWorkspaceRoot: string,\n): RuntimePathContext {\n return {\n hostWorkspaceRoot,\n runtimeWorkspaceRoot,\n runtimeToHostPath: (runtimePath) =>\n translateMountedRuntimePathToHost(runtimePath, runtimeWorkspaceRoot, hostWorkspaceRoot),\n };\n}\n\nfunction translateMountedRuntimePathToHost(\n runtimePath: string,\n runtimeWorkspaceRoot: string,\n hostWorkspaceRoot: string,\n): string {\n const runtimeRoot = runtimeWorkspaceRoot.replace(/\\/+$/, \"\");\n if (runtimePath === runtimeRoot) {\n return hostWorkspaceRoot;\n }\n\n const workspacePrefix = `${runtimeRoot}/`;\n if (runtimePath.startsWith(workspacePrefix)) {\n return join(hostWorkspaceRoot, runtimePath.slice(workspacePrefix.length));\n }\n\n return runtimePath;\n}\n"]}
package/dist/sentry.d.ts CHANGED
@@ -11,8 +11,8 @@ export interface SentryRunScopeContext {
11
11
  provider?: string;
12
12
  model?: string;
13
13
  }
14
- export type UserFacingErrorDomain = "llm" | "chat_platform" | "mikan" | "sandbox" | "login" | "events" | "session_view";
15
- export type UserFacingErrorSeverity = "warning" | "error" | "fatal";
14
+ type UserFacingErrorDomain = "llm" | "chat_platform" | "mikan" | "sandbox" | "login" | "events" | "session_view";
15
+ type UserFacingErrorSeverity = "warning" | "error" | "fatal";
16
16
  export interface ReportUserFacingErrorOptions {
17
17
  domain: UserFacingErrorDomain;
18
18
  surface: string;