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 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. There is no hosted
6
- Waterbox mode and no JavaScript library API.
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@next
11
+ npx add-mcp waterbox
12
12
  ```
13
13
 
14
- This prerelease is distributed under the npm `next` tag. The command installs
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@next"] }`. It does not collect provider credentials. Running `npx
17
- waterbox@next` directly starts the stdio server; use an explicit argument for
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@next setup
22
- npx waterbox@next status
23
- npx waterbox@next logout
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/`. Status never prints credentials. Logout removes
30
- local settings and both keyring entries but does not alter remote resources or
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
- Box: WATERBOX_PROVIDER=box, BOX_API_KEY=<client-managed secret>
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
- Custom Box/Vercel endpoints are supported only when the complete provider
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` or `vercel` | none |
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` | No; whole minutes/hours such as `30m` or `2h` | provider default |
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
- `delete_sandbox`, `list_snapshots`, `create_snapshot`, `delete_snapshot`,
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
- Waterbox owns only records in its local SQLite database and does not infer
78
- ownership from provider inventory. Sandboxes persist after the MCP exits.
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.
@@ -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 ReadToolEventSchema = ReadToolResultSchema.extend({ type: external_exports.literal("result") });
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 { type: "result", ...await writeTool(root, body) };
13956
- if (name === "edit") return { type: "result", ...await editTool(root, body) };
13957
- if (name === "patch") return { type: "result", ...await patchTool(root, body) };
13958
- if (name === "read") return { type: "result", ...await readTool(root, body) };
13959
- if (name === "glob") return { type: "result", ...await globTool(root, body, combinedSignal) };
13960
- if (name === "grep") return { type: "result", ...await grepTool(root, body, combinedSignal) };
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, MAX_BASH_OUTPUT_BYTES2)
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 eventSchemas = {
22162
- read: ReadToolEventSchema,
22163
- write: WriteToolEventSchema,
22164
- edit: EditToolEventSchema,
22165
- patch: PatchToolEventSchema,
22166
- glob: GlobToolEventSchema,
22167
- grep: GrepToolEventSchema,
22168
- bash: BashToolEventSchema
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(eventSchemas) })}
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(BashToolEventSchema.parse(result))}
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(eventSchemas[invocation.tool].parse(result))}
22264
+ io.stdout(`${JSON.stringify(resultSchemas[invocation.tool].parse(result))}
22272
22265
  `);
22273
22266
  return 0;
22274
22267
  } catch (error45) {