@axiom-lattice/microsandbox-service 0.1.11 → 1.0.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/CHANGELOG.md CHANGED
@@ -1,5 +1,67 @@
1
1
  # @axiom-lattice/microsandbox-service
2
2
 
3
+ ## 1.0.0
4
+
5
+ ### Major Changes
6
+
7
+ - 726c98a: Consolidated release notes for all pending changesets. Each package takes the highest bump type among the merged entries.
8
+
9
+ ## Sandbox shell-exec timeout unified on milliseconds (ADR-178, breaking)
10
+
11
+ Unify the sandbox shell-exec timeout on **milliseconds** across the framework.
12
+
13
+ Breaking changes:
14
+
15
+ - `SandboxShellService.execCommand` now takes `timeoutMs` (milliseconds) instead of `timeout` (seconds). Custom `SandboxInstance` implementations must rename the field and adjust units.
16
+ - `MicrosandboxShellExecInput.timeout` → `timeoutMs`; the `POST /api/shell/exec` wire field is now `timeoutMs` for both `microsandbox-service` and `opensandbox-gateway`.
17
+ - `PACK_DIRECTORY_TIMEOUT_SECONDS` → `PACK_DIRECTORY_TIMEOUT_MS` (public core export via `sandbox_lattice`).
18
+ - `sandbox-console` `SandboxExecInput.timeout` → `timeoutMs`.
19
+
20
+ Behavior:
21
+
22
+ - Provider adapters convert once at their boundary: Daytona / agent-infra (`remote`) / OpenSandbox convert ms→seconds; E2B and Local pass milliseconds directly.
23
+ - **Fix:** `opensandbox-gateway` no longer divides an already-seconds value by 1000 (which capped every command at 1s); it now converts `timeoutMs` → the SDK's `timeoutSeconds` (floored to at least 1s).
24
+ - **Fix:** `microsandbox-service` now honors the timeout by streaming the shell and killing it on expiry; previously `timeout` was accepted but ignored.
25
+ - LLM-facing tools `shell_exec` (`timeout`, seconds) and `code_eval` (`timeout_sec`, seconds) intentionally keep seconds and convert once; documented as exceptions.
26
+ - Sandbox lifetime `timeoutSeconds` (create/renew/idle) is unchanged and remains in seconds.
27
+
28
+ ## Sandbox service base URL + missing-file semantics (ADR-174 / ADR-175)
29
+
30
+ Unify the sandbox service base-URL env and treat missing sandbox files as a structured not-found.
31
+
32
+ The resident plugin deployment runtime now reads `MICROSANDBOX_SERVICE_BASE_URL` only (dropping `MICROSANDBOX_SERVICE_URL`), so a provider-only config also enables `manage_plugin` deploy/undeploy/status; `sandbox-console` reads `VITE_MICROSANDBOX_SERVICE_BASE_URL`. `MICROSANDBOX_SERVICE_URL` no longer has any effect.
33
+
34
+ `opensandbox-gateway` now preserves the sandbox's upstream HTTP status (and surfaces execd's `{ code, message }`) instead of collapsing every failure to `500 "Download failed"`, and `MicrosandboxServiceClient` attaches the HTTP status to request errors; `microsandbox-service` `readFile`/`downloadFile` map missing files to `404 FILE_NOT_FOUND` (matching `deleteFile`). A missing sandbox file (e.g. the plugin-skill manifest) is therefore classified as not-found rather than a hard failure, fixing `Error loading skill content: ... Download failed`.
35
+
36
+ ## Sandbox plugin result contract (ADR-177)
37
+
38
+ - `invokePluginTool` no longer returns `undefined` when a sandbox plugin handler reports `ok:true` without a `result` field. That `undefined` made the MCP App `tools/call` route (`/api/mcp-apps/rpc`) send an empty body (client `Response.json()` threw "Unexpected end of JSON input"). It now falls back to the handler's raw stdout via core's new `handlerResultText`; handlers that supply `result` are unchanged. `handlerResultText` is exported from `@axiom-lattice/core`.
39
+ - Sandbox plugin tool results no longer collapse to the string `"null"` when a handler reports `ok:true` but omits the `result` field. The normalized text now falls back to the handler's raw stdout via the new `handlerResultText` helper (used by `SandboxPluginCompiler`), and `manage_plugin test` returns that same normalized text so authors catch the missing `result` wrapper instead of seeing the raw envelope in `stdout` while the inline result is `"null"`.
40
+ - The sandbox plugin authoring skills now show an explicit ✅/❌ for the required `result` wrapper (payload must go under `result`, not the envelope top level), and the UI skill states that `ui/notifications/tool-result` is a `CallToolResult` read via `params.content[0].text` rather than the handler payload at the top level.
41
+
42
+ ## Plugin Builder skills
43
+
44
+ - Plugin Builder's `skill` middleware now uses `readAll: true` instead of a sandbox-plugin-only whitelist, so built-in skills (e.g. `ptc`) are actually loadable rather than merely advertised in `<available_skills>` (loading them previously returned `RESOURCE_NOT_ALLOWED`).
45
+ - Close three Plugin Builder capability gaps: add `sandbox-plugins-consume-open` (how a handler calls the platform's Open MCP surface via the injected `API_GATEWAY_URL`/`OPEN_API_TOKEN` + `listTools()`/`callTool()`), add a "Scenarios → skills" routing table to `sandbox-plugins-patterns`, and document in `sandbox-plugins-ui` that a View `tools/call` response is the handler's `result` value directly (not a `CallToolResult` wrapper) plus a self-check/debug section. The Plugin Builder prompt now routes to the new skill.
46
+
47
+ ## MCP App UI push semantics
48
+
49
+ Document the MCP App push semantics in `sandbox-plugins-ui`: `ui/notifications/tool-result` replays the tool result **recorded when the agent ran** (it does not re-execute the handler), whereas a View `tools/call` re-executes the handler live. This is why a stale/`"null"` push can coexist with a correct `tools/call` — re-run the tool for a fresh push instead of debugging the host.
50
+
51
+ ## MCP App same-origin rendering
52
+
53
+ `McpAppRenderer` no longer refuses to render when the gateway shares the console origin. Single-origin deployments previously saw "MCP App unavailable" because the outer sandbox proxy iframe (which carries `allow-same-origin`) was required to be cross-origin per the MCP Apps spec. The renderer now only requires that a gateway origin can be derived from `client.config.baseURL`; `event.origin`/`event.source` validation is unchanged, and the untrusted View still runs in an opaque-origin inner frame. Cross-origin deployments keep the stronger containment and are unchanged; the trade-off is documented in ADR-117.
54
+
55
+ ## 0.1.12
56
+
57
+ ### Patch Changes
58
+
59
+ - a19e0aa: Unify the sandbox service base-URL env and treat missing sandbox files as a structured not-found (ADR-174 / ADR-175).
60
+
61
+ The resident plugin deployment runtime now reads `MICROSANDBOX_SERVICE_BASE_URL` only (dropping `MICROSANDBOX_SERVICE_URL`), so a provider-only config also enables `manage_plugin` deploy/undeploy/status; `sandbox-console` reads `VITE_MICROSANDBOX_SERVICE_BASE_URL`. `MICROSANDBOX_SERVICE_URL` no longer has any effect.
62
+
63
+ `opensandbox-gateway` now preserves the sandbox's upstream HTTP status (and surfaces execd's `{ code, message }`) instead of collapsing every failure to `500 "Download failed"`, and `MicrosandboxServiceClient` attaches the HTTP status to request errors; `microsandbox-service` `readFile`/`downloadFile` map missing files to `404 FILE_NOT_FOUND` (matching `deleteFile`). A missing sandbox file (e.g. the plugin-skill manifest) is therefore classified as not-found rather than a hard failure, fixing `Error loading skill content: ... Download failed`.
64
+
3
65
  ## 0.1.11
