@agimon-ai/po-sandbox 0.0.0-alpha.2 → 0.0.0-alpha.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -122,7 +122,7 @@ through. Turn the whole mechanism off with `PO_SANDBOX_BROKER=0`.
122
122
  ## In-session command
123
123
 
124
124
  ```text
125
- /doom-sandbox
125
+ /po-sandbox
126
126
  ```
127
127
 
128
128
  Reports whether the current session runs inside the sandbox container or directly on the host.
@@ -1 +1 @@
1
- {"version":3,"file":"mcp.cjs","names":["defineMcpSkill","readPackageResource","defineMcpPlugin","skillPoUseSandbox"],"sources":["../../src/extensions/workspaces/sessions/(backend)/skill/po-use-sandbox.mcp.ts","../../generated/mcp.ts"],"sourcesContent":["import { defineMcpSkill } from '@agimon-ai/po-core/mcpFacet';\nimport { readPackageResource } from '@agimon-ai/po-core/serverFacet';\n\nexport default defineMcpSkill({\n name: 'po-use-sandbox',\n description:\n 'Use @agimon-ai/po-sandbox: Container sandbox for Po launches: the agent, extensions, and tools run inside Docker or Podman while the terminal stays on the host',\n read: () => readPackageResource(import.meta.url, 'src/prompts/po-use-sandbox/SKILL.md'),\n});\n","// Generated by @agimon-ai/po-build. Do not edit by hand.\nimport { defineMcpPlugin, type DoomMcpSessionPlugin } from '@agimon-ai/po-core/mcpFacet';\n\nimport skillPoUseSandbox from '../src/extensions/workspaces/sessions/(backend)/skill/po-use-sandbox.mcp';\n\ntype Factory<T, C> = (context: C) => T;\nconst at = <T, C>(value: T | Factory<T, C>, context: C): T =>\n typeof value === 'function' ? (value as Factory<T, C>)(context) : value;\n\nexport const mcp = defineMcpPlugin({\n name: '@agimon-ai/po-sandbox',\n session: (context) => ({\n get skills(): DoomMcpSessionPlugin['skills'] { return [at(skillPoUseSandbox, context)]; },\n }) satisfies DoomMcpSessionPlugin,\n});\n\nexport default mcp;\n"],"mappings":";;;;;;;AAGA,IAAA,8BAAA,GAAeA,4BAAAA,eAAAA,CAAe;CAC5B,MAAM;CACN,aACE;CACF,aAAA,GAAYC,+BAAAA,oBAAAA,CAAAA,QAAAA,KAAAA,CAAAA,CAAAA,cAAAA,UAAAA,CAAAA,CAAAA,MAAqC,qCAAqC;AACxF,CAAC;;;ACFD,MAAM,MAAY,OAA0B,YAC1C,OAAO,UAAU,aAAc,MAAwB,OAAO,IAAI;AAEpE,MAAa,OAAA,GAAMC,4BAAAA,gBAAAA,CAAgB;CACjC,MAAM;CACN,UAAU,aAAa,EACrB,IAAI,SAAyC;EAAE,OAAO,CAAC,GAAGC,4BAAmB,OAAO,CAAC;CAAG,EAC1F;AACF,CAAC"}
1
+ {"version":3,"file":"mcp.cjs","names":["defineMcpSkill","readPackageResource","defineMcpPlugin","skillPoUseSandbox"],"sources":["../../src/extensions/workspaces/sessions/(backend)/skill/po-use-sandbox.mcp.ts","../../generated/mcp.ts"],"sourcesContent":["import { defineMcpSkill } from '@agimon-ai/po-core/mcpFacet';\nimport { readPackageResource } from '@agimon-ai/po-core/serverFacet';\n\nexport default defineMcpSkill({\n name: 'po-use-sandbox',\n description:\n 'Use @agimon-ai/po-sandbox: Container sandbox for Po launches: the agent, extensions, and tools run inside Docker or Podman while the terminal stays on the host',\n read: () => readPackageResource(import.meta.url, 'src/prompts/po-use-sandbox/SKILL.md'),\n});\n","// Generated by @agimon-ai/po-build. Do not edit by hand.\nimport { defineMcpPlugin, type PoMcpSessionPlugin } from '@agimon-ai/po-core/mcpFacet';\n\nimport skillPoUseSandbox from '../src/extensions/workspaces/sessions/(backend)/skill/po-use-sandbox.mcp';\n\ntype Factory<T, C> = (context: C) => T;\nconst at = <T, C>(value: T | Factory<T, C>, context: C): T =>\n typeof value === 'function' ? (value as Factory<T, C>)(context) : value;\n\nexport const mcp = defineMcpPlugin({\n name: '@agimon-ai/po-sandbox',\n session: (context) => ({\n get skills(): PoMcpSessionPlugin['skills'] { return [at(skillPoUseSandbox, context)]; },\n }) satisfies PoMcpSessionPlugin,\n});\n\nexport default mcp;\n"],"mappings":";;;;;;;AAGA,IAAA,8BAAA,GAAeA,4BAAAA,eAAAA,CAAe;CAC5B,MAAM;CACN,aACE;CACF,aAAA,GAAYC,+BAAAA,oBAAAA,CAAAA,QAAAA,KAAAA,CAAAA,CAAAA,cAAAA,UAAAA,CAAAA,CAAAA,MAAqC,qCAAqC;AACxF,CAAC;;;ACFD,MAAM,MAAY,OAA0B,YAC1C,OAAO,UAAU,aAAc,MAAwB,OAAO,IAAI;AAEpE,MAAa,OAAA,GAAMC,4BAAAA,gBAAAA,CAAgB;CACjC,MAAM;CACN,UAAU,aAAa,EACrB,IAAI,SAAuC;EAAE,OAAO,CAAC,GAAGC,4BAAmB,OAAO,CAAC;CAAG,EACxF;AACF,CAAC"}
@@ -1,5 +1,5 @@
1
1
  //#region generated/mcp.d.ts
2
- export declare const mcp: import("@agimon-ai/po-core/mcpFacet").DoomMcpPluginDefinition;
2
+ export declare const mcp: import("@agimon-ai/po-core/mcpFacet").PoMcpPluginDefinition;
3
3
  //#endregion
4
4
  export { mcp as default };
5
5
  //# sourceMappingURL=mcp.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"mcp.mjs","names":["skillPoUseSandbox"],"sources":["../../src/extensions/workspaces/sessions/(backend)/skill/po-use-sandbox.mcp.ts","../../generated/mcp.ts"],"sourcesContent":["import { defineMcpSkill } from '@agimon-ai/po-core/mcpFacet';\nimport { readPackageResource } from '@agimon-ai/po-core/serverFacet';\n\nexport default defineMcpSkill({\n name: 'po-use-sandbox',\n description:\n 'Use @agimon-ai/po-sandbox: Container sandbox for Po launches: the agent, extensions, and tools run inside Docker or Podman while the terminal stays on the host',\n read: () => readPackageResource(import.meta.url, 'src/prompts/po-use-sandbox/SKILL.md'),\n});\n","// Generated by @agimon-ai/po-build. Do not edit by hand.\nimport { defineMcpPlugin, type DoomMcpSessionPlugin } from '@agimon-ai/po-core/mcpFacet';\n\nimport skillPoUseSandbox from '../src/extensions/workspaces/sessions/(backend)/skill/po-use-sandbox.mcp';\n\ntype Factory<T, C> = (context: C) => T;\nconst at = <T, C>(value: T | Factory<T, C>, context: C): T =>\n typeof value === 'function' ? (value as Factory<T, C>)(context) : value;\n\nexport const mcp = defineMcpPlugin({\n name: '@agimon-ai/po-sandbox',\n session: (context) => ({\n get skills(): DoomMcpSessionPlugin['skills'] { return [at(skillPoUseSandbox, context)]; },\n }) satisfies DoomMcpSessionPlugin,\n});\n\nexport default mcp;\n"],"mappings":";;;AAGA,IAAA,6BAAe,eAAe;CAC5B,MAAM;CACN,aACE;CACF,YAAY,oBAAoB,YAAY,KAAK,qCAAqC;AACxF,CAAC;;;ACFD,MAAM,MAAY,OAA0B,YAC1C,OAAO,UAAU,aAAc,MAAwB,OAAO,IAAI;AAEpE,MAAa,MAAM,gBAAgB;CACjC,MAAM;CACN,UAAU,aAAa,EACrB,IAAI,SAAyC;EAAE,OAAO,CAAC,GAAGA,4BAAmB,OAAO,CAAC;CAAG,EAC1F;AACF,CAAC"}
1
+ {"version":3,"file":"mcp.mjs","names":["skillPoUseSandbox"],"sources":["../../src/extensions/workspaces/sessions/(backend)/skill/po-use-sandbox.mcp.ts","../../generated/mcp.ts"],"sourcesContent":["import { defineMcpSkill } from '@agimon-ai/po-core/mcpFacet';\nimport { readPackageResource } from '@agimon-ai/po-core/serverFacet';\n\nexport default defineMcpSkill({\n name: 'po-use-sandbox',\n description:\n 'Use @agimon-ai/po-sandbox: Container sandbox for Po launches: the agent, extensions, and tools run inside Docker or Podman while the terminal stays on the host',\n read: () => readPackageResource(import.meta.url, 'src/prompts/po-use-sandbox/SKILL.md'),\n});\n","// Generated by @agimon-ai/po-build. Do not edit by hand.\nimport { defineMcpPlugin, type PoMcpSessionPlugin } from '@agimon-ai/po-core/mcpFacet';\n\nimport skillPoUseSandbox from '../src/extensions/workspaces/sessions/(backend)/skill/po-use-sandbox.mcp';\n\ntype Factory<T, C> = (context: C) => T;\nconst at = <T, C>(value: T | Factory<T, C>, context: C): T =>\n typeof value === 'function' ? (value as Factory<T, C>)(context) : value;\n\nexport const mcp = defineMcpPlugin({\n name: '@agimon-ai/po-sandbox',\n session: (context) => ({\n get skills(): PoMcpSessionPlugin['skills'] { return [at(skillPoUseSandbox, context)]; },\n }) satisfies PoMcpSessionPlugin,\n});\n\nexport default mcp;\n"],"mappings":";;;AAGA,IAAA,6BAAe,eAAe;CAC5B,MAAM;CACN,aACE;CACF,YAAY,oBAAoB,YAAY,KAAK,qCAAqC;AACxF,CAAC;;;ACFD,MAAM,MAAY,OAA0B,YAC1C,OAAO,UAAU,aAAc,MAAwB,OAAO,IAAI;AAEpE,MAAa,MAAM,gBAAgB;CACjC,MAAM;CACN,UAAU,aAAa,EACrB,IAAI,SAAuC;EAAE,OAAO,CAAC,GAAGA,4BAAmB,OAAO,CAAC;CAAG,EACxF;AACF,CAAC"}
@@ -41,7 +41,7 @@ function brokeredProviderOverrides(environment) {
41
41
  }
42
42
  //#endregion
43
43
  //#region src/constants/sandbox.ts
44
- const COMMAND_NAME = "doom-sandbox";
44
+ const COMMAND_NAME = "po-sandbox";
45
45
  const COMMAND_DESCRIPTION = "Show sandbox container status for this session";
46
46
  const BROKER_STATUS_SOURCE = "po-sandbox-broker";
47
47
  //#endregion
