@konstantdotcloud/boombox 0.1.0 → 0.1.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
@@ -57,7 +57,7 @@ For MCP clients, add `boombox serve --mcp` after `boombox login` succeeds.
57
57
  [konstant]
58
58
  tenant_id = "example"
59
59
  api_key = "kn_example_***"
60
- gateway_url = "https://boombox.konstant.cloud/v2"
60
+ gateway_url = "https://boombox-example.exe.xyz"
61
61
 
62
62
  [runtime]
63
63
  default_kind = "vm"
package/dist/boombox.js CHANGED
@@ -36148,7 +36148,7 @@ var init_ask_gary = __esm({
36148
36148
  thread_id: external_exports2.string().optional().describe("Concrete thread_id to use instead of resolving a named alias. Useful when continuing an artifact discussion thread."),
36149
36149
  artifact_ref: external_exports2.string().optional().describe("Artifact id to pin as Gary context. Gary loads the artifact body and tags the resulting thread with artifact_ref."),
36150
36150
  fresh: external_exports2.boolean().optional().describe("If true, rotate the thread alias to a brand-new conversation, discarding prior history under that alias."),
36151
- model_tier: external_exports2.enum(["auto", "fast", "nano", "mini", "reason", "mid", "explore", "premium", "opus"]).optional().describe("Model tier override. Default 'explore' (Sonnet 4.6). Use 'premium' for GPT-5.5, 'opus' for Claude Opus 4.7."),
36151
+ model_tier: external_exports2.enum(["auto", "fast", "nano", "mini", "reason", "mid", "explore", "premium", "opus"]).optional().describe("Model tier override. Default 'explore' (Sonnet 4.6). Use 'premium' for Opus 4.8 at max thinking, 'opus' for Opus 4.8 standard."),
36152
36152
  thinking_level: external_exports2.enum(["off", "minimal", "low", "medium", "high", "xhigh"]).optional().describe("Reasoning effort override. Default 'medium'."),
36153
36153
  intent: external_exports2.enum(["cassette_author"]).optional().describe("Set to cassette_author to load Gary cassette-authoring mode prompt/doctrine.")
36154
36154
  };
@@ -37480,7 +37480,10 @@ function normalizeProfile(value) {
37480
37480
  }
37481
37481
  function guessPathPrefix(baseUrl) {
37482
37482
  if (!baseUrl) return "/api";
37483
- if (/\.fly\.dev/i.test(baseUrl)) return "";
37483
+ if (/\.exe\.(xyz|dev)/i.test(baseUrl)) {
37484
+ return /\/v2\/?$/.test(baseUrl) ? "/api" : "/v2/api";
37485
+ }
37486
+ if (/\.fly\.dev/i.test(baseUrl)) return "/api";
37484
37487
  if (/\.konstant\.cloud/i.test(baseUrl)) return "/api";
37485
37488
  if (/\.vercel\.app/i.test(baseUrl)) return "/api";
37486
37489
  return "";
@@ -37606,7 +37609,7 @@ var {
37606
37609
  init_esm_shims();
37607
37610
 
37608
37611
  // package.json
37609
- var version = "0.1.0";
37612
+ var version = "0.1.2";
37610
37613
 
37611
37614
  // src/lib/version.ts
37612
37615
  var BOOMBOX_VERSION = version;
@@ -42547,6 +42550,12 @@ init_esm_shims();
42547
42550
 
42548
42551
  // src/lib/resolve-gateway.ts
42549
42552
  init_esm_shims();
42553
+ var GatewayResolutionError = class extends Error {
42554
+ constructor(message) {
42555
+ super(message);
42556
+ this.name = "GatewayResolutionError";
42557
+ }
42558
+ };
42550
42559
  function resolveGateway(config2) {
42551
42560
  return resolveGatewayTarget(config2).url;
42552
42561
  }
@@ -42559,6 +42568,11 @@ function resolveGatewayTarget(config2) {
42559
42568
  label: "Mode 2: per-tenant VM"
42560
42569
  };
42561
42570
  }
42571
+ if (config2.runtime.default_kind === "vm") {
42572
+ throw new GatewayResolutionError(
42573
+ 'runtime.default_kind is "vm" but runtime.vm_url is unset \u2014 refusing to fall back to the shared cloud gateway (that would run HomeBase off-VM). Run `boombox login` or `boombox enroll` to repoint at your VM, or set runtime.default_kind = "cloud" to use Mode 1 deliberately.'
42574
+ );
42575
+ }
42562
42576
  return {
42563
42577
  url: normalizeUrl(config2.konstant.gateway_url),
42564
42578
  mode: "mode-1-shared-cloud",
@@ -42568,10 +42582,21 @@ function resolveGatewayTarget(config2) {
42568
42582
  function normalizeUrl(url) {
42569
42583
  return url.replace(/\/+$/, "");
42570
42584
  }
42585
+ function gatewayApiPrefix(baseUrl) {
42586
+ if (!baseUrl) return "/api";
42587
+ if (/\.exe\.(xyz|dev)/i.test(baseUrl)) {
42588
+ return /\/v2\/?$/.test(baseUrl) ? "/api" : "/v2/api";
42589
+ }
42590
+ return "/api";
42591
+ }
42592
+ function resolveGatewayApiBase(config2) {
42593
+ const origin = resolveGatewayTarget(config2).url;
42594
+ return `${origin}${gatewayApiPrefix(origin)}`;
42595
+ }
42571
42596
 
42572
42597
  // src/cli/_verify.ts
42573
42598
  async function verifyAuth(config2, fetchImpl = fetch) {
42574
- const url = `${resolveGateway(config2)}/api/gateway/rack/list`;
42599
+ const url = `${resolveGatewayApiBase(config2)}/gateway/rack/list`;
42575
42600
  let res;
42576
42601
  try {
42577
42602
  res = await fetchImpl(url, {
@@ -42623,7 +42648,7 @@ async function runInit(options = {}) {
42623
42648
  const preset = {
42624
42649
  tenant_id: options.tenantId ?? enrollPayload?.tenant_id ?? existing?.konstant.tenant_id ?? "",
42625
42650
  api_key: options.tenantApiKey ?? options.apiKey ?? enrollPayload?.tenant_api_key ?? enrollPayload?.api_key ?? existing?.konstant.api_key ?? "",
42626
- gateway_url: options.gatewayUrl ?? enrollPayload?.gateway_url ?? existing?.konstant.gateway_url ?? "https://boombox.konstant.cloud/v2",
42651
+ gateway_url: options.gatewayUrl ?? enrollPayload?.gateway_url ?? existing?.konstant.gateway_url ?? options.vmUrl ?? enrollPayload?.vm_url ?? existing?.runtime.vm_url ?? "",
42627
42652
  vm_url: options.vmUrl ?? enrollPayload?.vm_url ?? existing?.runtime.vm_url ?? ""
42628
42653
  };
42629
42654
  log("Welcome to Konstant Boombox.");
@@ -43288,7 +43313,6 @@ var open_default = open;
43288
43313
 
43289
43314
  // src/cli/enroll.ts
43290
43315
  var DEFAULT_AUTH_BASE = "https://boombox.konstant.cloud";
43291
- var DEFAULT_GATEWAY_BASE = "https://boombox.konstant.cloud/v2";
43292
43316
  var DEFAULT_TIMEOUT_MS = 10 * 60 * 1e3;
43293
43317
  async function runEnroll(options = {}) {
43294
43318
  const log = options.log ?? ((m) => console.log(m));
@@ -43314,8 +43338,13 @@ async function runEnroll(options = {}) {
43314
43338
  log,
43315
43339
  openBrowser: options.openBrowser
43316
43340
  });
43317
- const gatewayUrl = (options.gatewayUrl ?? payload.gateway_url ?? DEFAULT_GATEWAY_BASE).replace(/\/+$/, "");
43318
43341
  const vmUrl = (options.vmUrl ?? payload.vm_url)?.replace(/\/+$/, "");
43342
+ const gatewayUrl = (options.gatewayUrl ?? payload.gateway_url ?? vmUrl ?? "").replace(/\/+$/, "");
43343
+ if (!gatewayUrl) {
43344
+ throw new Error(
43345
+ "Enrollment returned no gateway: neither gateway_url nor vm_url present. Provision a tenant VM (Mode 2) \u2014 the shared cloud gateway (Mode 1) is retired."
43346
+ );
43347
+ }
43319
43348
  const apiKey = payload.tenant_api_key ?? payload.api_key;
43320
43349
  const httpPort = existing?.boombox.http_port ?? DEFAULT_HTTP_PORT;
43321
43350
  const config2 = BoomboxConfigSchema.parse({
@@ -47064,17 +47093,31 @@ async function runServe(options = {}) {
47064
47093
  const configPath = options.configPath ?? getConfigPath();
47065
47094
  const store = new ConfigStore({ path: configPath, log });
47066
47095
  const config2 = store.getCurrent();
47067
- store.onChange((next, prev) => {
47068
- if (next.konstant.api_key !== prev.konstant.api_key) {
47069
- log("config: api_key rotated; new requests will use the updated key.");
47070
- }
47071
- if (resolveGateway(next) !== resolveGateway(prev)) {
47072
- log(`config: target changed (${resolveGateway(prev)} \u2192 ${resolveGateway(next)}).`);
47096
+ try {
47097
+ resolveGatewayTarget(config2);
47098
+ } catch (err) {
47099
+ if (err instanceof GatewayResolutionError) {
47100
+ log(`error: ${err.message}`);
47101
+ process.exitCode = 1;
47102
+ return;
47073
47103
  }
47074
- if (next.konstant.tenant_id !== prev.konstant.tenant_id) {
47075
- log(
47076
- `config: tenant_id changed (${prev.konstant.tenant_id} \u2192 ${next.konstant.tenant_id}).`
47077
- );
47104
+ throw err;
47105
+ }
47106
+ store.onChange((next, prev) => {
47107
+ try {
47108
+ if (next.konstant.api_key !== prev.konstant.api_key) {
47109
+ log("config: api_key rotated; new requests will use the updated key.");
47110
+ }
47111
+ if (resolveGateway(next) !== resolveGateway(prev)) {
47112
+ log(`config: target changed (${resolveGateway(prev)} \u2192 ${resolveGateway(next)}).`);
47113
+ }
47114
+ if (next.konstant.tenant_id !== prev.konstant.tenant_id) {
47115
+ log(
47116
+ `config: tenant_id changed (${prev.konstant.tenant_id} \u2192 ${next.konstant.tenant_id}).`
47117
+ );
47118
+ }
47119
+ } catch (err) {
47120
+ log(`config: ${err instanceof Error ? err.message : String(err)}`);
47078
47121
  }
47079
47122
  });
47080
47123
  store.watch();
@@ -47256,17 +47299,25 @@ async function runDoctor(options = {}) {
47256
47299
  }
47257
47300
  }
47258
47301
  if (config2) {
47259
- const target = resolveGatewayTarget(config2);
47260
- checks.push({ name: "mode", ok: true, detail: `${target.label}, target=${target.url}` });
47261
- const reach = await reachable(target.url, options.fetchImpl);
47262
- checks.push(reach);
47263
- const auth = await verifyAuth(config2, options.fetchImpl);
47264
- checks.push({
47265
- name: "auth",
47266
- ok: auth.ok,
47267
- detail: auth.ok ? `whoami ok (status ${auth.status})` : `whoami failed (status ${auth.status}): ${auth.message}`
47268
- });
47269
- checks.push(await mcpHandshake(config2));
47302
+ let target = null;
47303
+ try {
47304
+ target = resolveGatewayTarget(config2);
47305
+ checks.push({ name: "mode", ok: true, detail: `${target.label}, target=${target.url}` });
47306
+ } catch (err) {
47307
+ if (!(err instanceof GatewayResolutionError)) throw err;
47308
+ checks.push({ name: "mode", ok: false, detail: err.message });
47309
+ }
47310
+ if (target) {
47311
+ const reach = await reachable(target.url, options.fetchImpl);
47312
+ checks.push(reach);
47313
+ const auth = await verifyAuth(config2, options.fetchImpl);
47314
+ checks.push({
47315
+ name: "auth",
47316
+ ok: auth.ok,
47317
+ detail: auth.ok ? `whoami ok (status ${auth.status})` : `whoami failed (status ${auth.status}): ${auth.message}`
47318
+ });
47319
+ checks.push(await mcpHandshake(config2));
47320
+ }
47270
47321
  }
47271
47322
  const ok = checks.every((c) => c.ok);
47272
47323
  for (const c of checks) {
@@ -48213,6 +48264,7 @@ function createOpsContext(options = {}) {
48213
48264
  return {
48214
48265
  config: config2,
48215
48266
  gateway: resolveGateway(config2),
48267
+ gatewayApi: resolveGatewayApiBase(config2),
48216
48268
  apiKey: config2.konstant.api_key,
48217
48269
  tenant_id: config2.konstant.tenant_id,
48218
48270
  fetchImpl: options.fetchImpl ?? fetch,
@@ -48232,7 +48284,8 @@ function joinUrl(base, path4) {
48232
48284
  return `${cleanBase}${cleanPath}`;
48233
48285
  }
48234
48286
  async function fetchText(ctx, path4, options = {}) {
48235
- const url = joinUrl(ctx.gateway, path4);
48287
+ const base = path4.startsWith("/gateway") ? ctx.gatewayApi : ctx.gateway;
48288
+ const url = joinUrl(base, path4);
48236
48289
  const headers = {
48237
48290
  ...options.auth === false ? {} : authHeaders(ctx, options.accept ?? "application/json"),
48238
48291
  ...options.headers ?? {}
package/dist/index.js CHANGED
@@ -28219,7 +28219,7 @@ var init_ask_gary = __esm({
28219
28219
  thread_id: external_exports2.string().optional().describe("Concrete thread_id to use instead of resolving a named alias. Useful when continuing an artifact discussion thread."),
28220
28220
  artifact_ref: external_exports2.string().optional().describe("Artifact id to pin as Gary context. Gary loads the artifact body and tags the resulting thread with artifact_ref."),
28221
28221
  fresh: external_exports2.boolean().optional().describe("If true, rotate the thread alias to a brand-new conversation, discarding prior history under that alias."),
28222
- model_tier: external_exports2.enum(["auto", "fast", "nano", "mini", "reason", "mid", "explore", "premium", "opus"]).optional().describe("Model tier override. Default 'explore' (Sonnet 4.6). Use 'premium' for GPT-5.5, 'opus' for Claude Opus 4.7."),
28222
+ model_tier: external_exports2.enum(["auto", "fast", "nano", "mini", "reason", "mid", "explore", "premium", "opus"]).optional().describe("Model tier override. Default 'explore' (Sonnet 4.6). Use 'premium' for Opus 4.8 at max thinking, 'opus' for Opus 4.8 standard."),
28223
28223
  thinking_level: external_exports2.enum(["off", "minimal", "low", "medium", "high", "xhigh"]).optional().describe("Reasoning effort override. Default 'medium'."),
28224
28224
  intent: external_exports2.enum(["cassette_author"]).optional().describe("Set to cassette_author to load Gary cassette-authoring mode prompt/doctrine.")
28225
28225
  };
@@ -29551,7 +29551,10 @@ function normalizeProfile(value) {
29551
29551
  }
29552
29552
  function guessPathPrefix(baseUrl) {
29553
29553
  if (!baseUrl) return "/api";
29554
- if (/\.fly\.dev/i.test(baseUrl)) return "";
29554
+ if (/\.exe\.(xyz|dev)/i.test(baseUrl)) {
29555
+ return /\/v2\/?$/.test(baseUrl) ? "/api" : "/v2/api";
29556
+ }
29557
+ if (/\.fly\.dev/i.test(baseUrl)) return "/api";
29555
29558
  if (/\.konstant\.cloud/i.test(baseUrl)) return "/api";
29556
29559
  if (/\.vercel\.app/i.test(baseUrl)) return "/api";
29557
29560
  return "";
@@ -34581,7 +34584,7 @@ init_esm_shims();
34581
34584
  init_esm_shims();
34582
34585
 
34583
34586
  // package.json
34584
- var version = "0.1.0";
34587
+ var version = "0.1.2";
34585
34588
 
34586
34589
  // src/lib/version.ts
34587
34590
  var BOOMBOX_VERSION = version;
@@ -42576,6 +42579,12 @@ var serveStatic = (options = { root: "" }) => {
42576
42579
 
42577
42580
  // src/lib/resolve-gateway.ts
42578
42581
  init_esm_shims();
42582
+ var GatewayResolutionError = class extends Error {
42583
+ constructor(message) {
42584
+ super(message);
42585
+ this.name = "GatewayResolutionError";
42586
+ }
42587
+ };
42579
42588
  function resolveGateway(config2) {
42580
42589
  return resolveGatewayTarget(config2).url;
42581
42590
  }
@@ -42588,6 +42597,11 @@ function resolveGatewayTarget(config2) {
42588
42597
  label: "Mode 2: per-tenant VM"
42589
42598
  };
42590
42599
  }
42600
+ if (config2.runtime.default_kind === "vm") {
42601
+ throw new GatewayResolutionError(
42602
+ 'runtime.default_kind is "vm" but runtime.vm_url is unset \u2014 refusing to fall back to the shared cloud gateway (that would run HomeBase off-VM). Run `boombox login` or `boombox enroll` to repoint at your VM, or set runtime.default_kind = "cloud" to use Mode 1 deliberately.'
42603
+ );
42604
+ }
42591
42605
  return {
42592
42606
  url: normalizeUrl(config2.konstant.gateway_url),
42593
42607
  mode: "mode-1-shared-cloud",
@@ -42597,6 +42611,17 @@ function resolveGatewayTarget(config2) {
42597
42611
  function normalizeUrl(url) {
42598
42612
  return url.replace(/\/+$/, "");
42599
42613
  }
42614
+ function gatewayApiPrefix(baseUrl) {
42615
+ if (!baseUrl) return "/api";
42616
+ if (/\.exe\.(xyz|dev)/i.test(baseUrl)) {
42617
+ return /\/v2\/?$/.test(baseUrl) ? "/api" : "/v2/api";
42618
+ }
42619
+ return "/api";
42620
+ }
42621
+ function resolveGatewayApiBase(config2) {
42622
+ const origin = resolveGatewayTarget(config2).url;
42623
+ return `${origin}${gatewayApiPrefix(origin)}`;
42624
+ }
42600
42625
 
42601
42626
  // src/server/proxy.ts
42602
42627
  init_esm_shims();
@@ -42914,7 +42939,7 @@ import { readFileSync as readFileSync3 } from "fs";
42914
42939
  // src/cli/_verify.ts
42915
42940
  init_esm_shims();
42916
42941
  async function verifyAuth(config2, fetchImpl = fetch) {
42917
- const url = `${resolveGateway(config2)}/api/gateway/rack/list`;
42942
+ const url = `${resolveGatewayApiBase(config2)}/gateway/rack/list`;
42918
42943
  let res;
42919
42944
  try {
42920
42945
  res = await fetchImpl(url, {
@@ -42966,7 +42991,7 @@ async function runInit(options = {}) {
42966
42991
  const preset = {
42967
42992
  tenant_id: options.tenantId ?? enrollPayload?.tenant_id ?? existing?.konstant.tenant_id ?? "",
42968
42993
  api_key: options.tenantApiKey ?? options.apiKey ?? enrollPayload?.tenant_api_key ?? enrollPayload?.api_key ?? existing?.konstant.api_key ?? "",
42969
- gateway_url: options.gatewayUrl ?? enrollPayload?.gateway_url ?? existing?.konstant.gateway_url ?? "https://boombox.konstant.cloud/v2",
42994
+ gateway_url: options.gatewayUrl ?? enrollPayload?.gateway_url ?? existing?.konstant.gateway_url ?? options.vmUrl ?? enrollPayload?.vm_url ?? existing?.runtime.vm_url ?? "",
42970
42995
  vm_url: options.vmUrl ?? enrollPayload?.vm_url ?? existing?.runtime.vm_url ?? ""
42971
42996
  };
42972
42997
  log("Welcome to Konstant Boombox.");
@@ -43291,17 +43316,31 @@ async function runServe(options = {}) {
43291
43316
  const configPath = options.configPath ?? getConfigPath();
43292
43317
  const store = new ConfigStore({ path: configPath, log });
43293
43318
  const config2 = store.getCurrent();
43294
- store.onChange((next, prev) => {
43295
- if (next.konstant.api_key !== prev.konstant.api_key) {
43296
- log("config: api_key rotated; new requests will use the updated key.");
43297
- }
43298
- if (resolveGateway(next) !== resolveGateway(prev)) {
43299
- log(`config: target changed (${resolveGateway(prev)} \u2192 ${resolveGateway(next)}).`);
43319
+ try {
43320
+ resolveGatewayTarget(config2);
43321
+ } catch (err) {
43322
+ if (err instanceof GatewayResolutionError) {
43323
+ log(`error: ${err.message}`);
43324
+ process.exitCode = 1;
43325
+ return;
43300
43326
  }
43301
- if (next.konstant.tenant_id !== prev.konstant.tenant_id) {
43302
- log(
43303
- `config: tenant_id changed (${prev.konstant.tenant_id} \u2192 ${next.konstant.tenant_id}).`
43304
- );
43327
+ throw err;
43328
+ }
43329
+ store.onChange((next, prev) => {
43330
+ try {
43331
+ if (next.konstant.api_key !== prev.konstant.api_key) {
43332
+ log("config: api_key rotated; new requests will use the updated key.");
43333
+ }
43334
+ if (resolveGateway(next) !== resolveGateway(prev)) {
43335
+ log(`config: target changed (${resolveGateway(prev)} \u2192 ${resolveGateway(next)}).`);
43336
+ }
43337
+ if (next.konstant.tenant_id !== prev.konstant.tenant_id) {
43338
+ log(
43339
+ `config: tenant_id changed (${prev.konstant.tenant_id} \u2192 ${next.konstant.tenant_id}).`
43340
+ );
43341
+ }
43342
+ } catch (err) {
43343
+ log(`config: ${err instanceof Error ? err.message : String(err)}`);
43305
43344
  }
43306
43345
  });
43307
43346
  store.watch();
@@ -43419,17 +43458,25 @@ async function runDoctor(options = {}) {
43419
43458
  }
43420
43459
  }
43421
43460
  if (config2) {
43422
- const target = resolveGatewayTarget(config2);
43423
- checks.push({ name: "mode", ok: true, detail: `${target.label}, target=${target.url}` });
43424
- const reach = await reachable(target.url, options.fetchImpl);
43425
- checks.push(reach);
43426
- const auth = await verifyAuth(config2, options.fetchImpl);
43427
- checks.push({
43428
- name: "auth",
43429
- ok: auth.ok,
43430
- detail: auth.ok ? `whoami ok (status ${auth.status})` : `whoami failed (status ${auth.status}): ${auth.message}`
43431
- });
43432
- checks.push(await mcpHandshake(config2));
43461
+ let target = null;
43462
+ try {
43463
+ target = resolveGatewayTarget(config2);
43464
+ checks.push({ name: "mode", ok: true, detail: `${target.label}, target=${target.url}` });
43465
+ } catch (err) {
43466
+ if (!(err instanceof GatewayResolutionError)) throw err;
43467
+ checks.push({ name: "mode", ok: false, detail: err.message });
43468
+ }
43469
+ if (target) {
43470
+ const reach = await reachable(target.url, options.fetchImpl);
43471
+ checks.push(reach);
43472
+ const auth = await verifyAuth(config2, options.fetchImpl);
43473
+ checks.push({
43474
+ name: "auth",
43475
+ ok: auth.ok,
43476
+ detail: auth.ok ? `whoami ok (status ${auth.status})` : `whoami failed (status ${auth.status}): ${auth.message}`
43477
+ });
43478
+ checks.push(await mcpHandshake(config2));
43479
+ }
43433
43480
  }
43434
43481
  const ok = checks.every((c) => c.ok);
43435
43482
  for (const c of checks) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@konstantdotcloud/boombox",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "description": "Local Boombox runtime for Konstant cassettes — CLI, stdio MCP server, and local Hono proxy.",
5
5
  "license": "UNLICENSED",
6
6
  "type": "module",