@konstantdotcloud/boombox 0.1.2 → 0.2.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/README.md +2 -1
- package/dist/boombox.js +114 -14
- package/dist/index.js +14 -14
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -21,7 +21,7 @@ boombox admin # opens the VM admin UI through a short-lived cloud grant
|
|
|
21
21
|
boombox run bd_pre_meeting_brief --input meeting_uid=sample --input organizer=user@example.com --input attendees='["user@example.com"]' --input start_time=2026-06-02T15:00:00Z --input summary=Sample
|
|
22
22
|
```
|
|
23
23
|
|
|
24
|
-
For MCP clients, add `boombox serve --mcp` after `boombox login` succeeds.
|
|
24
|
+
For MCP clients, add `boombox serve --mcp` after `boombox login` succeeds. The MCP server defaults to the `advanced` profile (cassette authoring + rack verbs included); set `GARY_MCP_PROFILE=andrew` or pass `--profile` to narrow or change it.
|
|
25
25
|
|
|
26
26
|
## Commands
|
|
27
27
|
|
|
@@ -33,6 +33,7 @@ For MCP clients, add `boombox serve --mcp` after `boombox login` succeeds.
|
|
|
33
33
|
| `boombox run <cassette_name> [--input key=value ...] [--no-tail] [--json]` | Manually triggers a cassette through `/gateway/rack/run`, prompts for required missing inputs, then polls to a terminal state unless `--no-tail` is set. |
|
|
34
34
|
| `boombox cassettes list` | Lists active cassettes available to the tenant. |
|
|
35
35
|
| `boombox cassettes show <id> [--draft]` | Prints a cassette YAML projection. |
|
|
36
|
+
| `boombox cassettes publish <draft_id> --name <name> [--version <n>] [--json]` | Publishes a dry-run-approved cassette draft to the tenant tier via `POST /gateway/cassette/publish`; renders gate rejections (e.g. `dry-run-not-green`) with fix guidance. |
|
|
36
37
|
| `boombox runs list [--cassette=X] [--since=24h] [--actor=Y] [--status=completed] [--limit=20]` | Lists recent runs with run id, cassette, actor, status, duration, and start time. |
|
|
37
38
|
| `boombox runs show <run_id>` | Shows one run: spec hash, inputs, step trace, delivery receipts, error summary, and replay ref. |
|
|
38
39
|
| `boombox logs tail [--cassette=X]` | Tails run/step events from the VM SSE stream. |
|
package/dist/boombox.js
CHANGED
|
@@ -37341,12 +37341,6 @@ function registerCassetteAuthoringVerbs(server, gateway) {
|
|
|
37341
37341
|
publishSchema,
|
|
37342
37342
|
async (input) => renderGatewayResult("cassette_publish", await gateway.call("/gateway/cassette/publish", input))
|
|
37343
37343
|
);
|
|
37344
|
-
server.tool(
|
|
37345
|
-
"cassette_request_publish",
|
|
37346
|
-
"Request tenant_admin approval to publish a cassette draft when the caller lacks cassette.publish.",
|
|
37347
|
-
requestPublishSchema,
|
|
37348
|
-
async (input) => renderGatewayResult("cassette_request_publish", await gateway.call("/gateway/cassette/request-publish", input))
|
|
37349
|
-
);
|
|
37350
37344
|
server.tool(
|
|
37351
37345
|
"cassette_get",
|
|
37352
37346
|
"Get a cassette draft or published cassette as YAML plus describe payload.",
|
|
@@ -37399,7 +37393,7 @@ ${JSON.stringify(record2, null, 2)}`;
|
|
|
37399
37393
|
function isRecord(value) {
|
|
37400
37394
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
37401
37395
|
}
|
|
37402
|
-
var recordSchema, draftSchema, refineSchema, dryRunSchema, publishSchema,
|
|
37396
|
+
var recordSchema, draftSchema, refineSchema, dryRunSchema, publishSchema, getSchema, listDraftsSchema, listCapabilitiesSchema;
|
|
37403
37397
|
var init_cassette_authoring = __esm({
|
|
37404
37398
|
"../../lib/gary-mcp/verbs/cassette-authoring.ts"() {
|
|
37405
37399
|
"use strict";
|
|
@@ -37427,10 +37421,6 @@ var init_cassette_authoring = __esm({
|
|
|
37427
37421
|
name: external_exports2.string().min(1),
|
|
37428
37422
|
version: external_exports2.number().int().min(1).optional()
|
|
37429
37423
|
};
|
|
37430
|
-
requestPublishSchema = {
|
|
37431
|
-
draft_id: external_exports2.string().min(1),
|
|
37432
|
-
reason: external_exports2.string().min(1).optional()
|
|
37433
|
-
};
|
|
37434
37424
|
getSchema = {
|
|
37435
37425
|
id: external_exports2.string().min(1).optional(),
|
|
37436
37426
|
draft_id: external_exports2.string().min(1).optional()
|
|
@@ -37609,7 +37599,7 @@ var {
|
|
|
37609
37599
|
init_esm_shims();
|
|
37610
37600
|
|
|
37611
37601
|
// package.json
|
|
37612
|
-
var version = "0.
|
|
37602
|
+
var version = "0.2.0";
|
|
37613
37603
|
|
|
37614
37604
|
// src/lib/version.ts
|
|
37615
37605
|
var BOOMBOX_VERSION = version;
|
|
@@ -47050,12 +47040,20 @@ function relativeToCwd(absPath) {
|
|
|
47050
47040
|
|
|
47051
47041
|
// src/server/mcp-stdio.ts
|
|
47052
47042
|
init_esm_shims();
|
|
47043
|
+
var DEFAULT_MCP_PROFILE = "advanced";
|
|
47044
|
+
function resolveMcpProfileSelection(flag) {
|
|
47045
|
+
if (flag === void 0) return void 0;
|
|
47046
|
+
return flag === "default" ? DEFAULT_MCP_PROFILE : flag;
|
|
47047
|
+
}
|
|
47053
47048
|
async function startStdioMcp(config2) {
|
|
47054
47049
|
process.env.GARY_MCP_GATEWAY_URL = resolveGateway(config2);
|
|
47055
47050
|
process.env.GARY_MCP_API_KEY = config2.konstant.api_key;
|
|
47056
47051
|
if (!process.env.KONSTANT_API_KEY) {
|
|
47057
47052
|
process.env.KONSTANT_API_KEY = config2.konstant.api_key;
|
|
47058
47053
|
}
|
|
47054
|
+
if (!(process.env.GARY_MCP_PROFILE ?? "").trim()) {
|
|
47055
|
+
process.env.GARY_MCP_PROFILE = DEFAULT_MCP_PROFILE;
|
|
47056
|
+
}
|
|
47059
47057
|
const mod = await Promise.resolve().then(() => (init_gary_mcp(), gary_mcp_exports));
|
|
47060
47058
|
await mod.startGaryMcpServer();
|
|
47061
47059
|
}
|
|
@@ -47130,8 +47128,10 @@ async function runServe(options = {}) {
|
|
|
47130
47128
|
log("Note: mcp_enabled=false in config; enabling for this session.");
|
|
47131
47129
|
}
|
|
47132
47130
|
if (enableMcp) {
|
|
47133
|
-
const
|
|
47134
|
-
|
|
47131
|
+
const selectedProfile = resolveMcpProfileSelection(options.profile);
|
|
47132
|
+
if (selectedProfile !== void 0) {
|
|
47133
|
+
process.env.GARY_MCP_PROFILE = selectedProfile;
|
|
47134
|
+
}
|
|
47135
47135
|
process.env.GARY_MCP_TENANT_ID = config2.konstant.tenant_id;
|
|
47136
47136
|
}
|
|
47137
47137
|
const handle = await serve2({
|
|
@@ -49199,6 +49199,96 @@ async function runCassettesDryRun(options) {
|
|
|
49199
49199
|
}
|
|
49200
49200
|
printSimulation(body, ctx);
|
|
49201
49201
|
}
|
|
49202
|
+
async function runCassettesPublish(options) {
|
|
49203
|
+
const ctx = createOpsContext(options);
|
|
49204
|
+
const payload = {
|
|
49205
|
+
draft_id: options.draftId,
|
|
49206
|
+
name: options.name
|
|
49207
|
+
};
|
|
49208
|
+
if (options.version !== void 0) payload.version = options.version;
|
|
49209
|
+
let body;
|
|
49210
|
+
try {
|
|
49211
|
+
body = (await fetchJson(ctx, "/gateway/cassette/publish", { method: "POST", body: payload })).body;
|
|
49212
|
+
} catch (error2) {
|
|
49213
|
+
if (error2 instanceof OpsHttpError) {
|
|
49214
|
+
renderPublishRejection(error2.body, ctx, options, `HTTP ${error2.status}`);
|
|
49215
|
+
return 1;
|
|
49216
|
+
}
|
|
49217
|
+
throw error2;
|
|
49218
|
+
}
|
|
49219
|
+
const record2 = asRecord(body);
|
|
49220
|
+
if (record2 && (record2.ok === false || typeof record2.code === "string" && record2.ok !== true)) {
|
|
49221
|
+
renderPublishRejection(body, ctx, options, "rejected");
|
|
49222
|
+
return 1;
|
|
49223
|
+
}
|
|
49224
|
+
if (options.json) {
|
|
49225
|
+
ctx.log(JSON.stringify(body ?? null, null, 2));
|
|
49226
|
+
return 0;
|
|
49227
|
+
}
|
|
49228
|
+
const published = recordField(record2, "workflow") ?? recordField(record2, "published") ?? recordField(record2, "cassette") ?? record2 ?? {};
|
|
49229
|
+
printKeyValues([
|
|
49230
|
+
["draft", options.draftId],
|
|
49231
|
+
["workflow", field2(published, ["workflow_id", "id", "cassette_id"])],
|
|
49232
|
+
["version", field2(published, ["version", "workflow_version"])],
|
|
49233
|
+
["tier", field2(published, ["tier"])],
|
|
49234
|
+
["status", field2(published, ["status"])]
|
|
49235
|
+
], ctx.log);
|
|
49236
|
+
ctx.log("Published to the tenant tier.");
|
|
49237
|
+
return 0;
|
|
49238
|
+
}
|
|
49239
|
+
function publishRejectionHint(code, draftId) {
|
|
49240
|
+
switch (code.toLowerCase().replace(/_/g, "-")) {
|
|
49241
|
+
case "publish-blocked-draft-required":
|
|
49242
|
+
return `Publish requires a saved draft, not raw YAML. Save the draft first (cassette_draft / cassette_refine via MCP), then publish by draft_id (\`${draftId}\`).`;
|
|
49243
|
+
case "publish-blocked-dry-run-missing":
|
|
49244
|
+
return `This draft has no dry-run yet. Run one (cassette_dry_run via MCP, or \`boombox cassettes dry-run ${draftId} --draft\`) and get a ready_to_publish verdict before publishing.`;
|
|
49245
|
+
case "publish-blocked-dry-run-not-ready":
|
|
49246
|
+
case "dry-run-not-green":
|
|
49247
|
+
return `The draft's latest dry-run is not green. Re-run it (cassette_dry_run via MCP, or \`boombox cassettes dry-run ${draftId} --draft\`) until the verdict is ready_to_publish, then publish again.`;
|
|
49248
|
+
case "publish-blocked-dry-run-stale":
|
|
49249
|
+
case "dry-run-stale":
|
|
49250
|
+
// legacy aliases
|
|
49251
|
+
case "stale-dry-run":
|
|
49252
|
+
case "spec-hash-mismatch":
|
|
49253
|
+
return "The draft changed after its last green dry-run. Re-run the dry-run against the current draft revision, then publish again.";
|
|
49254
|
+
case "forbidden-authority":
|
|
49255
|
+
case "missing-authority":
|
|
49256
|
+
return "This API key lacks the cassette.publish grant. Use cassette_request_publish (MCP) to ask a tenant admin to publish, or have an admin add cassette.publish to your key.";
|
|
49257
|
+
case "publish-blocked-not-owner":
|
|
49258
|
+
case "forbidden-not-owner":
|
|
49259
|
+
case "forbidden":
|
|
49260
|
+
// legacy aliases
|
|
49261
|
+
case "draft-not-owned":
|
|
49262
|
+
return "This draft is not owned by your API key actor. Publish from the key that created the draft, or ask a tenant admin to publish it.";
|
|
49263
|
+
case "not-found":
|
|
49264
|
+
return `No draft "${draftId}" is visible to this key. Check \`boombox cassettes list --status draft\` (or cassette_list_drafts via MCP).`;
|
|
49265
|
+
case "publish-blocked-master-shadow":
|
|
49266
|
+
case "master-shadowing":
|
|
49267
|
+
// legacy aliases
|
|
49268
|
+
case "master-collision":
|
|
49269
|
+
return "The cassette id collides with a master/builtin cassette, which tenant publishes are not allowed to shadow. Rename the cassette and publish again.";
|
|
49270
|
+
case "publish-blocked-event-trigger":
|
|
49271
|
+
return "Tenant cassettes cannot publish with event triggers (those are reserved for master-tier cassettes). Remove the event trigger, or request an admin publish.";
|
|
49272
|
+
case "cassette-yaml-invalid":
|
|
49273
|
+
return "The draft YAML failed validation. Fix the reported errors (cassette_refine via MCP) and re-run a dry-run before publishing.";
|
|
49274
|
+
case "validation-error":
|
|
49275
|
+
return "The publish payload was rejected. Check the draft_id and --name values, then re-validate the draft before publishing.";
|
|
49276
|
+
default:
|
|
49277
|
+
return void 0;
|
|
49278
|
+
}
|
|
49279
|
+
}
|
|
49280
|
+
function renderPublishRejection(body, ctx, options, fallbackLabel) {
|
|
49281
|
+
if (options.json) {
|
|
49282
|
+
ctx.errLog(JSON.stringify(body ?? { code: fallbackLabel }, null, 2));
|
|
49283
|
+
return;
|
|
49284
|
+
}
|
|
49285
|
+
const record2 = asRecord(body);
|
|
49286
|
+
const code = stringField4(record2, "code") ?? fallbackLabel;
|
|
49287
|
+
const message = stringField4(record2, "message") ?? stringField4(record2, "error") ?? "no detail from gateway";
|
|
49288
|
+
ctx.errLog(`publish rejected (${code}): ${message}`);
|
|
49289
|
+
const hint = publishRejectionHint(code, options.draftId);
|
|
49290
|
+
if (hint) ctx.errLog(` fix: ${hint}`);
|
|
49291
|
+
}
|
|
49202
49292
|
function extractCassettes(body) {
|
|
49203
49293
|
const record2 = asRecord(body);
|
|
49204
49294
|
const list = arrayField2(record2, "cassettes") ?? arrayField2(record2, "items") ?? arrayField2(record2, "drafts") ?? (Array.isArray(body) ? body : []);
|
|
@@ -49604,6 +49694,16 @@ cassettes.command("validate <id>").description("Run cassette oracle readiness ch
|
|
|
49604
49694
|
cassettes.command("dry-run <id>").description("Run cassette v2 preview simulation with no durable side effects.").option("--draft", "load from cassette_drafts instead of published records").option("--json", "print raw JSON report").option("--config <path>", "override config path").action(async (id, opts) => {
|
|
49605
49695
|
await runCassettesDryRun({ id, draft: opts.draft, json: opts.json, configPath: opts.config });
|
|
49606
49696
|
});
|
|
49697
|
+
cassettes.command("publish <draft_id>").description("Publish a dry-run-approved cassette draft to the tenant tier.").requiredOption("--name <name>", "name for the published cassette").option("--version <n>", "explicit version (server assigns the next one when omitted)", parsePositiveIntFlag).option("--json", "print the raw JSON response").option("--config <path>", "override config path").action(async (draftId, opts) => {
|
|
49698
|
+
const code = await runCassettesPublish({
|
|
49699
|
+
draftId,
|
|
49700
|
+
name: opts.name,
|
|
49701
|
+
version: opts.version,
|
|
49702
|
+
json: opts.json,
|
|
49703
|
+
configPath: opts.config
|
|
49704
|
+
});
|
|
49705
|
+
if (code !== 0) process.exit(code);
|
|
49706
|
+
});
|
|
49607
49707
|
var artifacts = program2.command("artifacts").description("Operate on artifacts.");
|
|
49608
49708
|
artifacts.command("open <artifact_id>").description("Open an artifact URL in the system browser.").option("--config <path>", "override config path").action(async (artifactId, opts) => {
|
|
49609
49709
|
await runArtifactsOpen({ artifactId, configPath: opts.config });
|
package/dist/index.js
CHANGED
|
@@ -29412,12 +29412,6 @@ function registerCassetteAuthoringVerbs(server, gateway) {
|
|
|
29412
29412
|
publishSchema,
|
|
29413
29413
|
async (input) => renderGatewayResult("cassette_publish", await gateway.call("/gateway/cassette/publish", input))
|
|
29414
29414
|
);
|
|
29415
|
-
server.tool(
|
|
29416
|
-
"cassette_request_publish",
|
|
29417
|
-
"Request tenant_admin approval to publish a cassette draft when the caller lacks cassette.publish.",
|
|
29418
|
-
requestPublishSchema,
|
|
29419
|
-
async (input) => renderGatewayResult("cassette_request_publish", await gateway.call("/gateway/cassette/request-publish", input))
|
|
29420
|
-
);
|
|
29421
29415
|
server.tool(
|
|
29422
29416
|
"cassette_get",
|
|
29423
29417
|
"Get a cassette draft or published cassette as YAML plus describe payload.",
|
|
@@ -29470,7 +29464,7 @@ ${JSON.stringify(record2, null, 2)}`;
|
|
|
29470
29464
|
function isRecord(value) {
|
|
29471
29465
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
29472
29466
|
}
|
|
29473
|
-
var recordSchema, draftSchema, refineSchema, dryRunSchema, publishSchema,
|
|
29467
|
+
var recordSchema, draftSchema, refineSchema, dryRunSchema, publishSchema, getSchema, listDraftsSchema, listCapabilitiesSchema;
|
|
29474
29468
|
var init_cassette_authoring = __esm({
|
|
29475
29469
|
"../../lib/gary-mcp/verbs/cassette-authoring.ts"() {
|
|
29476
29470
|
"use strict";
|
|
@@ -29498,10 +29492,6 @@ var init_cassette_authoring = __esm({
|
|
|
29498
29492
|
name: external_exports2.string().min(1),
|
|
29499
29493
|
version: external_exports2.number().int().min(1).optional()
|
|
29500
29494
|
};
|
|
29501
|
-
requestPublishSchema = {
|
|
29502
|
-
draft_id: external_exports2.string().min(1),
|
|
29503
|
-
reason: external_exports2.string().min(1).optional()
|
|
29504
|
-
};
|
|
29505
29495
|
getSchema = {
|
|
29506
29496
|
id: external_exports2.string().min(1).optional(),
|
|
29507
29497
|
draft_id: external_exports2.string().min(1).optional()
|
|
@@ -34584,7 +34574,7 @@ init_esm_shims();
|
|
|
34584
34574
|
init_esm_shims();
|
|
34585
34575
|
|
|
34586
34576
|
// package.json
|
|
34587
|
-
var version = "0.
|
|
34577
|
+
var version = "0.2.0";
|
|
34588
34578
|
|
|
34589
34579
|
// src/lib/version.ts
|
|
34590
34580
|
var BOOMBOX_VERSION = version;
|
|
@@ -42894,12 +42884,20 @@ function relativeToCwd(absPath) {
|
|
|
42894
42884
|
|
|
42895
42885
|
// src/server/mcp-stdio.ts
|
|
42896
42886
|
init_esm_shims();
|
|
42887
|
+
var DEFAULT_MCP_PROFILE = "advanced";
|
|
42888
|
+
function resolveMcpProfileSelection(flag) {
|
|
42889
|
+
if (flag === void 0) return void 0;
|
|
42890
|
+
return flag === "default" ? DEFAULT_MCP_PROFILE : flag;
|
|
42891
|
+
}
|
|
42897
42892
|
async function startStdioMcp(config2) {
|
|
42898
42893
|
process.env.GARY_MCP_GATEWAY_URL = resolveGateway(config2);
|
|
42899
42894
|
process.env.GARY_MCP_API_KEY = config2.konstant.api_key;
|
|
42900
42895
|
if (!process.env.KONSTANT_API_KEY) {
|
|
42901
42896
|
process.env.KONSTANT_API_KEY = config2.konstant.api_key;
|
|
42902
42897
|
}
|
|
42898
|
+
if (!(process.env.GARY_MCP_PROFILE ?? "").trim()) {
|
|
42899
|
+
process.env.GARY_MCP_PROFILE = DEFAULT_MCP_PROFILE;
|
|
42900
|
+
}
|
|
42903
42901
|
const mod = await Promise.resolve().then(() => (init_gary_mcp(), gary_mcp_exports));
|
|
42904
42902
|
await mod.startGaryMcpServer();
|
|
42905
42903
|
}
|
|
@@ -43353,8 +43351,10 @@ async function runServe(options = {}) {
|
|
|
43353
43351
|
log("Note: mcp_enabled=false in config; enabling for this session.");
|
|
43354
43352
|
}
|
|
43355
43353
|
if (enableMcp) {
|
|
43356
|
-
const
|
|
43357
|
-
|
|
43354
|
+
const selectedProfile = resolveMcpProfileSelection(options.profile);
|
|
43355
|
+
if (selectedProfile !== void 0) {
|
|
43356
|
+
process.env.GARY_MCP_PROFILE = selectedProfile;
|
|
43357
|
+
}
|
|
43358
43358
|
process.env.GARY_MCP_TENANT_ID = config2.konstant.tenant_id;
|
|
43359
43359
|
}
|
|
43360
43360
|
const handle = await serve2({
|
package/package.json
CHANGED