@agimon-ai/po-sandbox 0.0.0-alpha.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +170 -0
- package/dist/apiContracts.cjs +17 -0
- package/dist/apiContracts.cjs.map +1 -0
- package/dist/apiContracts.d.mts +53 -0
- package/dist/apiContracts.d.mts.map +1 -0
- package/dist/apiContracts.mjs +13 -0
- package/dist/apiContracts.mjs.map +1 -0
- package/dist/cockpitHarness.cjs +600 -0
- package/dist/cockpitHarness.cjs.map +1 -0
- package/dist/cockpitHarness.d.mts +41 -0
- package/dist/cockpitHarness.d.mts.map +1 -0
- package/dist/cockpitHarness.mjs +594 -0
- package/dist/cockpitHarness.mjs.map +1 -0
- package/dist/extensionService-CoqM-IE1.cjs +25 -0
- package/dist/extensionService-CoqM-IE1.cjs.map +1 -0
- package/dist/extensionService-FmuCugPK.mjs +20 -0
- package/dist/extensionService-FmuCugPK.mjs.map +1 -0
- package/dist/extensions/mcp.cjs +26 -0
- package/dist/extensions/mcp.cjs.map +1 -0
- package/dist/extensions/mcp.d.mts +5 -0
- package/dist/extensions/mcp.d.mts.map +1 -0
- package/dist/extensions/mcp.mjs +21 -0
- package/dist/extensions/mcp.mjs.map +1 -0
- package/dist/extensions/server.cjs +476 -0
- package/dist/extensions/server.cjs.map +1 -0
- package/dist/extensions/server.d.mts +5 -0
- package/dist/extensions/server.d.mts.map +1 -0
- package/dist/extensions/server.mjs +466 -0
- package/dist/extensions/server.mjs.map +1 -0
- package/dist/index.cjs +3 -0
- package/dist/index.d.mts +22 -0
- package/dist/index.d.mts.map +1 -0
- package/dist/index.mjs +2 -0
- package/dist/sandboxBridge-77zC5pQR.mjs +13 -0
- package/dist/sandboxBridge-77zC5pQR.mjs.map +1 -0
- package/dist/sandboxBridge-Db6DubLC.cjs +81 -0
- package/dist/sandboxBridge-Db6DubLC.cjs.map +1 -0
- package/llms.txt +11 -0
- package/package.json +117 -0
- package/src/prompts/po-use-sandbox/SKILL.md +14 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Vuong Ngo
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
# @agimon-ai/po-sandbox
|
|
2
|
+
|
|
3
|
+
Run the Po web cockpit, its sessions, and their tools in a container while the browser stays on the host.
|
|
4
|
+
|
|
5
|
+
Part of the [Po distribution](https://www.npmjs.com/package/@agimon-ai/po).
|
|
6
|
+
|
|
7
|
+
## Requirements
|
|
8
|
+
|
|
9
|
+
- Node.js 24 or newer
|
|
10
|
+
- `@earendil-works/pi-coding-agent` 0.85.0
|
|
11
|
+
- One of `docker`, `podman`, `nerdctl`, or `finch` on the host for the cockpit container
|
|
12
|
+
|
|
13
|
+
## Install
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npm install @agimon-ai/po-sandbox
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
The cockpit container is started by the web cockpit (`po`, which runs `po-web`) and requires this package in the
|
|
20
|
+
selected major mode's layers, as shown below.
|
|
21
|
+
|
|
22
|
+
## Enable the sandbox
|
|
23
|
+
|
|
24
|
+
This layer is still in development, so `po init` does not add it to any mode. Opt in by hand:
|
|
25
|
+
name it as a layer in `.po/modes.yaml`, then list that layer on the mode you want it in.
|
|
26
|
+
|
|
27
|
+
```yaml
|
|
28
|
+
layers:
|
|
29
|
+
sandbox:
|
|
30
|
+
packages:
|
|
31
|
+
- '@agimon-ai/po-sandbox'
|
|
32
|
+
|
|
33
|
+
majorMode:
|
|
34
|
+
copilot:
|
|
35
|
+
description: General-purpose coding mode.
|
|
36
|
+
layers: [team, ask-user, task, sandbox]
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
A mode without this layer offers no cockpit container, and the cockpit refuses containment rather
|
|
40
|
+
than running uncontained.
|
|
41
|
+
|
|
42
|
+
## Web cockpit container
|
|
43
|
+
|
|
44
|
+
With this layer in the selected major mode, the web cockpit can hand over to one long-lived Linux
|
|
45
|
+
container that holds the hub and every session it spawns:
|
|
46
|
+
|
|
47
|
+
1. The cockpit resolves this package's `./cockpitHarness` export and delegates the container
|
|
48
|
+
lifecycle.
|
|
49
|
+
2. The layer detects the first available engine in the order `docker`, `podman`, `nerdctl`,
|
|
50
|
+
`finch` (override with `PO_SANDBOX_ENGINE`), and builds a
|
|
51
|
+
cockpit image when that tag is not cached. The digest covers the image definition and bridge
|
|
52
|
+
source. The image installs `@agimon-ai/po-web` from the registry rather than copying the
|
|
53
|
+
host's platform-specific packages.
|
|
54
|
+
3. It starts a detached container with each configured workspace bind-mounted at its host path,
|
|
55
|
+
a named home volume, and the cockpit port published on host loopback, then waits until the
|
|
56
|
+
cockpit inside answers `/api/health`.
|
|
57
|
+
|
|
58
|
+
The browser stays on the host while bash, file edits, extensions, MCP servers, and skills all
|
|
59
|
+
execute inside the container. `PO_SANDBOX=1` marks every sandboxed process; nesting is refused.
|
|
60
|
+
See [The container plan and current wiring](../../../docs/securities.md#the-container-plan-and-current-wiring)
|
|
61
|
+
for what the current server runtime connects.
|
|
62
|
+
|
|
63
|
+
Only an allowlisted environment enters the container: terminal and locale variables, proxy
|
|
64
|
+
settings, and `PO_PRESET`. Everything else a shell accumulates stays on the host.
|
|
65
|
+
|
|
66
|
+
## Engine and runtime selection
|
|
67
|
+
|
|
68
|
+
The harness invokes each supported engine with Docker-compatible arguments. The engine and its
|
|
69
|
+
VM, if any, must already be running on the host.
|
|
70
|
+
|
|
71
|
+
`PO_SANDBOX_RUN_FLAGS` passes extra options straight to the engine, which is how you select a
|
|
72
|
+
different isolation runtime without the layer having to know about it:
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
PO_SANDBOX_RUN_FLAGS=--runtime=runsc po
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
Options must be self-contained (`--flag` or `--flag=value`). A separated value such as
|
|
79
|
+
`--runtime runsc` is refused, because a bare word cannot be told apart from an image name and
|
|
80
|
+
would silently launch a different container.
|
|
81
|
+
|
|
82
|
+
Alternative isolation runtimes such as Kata or Firecracker are not verified here. Check their
|
|
83
|
+
mount and broker-transport support before relying on them.
|
|
84
|
+
|
|
85
|
+
## Workspace dev containers
|
|
86
|
+
|
|
87
|
+
The cockpit container ignores a workspace `.devcontainer` configuration. That file is
|
|
88
|
+
author-controlled and could mount anything, including the container socket, which would make the
|
|
89
|
+
containment claim false.
|
|
90
|
+
|
|
91
|
+
## Signing in from inside the sandbox
|
|
92
|
+
|
|
93
|
+
Subscription logins are not brokered. The cockpit container publishes only the cockpit port on
|
|
94
|
+
host loopback, so a `/login` callback that a provider sends to `localhost:<port>` does not reach
|
|
95
|
+
the container. Use a provider API key, which the credential broker below carries. Credentials stored inside the container land in the cockpit home
|
|
96
|
+
volume, so they survive later runs.
|
|
97
|
+
|
|
98
|
+
## Provider credential broker
|
|
99
|
+
|
|
100
|
+
For supported providers with a host API key, the credential broker:
|
|
101
|
+
|
|
102
|
+
1. Starts a broker on the host and grants the container exactly one route to it.
|
|
103
|
+
2. Replaces the credential variable with a random per-session token.
|
|
104
|
+
3. Redirects the provider's base URL at a loopback bridge inside the container, which forwards
|
|
105
|
+
raw bytes to the broker.
|
|
106
|
+
|
|
107
|
+
How the container reaches the broker depends on the engine:
|
|
108
|
+
|
|
109
|
+
| Host | Transport | Why |
|
|
110
|
+
| ----------------------------- | ------------------------------------------- | ---------------------------------------------------------------------------------- |
|
|
111
|
+
| Linux | Unix socket, bind-mounted, owner-only | Container and host share a kernel, so no port is needed |
|
|
112
|
+
| macOS, Windows, any VM engine | Loopback TCP through `host.docker.internal` | A container in a virtual machine cannot connect to a mounted host socket (ENOTSUP) |
|
|
113
|
+
|
|
114
|
+
On the TCP path the broker binds `127.0.0.1` on an ephemeral port, so it is never exposed beyond
|
|
115
|
+
the host, and the launch passes `--add-host` so the gateway name resolves on every engine. Another
|
|
116
|
+
local process could reach that port, and the session token is what stops it being useful.
|
|
117
|
+
|
|
118
|
+
The broker validates the token, swaps in the real key, and streams the provider response back.
|
|
119
|
+
Credentials for providers it cannot carry are withheld from the container rather than passed
|
|
120
|
+
through. Turn the whole mechanism off with `PO_SANDBOX_BROKER=0`.
|
|
121
|
+
|
|
122
|
+
## In-session command
|
|
123
|
+
|
|
124
|
+
```text
|
|
125
|
+
/doom-sandbox
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
Reports whether the current session runs inside the sandbox container or directly on the host.
|
|
129
|
+
|
|
130
|
+
## Current limits
|
|
131
|
+
|
|
132
|
+
- The broker carries a curated provider list. OAuth subscription logins are not brokered, and
|
|
133
|
+
their callbacks cannot reach the cockpit container.
|
|
134
|
+
- A host `*_BASE_URL` override is dropped rather than used as the broker's upstream.
|
|
135
|
+
- Compositions that declare local workspace packages cannot load their platform-specific
|
|
136
|
+
dependencies inside the Linux container; use registry-installed layers for sandboxed work.
|
|
137
|
+
- One container holds every cockpit session, so sessions can read each other's mounted
|
|
138
|
+
workspaces.
|
|
139
|
+
- Container network access follows the engine's defaults and is not restricted yet.
|
|
140
|
+
- Mounted repositories are writable. Containers do not protect those files from agent edits,
|
|
141
|
+
and the container engine remains part of the trusted base.
|
|
142
|
+
|
|
143
|
+
## Public API
|
|
144
|
+
|
|
145
|
+
```ts
|
|
146
|
+
import { DefaultSandboxExtensionService } from '@agimon-ai/po-sandbox';
|
|
147
|
+
import { createCockpitHarness, startCockpitContainer } from '@agimon-ai/po-sandbox/cockpitHarness';
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
The service layer is host-neutral. `/extensions/server` registers the in-session command, and
|
|
151
|
+
`/cockpitHarness` owns the web cockpit's container lifecycle and credential broker.
|
|
152
|
+
|
|
153
|
+
## Development
|
|
154
|
+
|
|
155
|
+
Run from this package directory in the workspace:
|
|
156
|
+
|
|
157
|
+
```bash
|
|
158
|
+
pnpm build
|
|
159
|
+
pnpm typecheck
|
|
160
|
+
pnpm test
|
|
161
|
+
pnpm lint
|
|
162
|
+
pnpm exec vibe-lint check .
|
|
163
|
+
npm pack --dry-run
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
Maintained by [Agimon](https://agimon.ai/about).
|
|
167
|
+
|
|
168
|
+
## License
|
|
169
|
+
|
|
170
|
+
MIT
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
Object.defineProperties(exports, {
|
|
2
|
+
__esModule: { value: true },
|
|
3
|
+
[Symbol.toStringTag]: { value: "Module" }
|
|
4
|
+
});
|
|
5
|
+
//#region src/schemas/apiContracts.ts
|
|
6
|
+
/** This facet contributes agent tools, commands or lifecycle only, with no separate HTTP or socket surface. */
|
|
7
|
+
const apiContracts = (0, require("@agimon-ai/po-core/apiContracts").defineApiContract)({
|
|
8
|
+
version: 1,
|
|
9
|
+
http: [],
|
|
10
|
+
sockets: [],
|
|
11
|
+
dynamic: []
|
|
12
|
+
});
|
|
13
|
+
//#endregion
|
|
14
|
+
exports.apiContracts = apiContracts;
|
|
15
|
+
exports.default = apiContracts;
|
|
16
|
+
|
|
17
|
+
//# sourceMappingURL=apiContracts.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"apiContracts.cjs","names":["defineApiContract"],"sources":["../src/schemas/apiContracts.ts"],"sourcesContent":["import { defineApiContract } from '@agimon-ai/po-core/apiContracts';\n\n/** This facet contributes agent tools, commands or lifecycle only, with no separate HTTP or socket surface. */\nexport const apiContracts = defineApiContract({ version: 1, http: [], sockets: [], dynamic: [] });\nexport default apiContracts;\n"],"mappings":";;;;;;AAGA,MAAa,gBAAA,4CAAeA,CAAAA,CAAAA,kBAAAA,CAAkB;CAAE,SAAS;CAAG,MAAM,CAAC;CAAG,SAAS,CAAC;CAAG,SAAS,CAAC;AAAE,CAAC"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
//#region src/schemas/apiContracts.d.ts
|
|
2
|
+
/** This facet contributes agent tools, commands or lifecycle only, with no separate HTTP or socket surface. */
|
|
3
|
+
export declare const apiContracts: {
|
|
4
|
+
version: 1;
|
|
5
|
+
protocols?: Record<string, string> | undefined;
|
|
6
|
+
schemas?: Record<string, boolean | object> | undefined;
|
|
7
|
+
http: {
|
|
8
|
+
id: string;
|
|
9
|
+
scope: "global" | "session" | "workspace";
|
|
10
|
+
basePath?: string | undefined;
|
|
11
|
+
path: string;
|
|
12
|
+
method: "DELETE" | "GET" | "HEAD" | "OPTIONS" | "PATCH" | "POST" | "PUT";
|
|
13
|
+
description: string;
|
|
14
|
+
availability?: string | undefined;
|
|
15
|
+
authentication: "device" | "none" | "owner";
|
|
16
|
+
parameters?: {
|
|
17
|
+
name: string;
|
|
18
|
+
in: "header" | "path" | "query";
|
|
19
|
+
required: boolean;
|
|
20
|
+
schema: boolean | object;
|
|
21
|
+
}[] | undefined;
|
|
22
|
+
body?: {
|
|
23
|
+
contentType: string;
|
|
24
|
+
contentTypes?: string[] | undefined;
|
|
25
|
+
required: boolean;
|
|
26
|
+
schema: boolean | object;
|
|
27
|
+
} | undefined;
|
|
28
|
+
responses: Record<string, {
|
|
29
|
+
description: string;
|
|
30
|
+
contentType?: string | undefined;
|
|
31
|
+
schema?: boolean | object | undefined;
|
|
32
|
+
events?: Record<string, boolean | object> | undefined;
|
|
33
|
+
}>;
|
|
34
|
+
}[];
|
|
35
|
+
sockets: {
|
|
36
|
+
id: string;
|
|
37
|
+
scope: "global" | "session" | "workspace";
|
|
38
|
+
description: string;
|
|
39
|
+
availability?: string | undefined;
|
|
40
|
+
service: string;
|
|
41
|
+
member: string;
|
|
42
|
+
direction: "client-to-server" | "server-to-client";
|
|
43
|
+
kind: "channel" | "method" | "state";
|
|
44
|
+
input: boolean | object;
|
|
45
|
+
output?: boolean | object | undefined;
|
|
46
|
+
errors?: boolean | object | undefined;
|
|
47
|
+
}[];
|
|
48
|
+
dynamic: string[];
|
|
49
|
+
gaps?: string[] | undefined;
|
|
50
|
+
};
|
|
51
|
+
//#endregion
|
|
52
|
+
export { apiContracts as default };
|
|
53
|
+
//# sourceMappingURL=apiContracts.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"apiContracts.d.mts","names":[],"sources":["../src/schemas/apiContracts.ts"],"mappings":";;qBAGa"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { defineApiContract } from "@agimon-ai/po-core/apiContracts";
|
|
2
|
+
//#region src/schemas/apiContracts.ts
|
|
3
|
+
/** This facet contributes agent tools, commands or lifecycle only, with no separate HTTP or socket surface. */
|
|
4
|
+
const apiContracts = defineApiContract({
|
|
5
|
+
version: 1,
|
|
6
|
+
http: [],
|
|
7
|
+
sockets: [],
|
|
8
|
+
dynamic: []
|
|
9
|
+
});
|
|
10
|
+
//#endregion
|
|
11
|
+
export { apiContracts, apiContracts as default };
|
|
12
|
+
|
|
13
|
+
//# sourceMappingURL=apiContracts.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"apiContracts.mjs","names":[],"sources":["../src/schemas/apiContracts.ts"],"sourcesContent":["import { defineApiContract } from '@agimon-ai/po-core/apiContracts';\n\n/** This facet contributes agent tools, commands or lifecycle only, with no separate HTTP or socket surface. */\nexport const apiContracts = defineApiContract({ version: 1, http: [], sockets: [], dynamic: [] });\nexport default apiContracts;\n"],"mappings":";;;AAGA,MAAa,eAAe,kBAAkB;CAAE,SAAS;CAAG,MAAM,CAAC;CAAG,SAAS,CAAC;CAAG,SAAS,CAAC;AAAE,CAAC"}
|