@@ -1 +1 @@
1
- {"version":3,"file":"server.cjs","names":["BROKER_PROVIDERS_ENV","BROKER_CONTAINER_PORT","BROKER_ADDRESS_ENV","timingSafeEqual","https","http","randomBytes","fs","path","os","DefaultSandboxExtensionService","defineRoot","defineCommand","defineRoutedContribution","readPackageResource","defineRoutedContribution","defineResource","defineRoutedContribution","defineResource","defineServerPlugin","composeServerProviderOverrides","rootSession","composeRootHooks","commandSandbox","resourcePoSandbox","resourcePoUseSandbox"],"sources":["../../src/services/brokerProviders/index.ts","../../src/constants/sandbox.ts","../../src/services/brokerRoutes/index.ts","../../src/services/brokerServer/index.ts","../../src/services/brokerHost/index.ts","../../src/extensions/workspaces/sessions/(backend)/_lib/serverRoot.ts","../../src/extensions/workspaces/sessions/(backend)/root.server.ts","../../src/extensions/workspaces/sessions/(backend)/_lib/sandboxCommands.ts","../../src/extensions/workspaces/sessions/(backend)/command/sandbox.server.ts","../../src/extensions/workspaces/sessions/(backend)/_lib/sandboxResources.ts","../../src/extensions/workspaces/sessions/(backend)/resource/poSandbox.server.ts","../../src/extensions/workspaces/sessions/(backend)/resource/poUseSandbox.server.ts","../../generated/server.ts"],"sourcesContent":["import { BROKER_ADDRESS_ENV, BROKER_CONTAINER_PORT, BROKER_PROVIDERS_ENV, BROKER_SOCKET_ENV } from '../sandboxBridge';\n\nexport interface BrokeredProviderOverride {\n provider: string;\n baseUrl: string;\n}\n\n/**\n * Points brokered providers at whichever route the launch granted.\n *\n * Pi reads base URLs from package-internal provider data, so redirecting a\n * session means overriding the provider rather than setting an environment\n * variable. The credential is left alone: the launch already replaced it with\n * the session token the broker expects.\n *\n * A broker on a host port is addressed directly. Only a mounted socket needs\n * the loopback bridge, because no SDK can dial a socket path.\n */\nexport function brokeredProviderOverrides(\n environment: Readonly<Record<string, string | undefined>>,\n): BrokeredProviderOverride[] {\n const configured = environment[BROKER_PROVIDERS_ENV]?.trim();\n if (!configured) return [];\n const authority = environment[BROKER_SOCKET_ENV]?.trim()\n ? `127.0.0.1:${BROKER_CONTAINER_PORT}`\n : environment[BROKER_ADDRESS_ENV]?.trim();\n if (!authority) return [];\n return configured\n .split(',')\n .map((provider) => provider.trim())\n .filter((provider) => provider.length > 0)\n .map((provider) => ({ provider, baseUrl: `http://${authority}/${provider}` }));\n}\n","export const COMMAND_NAME = 'doom-sandbox';\nexport const COMMAND_DESCRIPTION = 'Show sandbox container status for this session';\n\nexport const BROKER_DISABLED_ENV = 'PO_SANDBOX_BROKER';\nexport const DISABLED_VALUE = '0';\nexport const BROKER_STATUS_SOURCE = 'po-sandbox-broker';\n","export interface BrokerRoute {\n /** Pi provider name, which is also the broker's first path segment. */\n provider: string;\n /** Upstream origin plus any base path Pi's own provider data carries. */\n upstream: string;\n /** Host environment variables that may hold the real credential, in priority order. */\n hostKeyEnv: readonly string[];\n}\n\n/**\n * Providers the broker can terminate, mirroring Pi's own provider data.\n *\n * Curated rather than derived: Pi keeps base URLs in package-internal JSON that\n * carries no stability promise. A provider missing here is simply not brokered,\n * and its credential stays on the host.\n */\nconst ROUTES: readonly BrokerRoute[] = [\n { provider: 'anthropic', upstream: 'https://api.anthropic.com', hostKeyEnv: ['ANTHROPIC_API_KEY'] },\n { provider: 'openai', upstream: 'https://api.openai.com/v1', hostKeyEnv: ['OPENAI_API_KEY'] },\n { provider: 'google', upstream: 'https://generativelanguage.googleapis.com/v1beta', hostKeyEnv: ['GEMINI_API_KEY'] },\n { provider: 'groq', upstream: 'https://api.groq.com/openai/v1', hostKeyEnv: ['GROQ_API_KEY'] },\n { provider: 'xai', upstream: 'https://api.x.ai/v1', hostKeyEnv: ['XAI_API_KEY'] },\n { provider: 'openrouter', upstream: 'https://openrouter.ai/api/v1', hostKeyEnv: ['OPENROUTER_API_KEY'] },\n { provider: 'deepseek', upstream: 'https://api.deepseek.com', hostKeyEnv: ['DEEPSEEK_API_KEY'] },\n { provider: 'mistral', upstream: 'https://api.mistral.ai', hostKeyEnv: ['MISTRAL_API_KEY'] },\n { provider: 'together', upstream: 'https://api.together.ai/v1', hostKeyEnv: ['TOGETHER_API_KEY'] },\n { provider: 'cerebras', upstream: 'https://api.cerebras.ai/v1', hostKeyEnv: ['CEREBRAS_API_KEY'] },\n { provider: 'moonshotai', upstream: 'https://api.moonshot.ai/v1', hostKeyEnv: ['MOONSHOT_API_KEY'] },\n { provider: 'zai', upstream: 'https://api.z.ai/api/coding/paas/v4', hostKeyEnv: ['ZAI_API_KEY'] },\n];\n\nexport function brokerRoutes(): readonly BrokerRoute[] {\n return ROUTES;\n}\n\nexport function findBrokerRoute(provider: string): BrokerRoute | undefined {\n return ROUTES.find((route) => route.provider === provider);\n}\n\nexport interface ResolvedCredential {\n route: BrokerRoute;\n /** Environment variable the container would otherwise have received. */\n envName: string;\n value: string;\n}\n\n/** Selects the providers this host can actually broker for a session. */\nexport function resolveBrokeredCredentials(\n environment: Readonly<Record<string, string | undefined>>,\n): ResolvedCredential[] {\n const resolved: ResolvedCredential[] = [];\n for (const route of ROUTES) {\n for (const envName of route.hostKeyEnv) {\n const value = environment[envName]?.trim();\n if (value) {\n resolved.push({ route, envName, value });\n break;\n }\n }\n }\n return resolved;\n}\n","import { timingSafeEqual } from 'node:crypto';\nimport http from 'node:http';\nimport https from 'node:https';\n\nimport type { ResolvedCredential } from '../brokerRoutes';\n\n/** Headers a provider SDK may carry its credential in. */\nconst CREDENTIAL_HEADERS = ['x-api-key', 'authorization', 'x-goog-api-key'] as const;\n/** Query parameter some Google clients use instead of a header. */\nconst CREDENTIAL_QUERY = 'key';\nconst BEARER_PREFIX = 'bearer ';\n/** Connection-scoped headers that must not cross to the upstream request. */\nconst HOP_BY_HOP = new Set([\n 'connection',\n 'keep-alive',\n 'proxy-authenticate',\n 'proxy-authorization',\n 'te',\n 'trailer',\n 'transfer-encoding',\n 'upgrade',\n 'host',\n 'content-length',\n]);\nconst PATH_PATTERN = /^\\/([^/?]+)(.*)$/;\n\nexport interface BrokerServerOptions {\n /** Providers this session may reach, keyed by Pi provider name. */\n credentials: ReadonlyMap<string, ResolvedCredential>;\n /** Secret the container presents in place of a real credential. */\n token: string;\n /** Receives one line per rejected request, for host-side visibility. */\n onDenied?: (reason: string) => void;\n /** Seam for tests; defaults to the real https transport. */\n requestUpstream?: typeof https.request;\n}\n\nfunction matchesToken(candidate: string, token: string): boolean {\n const left = Buffer.from(candidate);\n const right = Buffer.from(token);\n return left.length === right.length && timingSafeEqual(left, right);\n}\n\nfunction credentialValue(headerValue: string): string {\n return headerValue.toLowerCase().startsWith(BEARER_PREFIX) ? headerValue.slice(BEARER_PREFIX.length) : headerValue;\n}\n\nfunction withCredential(headerValue: string, realKey: string): string {\n return headerValue.toLowerCase().startsWith(BEARER_PREFIX) ? `Bearer ${realKey}` : realKey;\n}\n\n/**\n * Rewrites the request's credential, proving the caller holds the session token.\n *\n * Which header carries the key differs per provider SDK, so every candidate is\n * checked rather than assumed. Returns undefined when nothing presented the\n * token, which is what makes an unauthenticated caller indistinguishable from a\n * misrouted one.\n */\nfunction swapCredential(\n headers: http.IncomingHttpHeaders,\n search: URLSearchParams,\n token: string,\n realKey: string,\n): http.OutgoingHttpHeaders | undefined {\n const forwarded: http.OutgoingHttpHeaders = {};\n let authenticated = false;\n\n for (const [name, value] of Object.entries(headers)) {\n if (value === undefined || HOP_BY_HOP.has(name)) continue;\n const single = Array.isArray(value) ? value[0] : value;\n if ((CREDENTIAL_HEADERS as readonly string[]).includes(name) && single !== undefined) {\n if (!matchesToken(credentialValue(single), token)) return undefined;\n forwarded[name] = withCredential(single, realKey);\n authenticated = true;\n continue;\n }\n forwarded[name] = value;\n }\n\n const queryKey = search.get(CREDENTIAL_QUERY);\n if (queryKey !== null) {\n if (!matchesToken(queryKey, token)) return undefined;\n search.set(CREDENTIAL_QUERY, realKey);\n authenticated = true;\n }\n\n return authenticated ? forwarded : undefined;\n}\n\nfunction reject(response: http.ServerResponse, status: number, message: string): void {\n response.writeHead(status, { 'content-type': 'application/json' });\n response.end(JSON.stringify({ error: { type: 'po_broker', message } }));\n}\n\n/**\n * Terminates provider calls from a sandboxed session on the host.\n *\n * The container never holds a real credential: it presents the session token,\n * and only a request that proves possession of it is forwarded upstream with\n * the host's key attached. Bodies stream in both directions so token-by-token\n * responses are not buffered.\n */\nexport function createBrokerServer(options: BrokerServerOptions): http.Server {\n const requestUpstream = options.requestUpstream ?? https.request;\n\n return http.createServer((request, response) => {\n const match = PATH_PATTERN.exec(request.url ?? '');\n const provider = match?.[1];\n const credential = provider ? options.credentials.get(provider) : undefined;\n if (!match || !credential) {\n options.onDenied?.(`unroutable provider path ${request.url ?? ''}`);\n reject(response, 404, 'Unknown provider for this sandbox session.');\n return;\n }\n\n const target = new URL(`${credential.route.upstream}${match[2] || ''}`);\n const headers = swapCredential(request.headers, target.searchParams, options.token, credential.value);\n if (!headers) {\n options.onDenied?.(`missing or invalid session token for ${credential.route.provider}`);\n reject(response, 401, 'This sandbox session did not present its broker token.');\n return;\n }\n\n const upstream = requestUpstream(\n target,\n { method: request.method, headers: { ...headers, host: target.host } },\n (upstreamResponse) => {\n response.writeHead(upstreamResponse.statusCode ?? 502, upstreamResponse.headers);\n upstreamResponse.pipe(response);\n },\n );\n upstream.on('error', (error: Error) => {\n options.onDenied?.(`upstream ${credential.route.provider} failed: ${error.message}`);\n if (!response.headersSent) reject(response, 502, 'The provider could not be reached from the host.');\n else response.destroy();\n });\n request.on('aborted', () => upstream.destroy());\n request.pipe(upstream);\n });\n}\n","import { randomBytes } from 'node:crypto';\nimport fs from 'node:fs';\nimport type http from 'node:http';\nimport os from 'node:os';\nimport path from 'node:path';\n\nimport type { BrokerEndpoint } from '../../types/sandboxHarness';\nimport { resolveBrokeredCredentials } from '../brokerRoutes';\nimport { createBrokerServer } from '../brokerServer';\n\nconst SOCKET_DIRECTORY_PREFIX = 'po-broker-';\nconst SOCKET_FILE_NAME = 'broker.sock';\nconst OWNER_ONLY_DIRECTORY = 0o700;\nconst TOKEN_BYTES = 32;\nconst LOOPBACK = '127.0.0.1';\nconst LINUX_PLATFORM = 'linux';\nconst EPHEMERAL_PORT = 0;\n\nexport interface RunningBroker {\n /** How a container reaches this broker. */\n endpoint: BrokerEndpoint;\n /** Secret the container presents instead of any real credential. */\n token: string;\n /** Pi provider names the session may reach. */\n providers: string[];\n /** Host environment variables whose value the container receives as the token. */\n withheldEnv: string[];\n stop(): Promise<void>;\n}\n\nexport interface StartBrokerOptions {\n environment: Readonly<Record<string, string | undefined>>;\n onDenied?: (reason: string) => void;\n /** Seam for tests; defaults to this host's platform. */\n platform?: string;\n /**\n * Fixed directory to hold the socket instead of a fresh temporary one.\n *\n * A reused container keeps the mounts it was created with, so a dev\n * container has to find the socket at the same path on every launch.\n */\n socketDirectory?: string;\n /** Forces the loopback transport even where a socket would work. */\n forceLoopback?: boolean;\n}\n\n/**\n * Starts the host-side provider broker for one sandboxed session.\n *\n * Answers undefined when the host holds no brokerable credential, which keeps\n * a session that authenticates some other way on the unbrokered path instead\n * of failing it.\n */\nexport async function startBroker(options: StartBrokerOptions): Promise<RunningBroker | undefined> {\n const resolved = resolveBrokeredCredentials(options.environment);\n if (resolved.length === 0) return undefined;\n\n const token = randomBytes(TOKEN_BYTES).toString('base64url');\n const credentials = new Map(resolved.map((credential) => [credential.route.provider, credential]));\n const server = createBrokerServer({ credentials, token, onDenied: options.onDenied });\n const platform = options.platform ?? process.platform;\n\n const useSocket = platform === LINUX_PLATFORM && options.forceLoopback !== true;\n const { endpoint, dispose } = useSocket\n ? await listenOnSocket(server, options.socketDirectory)\n : await listenOnLoopback(server);\n\n return {\n endpoint,\n token,\n providers: resolved.map((credential) => credential.route.provider),\n withheldEnv: resolved.map((credential) => credential.envName),\n stop: () => stopBroker(server, dispose),\n };\n}\n\n/**\n * Native Linux shares a kernel with the container, so a bind-mounted socket\n * needs no port and is reachable as an ordinary file.\n */\nasync function listenOnSocket(\n server: http.Server,\n fixedDirectory?: string,\n): Promise<{ endpoint: BrokerEndpoint; dispose: () => void }> {\n const socketDirectory = fixedDirectory ?? fs.mkdtempSync(path.join(os.tmpdir(), SOCKET_DIRECTORY_PREFIX));\n fs.mkdirSync(socketDirectory, { recursive: true });\n fs.chmodSync(socketDirectory, OWNER_ONLY_DIRECTORY);\n const socketPath = path.join(socketDirectory, SOCKET_FILE_NAME);\n // A previous session that died without closing leaves the node behind, and\n // bind fails on an existing path.\n fs.rmSync(socketPath, { force: true });\n await new Promise<void>((resolve, reject) => {\n server.once('error', reject);\n server.listen(socketPath, () => resolve());\n });\n return {\n endpoint: { transport: 'unix', socketDirectory },\n // A fixed directory outlives the session that borrowed it.\n dispose: () =>\n fixedDirectory\n ? fs.rmSync(socketPath, { force: true })\n : fs.rmSync(socketDirectory, { recursive: true, force: true }),\n };\n}\n\n/**\n * Everywhere else the container runs in its own virtual machine, which cannot\n * connect to a host unix socket even when the file is shared through: the\n * connect fails with ENOTSUP. Loopback keeps the broker off the network while\n * still being reachable through the engine's host gateway, and the session\n * token is what stops another local process from using it.\n */\nasync function listenOnLoopback(server: http.Server): Promise<{ endpoint: BrokerEndpoint; dispose: () => void }> {\n await new Promise<void>((resolve, reject) => {\n server.once('error', reject);\n server.listen(EPHEMERAL_PORT, LOOPBACK, () => resolve());\n });\n const address = server.address();\n if (address === null || typeof address === 'string') throw new Error('The broker did not report a listening port.');\n return { endpoint: { transport: 'tcp', port: address.port }, dispose: () => undefined };\n}\n\nasync function stopBroker(server: http.Server, dispose: () => void): Promise<void> {\n await new Promise<void>((resolve) => {\n server.close(() => resolve());\n // An in-flight provider stream would otherwise hold the listener open past\n // the session that owns it.\n server.closeAllConnections();\n });\n dispose();\n}\n","import type { DoomHeadlessActivity } from '@agimon-ai/po-core/headless';\nimport type { DoomServerPluginContext } from '@agimon-ai/po-core/serverFacet';\n\nimport { BROKER_DISABLED_ENV, BROKER_STATUS_SOURCE, DISABLED_VALUE } from '../../../../../constants/sandbox';\nimport { startBroker, type RunningBroker } from '../../../../../services/brokerHost';\nimport { DefaultSandboxExtensionService } from '../../../../../services/extensionService';\nimport type { SandboxServerScope } from './sandboxScope';\n\nexport function createSandboxServerRoot({ agent }: DoomServerPluginContext) {\n if (!agent) return { value: {} as SandboxServerScope };\n const service = new DefaultSandboxExtensionService(agent.context.environment);\n let broker: RunningBroker | undefined;\n const activity: DoomHeadlessActivity = {\n name: BROKER_STATUS_SOURCE,\n async start(execution) {\n if (execution.environment[BROKER_DISABLED_ENV] === DISABLED_VALUE) return () => undefined;\n const started = await startBroker({\n environment: execution.environment,\n onDenied: (reason) => {\n void execution.client.notify({\n title: 'Po sandbox broker',\n body: `Broker refused a call: ${reason}`,\n level: 'warning',\n });\n },\n });\n broker = started;\n if (started === undefined) return () => undefined;\n execution.client.setStatus(BROKER_STATUS_SOURCE, `provider broker: ${started.providers.join(', ')}`);\n return async () => {\n if (broker !== started) return;\n broker = undefined;\n execution.client.setStatus(BROKER_STATUS_SOURCE, undefined);\n await started.stop();\n };\n },\n };\n return { value: { service }, activities: [activity] };\n}\n","import { defineRoot } from '@agimon-ai/po-core/extensionFile';\nimport type { DoomServerProviderOverrides } from '@agimon-ai/po-core/serverFacet';\n\nimport { brokeredProviderOverrides } from '../../../../services/brokerProviders';\nimport { createSandboxServerRoot } from './_lib/serverRoot';\n\nexport default defineRoot(\n Object.assign(createSandboxServerRoot, {\n providerOverrides: (({ environment }) =>\n brokeredProviderOverrides(environment).map(({ provider, baseUrl }) => ({\n name: provider,\n baseUrl,\n }))) satisfies DoomServerProviderOverrides,\n }),\n);\n","import type { WithRoot } from '@agimon-ai/po-core/extensionFile';\nimport { defineCommand } from '@agimon-ai/po-core/serverFacet';\nimport type { DoomServerPluginContext } from '@agimon-ai/po-core/serverFacet';\n\nimport { COMMAND_DESCRIPTION, COMMAND_NAME } from '../../../../../constants/sandbox';\nimport type { SandboxServerScope } from './sandboxScope';\n\nexport function createSandboxServerCommands(context: WithRoot<DoomServerPluginContext, SandboxServerScope>) {\n const service = context.root.service;\n if (!service) return [];\n return [\n defineCommand({\n name: COMMAND_NAME,\n description: COMMAND_DESCRIPTION,\n async execute() {\n const result = await service.execute();\n await context.agent?.context.client.notify({ body: result.message, level: result.level });\n },\n }),\n ];\n}\n","import { defineRoutedContribution, type WithRoot } from '@agimon-ai/po-core/extensionFile';\nimport type { DoomServerPluginContext } from '@agimon-ai/po-core/serverFacet';\n\nimport { createSandboxServerCommands } from '../_lib/sandboxCommands';\nimport type { SandboxServerScope } from '../_lib/sandboxScope';\n\nexport default defineRoutedContribution(\n (context: WithRoot<DoomServerPluginContext, SandboxServerScope>) => createSandboxServerCommands(context),\n { cardinality: 'many' },\n);\n","import type { DoomHeadlessResource } from '@agimon-ai/po-core/headless';\nimport { readPackageResource } from '@agimon-ai/po-core/serverFacet';\n\nimport type { SandboxServerScope } from './sandboxScope';\n\nexport function createSandboxContextResource(root: SandboxServerScope): DoomHeadlessResource | undefined {\n if (!root.service) return undefined;\n return {\n // The package index is Help-catalog material, not standing instruction. The Pi\n // facet has always routed it through the Help service; this is the server's gate.\n when: { state: { 'minor-mode': 'help' }, attribution: { kind: 'minor', mode: 'help' } },\n name: 'po-sandbox',\n kind: 'context',\n read: () => readPackageResource(import.meta.url, 'llms.txt'),\n };\n}\n\nexport function createSandboxSkillResource(root: SandboxServerScope): DoomHeadlessResource | undefined {\n if (!root.service) return undefined;\n return {\n name: 'po-use-sandbox',\n when: { state: { 'minor-mode': 'help' }, attribution: { kind: 'minor', mode: 'help' } },\n kind: 'skill',\n read: () => readPackageResource(import.meta.url, 'src/prompts/po-use-sandbox/SKILL.md'),\n };\n}\n\nexport function createSandboxHelpResource(moduleUrl: string) {\n return {\n source: '@agimon-ai/po-sandbox',\n moduleUrl,\n skills: [\n {\n name: 'po-use-sandbox',\n description:\n 'Use @agimon-ai/po-sandbox: Container sandbox for Po launches: the agent, extensions, and tools run inside Docker or Podman while the terminal stays on the host',\n },\n ],\n };\n}\n","import { defineResource, defineRoutedContribution, type WithRoot } from '@agimon-ai/po-core/extensionFile';\nimport type { DoomServerPluginContext } from '@agimon-ai/po-core/serverFacet';\n\nimport { createSandboxContextResource } from '../_lib/sandboxResources';\nimport type { SandboxServerScope } from '../_lib/sandboxScope';\n\nexport default defineRoutedContribution(\n defineResource((context: WithRoot<DoomServerPluginContext, SandboxServerScope>) =>\n createSandboxContextResource(context.root),\n ),\n { cardinality: 'optional' },\n);\n","import { defineResource, defineRoutedContribution, type WithRoot } from '@agimon-ai/po-core/extensionFile';\nimport type { DoomServerPluginContext } from '@agimon-ai/po-core/serverFacet';\n\nimport { createSandboxSkillResource } from '../_lib/sandboxResources';\nimport type { SandboxServerScope } from '../_lib/sandboxScope';\n\nexport default defineRoutedContribution(\n defineResource((context: WithRoot<DoomServerPluginContext, SandboxServerScope>) =>\n createSandboxSkillResource(context.root),\n ),\n { cardinality: 'optional' },\n);\n","// Generated by @agimon-ai/po-build. Do not edit by hand.\nimport { defineServerPlugin, type DoomServerSessionPlugin } from '@agimon-ai/po-core/serverFacet';\nimport { composeServerProviderOverrides } from '@agimon-ai/po-core/serverFacet';\nimport { composeRootHooks } from '@agimon-ai/po-core/extensionFile';\n\nimport rootSession from '../src/extensions/workspaces/sessions/(backend)/root.server';\nimport commandSandbox from '../src/extensions/workspaces/sessions/(backend)/command/sandbox.server';\nimport resourcePoSandbox from '../src/extensions/workspaces/sessions/(backend)/resource/poSandbox.server';\nimport resourcePoUseSandbox from '../src/extensions/workspaces/sessions/(backend)/resource/poUseSandbox.server';\n\ntype Factory<T, C> = (context: C) => T;\nconst at = <T, C>(value: T | Factory<T, C>, context: C): T =>\n typeof value === 'function' ? (value as Factory<T, C>)(context) : value;\ntype ManyFactory<T, C> = (context: C) => T | readonly T[];\nconst many = <T, C>(value: T | readonly T[] | ManyFactory<T, C>, context: C): readonly T[] => {\n const resolved = typeof value === 'function' ? (value as ManyFactory<T, C>)(context) : value;\n return Array.isArray(resolved) ? resolved : [resolved as T];\n};\n/* v8 ignore next -- generated normalization branch */\nconst defined = <T>(values: readonly (T | undefined)[]): T[] =>\n values.filter((value): value is T => value !== undefined);\n\nexport const facet = defineServerPlugin({\n name: '@agimon-ai/po-sandbox',\n providerOverrides: composeServerProviderOverrides(rootSession),\n session: async (context) => {\n const scopeSession = await rootSession(context);\n const contextSession = { ...context, root: scopeSession.value };\n return {\n ...composeRootHooks(scopeSession),\n get commands(): DoomServerSessionPlugin['commands'] { return [...many(commandSandbox, contextSession)]; },\n get resources(): DoomServerSessionPlugin['resources'] { return defined([at(resourcePoSandbox, contextSession), at(resourcePoUseSandbox, contextSession)]); },\n services: [...(scopeSession.services ?? [])],\n get activities(): DoomServerSessionPlugin['activities'] { return [...(scopeSession.activities ?? [])]; },\n };\n },\n});\n\nexport default facet;\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAkBA,SAAgB,0BACd,aAC4B;CAC5B,MAAM,aAAa,YAAYA,sBAAAA,qBAAqB,EAAE,KAAK;CAC3D,IAAI,CAAC,YAAY,OAAO,CAAC;CACzB,MAAM,YAAY,YAAA,mBAA8B,EAAE,KAAK,IACnD,aAAaC,sBAAAA,0BACb,YAAYC,sBAAAA,mBAAmB,EAAE,KAAK;CAC1C,IAAI,CAAC,WAAW,OAAO,CAAC;CACxB,OAAO,WACJ,MAAM,GAAG,CAAC,CACV,KAAK,aAAa,SAAS,KAAK,CAAC,CAAC,CAClC,QAAQ,aAAa,SAAS,SAAS,CAAC,CAAC,CACzC,KAAK,cAAc;EAAE;EAAU,SAAS,UAAU,UAAU,GAAG;CAAW,EAAE;AACjF;;;AChCA,MAAa,eAAe;AAC5B,MAAa,sBAAsB;AAInC,MAAa,uBAAuB;;;;;;;;;;ACWpC,MAAM,SAAiC;CACrC;EAAE,UAAU;EAAa,UAAU;EAA6B,YAAY,CAAC,mBAAmB;CAAE;CAClG;EAAE,UAAU;EAAU,UAAU;EAA6B,YAAY,CAAC,gBAAgB;CAAE;CAC5F;EAAE,UAAU;EAAU,UAAU;EAAoD,YAAY,CAAC,gBAAgB;CAAE;CACnH;EAAE,UAAU;EAAQ,UAAU;EAAkC,YAAY,CAAC,cAAc;CAAE;CAC7F;EAAE,UAAU;EAAO,UAAU;EAAuB,YAAY,CAAC,aAAa;CAAE;CAChF;EAAE,UAAU;EAAc,UAAU;EAAgC,YAAY,CAAC,oBAAoB;CAAE;CACvG;EAAE,UAAU;EAAY,UAAU;EAA4B,YAAY,CAAC,kBAAkB;CAAE;CAC/F;EAAE,UAAU;EAAW,UAAU;EAA0B,YAAY,CAAC,iBAAiB;CAAE;CAC3F;EAAE,UAAU;EAAY,UAAU;EAA8B,YAAY,CAAC,kBAAkB;CAAE;CACjG;EAAE,UAAU;EAAY,UAAU;EAA8B,YAAY,CAAC,kBAAkB;CAAE;CACjG;EAAE,UAAU;EAAc,UAAU;EAA8B,YAAY,CAAC,kBAAkB;CAAE;CACnG;EAAE,UAAU;EAAO,UAAU;EAAuC,YAAY,CAAC,aAAa;CAAE;AAClG;;AAkBA,SAAgB,2BACd,aACsB;CACtB,MAAM,WAAiC,CAAC;CACxC,KAAK,MAAM,SAAS,QAClB,KAAK,MAAM,WAAW,MAAM,YAAY;EACtC,MAAM,QAAQ,YAAY,QAAQ,EAAE,KAAK;EACzC,IAAI,OAAO;GACT,SAAS,KAAK;IAAE;IAAO;IAAS;GAAM,CAAC;GACvC;EACF;CACF;CAEF,OAAO;AACT;;;;ACtDA,MAAM,qBAAqB;CAAC;CAAa;CAAiB;AAAgB;;AAE1E,MAAM,mBAAmB;AACzB,MAAM,gBAAgB;;AAEtB,MAAM,6BAAa,IAAI,IAAI;CACzB;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACF,CAAC;AACD,MAAM,eAAe;AAarB,SAAS,aAAa,WAAmB,OAAwB;CAC/D,MAAM,OAAO,OAAO,KAAK,SAAS;CAClC,MAAM,QAAQ,OAAO,KAAK,KAAK;CAC/B,OAAO,KAAK,WAAW,MAAM,WAAA,GAAUC,YAAAA,gBAAAA,CAAgB,MAAM,KAAK;AACpE;AAEA,SAAS,gBAAgB,aAA6B;CACpD,OAAO,YAAY,YAAY,CAAC,CAAC,WAAW,aAAa,IAAI,YAAY,MAAM,CAAoB,IAAI;AACzG;AAEA,SAAS,eAAe,aAAqB,SAAyB;CACpE,OAAO,YAAY,YAAY,CAAC,CAAC,WAAW,aAAa,IAAI,UAAU,YAAY;AACrF;;;;;;;;;AAUA,SAAS,eACP,SACA,QACA,OACA,SACsC;CACtC,MAAM,YAAsC,CAAC;CAC7C,IAAI,gBAAgB;CAEpB,KAAK,MAAM,CAAC,MAAM,UAAU,OAAO,QAAQ,OAAO,GAAG;EACnD,IAAI,UAAU,KAAA,KAAa,WAAW,IAAI,IAAI,GAAG;EACjD,MAAM,SAAS,MAAM,QAAQ,KAAK,IAAI,MAAM,KAAK;EACjD,IAAK,mBAAyC,SAAS,IAAI,KAAK,WAAW,KAAA,GAAW;GACpF,IAAI,CAAC,aAAa,gBAAgB,MAAM,GAAG,KAAK,GAAG,OAAO,KAAA;GAC1D,UAAU,QAAQ,eAAe,QAAQ,OAAO;GAChD,gBAAgB;GAChB;EACF;EACA,UAAU,QAAQ;CACpB;CAEA,MAAM,WAAW,OAAO,IAAI,gBAAgB;CAC5C,IAAI,aAAa,MAAM;EACrB,IAAI,CAAC,aAAa,UAAU,KAAK,GAAG,OAAO,KAAA;EAC3C,OAAO,IAAI,kBAAkB,OAAO;EACpC,gBAAgB;CAClB;CAEA,OAAO,gBAAgB,YAAY,KAAA;AACrC;AAEA,SAAS,OAAO,UAA+B,QAAgB,SAAuB;CACpF,SAAS,UAAU,QAAQ,EAAE,gBAAgB,mBAAmB,CAAC;CACjE,SAAS,IAAI,KAAK,UAAU,EAAE,OAAO;EAAE,MAAM;EAAa;CAAQ,EAAE,CAAC,CAAC;AACxE;;;;;;;;;AAUA,SAAgB,mBAAmB,SAA2C;CAC5E,MAAM,kBAAkB,QAAQ,mBAAmBC,WAAAA,QAAM;CAEzD,OAAOC,UAAAA,QAAK,cAAc,SAAS,aAAa;EAC9C,MAAM,QAAQ,aAAa,KAAK,QAAQ,OAAO,EAAE;EACjD,MAAM,WAAW,QAAQ;EACzB,MAAM,aAAa,WAAW,QAAQ,YAAY,IAAI,QAAQ,IAAI,KAAA;EAClE,IAAI,CAAC,SAAS,CAAC,YAAY;GACzB,QAAQ,WAAW,4BAA4B,QAAQ,OAAO,IAAI;GAClE,OAAO,UAAU,KAAK,4CAA4C;GAClE;EACF;EAEA,MAAM,SAAS,IAAI,IAAI,GAAG,WAAW,MAAM,WAAW,MAAM,MAAM,IAAI;EACtE,MAAM,UAAU,eAAe,QAAQ,SAAS,OAAO,cAAc,QAAQ,OAAO,WAAW,KAAK;EACpG,IAAI,CAAC,SAAS;GACZ,QAAQ,WAAW,wCAAwC,WAAW,MAAM,UAAU;GACtF,OAAO,UAAU,KAAK,wDAAwD;GAC9E;EACF;EAEA,MAAM,WAAW,gBACf,QACA;GAAE,QAAQ,QAAQ;GAAQ,SAAS;IAAE,GAAG;IAAS,MAAM,OAAO;GAAK;EAAE,IACpE,qBAAqB;GACpB,SAAS,UAAU,iBAAiB,cAAc,KAAK,iBAAiB,OAAO;GAC/E,iBAAiB,KAAK,QAAQ;EAChC,CACF;EACA,SAAS,GAAG,UAAU,UAAiB;GACrC,QAAQ,WAAW,YAAY,WAAW,MAAM,SAAS,WAAW,MAAM,SAAS;GACnF,IAAI,CAAC,SAAS,aAAa,OAAO,UAAU,KAAK,kDAAkD;QAC9F,SAAS,QAAQ;EACxB,CAAC;EACD,QAAQ,GAAG,iBAAiB,SAAS,QAAQ,CAAC;EAC9C,QAAQ,KAAK,QAAQ;CACvB,CAAC;AACH;;;AClIA,MAAM,0BAA0B;AAChC,MAAM,mBAAmB;AACzB,MAAM,uBAAuB;AAC7B,MAAM,cAAc;AACpB,MAAM,WAAW;AACjB,MAAM,iBAAiB;AACvB,MAAM,iBAAiB;;;;;;;;AAqCvB,eAAsB,YAAY,SAAiE;CACjG,MAAM,WAAW,2BAA2B,QAAQ,WAAW;CAC/D,IAAI,SAAS,WAAW,GAAG,OAAO,KAAA;CAElC,MAAM,SAAA,GAAQC,YAAAA,YAAAA,CAAY,WAAW,CAAC,CAAC,SAAS,WAAW;CAE3D,MAAM,SAAS,mBAAmB;EAAE,iBADZ,IAAI,SAAS,KAAK,eAAe,CAAC,WAAW,MAAM,UAAU,UAAU,CAAC,CAC5D;EAAa;EAAO,UAAU,QAAQ;CAAS,CAAC;CAIpF,MAAM,EAAE,UAAU,aAHD,QAAQ,YAAY,QAAQ,cAEd,kBAAkB,QAAQ,kBAAkB,OAEvE,MAAM,eAAe,QAAQ,QAAQ,eAAe,IACpD,MAAM,iBAAiB,MAAM;CAEjC,OAAO;EACL;EACA;EACA,WAAW,SAAS,KAAK,eAAe,WAAW,MAAM,QAAQ;EACjE,aAAa,SAAS,KAAK,eAAe,WAAW,OAAO;EAC5D,YAAY,WAAW,QAAQ,OAAO;CACxC;AACF;;;;;AAMA,eAAe,eACb,QACA,gBAC4D;CAC5D,MAAM,kBAAkB,kBAAkBC,QAAAA,QAAG,YAAYC,UAAAA,QAAK,KAAKC,QAAAA,QAAG,OAAO,GAAG,uBAAuB,CAAC;CACxG,QAAA,QAAG,UAAU,iBAAiB,EAAE,WAAW,KAAK,CAAC;CACjD,QAAA,QAAG,UAAU,iBAAiB,oBAAoB;CAClD,MAAM,aAAaD,UAAAA,QAAK,KAAK,iBAAiB,gBAAgB;CAG9D,QAAA,QAAG,OAAO,YAAY,EAAE,OAAO,KAAK,CAAC;CACrC,MAAM,IAAI,SAAe,SAAS,WAAW;EAC3C,OAAO,KAAK,SAAS,MAAM;EAC3B,OAAO,OAAO,kBAAkB,QAAQ,CAAC;CAC3C,CAAC;CACD,OAAO;EACL,UAAU;GAAE,WAAW;GAAQ;EAAgB;EAE/C,eACE,iBACID,QAAAA,QAAG,OAAO,YAAY,EAAE,OAAO,KAAK,CAAC,IACrCA,QAAAA,QAAG,OAAO,iBAAiB;GAAE,WAAW;GAAM,OAAO;EAAK,CAAC;CACnE;AACF;;;;;;;;AASA,eAAe,iBAAiB,QAAiF;CAC/G,MAAM,IAAI,SAAe,SAAS,WAAW;EAC3C,OAAO,KAAK,SAAS,MAAM;EAC3B,OAAO,OAAO,gBAAgB,gBAAgB,QAAQ,CAAC;CACzD,CAAC;CACD,MAAM,UAAU,OAAO,QAAQ;CAC/B,IAAI,YAAY,QAAQ,OAAO,YAAY,UAAU,MAAM,IAAI,MAAM,6CAA6C;CAClH,OAAO;EAAE,UAAU;GAAE,WAAW;GAAO,MAAM,QAAQ;EAAK;EAAG,eAAe,KAAA;CAAU;AACxF;AAEA,eAAe,WAAW,QAAqB,SAAoC;CACjF,MAAM,IAAI,SAAe,YAAY;EACnC,OAAO,YAAY,QAAQ,CAAC;EAG5B,OAAO,oBAAoB;CAC7B,CAAC;CACD,QAAQ;AACV;;;AC1HA,SAAgB,wBAAwB,EAAE,SAAkC;CAC1E,IAAI,CAAC,OAAO,OAAO,EAAE,OAAO,CAAC,EAAwB;CACrD,MAAM,UAAU,IAAIG,yBAAAA,+BAA+B,MAAM,QAAQ,WAAW;CAC5E,IAAI;CA0BJ,OAAO;EAAE,OAAO,EAAE,QAAQ;EAAG,YAAY,CAAC;GAxBxC,MAAM;GACN,MAAM,MAAM,WAAW;IACrB,IAAI,UAAU,YAAA,yBAAA,KAAqD,aAAa,KAAA;IAChF,MAAM,UAAU,MAAM,YAAY;KAChC,aAAa,UAAU;KACvB,WAAW,WAAW;MACpB,UAAe,OAAO,OAAO;OAC3B,OAAO;OACP,MAAM,0BAA0B;OAChC,OAAO;MACT,CAAC;KACH;IACF,CAAC;IACD,SAAS;IACT,IAAI,YAAY,KAAA,GAAW,aAAa,KAAA;IACxC,UAAU,OAAO,UAAU,sBAAsB,oBAAoB,QAAQ,UAAU,KAAK,IAAI,GAAG;IACnG,OAAO,YAAY;KACjB,IAAI,WAAW,SAAS;KACxB,SAAS,KAAA;KACT,UAAU,OAAO,UAAU,sBAAsB,KAAA,CAAS;KAC1D,MAAM,QAAQ,KAAK;IACrB;GACF;EAE+C,CAAC;CAAE;AACtD;;;AChCA,IAAA,uBAAA,GAAeC,iCAAAA,WAAAA,CACb,OAAO,OAAO,yBAAyB,EACrC,qBAAqB,EAAE,kBACrB,0BAA0B,WAAW,CAAC,CAAC,KAAK,EAAE,UAAU,eAAe;CACrE,MAAM;CACN;AACF,EAAE,GACN,CAAC,CACH;;;ACPA,SAAgB,4BAA4B,SAAgE;CAC1G,MAAM,UAAU,QAAQ,KAAK;CAC7B,IAAI,CAAC,SAAS,OAAO,CAAC;CACtB,OAAO,EAAA,GACLC,+BAAAA,cAAAA,CAAc;EACZ,MAAM;EACN,aAAa;EACb,MAAM,UAAU;GACd,MAAM,SAAS,MAAM,QAAQ,QAAQ;GACrC,MAAM,QAAQ,OAAO,QAAQ,OAAO,OAAO;IAAE,MAAM,OAAO;IAAS,OAAO,OAAO;GAAM,CAAC;EAC1F;CACF,CAAC,CACH;AACF;;;ACdA,IAAA,0BAAA,GAAeC,iCAAAA,yBAAAA,EACZ,YAAmE,4BAA4B,OAAO,GACvG,EAAE,aAAa,OAAO,CACxB;;;ACJA,SAAgB,6BAA6B,MAA4D;CACvG,IAAI,CAAC,KAAK,SAAS,OAAO,KAAA;CAC1B,OAAO;EAGL,MAAM;GAAE,OAAO,EAAE,cAAc,OAAO;GAAG,aAAa;IAAE,MAAM;IAAS,MAAM;GAAO;EAAE;EACtF,MAAM;EACN,MAAM;EACN,aAAA,GAAYC,+BAAAA,oBAAAA,CAAAA,QAAAA,KAAAA,CAAAA,CAAAA,cAAAA,UAAAA,CAAAA,CAAAA,MAAqC,UAAU;CAC7D;AACF;AAEA,SAAgB,2BAA2B,MAA4D;CACrG,IAAI,CAAC,KAAK,SAAS,OAAO,KAAA;CAC1B,OAAO;EACL,MAAM;EACN,MAAM;GAAE,OAAO,EAAE,cAAc,OAAO;GAAG,aAAa;IAAE,MAAM;IAAS,MAAM;GAAO;EAAE;EACtF,MAAM;EACN,aAAA,GAAYA,+BAAAA,oBAAAA,CAAAA,QAAAA,KAAAA,CAAAA,CAAAA,cAAAA,UAAAA,CAAAA,CAAAA,MAAqC,qCAAqC;CACxF;AACF;;;ACnBA,IAAA,4BAAA,GAAeC,iCAAAA,yBAAAA,EAAAA,GACbC,iCAAAA,eAAAA,EAAgB,YACd,6BAA6B,QAAQ,IAAI,CAC3C,GACA,EAAE,aAAa,WAAW,CAC5B;;;ACLA,IAAA,+BAAA,GAAeC,iCAAAA,yBAAAA,EAAAA,GACbC,iCAAAA,eAAAA,EAAgB,YACd,2BAA2B,QAAQ,IAAI,CACzC,GACA,EAAE,aAAa,WAAW,CAC5B;;;ACAA,MAAM,MAAY,OAA0B,YAC1C,OAAO,UAAU,aAAc,MAAwB,OAAO,IAAI;AAEpE,MAAM,QAAc,OAA6C,YAA6B;CAC5F,MAAM,WAAW,OAAO,UAAU,aAAc,MAA4B,OAAO,IAAI;CACvF,OAAO,MAAM,QAAQ,QAAQ,IAAI,WAAW,CAAC,QAAa;AAC5D;;AAEA,MAAM,WAAc,WAClB,OAAO,QAAQ,UAAsB,UAAU,KAAA,CAAS;AAE1D,MAAa,SAAA,GAAQC,+BAAAA,mBAAAA,CAAmB;CACtC,MAAM;CACN,oBAAA,GAAmBC,+BAAAA,+BAAAA,CAA+BC,mBAAW;CAC7D,SAAS,OAAO,YAAY;EAC1B,MAAM,eAAe,MAAMA,oBAAY,OAAO;EAC9C,MAAM,iBAAiB;GAAE,GAAG;GAAS,MAAM,aAAa;EAAM;EAC9D,OAAO;GACL,IAAA,GAAGC,iCAAAA,iBAAAA,CAAiB,YAAY;GAChC,IAAI,WAAgD;IAAE,OAAO,CAAC,GAAG,KAAKC,wBAAgB,cAAc,CAAC;GAAG;GACxG,IAAI,YAAkD;IAAE,OAAO,QAAQ,CAAC,GAAGC,0BAAmB,cAAc,GAAG,GAAGC,6BAAsB,cAAc,CAAC,CAAC;GAAG;GAC3J,UAAU,CAAC,GAAI,aAAa,YAAY,CAAC,CAAE;GAC3C,IAAI,aAAoD;IAAE,OAAO,CAAC,GAAI,aAAa,cAAc,CAAC,CAAE;GAAG;EACzG;CACF;AACF,CAAC"}
1
+ {"version":3,"file":"server.cjs","names":["BROKER_PROVIDERS_ENV","BROKER_CONTAINER_PORT","BROKER_ADDRESS_ENV","timingSafeEqual","https","http","randomBytes","fs","path","os","DefaultSandboxExtensionService","defineRoot","defineCommand","defineRoutedContribution","readPackageResource","defineRoutedContribution","defineResource","defineRoutedContribution","defineResource","defineServerPlugin","composeServerProviderOverrides","rootSession","composeRootHooks","commandSandbox","resourcePoSandbox","resourcePoUseSandbox"],"sources":["../../src/services/brokerProviders/index.ts","../../src/constants/sandbox.ts","../../src/services/brokerRoutes/index.ts","../../src/services/brokerServer/index.ts","../../src/services/brokerHost/index.ts","../../src/extensions/workspaces/sessions/(backend)/_lib/serverRoot.ts","../../src/extensions/workspaces/sessions/(backend)/root.server.ts","../../src/extensions/workspaces/sessions/(backend)/_lib/sandboxCommands.ts","../../src/extensions/workspaces/sessions/(backend)/command/sandbox.server.ts","../../src/extensions/workspaces/sessions/(backend)/_lib/sandboxResources.ts","../../src/extensions/workspaces/sessions/(backend)/resource/poSandbox.server.ts","../../src/extensions/workspaces/sessions/(backend)/resource/poUseSandbox.server.ts","../../generated/server.ts"],"sourcesContent":["import { BROKER_ADDRESS_ENV, BROKER_CONTAINER_PORT, BROKER_PROVIDERS_ENV, BROKER_SOCKET_ENV } from '../sandboxBridge';\n\nexport interface BrokeredProviderOverride {\n provider: string;\n baseUrl: string;\n}\n\n/**\n * Points brokered providers at whichever route the launch granted.\n *\n * Pi reads base URLs from package-internal provider data, so redirecting a\n * session means overriding the provider rather than setting an environment\n * variable. The credential is left alone: the launch already replaced it with\n * the session token the broker expects.\n *\n * A broker on a host port is addressed directly. Only a mounted socket needs\n * the loopback bridge, because no SDK can dial a socket path.\n */\nexport function brokeredProviderOverrides(\n environment: Readonly<Record<string, string | undefined>>,\n): BrokeredProviderOverride[] {\n const configured = environment[BROKER_PROVIDERS_ENV]?.trim();\n if (!configured) return [];\n const authority = environment[BROKER_SOCKET_ENV]?.trim()\n ? `127.0.0.1:${BROKER_CONTAINER_PORT}`\n : environment[BROKER_ADDRESS_ENV]?.trim();\n if (!authority) return [];\n return configured\n .split(',')\n .map((provider) => provider.trim())\n .filter((provider) => provider.length > 0)\n .map((provider) => ({ provider, baseUrl: `http://${authority}/${provider}` }));\n}\n","export const COMMAND_NAME = 'po-sandbox';\nexport const COMMAND_DESCRIPTION = 'Show sandbox container status for this session';\n\nexport const BROKER_DISABLED_ENV = 'PO_SANDBOX_BROKER';\nexport const DISABLED_VALUE = '0';\nexport const BROKER_STATUS_SOURCE = 'po-sandbox-broker';\n","export interface BrokerRoute {\n /** Pi provider name, which is also the broker's first path segment. */\n provider: string;\n /** Upstream origin plus any base path Pi's own provider data carries. */\n upstream: string;\n /** Host environment variables that may hold the real credential, in priority order. */\n hostKeyEnv: readonly string[];\n}\n\n/**\n * Providers the broker can terminate, mirroring Pi's own provider data.\n *\n * Curated rather than derived: Pi keeps base URLs in package-internal JSON that\n * carries no stability promise. A provider missing here is simply not brokered,\n * and its credential stays on the host.\n */\nconst ROUTES: readonly BrokerRoute[] = [\n { provider: 'anthropic', upstream: 'https://api.anthropic.com', hostKeyEnv: ['ANTHROPIC_API_KEY'] },\n { provider: 'openai', upstream: 'https://api.openai.com/v1', hostKeyEnv: ['OPENAI_API_KEY'] },\n { provider: 'google', upstream: 'https://generativelanguage.googleapis.com/v1beta', hostKeyEnv: ['GEMINI_API_KEY'] },\n { provider: 'groq', upstream: 'https://api.groq.com/openai/v1', hostKeyEnv: ['GROQ_API_KEY'] },\n { provider: 'xai', upstream: 'https://api.x.ai/v1', hostKeyEnv: ['XAI_API_KEY'] },\n { provider: 'openrouter', upstream: 'https://openrouter.ai/api/v1', hostKeyEnv: ['OPENROUTER_API_KEY'] },\n { provider: 'deepseek', upstream: 'https://api.deepseek.com', hostKeyEnv: ['DEEPSEEK_API_KEY'] },\n { provider: 'mistral', upstream: 'https://api.mistral.ai', hostKeyEnv: ['MISTRAL_API_KEY'] },\n { provider: 'together', upstream: 'https://api.together.ai/v1', hostKeyEnv: ['TOGETHER_API_KEY'] },\n { provider: 'cerebras', upstream: 'https://api.cerebras.ai/v1', hostKeyEnv: ['CEREBRAS_API_KEY'] },\n { provider: 'moonshotai', upstream: 'https://api.moonshot.ai/v1', hostKeyEnv: ['MOONSHOT_API_KEY'] },\n { provider: 'zai', upstream: 'https://api.z.ai/api/coding/paas/v4', hostKeyEnv: ['ZAI_API_KEY'] },\n];\n\nexport function brokerRoutes(): readonly BrokerRoute[] {\n return ROUTES;\n}\n\nexport function findBrokerRoute(provider: string): BrokerRoute | undefined {\n return ROUTES.find((route) => route.provider === provider);\n}\n\nexport interface ResolvedCredential {\n route: BrokerRoute;\n /** Environment variable the container would otherwise have received. */\n envName: string;\n value: string;\n}\n\n/** Selects the providers this host can actually broker for a session. */\nexport function resolveBrokeredCredentials(\n environment: Readonly<Record<string, string | undefined>>,\n): ResolvedCredential[] {\n const resolved: ResolvedCredential[] = [];\n for (const route of ROUTES) {\n for (const envName of route.hostKeyEnv) {\n const value = environment[envName]?.trim();\n if (value) {\n resolved.push({ route, envName, value });\n break;\n }\n }\n }\n return resolved;\n}\n","import { timingSafeEqual } from 'node:crypto';\nimport http from 'node:http';\nimport https from 'node:https';\n\nimport type { ResolvedCredential } from '../brokerRoutes';\n\n/** Headers a provider SDK may carry its credential in. */\nconst CREDENTIAL_HEADERS = ['x-api-key', 'authorization', 'x-goog-api-key'] as const;\n/** Query parameter some Google clients use instead of a header. */\nconst CREDENTIAL_QUERY = 'key';\nconst BEARER_PREFIX = 'bearer ';\n/** Connection-scoped headers that must not cross to the upstream request. */\nconst HOP_BY_HOP = new Set([\n 'connection',\n 'keep-alive',\n 'proxy-authenticate',\n 'proxy-authorization',\n 'te',\n 'trailer',\n 'transfer-encoding',\n 'upgrade',\n 'host',\n 'content-length',\n]);\nconst PATH_PATTERN = /^\\/([^/?]+)(.*)$/;\n\nexport interface BrokerServerOptions {\n /** Providers this session may reach, keyed by Pi provider name. */\n credentials: ReadonlyMap<string, ResolvedCredential>;\n /** Secret the container presents in place of a real credential. */\n token: string;\n /** Receives one line per rejected request, for host-side visibility. */\n onDenied?: (reason: string) => void;\n /** Seam for tests; defaults to the real https transport. */\n requestUpstream?: typeof https.request;\n}\n\nfunction matchesToken(candidate: string, token: string): boolean {\n const left = Buffer.from(candidate);\n const right = Buffer.from(token);\n return left.length === right.length && timingSafeEqual(left, right);\n}\n\nfunction credentialValue(headerValue: string): string {\n return headerValue.toLowerCase().startsWith(BEARER_PREFIX) ? headerValue.slice(BEARER_PREFIX.length) : headerValue;\n}\n\nfunction withCredential(headerValue: string, realKey: string): string {\n return headerValue.toLowerCase().startsWith(BEARER_PREFIX) ? `Bearer ${realKey}` : realKey;\n}\n\n/**\n * Rewrites the request's credential, proving the caller holds the session token.\n *\n * Which header carries the key differs per provider SDK, so every candidate is\n * checked rather than assumed. Returns undefined when nothing presented the\n * token, which is what makes an unauthenticated caller indistinguishable from a\n * misrouted one.\n */\nfunction swapCredential(\n headers: http.IncomingHttpHeaders,\n search: URLSearchParams,\n token: string,\n realKey: string,\n): http.OutgoingHttpHeaders | undefined {\n const forwarded: http.OutgoingHttpHeaders = {};\n let authenticated = false;\n\n for (const [name, value] of Object.entries(headers)) {\n if (value === undefined || HOP_BY_HOP.has(name)) continue;\n const single = Array.isArray(value) ? value[0] : value;\n if ((CREDENTIAL_HEADERS as readonly string[]).includes(name) && single !== undefined) {\n if (!matchesToken(credentialValue(single), token)) return undefined;\n forwarded[name] = withCredential(single, realKey);\n authenticated = true;\n continue;\n }\n forwarded[name] = value;\n }\n\n const queryKey = search.get(CREDENTIAL_QUERY);\n if (queryKey !== null) {\n if (!matchesToken(queryKey, token)) return undefined;\n search.set(CREDENTIAL_QUERY, realKey);\n authenticated = true;\n }\n\n return authenticated ? forwarded : undefined;\n}\n\nfunction reject(response: http.ServerResponse, status: number, message: string): void {\n response.writeHead(status, { 'content-type': 'application/json' });\n response.end(JSON.stringify({ error: { type: 'po_broker', message } }));\n}\n\n/**\n * Terminates provider calls from a sandboxed session on the host.\n *\n * The container never holds a real credential: it presents the session token,\n * and only a request that proves possession of it is forwarded upstream with\n * the host's key attached. Bodies stream in both directions so token-by-token\n * responses are not buffered.\n */\nexport function createBrokerServer(options: BrokerServerOptions): http.Server {\n const requestUpstream = options.requestUpstream ?? https.request;\n\n return http.createServer((request, response) => {\n const match = PATH_PATTERN.exec(request.url ?? '');\n const provider = match?.[1];\n const credential = provider ? options.credentials.get(provider) : undefined;\n if (!match || !credential) {\n options.onDenied?.(`unroutable provider path ${request.url ?? ''}`);\n reject(response, 404, 'Unknown provider for this sandbox session.');\n return;\n }\n\n const target = new URL(`${credential.route.upstream}${match[2] || ''}`);\n const headers = swapCredential(request.headers, target.searchParams, options.token, credential.value);\n if (!headers) {\n options.onDenied?.(`missing or invalid session token for ${credential.route.provider}`);\n reject(response, 401, 'This sandbox session did not present its broker token.');\n return;\n }\n\n const upstream = requestUpstream(\n target,\n { method: request.method, headers: { ...headers, host: target.host } },\n (upstreamResponse) => {\n response.writeHead(upstreamResponse.statusCode ?? 502, upstreamResponse.headers);\n upstreamResponse.pipe(response);\n },\n );\n upstream.on('error', (error: Error) => {\n options.onDenied?.(`upstream ${credential.route.provider} failed: ${error.message}`);\n if (!response.headersSent) reject(response, 502, 'The provider could not be reached from the host.');\n else response.destroy();\n });\n request.on('aborted', () => upstream.destroy());\n request.pipe(upstream);\n });\n}\n","import { randomBytes } from 'node:crypto';\nimport fs from 'node:fs';\nimport type http from 'node:http';\nimport os from 'node:os';\nimport path from 'node:path';\n\nimport type { BrokerEndpoint } from '../../types/sandboxHarness';\nimport { resolveBrokeredCredentials } from '../brokerRoutes';\nimport { createBrokerServer } from '../brokerServer';\n\nconst SOCKET_DIRECTORY_PREFIX = 'po-broker-';\nconst SOCKET_FILE_NAME = 'broker.sock';\nconst OWNER_ONLY_DIRECTORY = 0o700;\nconst TOKEN_BYTES = 32;\nconst LOOPBACK = '127.0.0.1';\nconst LINUX_PLATFORM = 'linux';\nconst EPHEMERAL_PORT = 0;\n\nexport interface RunningBroker {\n /** How a container reaches this broker. */\n endpoint: BrokerEndpoint;\n /** Secret the container presents instead of any real credential. */\n token: string;\n /** Pi provider names the session may reach. */\n providers: string[];\n /** Host environment variables whose value the container receives as the token. */\n withheldEnv: string[];\n stop(): Promise<void>;\n}\n\nexport interface StartBrokerOptions {\n environment: Readonly<Record<string, string | undefined>>;\n onDenied?: (reason: string) => void;\n /** Seam for tests; defaults to this host's platform. */\n platform?: string;\n /**\n * Fixed directory to hold the socket instead of a fresh temporary one.\n *\n * A reused container keeps the mounts it was created with, so a dev\n * container has to find the socket at the same path on every launch.\n */\n socketDirectory?: string;\n /** Forces the loopback transport even where a socket would work. */\n forceLoopback?: boolean;\n}\n\n/**\n * Starts the host-side provider broker for one sandboxed session.\n *\n * Answers undefined when the host holds no brokerable credential, which keeps\n * a session that authenticates some other way on the unbrokered path instead\n * of failing it.\n */\nexport async function startBroker(options: StartBrokerOptions): Promise<RunningBroker | undefined> {\n const resolved = resolveBrokeredCredentials(options.environment);\n if (resolved.length === 0) return undefined;\n\n const token = randomBytes(TOKEN_BYTES).toString('base64url');\n const credentials = new Map(resolved.map((credential) => [credential.route.provider, credential]));\n const server = createBrokerServer({ credentials, token, onDenied: options.onDenied });\n const platform = options.platform ?? process.platform;\n\n const useSocket = platform === LINUX_PLATFORM && options.forceLoopback !== true;\n const { endpoint, dispose } = useSocket\n ? await listenOnSocket(server, options.socketDirectory)\n : await listenOnLoopback(server);\n\n return {\n endpoint,\n token,\n providers: resolved.map((credential) => credential.route.provider),\n withheldEnv: resolved.map((credential) => credential.envName),\n stop: () => stopBroker(server, dispose),\n };\n}\n\n/**\n * Native Linux shares a kernel with the container, so a bind-mounted socket\n * needs no port and is reachable as an ordinary file.\n */\nasync function listenOnSocket(\n server: http.Server,\n fixedDirectory?: string,\n): Promise<{ endpoint: BrokerEndpoint; dispose: () => void }> {\n const socketDirectory = fixedDirectory ?? fs.mkdtempSync(path.join(os.tmpdir(), SOCKET_DIRECTORY_PREFIX));\n fs.mkdirSync(socketDirectory, { recursive: true });\n fs.chmodSync(socketDirectory, OWNER_ONLY_DIRECTORY);\n const socketPath = path.join(socketDirectory, SOCKET_FILE_NAME);\n // A previous session that died without closing leaves the node behind, and\n // bind fails on an existing path.\n fs.rmSync(socketPath, { force: true });\n await new Promise<void>((resolve, reject) => {\n server.once('error', reject);\n server.listen(socketPath, () => resolve());\n });\n return {\n endpoint: { transport: 'unix', socketDirectory },\n // A fixed directory outlives the session that borrowed it.\n dispose: () =>\n fixedDirectory\n ? fs.rmSync(socketPath, { force: true })\n : fs.rmSync(socketDirectory, { recursive: true, force: true }),\n };\n}\n\n/**\n * Everywhere else the container runs in its own virtual machine, which cannot\n * connect to a host unix socket even when the file is shared through: the\n * connect fails with ENOTSUP. Loopback keeps the broker off the network while\n * still being reachable through the engine's host gateway, and the session\n * token is what stops another local process from using it.\n */\nasync function listenOnLoopback(server: http.Server): Promise<{ endpoint: BrokerEndpoint; dispose: () => void }> {\n await new Promise<void>((resolve, reject) => {\n server.once('error', reject);\n server.listen(EPHEMERAL_PORT, LOOPBACK, () => resolve());\n });\n const address = server.address();\n if (address === null || typeof address === 'string') throw new Error('The broker did not report a listening port.');\n return { endpoint: { transport: 'tcp', port: address.port }, dispose: () => undefined };\n}\n\nasync function stopBroker(server: http.Server, dispose: () => void): Promise<void> {\n await new Promise<void>((resolve) => {\n server.close(() => resolve());\n // An in-flight provider stream would otherwise hold the listener open past\n // the session that owns it.\n server.closeAllConnections();\n });\n dispose();\n}\n","import type { PoHeadlessActivity } from '@agimon-ai/po-core/headless';\nimport type { PoServerPluginContext } from '@agimon-ai/po-core/serverFacet';\n\nimport { BROKER_DISABLED_ENV, BROKER_STATUS_SOURCE, DISABLED_VALUE } from '../../../../../constants/sandbox';\nimport { startBroker, type RunningBroker } from '../../../../../services/brokerHost';\nimport { DefaultSandboxExtensionService } from '../../../../../services/extensionService';\nimport type { SandboxServerScope } from './sandboxScope';\n\nexport function createSandboxServerRoot({ agent }: PoServerPluginContext) {\n if (!agent) return { value: {} as SandboxServerScope };\n const service = new DefaultSandboxExtensionService(agent.context.environment);\n let broker: RunningBroker | undefined;\n const activity: PoHeadlessActivity = {\n name: BROKER_STATUS_SOURCE,\n async start(execution) {\n if (execution.environment[BROKER_DISABLED_ENV] === DISABLED_VALUE) return () => undefined;\n const started = await startBroker({\n environment: execution.environment,\n onDenied: (reason) => {\n void execution.client.notify({\n title: 'Po sandbox broker',\n body: `Broker refused a call: ${reason}`,\n level: 'warning',\n });\n },\n });\n broker = started;\n if (started === undefined) return () => undefined;\n execution.client.setStatus(BROKER_STATUS_SOURCE, `provider broker: ${started.providers.join(', ')}`);\n return async () => {\n if (broker !== started) return;\n broker = undefined;\n execution.client.setStatus(BROKER_STATUS_SOURCE, undefined);\n await started.stop();\n };\n },\n };\n return { value: { service }, activities: [activity] };\n}\n","import { defineRoot } from '@agimon-ai/po-core/extensionFile';\nimport type { PoServerProviderOverrides } from '@agimon-ai/po-core/serverFacet';\n\nimport { brokeredProviderOverrides } from '../../../../services/brokerProviders';\nimport { createSandboxServerRoot } from './_lib/serverRoot';\n\nexport default defineRoot(\n Object.assign(createSandboxServerRoot, {\n providerOverrides: (({ environment }) =>\n brokeredProviderOverrides(environment).map(({ provider, baseUrl }) => ({\n name: provider,\n baseUrl,\n }))) satisfies PoServerProviderOverrides,\n }),\n);\n","import type { WithRoot } from '@agimon-ai/po-core/extensionFile';\nimport { defineCommand } from '@agimon-ai/po-core/serverFacet';\nimport type { PoServerPluginContext } from '@agimon-ai/po-core/serverFacet';\n\nimport { COMMAND_DESCRIPTION, COMMAND_NAME } from '../../../../../constants/sandbox';\nimport type { SandboxServerScope } from './sandboxScope';\n\nexport function createSandboxServerCommands(context: WithRoot<PoServerPluginContext, SandboxServerScope>) {\n const service = context.root.service;\n if (!service) return [];\n return [\n defineCommand({\n name: COMMAND_NAME,\n description: COMMAND_DESCRIPTION,\n async execute() {\n const result = await service.execute();\n await context.agent?.context.client.notify({ body: result.message, level: result.level });\n },\n }),\n ];\n}\n","import { defineRoutedContribution, type WithRoot } from '@agimon-ai/po-core/extensionFile';\nimport type { PoServerPluginContext } from '@agimon-ai/po-core/serverFacet';\n\nimport { createSandboxServerCommands } from '../_lib/sandboxCommands';\nimport type { SandboxServerScope } from '../_lib/sandboxScope';\n\nexport default defineRoutedContribution(\n (context: WithRoot<PoServerPluginContext, SandboxServerScope>) => createSandboxServerCommands(context),\n { cardinality: 'many' },\n);\n","import type { PoHeadlessResource } from '@agimon-ai/po-core/headless';\nimport { readPackageResource } from '@agimon-ai/po-core/serverFacet';\n\nimport type { SandboxServerScope } from './sandboxScope';\n\nexport function createSandboxContextResource(root: SandboxServerScope): PoHeadlessResource | undefined {\n if (!root.service) return undefined;\n return {\n // The package index is Help-catalog material, not standing instruction. The Pi\n // facet has always routed it through the Help service; this is the server's gate.\n when: { state: { 'minor-mode': 'help' }, attribution: { kind: 'minor', mode: 'help' } },\n name: 'po-sandbox',\n kind: 'context',\n read: () => readPackageResource(import.meta.url, 'llms.txt'),\n };\n}\n\nexport function createSandboxSkillResource(root: SandboxServerScope): PoHeadlessResource | undefined {\n if (!root.service) return undefined;\n return {\n name: 'po-use-sandbox',\n when: { state: { 'minor-mode': 'help' }, attribution: { kind: 'minor', mode: 'help' } },\n kind: 'skill',\n read: () => readPackageResource(import.meta.url, 'src/prompts/po-use-sandbox/SKILL.md'),\n };\n}\n\nexport function createSandboxHelpResource(moduleUrl: string) {\n return {\n source: '@agimon-ai/po-sandbox',\n moduleUrl,\n skills: [\n {\n name: 'po-use-sandbox',\n description:\n 'Use @agimon-ai/po-sandbox: Container sandbox for Po launches: the agent, extensions, and tools run inside Docker or Podman while the terminal stays on the host',\n },\n ],\n };\n}\n","import { defineResource, defineRoutedContribution, type WithRoot } from '@agimon-ai/po-core/extensionFile';\nimport type { PoServerPluginContext } from '@agimon-ai/po-core/serverFacet';\n\nimport { createSandboxContextResource } from '../_lib/sandboxResources';\nimport type { SandboxServerScope } from '../_lib/sandboxScope';\n\nexport default defineRoutedContribution(\n defineResource((context: WithRoot<PoServerPluginContext, SandboxServerScope>) =>\n createSandboxContextResource(context.root),\n ),\n { cardinality: 'optional' },\n);\n","import { defineResource, defineRoutedContribution, type WithRoot } from '@agimon-ai/po-core/extensionFile';\nimport type { PoServerPluginContext } from '@agimon-ai/po-core/serverFacet';\n\nimport { createSandboxSkillResource } from '../_lib/sandboxResources';\nimport type { SandboxServerScope } from '../_lib/sandboxScope';\n\nexport default defineRoutedContribution(\n defineResource((context: WithRoot<PoServerPluginContext, SandboxServerScope>) =>\n createSandboxSkillResource(context.root),\n ),\n { cardinality: 'optional' },\n);\n","// Generated by @agimon-ai/po-build. Do not edit by hand.\nimport { defineServerPlugin, type PoServerSessionPlugin } from '@agimon-ai/po-core/serverFacet';\nimport { composeServerProviderOverrides } from '@agimon-ai/po-core/serverFacet';\nimport { composeRootHooks } from '@agimon-ai/po-core/extensionFile';\n\nimport rootSession from '../src/extensions/workspaces/sessions/(backend)/root.server';\nimport commandSandbox from '../src/extensions/workspaces/sessions/(backend)/command/sandbox.server';\nimport resourcePoSandbox from '../src/extensions/workspaces/sessions/(backend)/resource/poSandbox.server';\nimport resourcePoUseSandbox from '../src/extensions/workspaces/sessions/(backend)/resource/poUseSandbox.server';\n\ntype Factory<T, C> = (context: C) => T;\nconst at = <T, C>(value: T | Factory<T, C>, context: C): T =>\n typeof value === 'function' ? (value as Factory<T, C>)(context) : value;\ntype ManyFactory<T, C> = (context: C) => T | readonly T[];\nconst many = <T, C>(value: T | readonly T[] | ManyFactory<T, C>, context: C): readonly T[] => {\n const resolved = typeof value === 'function' ? (value as ManyFactory<T, C>)(context) : value;\n return Array.isArray(resolved) ? resolved : [resolved as T];\n};\n/* v8 ignore next -- generated normalization branch */\nconst defined = <T>(values: readonly (T | undefined)[]): T[] =>\n values.filter((value): value is T => value !== undefined);\n\nexport const facet = defineServerPlugin({\n name: '@agimon-ai/po-sandbox',\n providerOverrides: composeServerProviderOverrides(rootSession),\n session: async (context) => {\n const scopeSession = await rootSession(context);\n const contextSession = { ...context, root: scopeSession.value };\n return {\n ...composeRootHooks(scopeSession),\n get commands(): PoServerSessionPlugin['commands'] { return [...many(commandSandbox, contextSession)]; },\n get resources(): PoServerSessionPlugin['resources'] { return defined([at(resourcePoSandbox, contextSession), at(resourcePoUseSandbox, contextSession)]); },\n services: [...(scopeSession.services ?? [])],\n get activities(): PoServerSessionPlugin['activities'] { return [...(scopeSession.activities ?? [])]; },\n };\n },\n});\n\nexport default facet;\n"],"mappings":"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"}
@@ -1,5 +1,5 @@
1
1
  //#region generated/server.d.ts
2
- export declare const facet: import("@agimon-ai/po-core/serverFacet").DoomServerFacet;
2
+ export declare const facet: import("@agimon-ai/po-core/serverFacet").PoServerFacet;
3
3
  //#endregion
4
4
  export { facet as default };
5
5
  //# sourceMappingURL=server.d.mts.map
@@ -32,7 +32,7 @@ function brokeredProviderOverrides(environment) {
32
32
  }
33
33
  //#endregion
34
34
  //#region src/constants/sandbox.ts
35
- const COMMAND_NAME = "doom-sandbox";
35
+ const COMMAND_NAME = "po-sandbox";
36
36
  const COMMAND_DESCRIPTION = "Show sandbox container status for this session";
37
37
  const BROKER_STATUS_SOURCE = "po-sandbox-broker";
38
38
  //#endregion
@@ -1 +1 @@
1
- {"version":3,"file":"server.mjs","names":["rootSession","commandSandbox","resourcePoSandbox","resourcePoUseSandbox"],"sources":["../../src/services/brokerProviders/index.ts","../../src/constants/sandbox.ts","../../src/services/brokerRoutes/index.ts","../../src/services/brokerServer/index.ts","../../src/services/brokerHost/index.ts","../../src/extensions/workspaces/sessions/(backend)/_lib/serverRoot.ts","../../src/extensions/workspaces/sessions/(backend)/root.server.ts","../../src/extensions/workspaces/sessions/(backend)/_lib/sandboxCommands.ts","../../src/extensions/workspaces/sessions/(backend)/command/sandbox.server.ts","../../src/extensions/workspaces/sessions/(backend)/_lib/sandboxResources.ts","../../src/extensions/workspaces/sessions/(backend)/resource/poSandbox.server.ts","../../src/extensions/workspaces/sessions/(backend)/resource/poUseSandbox.server.ts","../../generated/server.ts"],"sourcesContent":["import { BROKER_ADDRESS_ENV, BROKER_CONTAINER_PORT, BROKER_PROVIDERS_ENV, BROKER_SOCKET_ENV } from '../sandboxBridge';\n\nexport interface BrokeredProviderOverride {\n provider: string;\n baseUrl: string;\n}\n\n/**\n * Points brokered providers at whichever route the launch granted.\n *\n * Pi reads base URLs from package-internal provider data, so redirecting a\n * session means overriding the provider rather than setting an environment\n * variable. The credential is left alone: the launch already replaced it with\n * the session token the broker expects.\n *\n * A broker on a host port is addressed directly. Only a mounted socket needs\n * the loopback bridge, because no SDK can dial a socket path.\n */\nexport function brokeredProviderOverrides(\n environment: Readonly<Record<string, string | undefined>>,\n): BrokeredProviderOverride[] {\n const configured = environment[BROKER_PROVIDERS_ENV]?.trim();\n if (!configured) return [];\n const authority = environment[BROKER_SOCKET_ENV]?.trim()\n ? `127.0.0.1:${BROKER_CONTAINER_PORT}`\n : environment[BROKER_ADDRESS_ENV]?.trim();\n if (!authority) return [];\n return configured\n .split(',')\n .map((provider) => provider.trim())\n .filter((provider) => provider.length > 0)\n .map((provider) => ({ provider, baseUrl: `http://${authority}/${provider}` }));\n}\n","export const COMMAND_NAME = 'doom-sandbox';\nexport const COMMAND_DESCRIPTION = 'Show sandbox container status for this session';\n\nexport const BROKER_DISABLED_ENV = 'PO_SANDBOX_BROKER';\nexport const DISABLED_VALUE = '0';\nexport const BROKER_STATUS_SOURCE = 'po-sandbox-broker';\n","export interface BrokerRoute {\n /** Pi provider name, which is also the broker's first path segment. */\n provider: string;\n /** Upstream origin plus any base path Pi's own provider data carries. */\n upstream: string;\n /** Host environment variables that may hold the real credential, in priority order. */\n hostKeyEnv: readonly string[];\n}\n\n/**\n * Providers the broker can terminate, mirroring Pi's own provider data.\n *\n * Curated rather than derived: Pi keeps base URLs in package-internal JSON that\n * carries no stability promise. A provider missing here is simply not brokered,\n * and its credential stays on the host.\n */\nconst ROUTES: readonly BrokerRoute[] = [\n { provider: 'anthropic', upstream: 'https://api.anthropic.com', hostKeyEnv: ['ANTHROPIC_API_KEY'] },\n { provider: 'openai', upstream: 'https://api.openai.com/v1', hostKeyEnv: ['OPENAI_API_KEY'] },\n { provider: 'google', upstream: 'https://generativelanguage.googleapis.com/v1beta', hostKeyEnv: ['GEMINI_API_KEY'] },\n { provider: 'groq', upstream: 'https://api.groq.com/openai/v1', hostKeyEnv: ['GROQ_API_KEY'] },\n { provider: 'xai', upstream: 'https://api.x.ai/v1', hostKeyEnv: ['XAI_API_KEY'] },\n { provider: 'openrouter', upstream: 'https://openrouter.ai/api/v1', hostKeyEnv: ['OPENROUTER_API_KEY'] },\n { provider: 'deepseek', upstream: 'https://api.deepseek.com', hostKeyEnv: ['DEEPSEEK_API_KEY'] },\n { provider: 'mistral', upstream: 'https://api.mistral.ai', hostKeyEnv: ['MISTRAL_API_KEY'] },\n { provider: 'together', upstream: 'https://api.together.ai/v1', hostKeyEnv: ['TOGETHER_API_KEY'] },\n { provider: 'cerebras', upstream: 'https://api.cerebras.ai/v1', hostKeyEnv: ['CEREBRAS_API_KEY'] },\n { provider: 'moonshotai', upstream: 'https://api.moonshot.ai/v1', hostKeyEnv: ['MOONSHOT_API_KEY'] },\n { provider: 'zai', upstream: 'https://api.z.ai/api/coding/paas/v4', hostKeyEnv: ['ZAI_API_KEY'] },\n];\n\nexport function brokerRoutes(): readonly BrokerRoute[] {\n return ROUTES;\n}\n\nexport function findBrokerRoute(provider: string): BrokerRoute | undefined {\n return ROUTES.find((route) => route.provider === provider);\n}\n\nexport interface ResolvedCredential {\n route: BrokerRoute;\n /** Environment variable the container would otherwise have received. */\n envName: string;\n value: string;\n}\n\n/** Selects the providers this host can actually broker for a session. */\nexport function resolveBrokeredCredentials(\n environment: Readonly<Record<string, string | undefined>>,\n): ResolvedCredential[] {\n const resolved: ResolvedCredential[] = [];\n for (const route of ROUTES) {\n for (const envName of route.hostKeyEnv) {\n const value = environment[envName]?.trim();\n if (value) {\n resolved.push({ route, envName, value });\n break;\n }\n }\n }\n return resolved;\n}\n","import { timingSafeEqual } from 'node:crypto';\nimport http from 'node:http';\nimport https from 'node:https';\n\nimport type { ResolvedCredential } from '../brokerRoutes';\n\n/** Headers a provider SDK may carry its credential in. */\nconst CREDENTIAL_HEADERS = ['x-api-key', 'authorization', 'x-goog-api-key'] as const;\n/** Query parameter some Google clients use instead of a header. */\nconst CREDENTIAL_QUERY = 'key';\nconst BEARER_PREFIX = 'bearer ';\n/** Connection-scoped headers that must not cross to the upstream request. */\nconst HOP_BY_HOP = new Set([\n 'connection',\n 'keep-alive',\n 'proxy-authenticate',\n 'proxy-authorization',\n 'te',\n 'trailer',\n 'transfer-encoding',\n 'upgrade',\n 'host',\n 'content-length',\n]);\nconst PATH_PATTERN = /^\\/([^/?]+)(.*)$/;\n\nexport interface BrokerServerOptions {\n /** Providers this session may reach, keyed by Pi provider name. */\n credentials: ReadonlyMap<string, ResolvedCredential>;\n /** Secret the container presents in place of a real credential. */\n token: string;\n /** Receives one line per rejected request, for host-side visibility. */\n onDenied?: (reason: string) => void;\n /** Seam for tests; defaults to the real https transport. */\n requestUpstream?: typeof https.request;\n}\n\nfunction matchesToken(candidate: string, token: string): boolean {\n const left = Buffer.from(candidate);\n const right = Buffer.from(token);\n return left.length === right.length && timingSafeEqual(left, right);\n}\n\nfunction credentialValue(headerValue: string): string {\n return headerValue.toLowerCase().startsWith(BEARER_PREFIX) ? headerValue.slice(BEARER_PREFIX.length) : headerValue;\n}\n\nfunction withCredential(headerValue: string, realKey: string): string {\n return headerValue.toLowerCase().startsWith(BEARER_PREFIX) ? `Bearer ${realKey}` : realKey;\n}\n\n/**\n * Rewrites the request's credential, proving the caller holds the session token.\n *\n * Which header carries the key differs per provider SDK, so every candidate is\n * checked rather than assumed. Returns undefined when nothing presented the\n * token, which is what makes an unauthenticated caller indistinguishable from a\n * misrouted one.\n */\nfunction swapCredential(\n headers: http.IncomingHttpHeaders,\n search: URLSearchParams,\n token: string,\n realKey: string,\n): http.OutgoingHttpHeaders | undefined {\n const forwarded: http.OutgoingHttpHeaders = {};\n let authenticated = false;\n\n for (const [name, value] of Object.entries(headers)) {\n if (value === undefined || HOP_BY_HOP.has(name)) continue;\n const single = Array.isArray(value) ? value[0] : value;\n if ((CREDENTIAL_HEADERS as readonly string[]).includes(name) && single !== undefined) {\n if (!matchesToken(credentialValue(single), token)) return undefined;\n forwarded[name] = withCredential(single, realKey);\n authenticated = true;\n continue;\n }\n forwarded[name] = value;\n }\n\n const queryKey = search.get(CREDENTIAL_QUERY);\n if (queryKey !== null) {\n if (!matchesToken(queryKey, token)) return undefined;\n search.set(CREDENTIAL_QUERY, realKey);\n authenticated = true;\n }\n\n return authenticated ? forwarded : undefined;\n}\n\nfunction reject(response: http.ServerResponse, status: number, message: string): void {\n response.writeHead(status, { 'content-type': 'application/json' });\n response.end(JSON.stringify({ error: { type: 'po_broker', message } }));\n}\n\n/**\n * Terminates provider calls from a sandboxed session on the host.\n *\n * The container never holds a real credential: it presents the session token,\n * and only a request that proves possession of it is forwarded upstream with\n * the host's key attached. Bodies stream in both directions so token-by-token\n * responses are not buffered.\n */\nexport function createBrokerServer(options: BrokerServerOptions): http.Server {\n const requestUpstream = options.requestUpstream ?? https.request;\n\n return http.createServer((request, response) => {\n const match = PATH_PATTERN.exec(request.url ?? '');\n const provider = match?.[1];\n const credential = provider ? options.credentials.get(provider) : undefined;\n if (!match || !credential) {\n options.onDenied?.(`unroutable provider path ${request.url ?? ''}`);\n reject(response, 404, 'Unknown provider for this sandbox session.');\n return;\n }\n\n const target = new URL(`${credential.route.upstream}${match[2] || ''}`);\n const headers = swapCredential(request.headers, target.searchParams, options.token, credential.value);\n if (!headers) {\n options.onDenied?.(`missing or invalid session token for ${credential.route.provider}`);\n reject(response, 401, 'This sandbox session did not present its broker token.');\n return;\n }\n\n const upstream = requestUpstream(\n target,\n { method: request.method, headers: { ...headers, host: target.host } },\n (upstreamResponse) => {\n response.writeHead(upstreamResponse.statusCode ?? 502, upstreamResponse.headers);\n upstreamResponse.pipe(response);\n },\n );\n upstream.on('error', (error: Error) => {\n options.onDenied?.(`upstream ${credential.route.provider} failed: ${error.message}`);\n if (!response.headersSent) reject(response, 502, 'The provider could not be reached from the host.');\n else response.destroy();\n });\n request.on('aborted', () => upstream.destroy());\n request.pipe(upstream);\n });\n}\n","import { randomBytes } from 'node:crypto';\nimport fs from 'node:fs';\nimport type http from 'node:http';\nimport os from 'node:os';\nimport path from 'node:path';\n\nimport type { BrokerEndpoint } from '../../types/sandboxHarness';\nimport { resolveBrokeredCredentials } from '../brokerRoutes';\nimport { createBrokerServer } from '../brokerServer';\n\nconst SOCKET_DIRECTORY_PREFIX = 'po-broker-';\nconst SOCKET_FILE_NAME = 'broker.sock';\nconst OWNER_ONLY_DIRECTORY = 0o700;\nconst TOKEN_BYTES = 32;\nconst LOOPBACK = '127.0.0.1';\nconst LINUX_PLATFORM = 'linux';\nconst EPHEMERAL_PORT = 0;\n\nexport interface RunningBroker {\n /** How a container reaches this broker. */\n endpoint: BrokerEndpoint;\n /** Secret the container presents instead of any real credential. */\n token: string;\n /** Pi provider names the session may reach. */\n providers: string[];\n /** Host environment variables whose value the container receives as the token. */\n withheldEnv: string[];\n stop(): Promise<void>;\n}\n\nexport interface StartBrokerOptions {\n environment: Readonly<Record<string, string | undefined>>;\n onDenied?: (reason: string) => void;\n /** Seam for tests; defaults to this host's platform. */\n platform?: string;\n /**\n * Fixed directory to hold the socket instead of a fresh temporary one.\n *\n * A reused container keeps the mounts it was created with, so a dev\n * container has to find the socket at the same path on every launch.\n */\n socketDirectory?: string;\n /** Forces the loopback transport even where a socket would work. */\n forceLoopback?: boolean;\n}\n\n/**\n * Starts the host-side provider broker for one sandboxed session.\n *\n * Answers undefined when the host holds no brokerable credential, which keeps\n * a session that authenticates some other way on the unbrokered path instead\n * of failing it.\n */\nexport async function startBroker(options: StartBrokerOptions): Promise<RunningBroker | undefined> {\n const resolved = resolveBrokeredCredentials(options.environment);\n if (resolved.length === 0) return undefined;\n\n const token = randomBytes(TOKEN_BYTES).toString('base64url');\n const credentials = new Map(resolved.map((credential) => [credential.route.provider, credential]));\n const server = createBrokerServer({ credentials, token, onDenied: options.onDenied });\n const platform = options.platform ?? process.platform;\n\n const useSocket = platform === LINUX_PLATFORM && options.forceLoopback !== true;\n const { endpoint, dispose } = useSocket\n ? await listenOnSocket(server, options.socketDirectory)\n : await listenOnLoopback(server);\n\n return {\n endpoint,\n token,\n providers: resolved.map((credential) => credential.route.provider),\n withheldEnv: resolved.map((credential) => credential.envName),\n stop: () => stopBroker(server, dispose),\n };\n}\n\n/**\n * Native Linux shares a kernel with the container, so a bind-mounted socket\n * needs no port and is reachable as an ordinary file.\n */\nasync function listenOnSocket(\n server: http.Server,\n fixedDirectory?: string,\n): Promise<{ endpoint: BrokerEndpoint; dispose: () => void }> {\n const socketDirectory = fixedDirectory ?? fs.mkdtempSync(path.join(os.tmpdir(), SOCKET_DIRECTORY_PREFIX));\n fs.mkdirSync(socketDirectory, { recursive: true });\n fs.chmodSync(socketDirectory, OWNER_ONLY_DIRECTORY);\n const socketPath = path.join(socketDirectory, SOCKET_FILE_NAME);\n // A previous session that died without closing leaves the node behind, and\n // bind fails on an existing path.\n fs.rmSync(socketPath, { force: true });\n await new Promise<void>((resolve, reject) => {\n server.once('error', reject);\n server.listen(socketPath, () => resolve());\n });\n return {\n endpoint: { transport: 'unix', socketDirectory },\n // A fixed directory outlives the session that borrowed it.\n dispose: () =>\n fixedDirectory\n ? fs.rmSync(socketPath, { force: true })\n : fs.rmSync(socketDirectory, { recursive: true, force: true }),\n };\n}\n\n/**\n * Everywhere else the container runs in its own virtual machine, which cannot\n * connect to a host unix socket even when the file is shared through: the\n * connect fails with ENOTSUP. Loopback keeps the broker off the network while\n * still being reachable through the engine's host gateway, and the session\n * token is what stops another local process from using it.\n */\nasync function listenOnLoopback(server: http.Server): Promise<{ endpoint: BrokerEndpoint; dispose: () => void }> {\n await new Promise<void>((resolve, reject) => {\n server.once('error', reject);\n server.listen(EPHEMERAL_PORT, LOOPBACK, () => resolve());\n });\n const address = server.address();\n if (address === null || typeof address === 'string') throw new Error('The broker did not report a listening port.');\n return { endpoint: { transport: 'tcp', port: address.port }, dispose: () => undefined };\n}\n\nasync function stopBroker(server: http.Server, dispose: () => void): Promise<void> {\n await new Promise<void>((resolve) => {\n server.close(() => resolve());\n // An in-flight provider stream would otherwise hold the listener open past\n // the session that owns it.\n server.closeAllConnections();\n });\n dispose();\n}\n","import type { DoomHeadlessActivity } from '@agimon-ai/po-core/headless';\nimport type { DoomServerPluginContext } from '@agimon-ai/po-core/serverFacet';\n\nimport { BROKER_DISABLED_ENV, BROKER_STATUS_SOURCE, DISABLED_VALUE } from '../../../../../constants/sandbox';\nimport { startBroker, type RunningBroker } from '../../../../../services/brokerHost';\nimport { DefaultSandboxExtensionService } from '../../../../../services/extensionService';\nimport type { SandboxServerScope } from './sandboxScope';\n\nexport function createSandboxServerRoot({ agent }: DoomServerPluginContext) {\n if (!agent) return { value: {} as SandboxServerScope };\n const service = new DefaultSandboxExtensionService(agent.context.environment);\n let broker: RunningBroker | undefined;\n const activity: DoomHeadlessActivity = {\n name: BROKER_STATUS_SOURCE,\n async start(execution) {\n if (execution.environment[BROKER_DISABLED_ENV] === DISABLED_VALUE) return () => undefined;\n const started = await startBroker({\n environment: execution.environment,\n onDenied: (reason) => {\n void execution.client.notify({\n title: 'Po sandbox broker',\n body: `Broker refused a call: ${reason}`,\n level: 'warning',\n });\n },\n });\n broker = started;\n if (started === undefined) return () => undefined;\n execution.client.setStatus(BROKER_STATUS_SOURCE, `provider broker: ${started.providers.join(', ')}`);\n return async () => {\n if (broker !== started) return;\n broker = undefined;\n execution.client.setStatus(BROKER_STATUS_SOURCE, undefined);\n await started.stop();\n };\n },\n };\n return { value: { service }, activities: [activity] };\n}\n","import { defineRoot } from '@agimon-ai/po-core/extensionFile';\nimport type { DoomServerProviderOverrides } from '@agimon-ai/po-core/serverFacet';\n\nimport { brokeredProviderOverrides } from '../../../../services/brokerProviders';\nimport { createSandboxServerRoot } from './_lib/serverRoot';\n\nexport default defineRoot(\n Object.assign(createSandboxServerRoot, {\n providerOverrides: (({ environment }) =>\n brokeredProviderOverrides(environment).map(({ provider, baseUrl }) => ({\n name: provider,\n baseUrl,\n }))) satisfies DoomServerProviderOverrides,\n }),\n);\n","import type { WithRoot } from '@agimon-ai/po-core/extensionFile';\nimport { defineCommand } from '@agimon-ai/po-core/serverFacet';\nimport type { DoomServerPluginContext } from '@agimon-ai/po-core/serverFacet';\n\nimport { COMMAND_DESCRIPTION, COMMAND_NAME } from '../../../../../constants/sandbox';\nimport type { SandboxServerScope } from './sandboxScope';\n\nexport function createSandboxServerCommands(context: WithRoot<DoomServerPluginContext, SandboxServerScope>) {\n const service = context.root.service;\n if (!service) return [];\n return [\n defineCommand({\n name: COMMAND_NAME,\n description: COMMAND_DESCRIPTION,\n async execute() {\n const result = await service.execute();\n await context.agent?.context.client.notify({ body: result.message, level: result.level });\n },\n }),\n ];\n}\n","import { defineRoutedContribution, type WithRoot } from '@agimon-ai/po-core/extensionFile';\nimport type { DoomServerPluginContext } from '@agimon-ai/po-core/serverFacet';\n\nimport { createSandboxServerCommands } from '../_lib/sandboxCommands';\nimport type { SandboxServerScope } from '../_lib/sandboxScope';\n\nexport default defineRoutedContribution(\n (context: WithRoot<DoomServerPluginContext, SandboxServerScope>) => createSandboxServerCommands(context),\n { cardinality: 'many' },\n);\n","import type { DoomHeadlessResource } from '@agimon-ai/po-core/headless';\nimport { readPackageResource } from '@agimon-ai/po-core/serverFacet';\n\nimport type { SandboxServerScope } from './sandboxScope';\n\nexport function createSandboxContextResource(root: SandboxServerScope): DoomHeadlessResource | undefined {\n if (!root.service) return undefined;\n return {\n // The package index is Help-catalog material, not standing instruction. The Pi\n // facet has always routed it through the Help service; this is the server's gate.\n when: { state: { 'minor-mode': 'help' }, attribution: { kind: 'minor', mode: 'help' } },\n name: 'po-sandbox',\n kind: 'context',\n read: () => readPackageResource(import.meta.url, 'llms.txt'),\n };\n}\n\nexport function createSandboxSkillResource(root: SandboxServerScope): DoomHeadlessResource | undefined {\n if (!root.service) return undefined;\n return {\n name: 'po-use-sandbox',\n when: { state: { 'minor-mode': 'help' }, attribution: { kind: 'minor', mode: 'help' } },\n kind: 'skill',\n read: () => readPackageResource(import.meta.url, 'src/prompts/po-use-sandbox/SKILL.md'),\n };\n}\n\nexport function createSandboxHelpResource(moduleUrl: string) {\n return {\n source: '@agimon-ai/po-sandbox',\n moduleUrl,\n skills: [\n {\n name: 'po-use-sandbox',\n description:\n 'Use @agimon-ai/po-sandbox: Container sandbox for Po launches: the agent, extensions, and tools run inside Docker or Podman while the terminal stays on the host',\n },\n ],\n };\n}\n","import { defineResource, defineRoutedContribution, type WithRoot } from '@agimon-ai/po-core/extensionFile';\nimport type { DoomServerPluginContext } from '@agimon-ai/po-core/serverFacet';\n\nimport { createSandboxContextResource } from '../_lib/sandboxResources';\nimport type { SandboxServerScope } from '../_lib/sandboxScope';\n\nexport default defineRoutedContribution(\n defineResource((context: WithRoot<DoomServerPluginContext, SandboxServerScope>) =>\n createSandboxContextResource(context.root),\n ),\n { cardinality: 'optional' },\n);\n","import { defineResource, defineRoutedContribution, type WithRoot } from '@agimon-ai/po-core/extensionFile';\nimport type { DoomServerPluginContext } from '@agimon-ai/po-core/serverFacet';\n\nimport { createSandboxSkillResource } from '../_lib/sandboxResources';\nimport type { SandboxServerScope } from '../_lib/sandboxScope';\n\nexport default defineRoutedContribution(\n defineResource((context: WithRoot<DoomServerPluginContext, SandboxServerScope>) =>\n createSandboxSkillResource(context.root),\n ),\n { cardinality: 'optional' },\n);\n","// Generated by @agimon-ai/po-build. Do not edit by hand.\nimport { defineServerPlugin, type DoomServerSessionPlugin } from '@agimon-ai/po-core/serverFacet';\nimport { composeServerProviderOverrides } from '@agimon-ai/po-core/serverFacet';\nimport { composeRootHooks } from '@agimon-ai/po-core/extensionFile';\n\nimport rootSession from '../src/extensions/workspaces/sessions/(backend)/root.server';\nimport commandSandbox from '../src/extensions/workspaces/sessions/(backend)/command/sandbox.server';\nimport resourcePoSandbox from '../src/extensions/workspaces/sessions/(backend)/resource/poSandbox.server';\nimport resourcePoUseSandbox from '../src/extensions/workspaces/sessions/(backend)/resource/poUseSandbox.server';\n\ntype Factory<T, C> = (context: C) => T;\nconst at = <T, C>(value: T | Factory<T, C>, context: C): T =>\n typeof value === 'function' ? (value as Factory<T, C>)(context) : value;\ntype ManyFactory<T, C> = (context: C) => T | readonly T[];\nconst many = <T, C>(value: T | readonly T[] | ManyFactory<T, C>, context: C): readonly T[] => {\n const resolved = typeof value === 'function' ? (value as ManyFactory<T, C>)(context) : value;\n return Array.isArray(resolved) ? resolved : [resolved as T];\n};\n/* v8 ignore next -- generated normalization branch */\nconst defined = <T>(values: readonly (T | undefined)[]): T[] =>\n values.filter((value): value is T => value !== undefined);\n\nexport const facet = defineServerPlugin({\n name: '@agimon-ai/po-sandbox',\n providerOverrides: composeServerProviderOverrides(rootSession),\n session: async (context) => {\n const scopeSession = await rootSession(context);\n const contextSession = { ...context, root: scopeSession.value };\n return {\n ...composeRootHooks(scopeSession),\n get commands(): DoomServerSessionPlugin['commands'] { return [...many(commandSandbox, contextSession)]; },\n get resources(): DoomServerSessionPlugin['resources'] { return defined([at(resourcePoSandbox, contextSession), at(resourcePoUseSandbox, contextSession)]); },\n services: [...(scopeSession.services ?? [])],\n get activities(): DoomServerSessionPlugin['activities'] { return [...(scopeSession.activities ?? [])]; },\n };\n },\n});\n\nexport default facet;\n"],"mappings":"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"}
1
+ {"version":3,"file":"server.mjs","names":["rootSession","commandSandbox","resourcePoSandbox","resourcePoUseSandbox"],"sources":["../../src/services/brokerProviders/index.ts","../../src/constants/sandbox.ts","../../src/services/brokerRoutes/index.ts","../../src/services/brokerServer/index.ts","../../src/services/brokerHost/index.ts","../../src/extensions/workspaces/sessions/(backend)/_lib/serverRoot.ts","../../src/extensions/workspaces/sessions/(backend)/root.server.ts","../../src/extensions/workspaces/sessions/(backend)/_lib/sandboxCommands.ts","../../src/extensions/workspaces/sessions/(backend)/command/sandbox.server.ts","../../src/extensions/workspaces/sessions/(backend)/_lib/sandboxResources.ts","../../src/extensions/workspaces/sessions/(backend)/resource/poSandbox.server.ts","../../src/extensions/workspaces/sessions/(backend)/resource/poUseSandbox.server.ts","../../generated/server.ts"],"sourcesContent":["import { BROKER_ADDRESS_ENV, BROKER_CONTAINER_PORT, BROKER_PROVIDERS_ENV, BROKER_SOCKET_ENV } from '../sandboxBridge';\n\nexport interface BrokeredProviderOverride {\n provider: string;\n baseUrl: string;\n}\n\n/**\n * Points brokered providers at whichever route the launch granted.\n *\n * Pi reads base URLs from package-internal provider data, so redirecting a\n * session means overriding the provider rather than setting an environment\n * variable. The credential is left alone: the launch already replaced it with\n * the session token the broker expects.\n *\n * A broker on a host port is addressed directly. Only a mounted socket needs\n * the loopback bridge, because no SDK can dial a socket path.\n */\nexport function brokeredProviderOverrides(\n environment: Readonly<Record<string, string | undefined>>,\n): BrokeredProviderOverride[] {\n const configured = environment[BROKER_PROVIDERS_ENV]?.trim();\n if (!configured) return [];\n const authority = environment[BROKER_SOCKET_ENV]?.trim()\n ? `127.0.0.1:${BROKER_CONTAINER_PORT}`\n : environment[BROKER_ADDRESS_ENV]?.trim();\n if (!authority) return [];\n return configured\n .split(',')\n .map((provider) => provider.trim())\n .filter((provider) => provider.length > 0)\n .map((provider) => ({ provider, baseUrl: `http://${authority}/${provider}` }));\n}\n","export const COMMAND_NAME = 'po-sandbox';\nexport const COMMAND_DESCRIPTION = 'Show sandbox container status for this session';\n\nexport const BROKER_DISABLED_ENV = 'PO_SANDBOX_BROKER';\nexport const DISABLED_VALUE = '0';\nexport const BROKER_STATUS_SOURCE = 'po-sandbox-broker';\n","export interface BrokerRoute {\n /** Pi provider name, which is also the broker's first path segment. */\n provider: string;\n /** Upstream origin plus any base path Pi's own provider data carries. */\n upstream: string;\n /** Host environment variables that may hold the real credential, in priority order. */\n hostKeyEnv: readonly string[];\n}\n\n/**\n * Providers the broker can terminate, mirroring Pi's own provider data.\n *\n * Curated rather than derived: Pi keeps base URLs in package-internal JSON that\n * carries no stability promise. A provider missing here is simply not brokered,\n * and its credential stays on the host.\n */\nconst ROUTES: readonly BrokerRoute[] = [\n { provider: 'anthropic', upstream: 'https://api.anthropic.com', hostKeyEnv: ['ANTHROPIC_API_KEY'] },\n { provider: 'openai', upstream: 'https://api.openai.com/v1', hostKeyEnv: ['OPENAI_API_KEY'] },\n { provider: 'google', upstream: 'https://generativelanguage.googleapis.com/v1beta', hostKeyEnv: ['GEMINI_API_KEY'] },\n { provider: 'groq', upstream: 'https://api.groq.com/openai/v1', hostKeyEnv: ['GROQ_API_KEY'] },\n { provider: 'xai', upstream: 'https://api.x.ai/v1', hostKeyEnv: ['XAI_API_KEY'] },\n { provider: 'openrouter', upstream: 'https://openrouter.ai/api/v1', hostKeyEnv: ['OPENROUTER_API_KEY'] },\n { provider: 'deepseek', upstream: 'https://api.deepseek.com', hostKeyEnv: ['DEEPSEEK_API_KEY'] },\n { provider: 'mistral', upstream: 'https://api.mistral.ai', hostKeyEnv: ['MISTRAL_API_KEY'] },\n { provider: 'together', upstream: 'https://api.together.ai/v1', hostKeyEnv: ['TOGETHER_API_KEY'] },\n { provider: 'cerebras', upstream: 'https://api.cerebras.ai/v1', hostKeyEnv: ['CEREBRAS_API_KEY'] },\n { provider: 'moonshotai', upstream: 'https://api.moonshot.ai/v1', hostKeyEnv: ['MOONSHOT_API_KEY'] },\n { provider: 'zai', upstream: 'https://api.z.ai/api/coding/paas/v4', hostKeyEnv: ['ZAI_API_KEY'] },\n];\n\nexport function brokerRoutes(): readonly BrokerRoute[] {\n return ROUTES;\n}\n\nexport function findBrokerRoute(provider: string): BrokerRoute | undefined {\n return ROUTES.find((route) => route.provider === provider);\n}\n\nexport interface ResolvedCredential {\n route: BrokerRoute;\n /** Environment variable the container would otherwise have received. */\n envName: string;\n value: string;\n}\n\n/** Selects the providers this host can actually broker for a session. */\nexport function resolveBrokeredCredentials(\n environment: Readonly<Record<string, string | undefined>>,\n): ResolvedCredential[] {\n const resolved: ResolvedCredential[] = [];\n for (const route of ROUTES) {\n for (const envName of route.hostKeyEnv) {\n const value = environment[envName]?.trim();\n if (value) {\n resolved.push({ route, envName, value });\n break;\n }\n }\n }\n return resolved;\n}\n","import { timingSafeEqual } from 'node:crypto';\nimport http from 'node:http';\nimport https from 'node:https';\n\nimport type { ResolvedCredential } from '../brokerRoutes';\n\n/** Headers a provider SDK may carry its credential in. */\nconst CREDENTIAL_HEADERS = ['x-api-key', 'authorization', 'x-goog-api-key'] as const;\n/** Query parameter some Google clients use instead of a header. */\nconst CREDENTIAL_QUERY = 'key';\nconst BEARER_PREFIX = 'bearer ';\n/** Connection-scoped headers that must not cross to the upstream request. */\nconst HOP_BY_HOP = new Set([\n 'connection',\n 'keep-alive',\n 'proxy-authenticate',\n 'proxy-authorization',\n 'te',\n 'trailer',\n 'transfer-encoding',\n 'upgrade',\n 'host',\n 'content-length',\n]);\nconst PATH_PATTERN = /^\\/([^/?]+)(.*)$/;\n\nexport interface BrokerServerOptions {\n /** Providers this session may reach, keyed by Pi provider name. */\n credentials: ReadonlyMap<string, ResolvedCredential>;\n /** Secret the container presents in place of a real credential. */\n token: string;\n /** Receives one line per rejected request, for host-side visibility. */\n onDenied?: (reason: string) => void;\n /** Seam for tests; defaults to the real https transport. */\n requestUpstream?: typeof https.request;\n}\n\nfunction matchesToken(candidate: string, token: string): boolean {\n const left = Buffer.from(candidate);\n const right = Buffer.from(token);\n return left.length === right.length && timingSafeEqual(left, right);\n}\n\nfunction credentialValue(headerValue: string): string {\n return headerValue.toLowerCase().startsWith(BEARER_PREFIX) ? headerValue.slice(BEARER_PREFIX.length) : headerValue;\n}\n\nfunction withCredential(headerValue: string, realKey: string): string {\n return headerValue.toLowerCase().startsWith(BEARER_PREFIX) ? `Bearer ${realKey}` : realKey;\n}\n\n/**\n * Rewrites the request's credential, proving the caller holds the session token.\n *\n * Which header carries the key differs per provider SDK, so every candidate is\n * checked rather than assumed. Returns undefined when nothing presented the\n * token, which is what makes an unauthenticated caller indistinguishable from a\n * misrouted one.\n */\nfunction swapCredential(\n headers: http.IncomingHttpHeaders,\n search: URLSearchParams,\n token: string,\n realKey: string,\n): http.OutgoingHttpHeaders | undefined {\n const forwarded: http.OutgoingHttpHeaders = {};\n let authenticated = false;\n\n for (const [name, value] of Object.entries(headers)) {\n if (value === undefined || HOP_BY_HOP.has(name)) continue;\n const single = Array.isArray(value) ? value[0] : value;\n if ((CREDENTIAL_HEADERS as readonly string[]).includes(name) && single !== undefined) {\n if (!matchesToken(credentialValue(single), token)) return undefined;\n forwarded[name] = withCredential(single, realKey);\n authenticated = true;\n continue;\n }\n forwarded[name] = value;\n }\n\n const queryKey = search.get(CREDENTIAL_QUERY);\n if (queryKey !== null) {\n if (!matchesToken(queryKey, token)) return undefined;\n search.set(CREDENTIAL_QUERY, realKey);\n authenticated = true;\n }\n\n return authenticated ? forwarded : undefined;\n}\n\nfunction reject(response: http.ServerResponse, status: number, message: string): void {\n response.writeHead(status, { 'content-type': 'application/json' });\n response.end(JSON.stringify({ error: { type: 'po_broker', message } }));\n}\n\n/**\n * Terminates provider calls from a sandboxed session on the host.\n *\n * The container never holds a real credential: it presents the session token,\n * and only a request that proves possession of it is forwarded upstream with\n * the host's key attached. Bodies stream in both directions so token-by-token\n * responses are not buffered.\n */\nexport function createBrokerServer(options: BrokerServerOptions): http.Server {\n const requestUpstream = options.requestUpstream ?? https.request;\n\n return http.createServer((request, response) => {\n const match = PATH_PATTERN.exec(request.url ?? '');\n const provider = match?.[1];\n const credential = provider ? options.credentials.get(provider) : undefined;\n if (!match || !credential) {\n options.onDenied?.(`unroutable provider path ${request.url ?? ''}`);\n reject(response, 404, 'Unknown provider for this sandbox session.');\n return;\n }\n\n const target = new URL(`${credential.route.upstream}${match[2] || ''}`);\n const headers = swapCredential(request.headers, target.searchParams, options.token, credential.value);\n if (!headers) {\n options.onDenied?.(`missing or invalid session token for ${credential.route.provider}`);\n reject(response, 401, 'This sandbox session did not present its broker token.');\n return;\n }\n\n const upstream = requestUpstream(\n target,\n { method: request.method, headers: { ...headers, host: target.host } },\n (upstreamResponse) => {\n response.writeHead(upstreamResponse.statusCode ?? 502, upstreamResponse.headers);\n upstreamResponse.pipe(response);\n },\n );\n upstream.on('error', (error: Error) => {\n options.onDenied?.(`upstream ${credential.route.provider} failed: ${error.message}`);\n if (!response.headersSent) reject(response, 502, 'The provider could not be reached from the host.');\n else response.destroy();\n });\n request.on('aborted', () => upstream.destroy());\n request.pipe(upstream);\n });\n}\n","import { randomBytes } from 'node:crypto';\nimport fs from 'node:fs';\nimport type http from 'node:http';\nimport os from 'node:os';\nimport path from 'node:path';\n\nimport type { BrokerEndpoint } from '../../types/sandboxHarness';\nimport { resolveBrokeredCredentials } from '../brokerRoutes';\nimport { createBrokerServer } from '../brokerServer';\n\nconst SOCKET_DIRECTORY_PREFIX = 'po-broker-';\nconst SOCKET_FILE_NAME = 'broker.sock';\nconst OWNER_ONLY_DIRECTORY = 0o700;\nconst TOKEN_BYTES = 32;\nconst LOOPBACK = '127.0.0.1';\nconst LINUX_PLATFORM = 'linux';\nconst EPHEMERAL_PORT = 0;\n\nexport interface RunningBroker {\n /** How a container reaches this broker. */\n endpoint: BrokerEndpoint;\n /** Secret the container presents instead of any real credential. */\n token: string;\n /** Pi provider names the session may reach. */\n providers: string[];\n /** Host environment variables whose value the container receives as the token. */\n withheldEnv: string[];\n stop(): Promise<void>;\n}\n\nexport interface StartBrokerOptions {\n environment: Readonly<Record<string, string | undefined>>;\n onDenied?: (reason: string) => void;\n /** Seam for tests; defaults to this host's platform. */\n platform?: string;\n /**\n * Fixed directory to hold the socket instead of a fresh temporary one.\n *\n * A reused container keeps the mounts it was created with, so a dev\n * container has to find the socket at the same path on every launch.\n */\n socketDirectory?: string;\n /** Forces the loopback transport even where a socket would work. */\n forceLoopback?: boolean;\n}\n\n/**\n * Starts the host-side provider broker for one sandboxed session.\n *\n * Answers undefined when the host holds no brokerable credential, which keeps\n * a session that authenticates some other way on the unbrokered path instead\n * of failing it.\n */\nexport async function startBroker(options: StartBrokerOptions): Promise<RunningBroker | undefined> {\n const resolved = resolveBrokeredCredentials(options.environment);\n if (resolved.length === 0) return undefined;\n\n const token = randomBytes(TOKEN_BYTES).toString('base64url');\n const credentials = new Map(resolved.map((credential) => [credential.route.provider, credential]));\n const server = createBrokerServer({ credentials, token, onDenied: options.onDenied });\n const platform = options.platform ?? process.platform;\n\n const useSocket = platform === LINUX_PLATFORM && options.forceLoopback !== true;\n const { endpoint, dispose } = useSocket\n ? await listenOnSocket(server, options.socketDirectory)\n : await listenOnLoopback(server);\n\n return {\n endpoint,\n token,\n providers: resolved.map((credential) => credential.route.provider),\n withheldEnv: resolved.map((credential) => credential.envName),\n stop: () => stopBroker(server, dispose),\n };\n}\n\n/**\n * Native Linux shares a kernel with the container, so a bind-mounted socket\n * needs no port and is reachable as an ordinary file.\n */\nasync function listenOnSocket(\n server: http.Server,\n fixedDirectory?: string,\n): Promise<{ endpoint: BrokerEndpoint; dispose: () => void }> {\n const socketDirectory = fixedDirectory ?? fs.mkdtempSync(path.join(os.tmpdir(), SOCKET_DIRECTORY_PREFIX));\n fs.mkdirSync(socketDirectory, { recursive: true });\n fs.chmodSync(socketDirectory, OWNER_ONLY_DIRECTORY);\n const socketPath = path.join(socketDirectory, SOCKET_FILE_NAME);\n // A previous session that died without closing leaves the node behind, and\n // bind fails on an existing path.\n fs.rmSync(socketPath, { force: true });\n await new Promise<void>((resolve, reject) => {\n server.once('error', reject);\n server.listen(socketPath, () => resolve());\n });\n return {\n endpoint: { transport: 'unix', socketDirectory },\n // A fixed directory outlives the session that borrowed it.\n dispose: () =>\n fixedDirectory\n ? fs.rmSync(socketPath, { force: true })\n : fs.rmSync(socketDirectory, { recursive: true, force: true }),\n };\n}\n\n/**\n * Everywhere else the container runs in its own virtual machine, which cannot\n * connect to a host unix socket even when the file is shared through: the\n * connect fails with ENOTSUP. Loopback keeps the broker off the network while\n * still being reachable through the engine's host gateway, and the session\n * token is what stops another local process from using it.\n */\nasync function listenOnLoopback(server: http.Server): Promise<{ endpoint: BrokerEndpoint; dispose: () => void }> {\n await new Promise<void>((resolve, reject) => {\n server.once('error', reject);\n server.listen(EPHEMERAL_PORT, LOOPBACK, () => resolve());\n });\n const address = server.address();\n if (address === null || typeof address === 'string') throw new Error('The broker did not report a listening port.');\n return { endpoint: { transport: 'tcp', port: address.port }, dispose: () => undefined };\n}\n\nasync function stopBroker(server: http.Server, dispose: () => void): Promise<void> {\n await new Promise<void>((resolve) => {\n server.close(() => resolve());\n // An in-flight provider stream would otherwise hold the listener open past\n // the session that owns it.\n server.closeAllConnections();\n });\n dispose();\n}\n","import type { PoHeadlessActivity } from '@agimon-ai/po-core/headless';\nimport type { PoServerPluginContext } from '@agimon-ai/po-core/serverFacet';\n\nimport { BROKER_DISABLED_ENV, BROKER_STATUS_SOURCE, DISABLED_VALUE } from '../../../../../constants/sandbox';\nimport { startBroker, type RunningBroker } from '../../../../../services/brokerHost';\nimport { DefaultSandboxExtensionService } from '../../../../../services/extensionService';\nimport type { SandboxServerScope } from './sandboxScope';\n\nexport function createSandboxServerRoot({ agent }: PoServerPluginContext) {\n if (!agent) return { value: {} as SandboxServerScope };\n const service = new DefaultSandboxExtensionService(agent.context.environment);\n let broker: RunningBroker | undefined;\n const activity: PoHeadlessActivity = {\n name: BROKER_STATUS_SOURCE,\n async start(execution) {\n if (execution.environment[BROKER_DISABLED_ENV] === DISABLED_VALUE) return () => undefined;\n const started = await startBroker({\n environment: execution.environment,\n onDenied: (reason) => {\n void execution.client.notify({\n title: 'Po sandbox broker',\n body: `Broker refused a call: ${reason}`,\n level: 'warning',\n });\n },\n });\n broker = started;\n if (started === undefined) return () => undefined;\n execution.client.setStatus(BROKER_STATUS_SOURCE, `provider broker: ${started.providers.join(', ')}`);\n return async () => {\n if (broker !== started) return;\n broker = undefined;\n execution.client.setStatus(BROKER_STATUS_SOURCE, undefined);\n await started.stop();\n };\n },\n };\n return { value: { service }, activities: [activity] };\n}\n","import { defineRoot } from '@agimon-ai/po-core/extensionFile';\nimport type { PoServerProviderOverrides } from '@agimon-ai/po-core/serverFacet';\n\nimport { brokeredProviderOverrides } from '../../../../services/brokerProviders';\nimport { createSandboxServerRoot } from './_lib/serverRoot';\n\nexport default defineRoot(\n Object.assign(createSandboxServerRoot, {\n providerOverrides: (({ environment }) =>\n brokeredProviderOverrides(environment).map(({ provider, baseUrl }) => ({\n name: provider,\n baseUrl,\n }))) satisfies PoServerProviderOverrides,\n }),\n);\n","import type { WithRoot } from '@agimon-ai/po-core/extensionFile';\nimport { defineCommand } from '@agimon-ai/po-core/serverFacet';\nimport type { PoServerPluginContext } from '@agimon-ai/po-core/serverFacet';\n\nimport { COMMAND_DESCRIPTION, COMMAND_NAME } from '../../../../../constants/sandbox';\nimport type { SandboxServerScope } from './sandboxScope';\n\nexport function createSandboxServerCommands(context: WithRoot<PoServerPluginContext, SandboxServerScope>) {\n const service = context.root.service;\n if (!service) return [];\n return [\n defineCommand({\n name: COMMAND_NAME,\n description: COMMAND_DESCRIPTION,\n async execute() {\n const result = await service.execute();\n await context.agent?.context.client.notify({ body: result.message, level: result.level });\n },\n }),\n ];\n}\n","import { defineRoutedContribution, type WithRoot } from '@agimon-ai/po-core/extensionFile';\nimport type { PoServerPluginContext } from '@agimon-ai/po-core/serverFacet';\n\nimport { createSandboxServerCommands } from '../_lib/sandboxCommands';\nimport type { SandboxServerScope } from '../_lib/sandboxScope';\n\nexport default defineRoutedContribution(\n (context: WithRoot<PoServerPluginContext, SandboxServerScope>) => createSandboxServerCommands(context),\n { cardinality: 'many' },\n);\n","import type { PoHeadlessResource } from '@agimon-ai/po-core/headless';\nimport { readPackageResource } from '@agimon-ai/po-core/serverFacet';\n\nimport type { SandboxServerScope } from './sandboxScope';\n\nexport function createSandboxContextResource(root: SandboxServerScope): PoHeadlessResource | undefined {\n if (!root.service) return undefined;\n return {\n // The package index is Help-catalog material, not standing instruction. The Pi\n // facet has always routed it through the Help service; this is the server's gate.\n when: { state: { 'minor-mode': 'help' }, attribution: { kind: 'minor', mode: 'help' } },\n name: 'po-sandbox',\n kind: 'context',\n read: () => readPackageResource(import.meta.url, 'llms.txt'),\n };\n}\n\nexport function createSandboxSkillResource(root: SandboxServerScope): PoHeadlessResource | undefined {\n if (!root.service) return undefined;\n return {\n name: 'po-use-sandbox',\n when: { state: { 'minor-mode': 'help' }, attribution: { kind: 'minor', mode: 'help' } },\n kind: 'skill',\n read: () => readPackageResource(import.meta.url, 'src/prompts/po-use-sandbox/SKILL.md'),\n };\n}\n\nexport function createSandboxHelpResource(moduleUrl: string) {\n return {\n source: '@agimon-ai/po-sandbox',\n moduleUrl,\n skills: [\n {\n name: 'po-use-sandbox',\n description:\n 'Use @agimon-ai/po-sandbox: Container sandbox for Po launches: the agent, extensions, and tools run inside Docker or Podman while the terminal stays on the host',\n },\n ],\n };\n}\n","import { defineResource, defineRoutedContribution, type WithRoot } from '@agimon-ai/po-core/extensionFile';\nimport type { PoServerPluginContext } from '@agimon-ai/po-core/serverFacet';\n\nimport { createSandboxContextResource } from '../_lib/sandboxResources';\nimport type { SandboxServerScope } from '../_lib/sandboxScope';\n\nexport default defineRoutedContribution(\n defineResource((context: WithRoot<PoServerPluginContext, SandboxServerScope>) =>\n createSandboxContextResource(context.root),\n ),\n { cardinality: 'optional' },\n);\n","import { defineResource, defineRoutedContribution, type WithRoot } from '@agimon-ai/po-core/extensionFile';\nimport type { PoServerPluginContext } from '@agimon-ai/po-core/serverFacet';\n\nimport { createSandboxSkillResource } from '../_lib/sandboxResources';\nimport type { SandboxServerScope } from '../_lib/sandboxScope';\n\nexport default defineRoutedContribution(\n defineResource((context: WithRoot<PoServerPluginContext, SandboxServerScope>) =>\n createSandboxSkillResource(context.root),\n ),\n { cardinality: 'optional' },\n);\n","// Generated by @agimon-ai/po-build. Do not edit by hand.\nimport { defineServerPlugin, type PoServerSessionPlugin } from '@agimon-ai/po-core/serverFacet';\nimport { composeServerProviderOverrides } from '@agimon-ai/po-core/serverFacet';\nimport { composeRootHooks } from '@agimon-ai/po-core/extensionFile';\n\nimport rootSession from '../src/extensions/workspaces/sessions/(backend)/root.server';\nimport commandSandbox from '../src/extensions/workspaces/sessions/(backend)/command/sandbox.server';\nimport resourcePoSandbox from '../src/extensions/workspaces/sessions/(backend)/resource/poSandbox.server';\nimport resourcePoUseSandbox from '../src/extensions/workspaces/sessions/(backend)/resource/poUseSandbox.server';\n\ntype Factory<T, C> = (context: C) => T;\nconst at = <T, C>(value: T | Factory<T, C>, context: C): T =>\n typeof value === 'function' ? (value as Factory<T, C>)(context) : value;\ntype ManyFactory<T, C> = (context: C) => T | readonly T[];\nconst many = <T, C>(value: T | readonly T[] | ManyFactory<T, C>, context: C): readonly T[] => {\n const resolved = typeof value === 'function' ? (value as ManyFactory<T, C>)(context) : value;\n return Array.isArray(resolved) ? resolved : [resolved as T];\n};\n/* v8 ignore next -- generated normalization branch */\nconst defined = <T>(values: readonly (T | undefined)[]): T[] =>\n values.filter((value): value is T => value !== undefined);\n\nexport const facet = defineServerPlugin({\n name: '@agimon-ai/po-sandbox',\n providerOverrides: composeServerProviderOverrides(rootSession),\n session: async (context) => {\n const scopeSession = await rootSession(context);\n const contextSession = { ...context, root: scopeSession.value };\n return {\n ...composeRootHooks(scopeSession),\n get commands(): PoServerSessionPlugin['commands'] { return [...many(commandSandbox, contextSession)]; },\n get resources(): PoServerSessionPlugin['resources'] { return defined([at(resourcePoSandbox, contextSession), at(resourcePoUseSandbox, contextSession)]); },\n services: [...(scopeSession.services ?? [])],\n get activities(): PoServerSessionPlugin['activities'] { return [...(scopeSession.activities ?? [])]; },\n };\n },\n});\n\nexport default facet;\n"],"mappings":"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"}
package/dist/index.mjs CHANGED
@@ -1,2 +1 @@
1
- import { t as DefaultSandboxExtensionService } from "./extensionService-FmuCugPK.mjs";
2
- export { DefaultSandboxExtensionService };
1
+ export { t as DefaultSandboxExtensionService } from "./extensionService-FmuCugPK.mjs";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agimon-ai/po-sandbox",
3
- "version": "0.0.0-alpha.2",
3
+ "version": "0.0.0-alpha.4",
4
4
  "description": "Container sandbox for Po launches: the agent, extensions, and tools run inside Docker or Podman while the terminal stays on the host",
5
5
  "keywords": [
6
6
  "ai",
@@ -64,18 +64,18 @@
64
64
  "access": "public"
65
65
  },
66
66
  "dependencies": {
67
- "@agimon-ai/po-core": "0.0.0-alpha.2",
67
+ "@agimon-ai/po-core": "0.0.0-alpha.4",
68
68
  "@deepseek-ai/cordis": "4.0.4"
69
69
  },
70
70
  "devDependencies": {
71
- "@agimon-ai/po-build": "0.0.0-alpha.2",
72
- "@agimon-ai/vibe-lint-plugin-doom-extension": "0.0.0-alpha.2",
71
+ "@agimon-ai/po-build": "0.0.0-alpha.4",
72
+ "@agimon-ai/vibe-lint-plugin-po-extension": "0.0.0-alpha.4",
73
73
  "@earendil-works/pi-coding-agent": "1.2.0",
74
- "@types/node": "26.6.4",
75
- "@vitest/coverage-v8": "5.0.3",
76
- "tsdown": "0.23.0",
74
+ "@types/node": "26.6.5",
75
+ "@vitest/coverage-v8": "5.0.4",
76
+ "tsdown": "0.23.1",
77
77
  "typescript": "7.0.2",
78
- "vitest": "5.0.3"
78
+ "vitest": "5.0.4"
79
79
  },
80
80
  "peerDependencies": {
81
81
  "@earendil-works/pi-coding-agent": "1.2.0"
@@ -9,6 +9,6 @@ Read the package [README](../../../README.md) for its exact installation, comman
9
9
 
10
10
  ## Guidance
11
11
 
12
- - Use `/doom-sandbox` as the package's primary Pi entrypoint.
12
+ - Use `/po-sandbox` as the package's primary Pi entrypoint.
13
13
  - Treat the package README and its linked resources as the source of truth for package-specific behavior.
14
14
  - Do not require Po Help for runtime activation. Help only makes this package guidance discoverable while the minor mode is active.