waterbox 0.1.0-alpha.2 → 0.1.0-alpha.3

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.
Files changed (3) hide show
  1. package/README.md +15 -8
  2. package/dist/waterbox.js +202 -47
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -23,22 +23,24 @@ npx waterbox status
23
23
  npx waterbox logout
24
24
  ```
25
25
 
26
- Interactive setup stores only the provider credential in the native keyring.
27
- Non-secret settings are written atomically to `~/.waterbox/config.json` using
26
+ Interactive setup stores only the provider credential in the native keyring on
27
+ macOS and Windows. Linux uses `~/.waterbox/credentials.json` with a `0700`
28
+ directory and `0600` file. Non-secret settings are written atomically to
29
+ `~/.waterbox/config.json` using
28
30
  the exact official direct-provider endpoints `https://ascii.dev/api/box/v1` and
29
31
  `https://api.vercel.com/`; hosted Waterbox is pinned to `https://api.waterbox.ai/`.
30
32
  Interactive setup shows hosted Waterbox only when its public capability document
31
- advertises availability. Existing hosted keyring configuration and explicit
33
+ advertises availability. Existing hosted persisted configuration and explicit
32
34
  `WATERBOX_PROVIDER=waterbox` environment configuration remain valid independently.
33
35
  `FORCE_DISPLAY_WATERBOX=1` is a development/debug override that shows it without
34
36
  fetching capabilities.
35
- Status never prints credentials. Logout removes local settings and all three
36
- keyring entries but does not alter remote resources or
37
+ Status never prints credentials and identifies the active credential backend.
38
+ Logout removes local settings and all three stored credentials but does not alter remote resources or
37
39
  the SQLite resource registry.
38
40
 
39
- For headless systems without a usable keyring, place a complete provider
40
- configuration in the MCP client's environment/secret facility. Do not put
41
- secrets in command arguments:
41
+ For containers, automation, or systems where user-readable file storage is not
42
+ appropriate, place a complete provider configuration in the MCP client's
43
+ environment/secret facility. Do not put secrets in command arguments:
42
44
 
