waterbox 0.1.0-alpha.1 → 0.1.0-alpha.2
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 +33 -20
- package/dist/waterbox-cli.js +21 -28
- package/dist/waterbox.js +16727 -3562
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -2,32 +2,38 @@
|
|
|
2
2
|
|
|
3
3
|
`waterbox` is the supported local stdio MCP for isolated, stateful coding
|
|
4
4
|
sandboxes. It requires Node.js 24.15.0 or newer, opens no listener, and supports
|
|
5
|
-
explicitly configured Box and Vercel Sandbox accounts.
|
|
6
|
-
Waterbox
|
|
5
|
+
hosted Waterbox plus explicitly configured direct Box and Vercel Sandbox accounts.
|
|
6
|
+
It is not the hosted Waterbox service and has no JavaScript library API.
|
|
7
7
|
|
|
8
8
|
## Install And Configure
|
|
9
9
|
|
|
10
10
|
```sh
|
|
11
|
-
npx add-mcp waterbox
|
|
11
|
+
npx add-mcp waterbox
|
|
12
12
|
```
|
|
13
13
|
|
|
14
|
-
This prerelease is
|
|
14
|
+
This prerelease is available through npm's default tag. The command installs
|
|
15
15
|
MCP client configuration equivalent to `{ "command": "npx", "args": ["-y",
|
|
16
|
-
"waterbox
|
|
17
|
-
waterbox
|
|
16
|
+
"waterbox"] }`. It does not collect provider credentials. Running `npx
|
|
17
|
+
waterbox` directly starts the stdio server; use an explicit argument for
|
|
18
18
|
terminal-only onboarding:
|
|
19
19
|
|
|
20
20
|
```sh
|
|
21
|
-
npx waterbox
|
|
22
|
-
npx waterbox
|
|
23
|
-
npx waterbox
|
|
21
|
+
npx waterbox setup
|
|
22
|
+
npx waterbox status
|
|
23
|
+
npx waterbox logout
|
|
24
24
|
```
|
|
25
25
|
|
|
26
26
|
Interactive setup stores only the provider credential in the native keyring.
|
|
27
27
|
Non-secret settings are written atomically to `~/.waterbox/config.json` using
|
|
28
|
-
the exact official endpoints `https://ascii.dev/api/box/v1` and
|
|
29
|
-
`https://api.vercel.com
|
|
30
|
-
|
|
28
|
+
the exact official direct-provider endpoints `https://ascii.dev/api/box/v1` and
|
|
29
|
+
`https://api.vercel.com/`; hosted Waterbox is pinned to `https://api.waterbox.ai/`.
|
|
30
|
+
Interactive setup shows hosted Waterbox only when its public capability document
|
|
31
|
+
advertises availability. Existing hosted keyring configuration and explicit
|
|
32
|
+
`WATERBOX_PROVIDER=waterbox` environment configuration remain valid independently.
|
|
33
|
+
`FORCE_DISPLAY_WATERBOX=1` is a development/debug override that shows it without
|
|
34
|
+
fetching capabilities.
|
|
35
|
+
Status never prints credentials. Logout removes local settings and all three
|
|
36
|
+
keyring entries but does not alter remote resources or
|
|
31
37
|
the SQLite resource registry.
|
|
32
38
|
|
|
33
39
|
For headless systems without a usable keyring, place a complete provider
|
|
@@ -35,12 +41,16 @@ configuration in the MCP client's environment/secret facility. Do not put
|
|
|
35
41
|
secrets in command arguments:
|
|
36
42
|
|
|
37
43
|
```text
|
|
38
|
-
|
|
44
|
+
Waterbox: WATERBOX_PROVIDER=waterbox, WATERBOX_API_KEY=<client-managed secret>
|
|
45
|
+
Box: WATERBOX_PROVIDER=box, BOX_API_KEY=<client-managed secret>,
|
|
46
|
+
WATERBOX_AUTO_STOP=40m
|
|
39
47
|
Vercel: WATERBOX_PROVIDER=vercel, VERCEL_TOKEN=<client-managed secret>,
|
|
40
|
-
VERCEL_TEAM_ID=<team identifier>, VERCEL_PROJECT_ID=<project identifier
|
|
48
|
+
VERCEL_TEAM_ID=<team identifier>, VERCEL_PROJECT_ID=<project identifier>,
|
|
49
|
+
WATERBOX_AUTO_STOP=40m
|
|
41
50
|
```
|
|
42
51
|
|
|
43
|
-
|
|
52
|
+
Hosted Waterbox always targets `https://api.waterbox.ai/`. Custom Box/Vercel
|
|
53
|
+
endpoints are supported only when the complete provider
|
|
44
54
|
selection and credentials come from process environment. Environment and
|
|
45
55
|
persisted sources are never mixed. `.env` is never loaded implicitly.
|
|
46
56
|
|
|
@@ -51,7 +61,9 @@ tools before filesystem, SQLite, artifact, or provider I/O.
|
|
|
51
61
|
|
|
52
62
|
| Variable | Required | Default |
|
|
53
63
|
| --- | --- | --- |
|
|
54
|
-
| `WATERBOX_PROVIDER` | Environment setup: `box
|
|
64
|
+
| `WATERBOX_PROVIDER` | Environment setup: `waterbox`, `box`, or `vercel` | none |
|
|
65
|
+
| `WATERBOX_API_KEY` | Hosted Waterbox environment setup | none |
|
|
66
|
+
| `FORCE_DISPLAY_WATERBOX` | Development/debug interactive-setup override; only `1` enables it | none |
|
|
55
67
|
| `BOX_API_KEY` | Box environment setup | none |
|
|
56
68
|
| `BOX_API_BASE_URL` | Environment-only override | `https://ascii.dev/api/box/v1` |
|
|
57
69
|
| `BOX_POLL_INTERVAL_MS` | No | `1000` |
|
|
@@ -63,19 +75,20 @@ tools before filesystem, SQLite, artifact, or provider I/O.
|
|
|
63
75
|
| `VERCEL_POLL_INTERVAL_MS` | No | `1000` |
|
|
64
76
|
| `VERCEL_POLL_TIMEOUT_MS` | No | `120000` |
|
|
65
77
|
| `VERCEL_REQUEST_TIMEOUT_MS` | No | `30000` |
|
|
66
|
-
| `WATERBOX_AUTO_STOP` |
|
|
78
|
+
| `WATERBOX_AUTO_STOP` | Box and Vercel environment setup; whole minutes/hours such as `40m` or `6h` | none |
|
|
67
79
|
| `WATERBOX_SQLITE_PATH` | No | `~/.waterbox/direct.sqlite` |
|
|
68
80
|
|
|
69
81
|
## Lifecycle And Tools
|
|
70
82
|
|
|
71
83
|
Waterbox exposes `create_sandbox`, `probe_sandbox`, `stop_sandbox`,
|
|
72
|
-
`
|
|
84
|
+
`list_snapshots`, `create_snapshot`, `delete_snapshot`,
|
|
73
85
|
`send_file_securely`, `read`, `write`, `edit`, `patch`, `glob`, `grep`, and
|
|
74
86
|
`bash`. Resource IDs are explicit; there is no selected sandbox or
|
|
75
87
|
`list_sandboxes` tool.
|
|
76
88
|
|
|
77
|
-
|
|
78
|
-
ownership from provider inventory.
|
|
89
|
+
Direct Box/Vercel mode owns only records in its local SQLite database and does
|
|
90
|
+
not infer ownership from provider inventory. Hosted Waterbox records are owned
|
|
91
|
+
by the hosted service. Sandboxes persist after the MCP exits.
|
|
79
92
|
Changing provider scope does not migrate or clean old resources, which may
|
|
80
93
|
continue to incur charges. Fresh and snapshot-sourced sandboxes receive the
|
|
81
94
|
current packaged Node CLI without a shared system snapshot, Bun, or daemon.
|
package/dist/waterbox-cli.js
CHANGED
|
@@ -12529,6 +12529,8 @@ var NonEmptyStringSchema = external_exports.string().min(1);
|
|
|
12529
12529
|
var PositiveIntegerSchema = external_exports.number().int().positive();
|
|
12530
12530
|
var FilePathSchema = NonEmptyStringSchema.max(4096);
|
|
12531
12531
|
var ToolNameSchema = external_exports.enum(["read", "write", "edit", "patch", "glob", "grep", "bash"]);
|
|
12532
|
+
var MAX_BASH_OUTPUT_BYTES = 1048576;
|
|
12533
|
+
var MAX_TOOL_RESULT_BYTES = 8 * 1024 * 1024;
|
|
12532
12534
|
var BashJobIdSchema = external_exports.string().regex(/^job_[0-9a-f]{32}$/);
|
|
12533
12535
|
var ReadToolArgumentsSchema = external_exports.object({
|
|
12534
12536
|
filePath: FilePathSchema,
|
|
@@ -12659,12 +12661,7 @@ var BashToolResultSchema = external_exports.discriminatedUnion("outcome", [
|
|
|
12659
12661
|
CompletedBashToolResultSchema,
|
|
12660
12662
|
DispatchedBashToolResultSchema
|
|
12661
12663
|
]);
|
|
12662
|
-
var
|
|
12663
|
-
var WriteToolEventSchema = WriteToolResultSchema.extend({ type: external_exports.literal("result") });
|
|
12664
|
-
var EditToolEventSchema = EditToolResultSchema.extend({ type: external_exports.literal("result") });
|
|
12665
|
-
var PatchToolEventSchema = PatchToolResultSchema.extend({ type: external_exports.literal("result") });
|
|
12666
|
-
var GlobToolEventSchema = GlobToolResultSchema.extend({ type: external_exports.literal("result") });
|
|
12667
|
-
var GrepToolEventSchema = GrepToolResultSchema.extend({ type: external_exports.literal("result") });
|
|
12664
|
+
var ToolResultSchema = external_exports.union([ReadToolResultSchema, WriteToolResultSchema, EditToolResultSchema, PatchToolResultSchema, GlobToolResultSchema, GrepToolResultSchema, BashToolResultSchema]);
|
|
12668
12665
|
var BashToolEventSchema = external_exports.union([
|
|
12669
12666
|
external_exports.object({ type: external_exports.literal("stdout"), data: external_exports.string() }).strict(),
|
|
12670
12667
|
external_exports.object({ type: external_exports.literal("stderr"), data: external_exports.string() }).strict(),
|
|
@@ -13434,7 +13431,6 @@ var normalize = (value) => value.replace(/[‘’‚‛]/g, "'").replace(/[“
|
|
|
13434
13431
|
var stripHeredoc = (input) => input.match(/^(?:cat\s+)?<<(['"]?)(\w+)\1\s*\n([\s\S]*?)\n\2\s*$/)?.[3] ?? input;
|
|
13435
13432
|
|
|
13436
13433
|
// packages/sandbox-runtime/src/runtime.ts
|
|
13437
|
-
var MAX_BASH_OUTPUT_BYTES = 1048576;
|
|
13438
13434
|
var MAX_TIMEOUT_MS = 2147483647;
|
|
13439
13435
|
var RuntimeError = class extends Error {
|
|
13440
13436
|
constructor(status, message) {
|
|
@@ -13952,12 +13948,12 @@ function createRuntime(options) {
|
|
|
13952
13948
|
combinedSignal.throwIfAborted();
|
|
13953
13949
|
const root = await realpath(configuredRoot);
|
|
13954
13950
|
combinedSignal.throwIfAborted();
|
|
13955
|
-
if (name === "write") return
|
|
13956
|
-
if (name === "edit") return
|
|
13957
|
-
if (name === "patch") return
|
|
13958
|
-
if (name === "read") return
|
|
13959
|
-
if (name === "glob") return
|
|
13960
|
-
if (name === "grep") return
|
|
13951
|
+
if (name === "write") return await writeTool(root, body);
|
|
13952
|
+
if (name === "edit") return await editTool(root, body);
|
|
13953
|
+
if (name === "patch") return await patchTool(root, body);
|
|
13954
|
+
if (name === "read") return await readTool(root, body);
|
|
13955
|
+
if (name === "glob") return await globTool(root, body, combinedSignal);
|
|
13956
|
+
if (name === "grep") return await grepTool(root, body, combinedSignal);
|
|
13961
13957
|
return await bashTool(root, body, combinedSignal);
|
|
13962
13958
|
},
|
|
13963
13959
|
shutdown() {
|
|
@@ -13974,7 +13970,6 @@ import { chmod, lstat as lstat3, mkdir as mkdir2, open as open3, readFile as rea
|
|
|
13974
13970
|
import { relative as relative2, resolve as resolve2, sep as sep3 } from "node:path";
|
|
13975
13971
|
var DEFAULT_BASH_JOBS_ROOT = "/run/waterbox/bash-jobs";
|
|
13976
13972
|
var DEFAULT_BASH_YIELD_AFTER_MS = 15e3;
|
|
13977
|
-
var MAX_BASH_OUTPUT_BYTES2 = 1048576;
|
|
13978
13973
|
var MAX_STATUS_BYTES = 64 * 1024;
|
|
13979
13974
|
function pathsFor(jobRoot, jobId) {
|
|
13980
13975
|
if (!/^job_[0-9a-f]{32}$/.test(jobId)) throw new Error("Invalid async Bash job ID");
|
|
@@ -14218,14 +14213,13 @@ async function runOneShotBash(workspaceRoot2, input, signal, options = {}) {
|
|
|
14218
14213
|
try {
|
|
14219
14214
|
[status, output] = await Promise.all([
|
|
14220
14215
|
readTerminalStatus(paths.statusPath),
|
|
14221
|
-
readBounded(paths.outputPath,
|
|
14216
|
+
readBounded(paths.outputPath, MAX_BASH_OUTPUT_BYTES)
|
|
14222
14217
|
]);
|
|
14223
14218
|
} catch {
|
|
14224
14219
|
throw new RuntimeError(500, "Async Bash worker failed");
|
|
14225
14220
|
}
|
|
14226
14221
|
await rm2(paths.directory, { recursive: true, force: true }).catch(() => void 0);
|
|
14227
14222
|
return {
|
|
14228
|
-
type: "result",
|
|
14229
14223
|
outcome: "completed",
|
|
14230
14224
|
title: args.description ?? "Bash command",
|
|
14231
14225
|
output: output.value || (status.timedOut ? "Command timed out" : "Command completed without output"),
|
|
@@ -14247,7 +14241,6 @@ async function runOneShotBash(workspaceRoot2, input, signal, options = {}) {
|
|
|
14247
14241
|
throw error45;
|
|
14248
14242
|
}
|
|
14249
14243
|
return {
|
|
14250
|
-
type: "result",
|
|
14251
14244
|
outcome: "dispatched",
|
|
14252
14245
|
title: "Bash command dispatched",
|
|
14253
14246
|
output: "Command dispatched. statusPath reports execution state, and outputPath receives output continuously. Repeated output reads can duplicate tokens and pollute context.",
|
|
@@ -22158,19 +22151,19 @@ function isExactObject(value, keys) {
|
|
|
22158
22151
|
}
|
|
22159
22152
|
|
|
22160
22153
|
// packages/sandbox-cli/src/index.ts
|
|
22161
|
-
var
|
|
22162
|
-
read:
|
|
22163
|
-
write:
|
|
22164
|
-
edit:
|
|
22165
|
-
patch:
|
|
22166
|
-
glob:
|
|
22167
|
-
grep:
|
|
22168
|
-
bash:
|
|
22154
|
+
var resultSchemas = {
|
|
22155
|
+
read: ReadToolResultSchema,
|
|
22156
|
+
write: WriteToolResultSchema,
|
|
22157
|
+
edit: EditToolResultSchema,
|
|
22158
|
+
patch: PatchToolResultSchema,
|
|
22159
|
+
glob: GlobToolResultSchema,
|
|
22160
|
+
grep: GrepToolResultSchema,
|
|
22161
|
+
bash: BashToolResultSchema
|
|
22169
22162
|
};
|
|
22170
22163
|
async function runCli(argv2, options) {
|
|
22171
22164
|
const io = options.io ?? { stdout: (value) => process.stdout.write(value), stderr: (value) => process.stderr.write(value) };
|
|
22172
22165
|
if (argv2.length === 1 && argv2[0] === "health") {
|
|
22173
|
-
io.stdout(`${JSON.stringify({ ok: true, protocolVersion: CLI_PROTOCOL_VERSION, tools: Object.keys(
|
|
22166
|
+
io.stdout(`${JSON.stringify({ ok: true, protocolVersion: CLI_PROTOCOL_VERSION, tools: Object.keys(resultSchemas) })}
|
|
22174
22167
|
`);
|
|
22175
22168
|
return 0;
|
|
22176
22169
|
}
|
|
@@ -22243,7 +22236,7 @@ async function runCli(argv2, options) {
|
|
|
22243
22236
|
if (invocation.tool === "bash") {
|
|
22244
22237
|
try {
|
|
22245
22238
|
const result = await runOneShotBash(options.workspaceRoot, invocation.arguments, options.signal, options.asyncBash);
|
|
22246
|
-
io.stdout(`${JSON.stringify(
|
|
22239
|
+
io.stdout(`${JSON.stringify(BashToolResultSchema.parse(result))}
|
|
22247
22240
|
`);
|
|
22248
22241
|
return 0;
|
|
22249
22242
|
} catch (error45) {
|
|
@@ -22268,7 +22261,7 @@ async function runCli(argv2, options) {
|
|
|
22268
22261
|
`);
|
|
22269
22262
|
return 0;
|
|
22270
22263
|
}
|
|
22271
|
-
io.stdout(`${JSON.stringify(
|
|
22264
|
+
io.stdout(`${JSON.stringify(resultSchemas[invocation.tool].parse(result))}
|
|
22272
22265
|
`);
|
|
22273
22266
|
return 0;
|
|
22274
22267
|
} catch (error45) {
|