@blotout/qinto-cli 0.1.9 → 0.1.11

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.
@@ -7,6 +7,10 @@ var __export = (target, all) => {
7
7
  // ../../../libs/app-manifest/src/cron.ts
8
8
  var MAX_CRON_LENGTH = 128;
9
9
 
10
+ // ../../../libs/app-manifest/src/tool-names.ts
11
+ var MAX_APP_TOOL_NAME_LENGTH = 32;
12
+ var APP_TOOL_NAME_PATTERN = /^[a-zA-Z][a-zA-Z0-9_]*$/;
13
+
10
14
  // ../../../node_modules/.pnpm/zod@4.4.3/node_modules/zod/v4/classic/external.js
11
15
  var external_exports = {};
12
16
  __export(external_exports, {
@@ -2348,13 +2352,13 @@ var $ZodType = /* @__PURE__ */ $constructor("$ZodType", (inst, def) => {
2348
2352
  canary.aborted = true;
2349
2353
  return canary;
2350
2354
  }
2351
- const checkResult = runChecks(payload, checks, ctx);
2352
- if (checkResult instanceof Promise) {
2355
+ const checkResult2 = runChecks(payload, checks, ctx);
2356
+ if (checkResult2 instanceof Promise) {
2353
2357
  if (ctx.async === false)
2354
2358
  throw new $ZodAsyncError();
2355
- return checkResult.then((checkResult2) => inst._zod.parse(checkResult2, ctx));
2359
+ return checkResult2.then((checkResult3) => inst._zod.parse(checkResult3, ctx));
2356
2360
  }
2357
- return inst._zod.parse(checkResult, ctx);
2361
+ return inst._zod.parse(checkResult2, ctx);
2358
2362
  };
2359
2363
  inst._zod.run = (payload, ctx) => {
2360
2364
  if (ctx.skipChecks) {
@@ -14628,6 +14632,11 @@ var envelopeResponseSchema = external_exports.object({
14628
14632
  var CONTROL_ENVELOPE_VERSION = 1;
14629
14633
  var MAX_CONTROL_BYTES = 256 * 1024;
14630
14634
  var MAX_MIGRATION_CHECKPOINT_BYTES = 16 * 1024;
14635
+ var MAX_TOOL_ARGUMENTS_BYTES = 32 * 1024;
14636
+ var MAX_TOOL_RESULT_BYTES = 64 * 1024;
14637
+ var MAX_TOOL_DATA_DEPTH = 8;
14638
+ var MAX_TOOL_DATA_NODES = 5e3;
14639
+ var MAX_TOOL_ERROR_MESSAGE_LENGTH = 512;
14631
14640
  var installSchema2 = external_exports.object({
14632
14641
  id: external_exports.string().min(1).max(64),
14633
14642
  ord: external_exports.number().int().min(1),
@@ -14697,19 +14706,39 @@ var scheduleSchema = external_exports.object({
14697
14706
  id: external_exports.string().min(1).max(64),
14698
14707
  ord: external_exports.number().int().min(1),
14699
14708
  label: external_exports.string().max(100),
14700
- catalogQueryTicket: external_exports.string().min(1).max(2048)
14709
+ queryTicket: external_exports.string().min(1).max(2048)
14701
14710
  }).strict()
14702
14711
  ).min(1).max(MAX_INSTALLS_PER_APP)
14703
14712
  }).strict();
14713
+ var toolSchema = external_exports.object({
14714
+ // The manifest's tools[].name, never the `<slug>_` composed MCP name: the slug is already in
14715
+ // `app.slug`, and carrying it twice would give the app worker two sources for one fact.
14716
+ name: external_exports.string().min(1).max(MAX_APP_TOOL_NAME_LENGTH).regex(APP_TOOL_NAME_PATTERN),
14717
+ // Who asked, and what they may do on this site. Derived from the bearer by the portal, never
14718
+ // from the model's arguments.
14719
+ viewer: external_exports.object({ userId: external_exports.string().min(1).max(64), access: external_exports.enum(["read", "write"]) }).strict(),
14720
+ // Already validated against the tool's declared inputSchema, with the reserved routing names
14721
+ // stripped. Every declared input schema has an object root, so this is an object too — and a
14722
+ // call that names no arguments normalizes to `{}` rather than reaching the handler as undefined.
14723
+ arguments: external_exports.record(external_exports.string(), external_exports.unknown()).default({}),
14724
+ // Stable across a collapsed retry, so an app can deduplicate its external effects on it.
14725
+ callId: external_exports.string().min(1).max(64),
14726
+ // The short-lived install/version-scoped credential used only by the app runtime's query
14727
+ // broker. It never enters handler arguments or app-authored result data.
14728
+ queryTicket: external_exports.string().min(1).max(2048)
14729
+ }).strict().refine((tool) => {
14730
+ return new TextEncoder().encode(JSON.stringify(tool.arguments)).length <= MAX_TOOL_ARGUMENTS_BYTES;
14731
+ }, "Tool arguments exceed the size cap");
14704
14732
  var controlEnvelopeSchema = external_exports.object({
14705
14733
  v: external_exports.literal(CONTROL_ENVELOPE_VERSION),
14706
- kind: external_exports.enum(["noop", "lifecycle", "migration", "schedule"]),
14734
+ kind: external_exports.enum(["noop", "lifecycle", "migration", "schedule", "tool"]),
14707
14735
  site: external_exports.object({ origin: external_exports.string().min(1).max(255) }).strict(),
14708
14736
  app: external_exports.object({ slug: external_exports.string().min(1).max(30), version: external_exports.string().min(1).max(32) }).strict(),
14709
14737
  install: external_exports.object({ id: external_exports.string().min(1).max(64), ord: external_exports.number().int().min(1), label: external_exports.string().max(100) }).strict().optional(),
14710
14738
  lifecycle: lifecycleEventSchema.optional(),
14711
14739
  migration: migrationSchema.optional(),
14712
- schedule: scheduleSchema.optional()
14740
+ schedule: scheduleSchema.optional(),
14741
+ tool: toolSchema.optional()
14713
14742
  }).strict().superRefine((envelope, ctx) => {
14714
14743
  if (envelope.kind === "lifecycle" !== (envelope.lifecycle !== void 0)) {
14715
14744
  ctx.addIssue({ code: "custom", path: ["lifecycle"], message: "Lifecycle payload must match the control kind" });
@@ -14720,13 +14749,50 @@ var controlEnvelopeSchema = external_exports.object({
14720
14749
  if (envelope.kind === "schedule" !== (envelope.schedule !== void 0)) {
14721
14750
  ctx.addIssue({ code: "custom", path: ["schedule"], message: "Schedule payload must match the control kind" });
14722
14751
  }
14752
+ if (envelope.kind === "tool" !== (envelope.tool !== void 0)) {
14753
+ ctx.addIssue({ code: "custom", path: ["tool"], message: "Tool payload must match the control kind" });
14754
+ }
14723
14755
  if (envelope.kind === "lifecycle" && envelope.install === void 0) {
14724
14756
  ctx.addIssue({ code: "custom", path: ["install"], message: "Lifecycle control needs an install" });
14725
14757
  }
14758
+ if (envelope.kind === "tool" && envelope.install === void 0) {
14759
+ ctx.addIssue({ code: "custom", path: ["install"], message: "Tool control needs an install" });
14760
+ }
14726
14761
  });
14762
+ var toolDataWithinLimits = (value) => {
14763
+ const stack = [{ value, depth: 0 }];
14764
+ let seen = 0;
14765
+ while (stack.length > 0) {
14766
+ const next = stack.pop();
14767
+ seen += 1;
14768
+ if (next.depth > MAX_TOOL_DATA_DEPTH || seen > MAX_TOOL_DATA_NODES) {
14769
+ return false;
14770
+ }
14771
+ if (next.value !== null && typeof next.value === "object") {
14772
+ for (const child of Object.values(next.value)) {
14773
+ stack.push({ value: child, depth: next.depth + 1 });
14774
+ }
14775
+ }
14776
+ }
14777
+ return true;
14778
+ };
14779
+ var toolResultSchema = external_exports.object({
14780
+ content: external_exports.string().max(MAX_TOOL_RESULT_BYTES),
14781
+ // MCP defines `structuredContent` as an object, so a bare scalar or array is refused here rather
14782
+ // than surfacing as a protocol error at the client.
14783
+ data: external_exports.record(external_exports.string(), external_exports.unknown()).optional(),
14784
+ // True when the handler chose to fail with `ctx.fail`: `content` is then the app's own message
14785
+ // for the model. A thrown handler is `ok: false` with no `tool` at all.
14786
+ failed: external_exports.boolean()
14787
+ }).strict().refine((result) => {
14788
+ return toolDataWithinLimits(result.data);
14789
+ }, "Tool result nests too deeply or carries too many values").refine((result) => {
14790
+ return new TextEncoder().encode(JSON.stringify({ content: result.content, data: result.data ?? null })).length <= MAX_TOOL_RESULT_BYTES;
14791
+ }, "Tool result exceeds the size cap");
14727
14792
  var controlResponseSchema = external_exports.object({
14728
14793
  v: external_exports.literal(CONTROL_ENVELOPE_VERSION),
14729
14794
  ok: external_exports.boolean(),
14795
+ tool: toolResultSchema.optional(),
14730
14796
  migration: external_exports.union([
14731
14797
  external_exports.object({ done: external_exports.literal(true) }).strict(),
14732
14798
  external_exports.object({ done: external_exports.literal(false), checkpoint: external_exports.unknown() }).strict().refine((result) => {
@@ -14869,6 +14935,9 @@ var appUiWritePath = (domainId, appSlug2) => {
14869
14935
  var appUiCatalogQueryPath = (domainId, appSlug2, resourceIdentifier) => {
14870
14936
  return `/app-ui/sites/${domainId}/apps/${appSlug2}/catalogs/${resourceIdentifier}/query`;
14871
14937
  };
14938
+ var appUiAnalyticsQueryPath = (domainId, appSlug2, resourceIdentifier) => {
14939
+ return `/app-ui/sites/${domainId}/apps/${appSlug2}/analytics/${resourceIdentifier}/query`;
14940
+ };
14872
14941
  var uiInstallSchema = external_exports.object({ id: external_exports.string().min(1).max(64), ord: external_exports.number().int().min(1), label: external_exports.string().max(100), enabled: external_exports.boolean() }).strict();
14873
14942
  var uiContextSchema = external_exports.object({
14874
14943
  version: external_exports.string().min(1).max(32),
@@ -16132,43 +16201,58 @@ var failureMessage = (error51) => {
16132
16201
  return message.slice(0, MAX_ERROR_MESSAGE_LENGTH);
16133
16202
  };
16134
16203
 
16135
- // src/catalog-query.ts
16136
- var MAX_CATALOG_RESPONSE_BYTES = 5 * 1024 * 1024;
16137
- var parseCatalogResponse = async (response) => {
16138
- const raw = await readBody(response, MAX_CATALOG_RESPONSE_BYTES);
16204
+ // src/query.ts
16205
+ var MAX_QUERY_RESPONSE_BYTES = 5 * 1024 * 1024;
16206
+ var parseQueryResponse = async (kind, response) => {
16207
+ const raw = await readBody(response, MAX_QUERY_RESPONSE_BYTES);
16139
16208
  if (raw === null) {
16140
- throw new Error("The catalog query response exceeded the app runtime limit.");
16209
+ throw new Error(`The ${kind} query response exceeded the app runtime limit.`);
16141
16210
  }
16142
16211
  let body;
16143
16212
  try {
16144
16213
  body = JSON.parse(raw);
16145
16214
  } catch {
16146
- throw new Error(`The catalog query returned an invalid response (${response.status}).`);
16215
+ throw new Error(`The ${kind} query returned an invalid response (${response.status}).`);
16147
16216
  }
16148
16217
  if (!response.ok) {
16149
16218
  const error51 = body;
16150
16219
  const reason = typeof error51.message === "string" ? error51.message : typeof error51.error === "string" ? error51.error : `request failed with status ${response.status}`;
16151
- throw new Error(`Catalog query failed: ${reason}`);
16220
+ throw new Error(`${kind === "catalog" ? "Catalog" : "Analytics"} query failed: ${reason}`);
16152
16221
  }
16153
16222
  return body;
16154
16223
  };
16155
- var buildCatalogQuery = ({ env, ticket, installId }) => {
16224
+ var buildQuery = ({ env, ticket, installId }) => {
16225
+ const platform = env;
16226
+ const context = JSON.parse(platform.APP_CONTEXT);
16227
+ const post = async (kind, path, resourceIdentifier, sql) => {
16228
+ return traced(
16229
+ `qinto.${kind}.query`,
16230
+ { attrs: { "app.install_id": installId, [`app.${kind}.resource`]: resourceIdentifier } },
16231
+ async () => {
16232
+ const response = await fetch(new URL(path, platform.QINTO_PLATFORM_API), {
16233
+ method: "POST",
16234
+ headers: { authorization: `Bearer ${ticket}`, "content-type": "application/json" },
16235
+ body: JSON.stringify({ sql })
16236
+ });
16237
+ return parseQueryResponse(kind, response);
16238
+ }
16239
+ );
16240
+ };
16156
16241
  return {
16157
16242
  catalog: async (resourceIdentifier, sql) => {
16158
- const platform = env;
16159
- const context = JSON.parse(platform.APP_CONTEXT);
16160
- const path = appUiCatalogQueryPath(context.domainId, context.slug, resourceIdentifier);
16161
- return traced(
16162
- "qinto.catalog.query",
16163
- { attrs: { "app.install_id": installId, "app.catalog.resource": resourceIdentifier } },
16164
- async () => {
16165
- const response = await fetch(new URL(path, platform.QINTO_PLATFORM_API), {
16166
- method: "POST",
16167
- headers: { authorization: `Bearer ${ticket}`, "content-type": "application/json" },
16168
- body: JSON.stringify({ sql })
16169
- });
16170
- return parseCatalogResponse(response);
16171
- }
16243
+ return post(
16244
+ "catalog",
16245
+ appUiCatalogQueryPath(context.domainId, context.slug, resourceIdentifier),
16246
+ resourceIdentifier,
16247
+ sql
16248
+ );
16249
+ },
16250
+ analytics: async (resourceIdentifier, sql) => {
16251
+ return post(
16252
+ "analytics",
16253
+ appUiAnalyticsQueryPath(context.domainId, context.slug, resourceIdentifier),
16254
+ resourceIdentifier,
16255
+ sql
16172
16256
  );
16173
16257
  }
16174
16258
  };
@@ -16177,7 +16261,8 @@ var buildCatalogQuery = ({ env, ticket, installId }) => {
16177
16261
  // src/schedule.ts
16178
16262
  var dispatchSchedule = async (envelope, bundle, env) => {
16179
16263
  const schedule = envelope.schedule;
16180
- const handler = bundle.schedules?.[schedule.id];
16264
+ const schedules = bundle.schedules;
16265
+ const handler = schedules !== void 0 && Object.hasOwn(schedules, schedule.id) ? schedules[schedule.id] : void 0;
16181
16266
  if (typeof handler !== "function") {
16182
16267
  return json2(200, {
16183
16268
  v: CONTROL_ENVELOPE_VERSION,
@@ -16209,7 +16294,7 @@ var dispatchSchedule = async (envelope, bundle, env) => {
16209
16294
  appSecrets: appScope.appSecrets,
16210
16295
  observability: buildObservability(install.ord),
16211
16296
  apps: buildAppsClient(env, { depth: 0 }),
16212
- query: buildCatalogQuery({ env, ticket: install.catalogQueryTicket, installId: install.id }),
16297
+ query: buildQuery({ env, ticket: install.queryTicket, installId: install.id }),
16213
16298
  push: async (userId, message) => {
16214
16299
  await brokerPush(env, {
16215
16300
  userId,
@@ -16239,6 +16324,142 @@ var dispatchSchedule = async (envelope, bundle, env) => {
16239
16324
  return json2(200, { v: CONTROL_ENVELOPE_VERSION, ok: true });
16240
16325
  };
16241
16326
 
16327
+ // src/tool.ts
16328
+ var failure = (message) => {
16329
+ return json2(200, { v: CONTROL_ENVELOPE_VERSION, ok: false, error: { message } });
16330
+ };
16331
+ var isFailure = (value) => {
16332
+ return value !== null && typeof value === "object" && value.kind === "tool_error" && typeof value.message === "string";
16333
+ };
16334
+ var isResult = (value) => {
16335
+ return value !== null && typeof value === "object" && typeof value.content === "string";
16336
+ };
16337
+ var isStructuredData = (data) => {
16338
+ return data === void 0 || data !== null && typeof data === "object" && !Array.isArray(data);
16339
+ };
16340
+ var isPlainContainer = (value) => {
16341
+ const prototype = Object.getPrototypeOf(value);
16342
+ return Array.isArray(value) || prototype === Object.prototype || prototype === null;
16343
+ };
16344
+ var jsonScalarVerdict = (value) => {
16345
+ if (value === null || typeof value === "string" || typeof value === "boolean") {
16346
+ return "ok";
16347
+ }
16348
+ if (typeof value === "number") {
16349
+ return Number.isFinite(value) ? "ok" : "invalid";
16350
+ }
16351
+ if (value === void 0 || typeof value !== "object" || !isPlainContainer(value)) {
16352
+ return "invalid";
16353
+ }
16354
+ return "container";
16355
+ };
16356
+ var isJsonValue = (root) => {
16357
+ const stack = [root];
16358
+ const seen = /* @__PURE__ */ new Set();
16359
+ while (stack.length > 0) {
16360
+ const value = stack.pop();
16361
+ const verdict = jsonScalarVerdict(value);
16362
+ if (verdict === "invalid") {
16363
+ return false;
16364
+ }
16365
+ if (verdict === "ok" || seen.has(value)) {
16366
+ continue;
16367
+ }
16368
+ seen.add(value);
16369
+ if (Array.isArray(value) && Object.keys(value).length !== value.length) {
16370
+ return false;
16371
+ }
16372
+ stack.push(...Object.values(value));
16373
+ }
16374
+ return true;
16375
+ };
16376
+ var truncate = (message) => {
16377
+ return message.length <= MAX_TOOL_ERROR_MESSAGE_LENGTH ? message : message.slice(0, MAX_TOOL_ERROR_MESSAGE_LENGTH);
16378
+ };
16379
+ var checkResult = (result) => {
16380
+ if (!isStructuredData(result.data)) {
16381
+ return "not_structured";
16382
+ }
16383
+ if (result.data !== void 0 && !isJsonValue(result.data)) {
16384
+ return "not_serializable";
16385
+ }
16386
+ let encoded;
16387
+ try {
16388
+ encoded = new TextEncoder().encode(JSON.stringify({ content: result.content, data: result.data ?? null }));
16389
+ } catch {
16390
+ return "not_serializable";
16391
+ }
16392
+ if (encoded.length > MAX_TOOL_RESULT_BYTES || !toolDataWithinLimits(result.data)) {
16393
+ return "too_large";
16394
+ }
16395
+ return "ok";
16396
+ };
16397
+ var buildContext2 = (envelope, env) => {
16398
+ const install = envelope.install;
16399
+ const { variableKinds, appVariableKinds } = contextKindsOf(env);
16400
+ const maps = parseVariableMaps(env);
16401
+ const appScope = projectAppValues(env, appVariableKinds);
16402
+ const { variables, secrets } = projectInstallValues(maps, variableKinds, install.ord);
16403
+ return {
16404
+ env,
16405
+ site: envelope.site,
16406
+ install: { id: install.id, label: install.label },
16407
+ viewer: envelope.tool.viewer,
16408
+ callId: envelope.tool.callId,
16409
+ startWorkflow: buildStartWorkflow({ ord: install.ord, installId: install.id, installLabel: install.label, anonId: null }),
16410
+ oauth: buildOauth(env, install.ord),
16411
+ variables,
16412
+ secrets,
16413
+ appVariables: appScope.appVariables,
16414
+ appSecrets: appScope.appSecrets,
16415
+ observability: buildObservability(install.ord),
16416
+ apps: buildAppsClient(env, { depth: 0 }),
16417
+ query: buildQuery({ env, ticket: envelope.tool.queryTicket, installId: install.id }),
16418
+ fail: (message) => {
16419
+ return { kind: "tool_error", message: truncate(message) };
16420
+ }
16421
+ };
16422
+ };
16423
+ var dispatchTool = async (envelope, bundle, env) => {
16424
+ const name = envelope.tool.name;
16425
+ const tools = bundle.tools;
16426
+ const handler = tools !== void 0 && Object.hasOwn(tools, name) ? tools[name] : void 0;
16427
+ if (typeof handler !== "function") {
16428
+ return failure(`missing handler: tools.${name}`);
16429
+ }
16430
+ setSpanAttributes({ "app.tool": name, "app.tool_call_id": envelope.tool.callId });
16431
+ let value;
16432
+ try {
16433
+ value = await installSpan(envelope.install, () => {
16434
+ return handler(envelope.tool.arguments, buildContext2(envelope, env));
16435
+ });
16436
+ } catch (error51) {
16437
+ console.warn(`tool ${name} failed for install ${envelope.install.id}: ${failureMessage(error51)}`);
16438
+ return failure(`tool ${name} failed`);
16439
+ }
16440
+ if (isFailure(value)) {
16441
+ return json2(200, { v: CONTROL_ENVELOPE_VERSION, ok: true, tool: { content: truncate(value.message), failed: true } });
16442
+ }
16443
+ if (!isResult(value)) {
16444
+ return failure(`tool ${name} returned no result`);
16445
+ }
16446
+ const verdict = checkResult(value);
16447
+ if (verdict === "not_structured") {
16448
+ return failure(`tool ${name} returned data that is not a JSON object`);
16449
+ }
16450
+ if (verdict === "not_serializable") {
16451
+ return failure(`tool ${name} returned data that cannot be serialized`);
16452
+ }
16453
+ if (verdict === "too_large") {
16454
+ return failure(`tool ${name} returned a result over the size cap`);
16455
+ }
16456
+ return json2(200, {
16457
+ v: CONTROL_ENVELOPE_VERSION,
16458
+ ok: true,
16459
+ tool: { content: value.content, ...value.data === void 0 ? {} : { data: value.data }, failed: false }
16460
+ });
16461
+ };
16462
+
16242
16463
  // src/control.ts
16243
16464
  var controlError = (status, message) => {
16244
16465
  return json2(status, { v: CONTROL_ENVELOPE_VERSION, ok: false, error: { message } });
@@ -16378,6 +16599,9 @@ var dispatchControl = async ({ request, env, versions }) => {
16378
16599
  case "schedule": {
16379
16600
  return dispatchSchedule(envelope, bundle, env);
16380
16601
  }
16602
+ case "tool": {
16603
+ return dispatchTool(envelope, bundle, env);
16604
+ }
16381
16605
  }
16382
16606
  };
16383
16607
 
@@ -16410,7 +16634,7 @@ var handlerRequest = (request, context, body) => {
16410
16634
  body: body.byteLength === 0 ? void 0 : body
16411
16635
  });
16412
16636
  };
16413
- var buildContext2 = ({ env, maps, kinds, appScope, install, site, params }) => {
16637
+ var buildContext3 = ({ env, maps, kinds, appScope, install, site, params }) => {
16414
16638
  const { variables, secrets } = projectInstallValues(maps, kinds, install.ord);
16415
16639
  return {
16416
16640
  env,
@@ -16502,7 +16726,7 @@ var dispatchEndpoint = async ({ request, env, versions }) => {
16502
16726
  return installSpan(install, () => {
16503
16727
  return handler(
16504
16728
  handlerRequest(request, context, body),
16505
- buildContext2({ env, maps, kinds: variableKinds, appScope, install, site: context.site, params: context.params })
16729
+ buildContext3({ env, maps, kinds: variableKinds, appScope, install, site: context.site, params: context.params })
16506
16730
  );
16507
16731
  });
16508
16732
  };
@@ -16788,7 +17012,7 @@ var runInstalls = async (envelope, env, invoke) => {
16788
17012
  const { variableKinds: kinds, appVariableKinds } = contextKindsOf(env);
16789
17013
  const appScope = projectAppValues(env, appVariableKinds);
16790
17014
  const groups = [];
16791
- let failure;
17015
+ let failure2;
16792
17016
  for (const install of envelope.installs) {
16793
17017
  const emits = [];
16794
17018
  const pushes = [];
@@ -16802,7 +17026,7 @@ var runInstalls = async (envelope, env, invoke) => {
16802
17026
  } catch (error51) {
16803
17027
  const message = failureMessage(error51);
16804
17028
  console.warn(`app hook failed for install ${install.id}: ${message}`);
16805
- failure ??= message;
17029
+ failure2 ??= message;
16806
17030
  continue;
16807
17031
  }
16808
17032
  const contextWrites = context.writes();
@@ -16814,8 +17038,8 @@ var runInstalls = async (envelope, env, invoke) => {
16814
17038
  if (groups.length > 0) {
16815
17039
  body.effects = groups;
16816
17040
  }
16817
- if (failure !== void 0) {
16818
- body.error = { message: failure };
17041
+ if (failure2 !== void 0) {
17042
+ body.error = { message: failure2 };
16819
17043
  }
16820
17044
  return json2(200, body);
16821
17045
  };