43
45
  ```text
44
46
  Waterbox: WATERBOX_PROVIDER=waterbox, WATERBOX_API_KEY=<client-managed secret>
@@ -54,6 +56,11 @@ endpoints are supported only when the complete provider
54
56
  selection and credentials come from process environment. Environment and
55
57
  persisted sources are never mixed. `.env` is never loaded implicitly.
56
58
 
59
+ Linux credentials are plaintext at rest and protected by filesystem ownership
60
+ and permissions. Same-user processes, root, and backups may read them. Linux
61
+ users upgrading from alpha.2 should rerun `npx waterbox setup`; native-keyring
62
+ entries are not migrated automatically.
63
+
57
64
  An unconfigured server remains connected and returns setup guidance from all
58
65
  tools before filesystem, SQLite, artifact, or provider I/O.
59
66
 
package/dist/waterbox.js CHANGED
@@ -2007,11 +2007,11 @@ var require_codegen = __commonJS({
2007
2007
  const rhs = this.rhs === void 0 ? "" : ` = ${this.rhs}`;
2008
2008
  return `${varKind} ${this.name}${rhs};` + _n;
2009
2009
  }
2010
- optimizeNames(names, constants2) {
2010
+ optimizeNames(names, constants3) {
2011
2011
  if (!names[this.name.str])
2012
2012
  return;
2013
2013
  if (this.rhs)
2014
- this.rhs = optimizeExpr(this.rhs, names, constants2);
2014
+ this.rhs = optimizeExpr(this.rhs, names, constants3);
2015
2015
  return this;
2016
2016
  }
2017
2017
  get names() {
@@ -2028,10 +2028,10 @@ var require_codegen = __commonJS({
2028
2028
  render({ _n }) {
2029
2029
  return `${this.lhs} = ${this.rhs};` + _n;
2030
2030
  }
2031
- optimizeNames(names, constants2) {
2031
+ optimizeNames(names, constants3) {
2032
2032
  if (this.lhs instanceof code_1.Name && !names[this.lhs.str] && !this.sideEffects)
2033
2033
  return;
2034
- this.rhs = optimizeExpr(this.rhs, names, constants2);
2034
+ this.rhs = optimizeExpr(this.rhs, names, constants3);
2035
2035
  return this;
2036
2036
  }
2037
2037
  get names() {
@@ -2092,8 +2092,8 @@ var require_codegen = __commonJS({
2092
2092
  optimizeNodes() {
2093
2093
  return `${this.code}` ? this : void 0;
2094
2094
  }
2095
- optimizeNames(names, constants2) {
2096
- this.code = optimizeExpr(this.code, names, constants2);
2095
+ optimizeNames(names, constants3) {
2096
+ this.code = optimizeExpr(this.code, names, constants3);
2097
2097
  return this;
2098
2098
  }
2099
2099
  get names() {
@@ -2122,12 +2122,12 @@ var require_codegen = __commonJS({
2122
2122
  }
2123
2123
  return nodes.length > 0 ? this : void 0;
2124
2124
  }
2125
- optimizeNames(names, constants2) {
2125
+ optimizeNames(names, constants3) {
2126
2126
  const { nodes } = this;
2127
2127
  let i = nodes.length;
2128
2128
  while (i--) {
2129
2129
  const n = nodes[i];
2130
- if (n.optimizeNames(names, constants2))
2130
+ if (n.optimizeNames(names, constants3))
2131
2131
  continue;
2132
2132
  subtractNames(names, n.names);
2133
2133
  nodes.splice(i, 1);
@@ -2180,12 +2180,12 @@ var require_codegen = __commonJS({
2180
2180
  return void 0;
2181
2181
  return this;
2182
2182
  }
2183
- optimizeNames(names, constants2) {
2183
+ optimizeNames(names, constants3) {
2184
2184
  var _a;
2185
- this.else = (_a = this.else) === null || _a === void 0 ? void 0 : _a.optimizeNames(names, constants2);
2186
- if (!(super.optimizeNames(names, constants2) || this.else))
2185
+ this.else = (_a = this.else) === null || _a === void 0 ? void 0 : _a.optimizeNames(names, constants3);
2186
+ if (!(super.optimizeNames(names, constants3) || this.else))
2187
2187
  return;
2188
- this.condition = optimizeExpr(this.condition, names, constants2);
2188
+ this.condition = optimizeExpr(this.condition, names, constants3);
2189
2189
  return this;
2190
2190
  }
2191
2191
  get names() {
@@ -2208,10 +2208,10 @@ var require_codegen = __commonJS({
2208
2208
  render(opts2) {
2209
2209
  return `for(${this.iteration})` + super.render(opts2);
2210
2210
  }
2211
- optimizeNames(names, constants2) {
2212
- if (!super.optimizeNames(names, constants2))
2211
+ optimizeNames(names, constants3) {
2212
+ if (!super.optimizeNames(names, constants3))
2213
2213
  return;
2214
- this.iteration = optimizeExpr(this.iteration, names, constants2);
2214
+ this.iteration = optimizeExpr(this.iteration, names, constants3);
2215
2215
  return this;
2216
2216
  }
2217
2217
  get names() {
@@ -2247,10 +2247,10 @@ var require_codegen = __commonJS({
2247
2247
  render(opts2) {
2248
2248
  return `for(${this.varKind} ${this.name} ${this.loop} ${this.iterable})` + super.render(opts2);
2249
2249
  }
2250
- optimizeNames(names, constants2) {
2251
- if (!super.optimizeNames(names, constants2))
2250
+ optimizeNames(names, constants3) {
2251
+ if (!super.optimizeNames(names, constants3))
2252
2252
  return;
2253
- this.iterable = optimizeExpr(this.iterable, names, constants2);
2253
+ this.iterable = optimizeExpr(this.iterable, names, constants3);
2254
2254
  return this;
2255
2255
  }
2256
2256
  get names() {
@@ -2292,11 +2292,11 @@ var require_codegen = __commonJS({
2292
2292
  (_b = this.finally) === null || _b === void 0 ? void 0 : _b.optimizeNodes();
2293
2293
  return this;
2294
2294
  }
2295
- optimizeNames(names, constants2) {
2295
+ optimizeNames(names, constants3) {
2296
2296
  var _a, _b;
2297
- super.optimizeNames(names, constants2);
2298
- (_a = this.catch) === null || _a === void 0 ? void 0 : _a.optimizeNames(names, constants2);
2299
- (_b = this.finally) === null || _b === void 0 ? void 0 : _b.optimizeNames(names, constants2);
2297
+ super.optimizeNames(names, constants3);
2298
+ (_a = this.catch) === null || _a === void 0 ? void 0 : _a.optimizeNames(names, constants3);
2299
+ (_b = this.finally) === null || _b === void 0 ? void 0 : _b.optimizeNames(names, constants3);
2300
2300
  return this;
2301
2301
  }
2302
2302
  get names() {
@@ -2597,7 +2597,7 @@ var require_codegen = __commonJS({
2597
2597
  function addExprNames(names, from) {
2598
2598
  return from instanceof code_1._CodeOrName ? addNames(names, from.names) : names;
2599
2599
  }
2600
- function optimizeExpr(expr, names, constants2) {
2600
+ function optimizeExpr(expr, names, constants3) {
2601
2601
  if (expr instanceof code_1.Name)
2602
2602
  return replaceName(expr);
2603
2603
  if (!canOptimize(expr))
@@ -2612,14 +2612,14 @@ var require_codegen = __commonJS({
2612
2612
  return items;
2613
2613
  }, []));
2614
2614
  function replaceName(n) {
2615
- const c = constants2[n.str];
2615
+ const c = constants3[n.str];
2616
2616
  if (c === void 0 || names[n.str] !== 1)
2617
2617
  return n;
2618
2618
  delete names[n.str];
2619
2619
  return c;
2620
2620
  }
2621
2621
  function canOptimize(e) {
2622
- return e instanceof code_1._Code && e._items.some((c) => c instanceof code_1.Name && names[c.str] === 1 && constants2[c.str] !== void 0);
2622
+ return e instanceof code_1._Code && e._items.some((c) => c instanceof code_1.Name && names[c.str] === 1 && constants3[c.str] !== void 0);
2623
2623
  }
2624
2624
  }
2625
2625
  function subtractNames(names, from) {
@@ -49971,7 +49971,7 @@ function snapshotName(accountId, snapshotId) {
49971
49971
  const digest2 = createHash2("sha256").update(accountId).update("\0").update(snapshotId).digest("hex").slice(0, 32);
49972
49972
  return `waterbox-${hint}-${digest2}`;
49973
49973
  }
49974
- var PACKAGE_VERSION = "0.1.0-alpha.1";
49974
+ var PACKAGE_VERSION = "0.1.0-alpha.2";
49975
49975
  var SystemBoxProviderClock = class {
49976
49976
  now() {
49977
49977
  return /* @__PURE__ */ new Date();
@@ -51913,7 +51913,7 @@ async function createEmbeddedApiBackend(config3, overrides = {}) {
51913
51913
  }
51914
51914
  async function createConfiguredEmbeddedApiBackend(configuration, artifactLocation, diagnostic) {
51915
51915
  validateConfiguredMcpBackend(configuration);
51916
- const artifact = await loadSandboxRuntimeArtifact(artifactLocation, "0.1.0-alpha.2");
51916
+ const artifact = await loadSandboxRuntimeArtifact(artifactLocation, "0.1.0-alpha.3");
51917
51917
  return createEmbeddedApiBackend({ sqlitePath: configuration.sqlitePath, accountId: "local", provider: { ...configuration.provider, runtimeArtifact: artifact }, ...diagnostic === void 0 ? {} : { diagnostic } });
51918
51918
  }
51919
51919
  function validateBaseConfiguration(config3, identityResolver) {
@@ -52026,7 +52026,8 @@ function positiveNumber(value) {
52026
52026
  }
52027
52027
 
52028
52028
  // packages/mcp/src/onboarding.ts
52029
- import { chmod, lstat, mkdir as mkdir2, readFile as readFile2, rename, rm, writeFile } from "node:fs/promises";
52029
+ import { constants } from "node:fs";
52030
+ import { chmod, lstat, mkdir as mkdir2, open, readFile as readFile2, rename, rm, writeFile } from "node:fs/promises";
52030
52031
  import { homedir as homedir2 } from "node:os";
52031
52032
  import { dirname as dirname2, join as join3 } from "node:path";
52032
52033
  var KEYRING_SERVICE = "waterbox";
@@ -52038,6 +52039,7 @@ var CAPABILITY_TIMEOUT_MS = 2e3;
52038
52039
  var MAX_CAPABILITY_BYTES = 16 * 1024;
52039
52040
  var accounts = { waterbox: "api-key", box: "box-api-key", vercel: "vercel-token" };
52040
52041
  var MAX_CONFIG_BYTES = 64 * 1024;
52042
+ var MAX_CREDENTIAL_BYTES = 64 * 1024;
52041
52043
  var setupGuidance = "Waterbox MCP is not configured. Run npx waterbox setup, then restart the MCP client. Environment-only setup is also supported: set WATERBOX_PROVIDER and the selected provider's variables (Waterbox: WATERBOX_API_KEY; Box: BOX_API_KEY, WATERBOX_AUTO_STOP; Vercel: VERCEL_TOKEN, VERCEL_TEAM_ID, VERCEL_PROJECT_ID, WATERBOX_AUTO_STOP). Do not provide credentials in chat or tool arguments.";
52042
52044
  var OnboardingError = class extends Error {
52043
52045
  constructor(message = setupGuidance) {
@@ -52096,6 +52098,7 @@ function configStorage(home = homedir2()) {
52096
52098
  }
52097
52099
  function nativeCredentialStore() {
52098
52100
  return {
52101
+ backend: "keyring",
52099
52102
  async get(provider) {
52100
52103
  const { AsyncEntry } = await import("@napi-rs/keyring");
52101
52104
  const value = await new AsyncEntry(KEYRING_SERVICE, accounts[provider]).getPassword();
@@ -52112,6 +52115,145 @@ function nativeCredentialStore() {
52112
52115
  }
52113
52116
  };
52114
52117
  }
52118
+ var credentialDocumentSchema = external_exports.object({ version: external_exports.literal(1), credentials: external_exports.object({ waterbox: external_exports.string().optional(), box: external_exports.string().optional(), vercel: external_exports.string().optional() }).strict() }).strict();
52119
+ var credentialQueues = /* @__PURE__ */ new Map();
52120
+ function fileCredentialStore(home = homedir2()) {
52121
+ const path = join3(home, ".waterbox", "credentials.json");
52122
+ const unavailable = () => new OnboardingError("Waterbox credential storage is unavailable. Run npx waterbox setup or use environment-only configuration.");
52123
+ const queued = async (operation) => {
52124
+ const prior = credentialQueues.get(path) ?? Promise.resolve();
52125
+ let release;
52126
+ const current = new Promise((resolve) => {
52127
+ release = resolve;
52128
+ });
52129
+ const waiting = prior.then(() => current, () => current);
52130
+ credentialQueues.set(path, waiting);
52131
+ await prior.catch(() => {
52132
+ });
52133
+ try {
52134
+ return await operation();
52135
+ } finally {
52136
+ release();
52137
+ if (credentialQueues.get(path) === waiting) credentialQueues.delete(path);
52138
+ }
52139
+ };
52140
+ const readDocument = async () => {
52141
+ try {
52142
+ const directory = await lstat(dirname2(path));
52143
+ if (directory.isSymbolicLink() || !directory.isDirectory() || (directory.mode & 511) !== 448 || !ownedByCurrentUser(directory.uid)) throw unavailable();
52144
+ const file3 = await open(path, constants.O_RDONLY | constants.O_NOFOLLOW);
52145
+ let value;
52146
+ try {
52147
+ const metadata = await file3.stat();
52148
+ if (!metadata.isFile() || metadata.nlink !== 1 || metadata.size > MAX_CREDENTIAL_BYTES || (metadata.mode & 511) !== 384 || !ownedByCurrentUser(metadata.uid)) throw unavailable();
52149
+ const bytes = Buffer.alloc(metadata.size + 1);
52150
+ let size = 0;
52151
+ while (size < bytes.length) {
52152
+ const result = await file3.read(bytes, size, bytes.length - size, size);
52153
+ if (result.bytesRead === 0) break;
52154
+ size += result.bytesRead;
52155
+ }
52156
+ if (size !== metadata.size) throw unavailable();
52157
+ value = JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(bytes.subarray(0, size)));
52158
+ } catch {
52159
+ throw unavailable();
52160
+ } finally {
52161
+ await file3.close();
52162
+ }
52163
+ const parsed = credentialDocumentSchema.safeParse(value);
52164
+ if (!parsed.success) throw unavailable();
52165
+ for (const secret of Object.values(parsed.data.credentials)) try {
52166
+ providerCredential(secret);
52167
+ } catch {
52168
+ throw unavailable();
52169
+ }
52170
+ return parsed.data;
52171
+ } catch (error47) {
52172
+ if (error47.code === "ENOENT") return void 0;
52173
+ if (error47 instanceof OnboardingError) throw error47;
52174
+ throw unavailable();
52175
+ }
52176
+ };
52177
+ const syncDirectory = async () => {
52178
+ try {
52179
+ const directory = await open(dirname2(path), "r");
52180
+ try {
52181
+ await directory.sync();
52182
+ } finally {
52183
+ await directory.close();
52184
+ }
52185
+ } catch (error47) {
52186
+ if (!["EINVAL", "ENOTSUP", "EPERM", "EISDIR"].includes(error47.code ?? "")) throw error47;
52187
+ }
52188
+ };
52189
+ const writeDocument = async (document) => {
52190
+ const directoryPath = dirname2(path);
52191
+ try {
52192
+ await mkdir2(directoryPath, { recursive: true, mode: 448 });
52193
+ const directory = await lstat(directoryPath);
52194
+ if (directory.isSymbolicLink() || !directory.isDirectory() || !ownedByCurrentUser(directory.uid)) throw unavailable();
52195
+ await chmod(directoryPath, 448);
52196
+ try {
52197
+ const existing = await lstat(path);
52198
+ if (existing.isSymbolicLink() || !existing.isFile()) throw unavailable();
52199
+ } catch (error47) {
52200
+ if (error47.code !== "ENOENT") throw error47;
52201
+ }
52202
+ const temporary = `${path}.${process.pid}.${crypto.randomUUID()}.tmp`;
52203
+ try {
52204
+ await writeFile(temporary, JSON.stringify(document), { encoding: "utf8", mode: 384, flag: "wx" });
52205
+ await chmod(temporary, 384);
52206
+ const file3 = await open(temporary, "r");
52207
+ try {
52208
+ await file3.sync();
52209
+ } finally {
52210
+ await file3.close();
52211
+ }
52212
+ await rename(temporary, path);
52213
+ await syncDirectory();
52214
+ } catch (error47) {
52215
+ await rm(temporary, { force: true }).catch(() => {
52216
+ });
52217
+ throw error47;
52218
+ }
52219
+ } catch (error47) {
52220
+ if (error47 instanceof OnboardingError) throw error47;
52221
+ throw unavailable();
52222
+ }
52223
+ };
52224
+ return {
52225
+ backend: "file",
52226
+ async get(provider) {
52227
+ return queued(async () => (await readDocument())?.credentials[provider]);
52228
+ },
52229
+ async set(provider, secret) {
52230
+ const accepted = providerCredential(secret);
52231
+ await queued(async () => {
52232
+ const document = await readDocument() ?? { version: 1, credentials: {} };
52233
+ document.credentials[provider] = accepted;
52234
+ await writeDocument(document);
52235
+ });
52236
+ },
52237
+ async delete(provider) {
52238
+ return queued(async () => {
52239
+ const document = await readDocument();
52240
+ if (!document || document.credentials[provider] === void 0) return false;
52241
+ delete document.credentials[provider];
52242
+ if (Object.keys(document.credentials).length === 0) {
52243
+ await rm(path);
52244
+ await syncDirectory();
52245
+ } else await writeDocument(document);
52246
+ return true;
52247
+ });
52248
+ }
52249
+ };
52250
+ }
52251
+ function defaultCredentialStore(home, platform = process.platform) {
52252
+ return platform === "linux" ? fileCredentialStore(home) : nativeCredentialStore();
52253
+ }
52254
+ function ownedByCurrentUser(uid) {
52255
+ return process.getuid === void 0 || uid === process.getuid();
52256
+ }
52115
52257
  async function loadPersisted(storage) {
52116
52258
  let raw2;
52117
52259
  try {
@@ -52162,7 +52304,7 @@ async function resolvedEnvironment(environment, storage, credentials) {
52162
52304
  const settings = config3.vercel;
52163
52305
  Object.assign(resolved, { VERCEL_TOKEN: secret, VERCEL_API_ORIGIN: settings.apiOrigin, VERCEL_TEAM_ID: settings.teamId, VERCEL_PROJECT_ID: settings.projectId, VERCEL_POLL_INTERVAL_MS: String(settings.pollIntervalMs), VERCEL_POLL_TIMEOUT_MS: String(settings.pollTimeoutMs), VERCEL_REQUEST_TIMEOUT_MS: String(settings.requestTimeoutMs), WATERBOX_AUTO_STOP: automaticStopEnvironmentValue(settings.automaticStopMs) });
52164
52306
  }
52165
- return { environment: resolved, source: "keyring", provider: config3.provider };
52307
+ return { environment: resolved, source: credentials.backend ?? "keyring", provider: config3.provider };
52166
52308
  }
52167
52309
  var automaticStopGuidance = "How often should a sandbox be automatically stopped?\n\nImportant: a stopped sandbox is automatically resumed by the next coding operation. This is not a limit on total sandbox runtime or spending.\n\nThis is a safety mechanism that limits wasted compute if the agent does not stop a sandbox when finished. Agents are instructed to stop unused sandboxes, but this is not enforced.\n\nA short duration, such as 40m, minimizes wasted compute but may interrupt long-running commands. For long workflows, consider 6h or more. Provider limits apply, so check your plan's maximum execution time.";
52168
52310
  async function setup(storage, credentials, prompts, dependencies = {}) {
@@ -52324,8 +52466,16 @@ async function logout(storage, credentials) {
52324
52466
  } catch {
52325
52467
  throw new OnboardingError("Waterbox logout could not read local configuration or credentials; no changes were made.");
52326
52468
  }
52327
- const removed = await Promise.allSettled(Object.keys(prior).map(async (provider) => ({ provider, deleted: await credentials.delete(provider) })));
52328
- const deletionFailed = removed.some((result) => result.status === "rejected" || !result.value.deleted && prior[result.value.provider] !== void 0);
52469
+ let deletionFailed = false;
52470
+ for (const provider of Object.keys(prior)) {
52471
+ try {
52472
+ const deleted = await credentials.delete(provider);
52473
+ if (!deleted && prior[provider] !== void 0) deletionFailed = true;
52474
+ } catch {
52475
+ deletionFailed = true;
52476
+ break;
52477
+ }
52478
+ }
52329
52479
  if (deletionFailed) {
52330
52480
  const restored = await restore(credentials, prior);
52331
52481
  throw new OnboardingError(`Waterbox logout could not remove all credentials${restored ? "; rollback was confirmed" : "; rollback could not be confirmed"}.`);
@@ -52387,12 +52537,17 @@ async function credentialSnapshot(credentials) {
52387
52537
  return { waterbox: await credentials.get("waterbox") ?? void 0, box: await credentials.get("box") ?? void 0, vercel: await credentials.get("vercel") ?? void 0 };
52388
52538
  }
52389
52539
  async function restore(credentials, prior, providers = Object.keys(prior), requireDeletionConfirmation = []) {
52390
- const results = await Promise.allSettled(providers.map(async (provider) => {
52391
- if (prior[provider] !== void 0) return credentials.set(provider, prior[provider]);
52392
- const deleted = await credentials.delete(provider);
52393
- if (!deleted && requireDeletionConfirmation.includes(provider)) throw new Error("credential deletion was not confirmed");
52394
- }));
52395
- return results.every((result) => result.status === "fulfilled");
52540
+ let restored = true;
52541
+ for (const provider of providers) try {
52542
+ if (prior[provider] !== void 0) await credentials.set(provider, prior[provider]);
52543
+ else {
52544
+ const deleted = await credentials.delete(provider);
52545
+ if (!deleted && requireDeletionConfirmation.includes(provider)) throw new Error("credential deletion was not confirmed");
52546
+ }
52547
+ } catch {
52548
+ restored = false;
52549
+ }
52550
+ return restored;
52396
52551
  }
52397
52552
  async function restoreRawConfig(storage, raw2) {
52398
52553
  try {
@@ -52413,7 +52568,7 @@ var McpConfigurationError = class extends Error {
52413
52568
  };
52414
52569
  async function parseMcpConfig(environment = process.env, homeDirectory, dependencies = {}) {
52415
52570
  try {
52416
- const resolved = await resolvedEnvironment(environment, dependencies.storage ?? configStorage(homeDirectory), dependencies.credentials ?? nativeCredentialStore());
52571
+ const resolved = await resolvedEnvironment(environment, dependencies.storage ?? configStorage(homeDirectory), dependencies.credentials ?? defaultCredentialStore(homeDirectory));
52417
52572
  if (resolved.provider === "waterbox") {
52418
52573
  try {
52419
52574
  return { provider: { type: "waterbox", apiKey: providerCredential(resolved.environment.WATERBOX_API_KEY) } };
@@ -57913,8 +58068,8 @@ var Server = class extends Protocol {
57913
58068
  var instructions = "Waterbox gives agents the ability to create, manage, and command isolated sandboxes.\n\nUse Waterbox when instructed by the user or when additional isolated compute would be useful, such as avoiding pressure on the local machine or running parallel work without workspace conflicts.\n\nWaterbox tools fall into two groups:\n\n- **Daily workflow:** create a sandbox, operate on it, then stop it.\n- **Reusable setup utilities:** list and create snapshots to avoid repeating environment setup.\n\n**Core workflow**\n\n1. Create a sandbox as the execution environment for your work.\n2. Use the filesystem and command tools to work inside the sandbox.\n3. When the task is complete and all material outcomes expected outside the sandbox have been exported or otherwise preserved, stop the sandbox to avoid unnecessary compute costs. Do not stop it merely because a conversational turn has ended while work remains active.\n\nSandbox filesystem state is resumable, but retention is provider-dependent. Neither sandboxes nor snapshots are definitive storage. Export material outcomes when the task is complete.\n\n**Reusable setup pattern**\n\nWhen you detect repeated setup work or dependencies, consider creating a snapshot so future sandboxes can start from a prepared environment. Give snapshots clear names and descriptions, and preserve reusable dependencies, tools, CLIs, and other environment preparation. Keep task-specific work in source control or another definitive external store so snapshots do not seed future work with stale project state.";
57914
58069
 
57915
58070
  // packages/mcp/src/secure-transfer.ts
57916
- import { constants } from "node:fs";
57917
- import { open, stat } from "node:fs/promises";
58071
+ import { constants as constants2 } from "node:fs";
58072
+ import { open as open2, stat } from "node:fs/promises";
57918
58073
  async function sendFileSecurely(client, input, context) {
57919
58074
  context.signal.throwIfAborted();
57920
58075
  const plaintext = await readLocalFile(input.sourcePath, context.signal);
@@ -57927,10 +58082,10 @@ async function sendFileSecurely(client, input, context) {
57927
58082
  async function readLocalFile(path, signal, overrides = {}) {
57928
58083
  signal.throwIfAborted();
57929
58084
  const statFile = overrides.stat ?? stat;
57930
- const openFile = overrides.open ?? open;
58085
+ const openFile = overrides.open ?? open2;
57931
58086
  const initial = await statFile(path);
57932
58087
  if (!initial.isFile()) throw new Error("Local source must be a regular file");
57933
- let handle = await openFile(path, constants.O_RDONLY);
58088
+ let handle = await openFile(path, constants2.O_RDONLY);
57934
58089
  let bytes;
57935
58090
  let transferred = false;
57936
58091
  try {
@@ -57990,7 +58145,7 @@ var tools = [
57990
58145
  tool("bash", "Runs unrestricted bash as root in the specified Waterbox sandbox, never on the local machine. The default working directory is the sandbox workspace.", ARGUMENT_SCHEMAS.bash, BashToolResultSchema)
57991
58146
  ];
57992
58147
  function createWaterboxMcpServer(client, options = {}) {
57993
- const server = new Server({ name: "waterbox", version: "0.1.0-alpha.2" }, { capabilities: { tools: {} }, instructions });
58148
+ const server = new Server({ name: "waterbox", version: "0.1.0-alpha.3" }, { capabilities: { tools: {} }, instructions });
57994
58149
  server.setRequestHandler(ListToolsRequestSchema, async () => ({ tools }));
57995
58150
  server.setRequestHandler(CallToolRequestSchema, async (request, extra) => {
57996
58151
  try {
@@ -58139,7 +58294,7 @@ var standardPrompts = {
58139
58294
  };
58140
58295
  async function runCli(arguments_, dependencies = {}) {
58141
58296
  const write = dependencies.write ?? ((line) => console.log(line)), error47 = dependencies.error ?? ((line) => console.error(line));
58142
- const storage = dependencies.storage ?? configStorage(), credentials = dependencies.credentials ?? nativeCredentialStore();
58297
+ const storage = dependencies.storage ?? configStorage(dependencies.homeDirectory), credentials = dependencies.credentials ?? defaultCredentialStore(dependencies.homeDirectory, dependencies.platform);
58143
58298
  if (arguments_.length !== 1 || !["setup", "status", "logout"].includes(arguments_[0] ?? "")) {
58144
58299
  error47("Usage: waterbox [setup|status|logout]. Run without arguments to start the MCP server.");
58145
58300
  return 2;
@@ -58165,15 +58320,15 @@ async function runCli(arguments_, dependencies = {}) {
58165
58320
  }
58166
58321
  const state3 = await credentialState(config3.provider, credentials);
58167
58322
  if (state3 === "available") await parseMcpConfig(environment, void 0, { storage, credentials });
58168
- write(`Waterbox status: provider ${config3.provider}; local configuration keyring; credential ${state3}.${state3 === "available" ? "" : ` Environment fallback: WATERBOX_PROVIDER=${config3.provider} and ${credentialVariable(config3.provider)}.`}`);
58323
+ write(`Waterbox status: provider ${config3.provider}; configuration file; credential ${credentials.backend ?? "keyring"} ${state3}.${state3 === "available" ? "" : ` Environment fallback: WATERBOX_PROVIDER=${config3.provider} and ${credentialVariable(config3.provider)}.`}`);
58169
58324
  if (state3 !== "available") return 1;
58170
58325
  return 0;
58171
58326
  }
58172
- const parsed = await parseMcpConfig(environment, void 0, { storage, credentials });
58327
+ const parsed = await parseMcpConfig(environment, dependencies.homeDirectory, { storage, credentials });
58173
58328
  const provider = parsed.provider.type === "waterbox" ? "waterbox" : parsed.provider.configuration.provider.kind;
58174
58329
  const source = environment.WATERBOX_PROVIDER === void 0 ? "keyring" : "environment";
58175
58330
  const state2 = source === "keyring" ? await credentialState(provider, credentials) : "available";
58176
- write(`Waterbox status: provider ${provider}; configuration ${source}; credential ${state2}.`);
58331
+ write(`Waterbox status: provider ${provider}; configuration ${source}; credential ${source === "environment" ? "environment" : credentials.backend ?? "keyring"} ${state2}.`);
58177
58332
  return 0;
58178
58333
  } catch (caught) {
58179
58334
  error47(caught instanceof OnboardingError || caught instanceof McpConfigurationError ? caught.message : "Waterbox command failed");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "waterbox",
3
- "version": "0.1.0-alpha.2",
3
+ "version": "0.1.0-alpha.3",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "description": "Run coding tools in isolated, stateful sandboxes through MCP.",