4
66
 
5
67
  ### Patch Changes
package/README.md CHANGED
@@ -54,6 +54,9 @@ Supported environment variables:
54
54
  - `MICROSANDBOX_IDLE_TIMEOUT_SEC` default: `600` — auto-stop after N seconds of inactivity. Set to `0` to disable
55
55
  - `MICROSANDBOX_VOLUME_QUOTA_MIB` default: `2048` — storage quota for named volumes
56
56
  - `MICROSANDBOX_CLI_PATH` default: `~/.microsandbox/bin/msb` — path to the `msb` binary
57
+ - `MICROSANDBOX_API_KEY` — when set, every `/api/*` request must send `Authorization: Bearer <key>` (`/health` stays open)
58
+ - `MICROSANDBOX_HOST_PORT_MIN` default: `40000` — lowest host port used when publishing guest ports
59
+ - `MICROSANDBOX_HOST_PORT_MAX` default: `60000` — highest host port used when publishing guest ports
57
60
 
58
61
  Example:
59
62
 
@@ -132,6 +135,73 @@ pnpm --filter @axiom-lattice/microsandbox-service dev
132
135
 
133
136
  This command watches source files, rebuilds the package, and restarts the service automatically.
134
137
 
138
+ ## HTTP API
139
+
140
+ Every response uses a JSON envelope: success is `{ "success": true, "data": ... }`, failure is
141
+ `{ "success": false, "error": { "code": "...", "message": "..." } }`. When `MICROSANDBOX_API_KEY`
142
+ is set, all `/api/*` requests require `Authorization: Bearer <key>` (`/health` stays open).
143
+
144
+ ### Sandbox lifecycle
145
+
146
+ | Method | Path | Purpose |
147
+ |--------|------|---------|
148
+ | `GET` | `/api/sandboxes` | List sandboxes |
149
+ | `GET` | `/api/sandboxes/:name` | Inspect one sandbox |
150
+ | `PUT` | `/api/sandboxes/:name` | Create-or-reuse (`ensure`) |
151
+ | `POST` | `/api/sandboxes/:name/start` | Start / resume |
152
+ | `POST` | `/api/sandboxes/:name/stop` | Pause |
153
+ | `POST` | `/api/sandboxes/:name/kill` | Kill |
154
+ | `DELETE` | `/api/sandboxes/:name` | Delete |
155
+ | `GET` | `/api/sandboxes/:name/status` | Status |
156
+ | `POST` | `/api/sandboxes/:name/logs` | Logs |
157
+ | `GET` | `/api/sandboxes/:name/endpoints/:port` | Resolve a published guest port |
158
+ | `ANY` | `/api/sandboxes/:name/proxy/:port/*` | Stream to a published guest port |
159
+
160
+ File/shell operations are under `/api/files/*` and `/api/shell/exec`; image and named-volume
161
+ operations under `/api/images/*` and `/api/volumes/:name/fs/*`.
162
+
163
+ ### Resident-service primitives
164
+
165
+ `PUT /api/sandboxes/:name` accepts three optional fields beyond image/resource/volume config:
166
+
167
+ | Field | Type | Meaning |
168
+ |-------|------|---------|
169
+ | `entrypoint` | `string[]` | Main process command; replaces the default `tail -f /dev/null`. Use this to run a long-lived service (e.g. a plugin host daemon). |
170
+ | `timeoutSeconds` | `number` \| `null` | Sandbox lifetime. `null` means no expiry. **Currently inert** in the registry — positive values are not yet applied and changes do not force a recreate (scheduled for Plan 3). |
171
+ | `exposePorts` | `number[]` | Guest ports to publish. Each is mapped to a deterministic host port within `[MICROSANDBOX_HOST_PORT_MIN, MICROSANDBOX_HOST_PORT_MAX]`. |
172
+
173
+ Create a resident service and resolve its endpoint:
174
+
175
+ ```bash
176
+ curl -X PUT http://127.0.0.1:4002/api/sandboxes/plugin-weather \
177
+ -H 'content-type: application/json' \
178
+ -d '{
179
+ "image": "kioko12520/sandbox:0.1.0",
180
+ "entrypoint": ["node", "/root/.agents/.plugin-host/server.js"],
181
+ "exposePorts": [8080]
182
+ }'
183
+
184
+ curl http://127.0.0.1:4002/api/sandboxes/plugin-weather/endpoints/8080
185
+ # { "success": true, "data": { "endpoint": "/api/sandboxes/plugin-weather/proxy/8080" } }
186
+ ```
187
+
188
+ `GET /api/sandboxes/:name/endpoints/:port` responses:
189
+
190
+ - `200` — `{ "success": true, "data": { "endpoint": "/api/sandboxes/<name>/proxy/<port>" } }`
191
+ (a relative proxy path the caller composes with its configured service base URL; may also
192
+ include `headers` for proxied backends)
193
+ - `400 INVALID_REQUEST` — `:port` is not an integer in `1..65535`
194
+ - `404 ENDPOINT_NOT_PUBLISHED` — the port was not published for this sandbox
195
+
196
+ The guest→host mapping is recovered from the sandbox's persisted config, so it survives service
197
+ restarts. The loopback mapping stays internal; the returned path points at the proxy route below.
198
+
199
+ `ANY /api/sandboxes/:name/proxy/:port/*` streams the request to the sandbox's published guest
200
+ port (any method, unbuffered, so SSE and large payloads pass through). Authenticate to this
201
+ service with `Authorization: Bearer <MICROSANDBOX_API_KEY>`; the proxy **strips** that header
202
+ before forwarding, so the service key never reaches the (unauthenticated) sandbox host behind
203
+ the loopback-bound port.
204
+
135
205
  ## Operational Notes
136
206
 
137
207
  - Invalid startup configuration exits non-zero.