@malloy-publisher/server 0.10.4 → 0.10.5

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.
@@ -6685,8 +6685,11 @@ components:
6685
6685
  `sqlSource` answer 400 for anything else (another statement type, or
6686
6686
  more than one statement), and `sqlTemporaryTable` answers 400 always.
6687
6687
  This is a rule on statements, not on what a SELECT reads: that is
6688
- bounded by the session's storage secret and catalog role. Absent or
6689
- false is the pre-existing behavior.
6688
+ bounded by the catalog role and by the session's storage secret,
6689
+ which on a managed connection attached read-only is scoped to the
6690
+ `main` schema's recorded path, so a SELECT reads only that schema's
6691
+ files; another schema's files, and any other object in the bucket,
6692
+ are unsigned (403). Absent or false is the pre-existing behavior.
6690
6693
  fingerprint:
6691
6694
  type: string
6692
6695
  description: >
@@ -7157,7 +7160,11 @@ components:
7157
7160
  every attach uses the data path the catalog recorded at its
7158
7161
  creation, and no attach ever creates a catalog: the shape for a
7159
7162
  catalog created elsewhere, through the admin API
7160
- (admin-api-doc.yaml) or by another tool.
7163
+ (admin-api-doc.yaml) or by another tool. Either way the storage
7164
+ secret is scoped to the data path, the given one or the
7165
+ recorded one: a read of any other object through this
7166
+ connection is unsigned, and on S3 the host's AWS_* environment
7167
+ never signs it either. A catalog with neither is not attached.
7161
7168
  s3Connection:
7162
7169
  $ref: "#/components/schemas/S3Connection"
7163
7170
  description: AWS S3 connection configuration for data storage
Binary file
@@ -164831,7 +164831,7 @@ var init_telemetry = __esm(() => {
164831
164831
  });
164832
164832
 
164833
164833
  // package.json
164834
- var version = "0.10.4";
164834
+ var version = "0.10.5";
164835
164835
  var init_package = () => {};
164836
164836
 
164837
164837
  // src/version.ts
package/dist/server.mjs CHANGED
@@ -164832,7 +164832,7 @@ var init_telemetry = __esm(() => {
164832
164832
  });
164833
164833
 
164834
164834
  // package.json
164835
- var version = "0.10.4";
164835
+ var version = "0.10.5";
164836
164836
  var init_package = () => {};
164837
164837
 
164838
164838
  // src/version.ts
@@ -268349,7 +268349,7 @@ var require_bcrypt_pbkdf = __commonJS((exports, module) => {
268349
268349
 
268350
268350
  // ../../node_modules/cpu-features/build/Release/cpufeatures.node
268351
268351
  var require_cpufeatures = __commonJS((exports, module) => {
268352
- module.exports = __require("./cpufeatures-1yrn0vtw.node");
268352
+ module.exports = __require("./cpufeatures-gknxfsk5.node");
268353
268353
  });
268354
268354
 
268355
268355
  // ../../node_modules/cpu-features/lib/index.js
@@ -268960,6 +268960,11 @@ var require_utils93 = __commonJS((exports, module) => {
268960
268960
  };
268961
268961
  });
268962
268962
 
268963
+ // ../../node_modules/ssh2/lib/protocol/crypto/build/Release/sshcrypto.node
268964
+ var require_sshcrypto = __commonJS((exports, module) => {
268965
+ module.exports = __require("./sshcrypto-a4xyg5e8.node");
268966
+ });
268967
+
268963
268968
  // ../../node_modules/ssh2/lib/protocol/crypto/poly1305.js
268964
268969
  var require_poly1305 = __commonJS((exports, module) => {
268965
268970
  var __dirname = "/home/runner/work/publisher/publisher/node_modules/ssh2/lib/protocol/crypto", __filename = "/home/runner/work/publisher/publisher/node_modules/ssh2/lib/protocol/crypto/poly1305.js";
@@ -269446,7 +269451,7 @@ var require_crypto = __commonJS((exports, module) => {
269446
269451
  var ChaChaPolyDecipher;
269447
269452
  var GenericDecipher;
269448
269453
  try {
269449
- binding = (()=>{throw new Error("Cannot require module "+"./crypto/build/Release/sshcrypto.node");})();
269454
+ binding = require_sshcrypto();
269450
269455
  ({
269451
269456
  AESGCMCipher,
269452
269457
  ChaChaPolyCipher,
@@ -283780,7 +283785,7 @@ function validateMotherDuckShape(connection) {
283780
283785
  throw new Error("MotherDuck access token is required.");
283781
283786
  }
283782
283787
  }
283783
- function validateDuckLakeShape(connection) {
283788
+ function validateDuckLakeShape(connection, origin2) {
283784
283789
  if (!connection.ducklakeConnection) {
283785
283790
  throw new Error("DuckLake connection configuration is missing.");
283786
283791
  }
@@ -283789,7 +283794,7 @@ function validateDuckLakeShape(connection) {
283789
283794
  }
283790
283795
  const storage = connection.ducklakeConnection.storage;
283791
283796
  if (storage?.s3Connection) {
283792
- validateS3ProviderShape(storage.s3Connection, connection.name);
283797
+ validateS3ProviderShape(storage.s3Connection, connection.name, origin2);
283793
283798
  }
283794
283799
  if (connection.ducklakeConnection.catalog.metadataSchema !== undefined) {
283795
283800
  const schema = connection.ducklakeConnection.catalog.metadataSchema;
@@ -283866,7 +283871,7 @@ function validatePublisherShape(connection) {
283866
283871
  throw new Error(`Invalid publisher connection '${connection.name}': connectionUri must use http or https (got '${parsedUri.protocol}'). ` + `Fix: set connectionUri to an absolute https URL like "https://…/connections/${connection.name}".`);
283867
283872
  }
283868
283873
  }
283869
- function validateTypeShape(connection) {
283874
+ function validateTypeShape(connection, origin2) {
283870
283875
  switch (connection.type) {
283871
283876
  case "sqlserver":
283872
283877
  return validateSqlServerShape(connection);
@@ -283877,7 +283882,7 @@ function validateTypeShape(connection) {
283877
283882
  case "motherduck":
283878
283883
  return validateMotherDuckShape(connection);
283879
283884
  case "ducklake":
283880
- return validateDuckLakeShape(connection);
283885
+ return validateDuckLakeShape(connection, origin2);
283881
283886
  case "trino":
283882
283887
  return validateTrinoShape(connection);
283883
283888
  case "databricks":
@@ -283888,7 +283893,7 @@ function validateTypeShape(connection) {
283888
283893
  return validatePublisherShape(connection);
283889
283894
  }
283890
283895
  }
283891
- function validateConnectionShape(connection) {
283896
+ function validateConnectionShape(connection, origin2 = "connection") {
283892
283897
  if (connection.proxy) {
283893
283898
  validateProxyShape(connection, connection.proxy);
283894
283899
  }
@@ -283901,7 +283906,7 @@ function validateConnectionShape(connection) {
283901
283906
  if (connection.postgresConnection) {
283902
283907
  postgresStatementTimeoutMs(connection.name ?? "", connection.postgresConnection);
283903
283908
  }
283904
- validateTypeShape(connection);
283909
+ validateTypeShape(connection, origin2);
283905
283910
  }
283906
283911
  function storageDestinationRoot(environmentPath) {
283907
283912
  return path4.join(environmentPath, STORAGE_DESTINATIONS_DIR);
@@ -283950,7 +283955,7 @@ function validateStorageDestinations(destinations = []) {
283950
283955
  continue;
283951
283956
  }
283952
283957
  try {
283953
- validateConnectionShape(destination);
283958
+ validateConnectionShape(destination, "storage-destination");
283954
283959
  const storage = destination.ducklakeConnection?.storage;
283955
283960
  if (storage?.s3Connection) {
283956
283961
  resolveCloudStorageCredentials({
@@ -284168,7 +284173,7 @@ function buildCoreEntry(connection, environmentPath) {
284168
284173
  throw new Error(`Unsupported connection type: ${connection.type}`);
284169
284174
  }
284170
284175
  }
284171
- function assembleEnvironmentConnections(connections = [], environmentPath = "") {
284176
+ function assembleEnvironmentConnections(connections = [], environmentPath = "", origin2 = "connection") {
284172
284177
  const pojo = { connections: {} };
284173
284178
  const metadata = new Map;
284174
284179
  const apiConnections = [];
@@ -284185,7 +284190,7 @@ function assembleEnvironmentConnections(connections = [], environmentPath = "")
284185
284190
  }
284186
284191
  processedConnections.add(connection.name);
284187
284192
  validateDuckdbApiSurface(connection);
284188
- validateConnectionShape(connection);
284193
+ validateConnectionShape(connection, origin2);
284189
284194
  warnOnConnectionQueryMetadata(connection);
284190
284195
  const apiConnection = cloneApiConnection(connection);
284191
284196
  apiConnection.attributes = getStaticConnectionAttributes(connection.type);
@@ -295166,17 +295171,19 @@ async function attachDuckLakeWithMode(connection, dbName, ducklakeConfig, option
295166
295171
  const hasS3 = !!ducklakeConfig.storage?.s3Connection;
295167
295172
  const hasGCS = !!ducklakeConfig.storage?.gcsConnection;
295168
295173
  if (hasS3) {
295169
- await attachCloudStorage(connection, {
295170
- name: `${dbName}_storage`,
295171
- type: "s3",
295172
- s3Connection: ducklakeConfig.storage.s3Connection
295173
- });
295174
- } else if (hasGCS) {
295175
- await attachCloudStorage(connection, {
295176
- name: `${dbName}_storage`,
295177
- type: "gcs",
295178
- gcsConnection: ducklakeConfig.storage.gcsConnection
295179
- });
295174
+ await connection.runSQL("SET GLOBAL enable_global_s3_configuration=false");
295175
+ }
295176
+ const storage = hasS3 ? {
295177
+ name: `${dbName}_storage`,
295178
+ type: "s3",
295179
+ s3Connection: ducklakeConfig.storage.s3Connection
295180
+ } : hasGCS ? {
295181
+ name: `${dbName}_storage`,
295182
+ type: "gcs",
295183
+ gcsConnection: ducklakeConfig.storage.gcsConnection
295184
+ } : undefined;
295185
+ if (storage) {
295186
+ resolveCloudStorageCredentials(storage, options.origin ?? "connection");
295180
295187
  }
295181
295188
  const pg = ducklakeConfig.catalog.postgresConnection;
295182
295189
  const pgConnString = buildPgConnectionString(pg, {
@@ -295189,8 +295196,8 @@ async function attachDuckLakeWithMode(connection, dbName, ducklakeConfig, option
295189
295196
  logger.debug(`Final escaped connection string: ${redactPgSecrets(escapedPgConnString)}`);
295190
295197
  const dataPath = ducklakeConfig.storage?.bucketUrl;
295191
295198
  const metadataSchema = ducklakeConfig.catalog.metadataSchema;
295192
- const presence = await preflightDuckLakeCatalogFormat(connection, dbName, pgConnString, metadataSchema);
295193
- if (presence === "absent") {
295199
+ const preflight = await preflightDuckLakeCatalogFormat(connection, dbName, pgConnString, metadataSchema);
295200
+ if (preflight === "absent") {
295194
295201
  if (options.skipIfAbsent) {
295195
295202
  return false;
295196
295203
  }
@@ -295215,14 +295222,44 @@ async function attachDuckLakeWithMode(connection, dbName, ducklakeConfig, option
295215
295222
  throw error;
295216
295223
  }
295217
295224
  }
295225
+ if (storage) {
295226
+ try {
295227
+ const scope = await attachedDuckLakeScope(connection, dbName, {
295228
+ configuredDataPath: dataPath,
295229
+ metadataSchema,
295230
+ readOnly: options.readOnly,
295231
+ managed: options.managed === true && options.origin !== "storage-destination"
295232
+ });
295233
+ if (!scope) {
295234
+ throw new Error(`No storage secret could be scoped for DuckLake catalog '${dbName}': ` + `the config states no data path, and the catalog records no data ` + `path (or, for a managed view, no path for its main schema) to ` + `scope it to. The catalog was not attached.`);
295235
+ }
295236
+ await attachCloudStorage(connection, storage, options.origin, scope);
295237
+ } catch (error) {
295238
+ await connection.runSQL(`DETACH ${quoteIdentifier(dbName, "duckdb")};`).catch(() => {});
295239
+ throw error;
295240
+ }
295241
+ }
295218
295242
  return true;
295219
295243
  }
295220
- async function recordedCatalogSettings(connection, alias, metadataSchema) {
295221
- const metadataCatalog = [
295244
+ async function attachedDuckLakeScope(connection, dbName, input) {
295245
+ const recordedDataPath = input.configuredDataPath ? undefined : (await recordedCatalogSettings(connection, dbName, input.metadataSchema)).data_path;
295246
+ const mainSchemaPath = input.managed && input.readOnly ? await recordedSchemaPath(connection, dbName, input.metadataSchema, "main") : undefined;
295247
+ return storageSecretScope({
295248
+ configuredDataPath: input.configuredDataPath,
295249
+ recordedDataPath,
295250
+ mainSchemaPath,
295251
+ readOnly: input.readOnly,
295252
+ managed: input.managed
295253
+ });
295254
+ }
295255
+ function metadataCatalogRef(alias, metadataSchema) {
295256
+ return [
295222
295257
  `__ducklake_metadata_${alias}`,
295223
295258
  ...metadataSchema ? [metadataSchema] : []
295224
295259
  ].map((part) => quoteIdentifier(part, "duckdb")).join(".");
295225
- const rows = runSQLRows(await connection.runSQL(`SELECT key, value FROM ${metadataCatalog}.ducklake_metadata WHERE scope IS NULL`));
295260
+ }
295261
+ async function recordedCatalogSettings(connection, alias, metadataSchema) {
295262
+ const rows = runSQLRows(await connection.runSQL(`SELECT key, value FROM ${metadataCatalogRef(alias, metadataSchema)}.ducklake_metadata WHERE scope IS NULL`));
295226
295263
  const settings = {};
295227
295264
  for (const row of rows) {
295228
295265
  if (typeof row.key === "string" && row.value != null) {
@@ -295231,6 +295268,43 @@ async function recordedCatalogSettings(connection, alias, metadataSchema) {
295231
295268
  }
295232
295269
  return settings;
295233
295270
  }
295271
+ async function recordedSchemaPath(connection, alias, metadataSchema, schemaName) {
295272
+ const row = runSQLRows(await connection.runSQL(`SELECT path, path_is_relative FROM ${metadataCatalogRef(alias, metadataSchema)}.ducklake_schema ` + `WHERE schema_name = '${escapeSQL(schemaName)}' AND end_snapshot IS NULL LIMIT 1`))[0];
295273
+ if (!row || typeof row.path !== "string")
295274
+ return;
295275
+ return { path: row.path, relative: row.path_is_relative !== false };
295276
+ }
295277
+ function stripTrailingSlashes(url3) {
295278
+ let end = url3.length;
295279
+ while (end > 0 && url3[end - 1] === "/")
295280
+ end--;
295281
+ return url3.slice(0, end);
295282
+ }
295283
+ function oneTrailingSlash(url3) {
295284
+ return `${stripTrailingSlashes(url3)}/`;
295285
+ }
295286
+ function isBucketlessUrl(url3) {
295287
+ const at = url3.indexOf("://");
295288
+ return at >= 0 && stripTrailingSlashes(url3.slice(at + 3)) === "";
295289
+ }
295290
+ function storageSecretScope(input) {
295291
+ const root = input.configuredDataPath || input.recordedDataPath;
295292
+ if (!root || isBucketlessUrl(root))
295293
+ return;
295294
+ if (input.managed && input.readOnly) {
295295
+ const main = input.mainSchemaPath;
295296
+ if (!main)
295297
+ return;
295298
+ if (!main.relative) {
295299
+ return isBucketlessUrl(main.path) ? undefined : oneTrailingSlash(main.path);
295300
+ }
295301
+ let start = 0;
295302
+ while (start < main.path.length && main.path[start] === "/")
295303
+ start++;
295304
+ return oneTrailingSlash(`${oneTrailingSlash(root)}${main.path.slice(start)}`);
295305
+ }
295306
+ return oneTrailingSlash(root);
295307
+ }
295234
295308
  function dataPathClauses(dataPath, readOnly) {
295235
295309
  if (dataPath) {
295236
295310
  return `DATA_PATH '${escapeSQL(dataPath)}', OVERRIDE_DATA_PATH true`;
@@ -295250,13 +295324,16 @@ function firstAttachClauses(options) {
295250
295324
  async function attachDuckLakeForCreate(connection, dbName, ducklakeConfig, settings) {
295251
295325
  await attachDuckLakeWithMode(connection, dbName, ducklakeConfig, {
295252
295326
  readOnly: false,
295327
+ origin: "storage-destination",
295253
295328
  encrypted: settings.encrypted,
295254
295329
  dataInliningRowLimit: settings.dataInliningRowLimit
295255
295330
  });
295256
295331
  }
295257
- async function attachDuckLake(connection, dbName, ducklakeConfig) {
295332
+ async function attachDuckLake(connection, dbName, ducklakeConfig, options = {}) {
295258
295333
  await attachDuckLakeWithMode(connection, dbName, ducklakeConfig, {
295259
- readOnly: ducklakeConfig.readOnly !== false
295334
+ readOnly: ducklakeConfig.readOnly !== false,
295335
+ origin: options.origin,
295336
+ managed: options.managed
295260
295337
  });
295261
295338
  }
295262
295339
  function managedSearchPathSql(dbName) {
@@ -295267,17 +295344,21 @@ async function setManagedSearchPath(connection, dbName) {
295267
295344
  await connection.runSQL(managedSearchPathSql(dbName));
295268
295345
  }
295269
295346
  async function attachDuckLakeReadOnly(connection, dbName, ducklakeConfig) {
295270
- await attachDuckLake(connection, dbName, ducklakeConfig);
295347
+ await attachDuckLake(connection, dbName, ducklakeConfig, {
295348
+ origin: "storage-destination"
295349
+ });
295271
295350
  }
295272
295351
  async function attachDuckLakeReadOnlyIfPresent(connection, dbName, ducklakeConfig) {
295273
295352
  return attachDuckLakeWithMode(connection, dbName, ducklakeConfig, {
295274
295353
  readOnly: true,
295275
- skipIfAbsent: true
295354
+ skipIfAbsent: true,
295355
+ origin: "storage-destination"
295276
295356
  });
295277
295357
  }
295278
295358
  async function attachDuckLakeReadWrite(connection, dbName, ducklakeConfig) {
295279
295359
  await attachDuckLakeWithMode(connection, dbName, ducklakeConfig, {
295280
- readOnly: false
295360
+ readOnly: false,
295361
+ origin: "storage-destination"
295281
295362
  });
295282
295363
  }
295283
295364
  async function federateSourceForPassthrough(connection, sourceType, config, deps = {}) {
@@ -295481,13 +295562,15 @@ function buildCloudStorageSecretSQL(secretName, credentials) {
295481
295562
  const isGCS = credentials.type === "gcs";
295482
295563
  const escapedKeyId = escapeSQL(credentials.accessKeyId);
295483
295564
  const escapedSecret = escapeSQL(credentials.secretAccessKey);
295565
+ const scopeClause = credentials.scope ? `SCOPE '${escapeSQL(credentials.scope)}'` : "";
295484
295566
  let createSecretCommand;
295485
295567
  if (isGCS) {
295486
295568
  createSecretCommand = `
295487
295569
  CREATE OR REPLACE SECRET ${secretName} (
295488
295570
  TYPE gcs,
295489
295571
  KEY_ID '${escapedKeyId}',
295490
- SECRET '${escapedSecret}'
295572
+ SECRET '${escapedSecret}'${scopeClause ? `,
295573
+ ${scopeClause}` : ""}
295491
295574
  );
295492
295575
  `;
295493
295576
  } else {
@@ -295523,6 +295606,9 @@ function buildCloudStorageSecretSQL(secretName, credentials) {
295523
295606
  if (credentials.provider !== "credential_chain" && credentials.sessionToken) {
295524
295607
  clauses.push(`SESSION_TOKEN '${escapeSQL(credentials.sessionToken)}'`);
295525
295608
  }
295609
+ if (scopeClause) {
295610
+ clauses.push(scopeClause);
295611
+ }
295526
295612
  createSecretCommand = `
295527
295613
  CREATE OR REPLACE SECRET ${secretName} (
295528
295614
  ${clauses.join(`,
@@ -295532,9 +295618,12 @@ function buildCloudStorageSecretSQL(secretName, credentials) {
295532
295618
  }
295533
295619
  return createSecretCommand;
295534
295620
  }
295535
- async function attachCloudStorage(connection, attachedDb) {
295621
+ async function attachCloudStorage(connection, attachedDb, origin2 = "connection", scope) {
295536
295622
  const storageType = attachedDb.type?.toUpperCase() || "";
295537
- const credentials = resolveCloudStorageCredentials(attachedDb);
295623
+ const credentials = {
295624
+ ...resolveCloudStorageCredentials(attachedDb, origin2),
295625
+ scope
295626
+ };
295538
295627
  await installAndLoadExtension(connection, "httpfs");
295539
295628
  if (credentials.provider === "credential_chain") {
295540
295629
  await installAndLoadExtension(connection, "aws");
@@ -295826,8 +295915,8 @@ function applyConnectionFingerprint(connection, metadata) {
295826
295915
  function isDuckDBConnection(connection) {
295827
295916
  return connection instanceof DuckDBConnection;
295828
295917
  }
295829
- function buildEnvironmentMalloyConfig(connections = [], environmentPath = "", isUpdateConnectionRequest = false) {
295830
- const assembled = assembleEnvironmentConnections(connections, environmentPath);
295918
+ function buildEnvironmentMalloyConfig(connections = [], environmentPath = "", isUpdateConnectionRequest = false, { origin: origin2 = "connection" } = {}) {
295919
+ const assembled = assembleEnvironmentConnections(connections, environmentPath, origin2);
295831
295920
  const duckLakeCache = new Map;
295832
295921
  const snowflakeJwtCache = new Map;
295833
295922
  const azureDuckDBCache = new Map;
@@ -295866,7 +295955,10 @@ function buildEnvironmentMalloyConfig(connections = [], environmentPath = "", is
295866
295955
  }
295867
295956
  const connection2 = await connectionPromise;
295868
295957
  if (isUpdateConnectionRequest || !await isDatabaseAttached(connection2, name)) {
295869
- await attachDuckLake(connection2, name, metadata.apiConnection.ducklakeConnection);
295958
+ await attachDuckLake(connection2, name, metadata.apiConnection.ducklakeConnection, {
295959
+ origin: origin2,
295960
+ managed: metadata.apiConnection.managed === true
295961
+ });
295870
295962
  if (metadata.apiConnection.managed === true) {
295871
295963
  await setManagedSearchPath(connection2, name);
295872
295964
  }
@@ -370400,10 +370492,10 @@ class Environment {
370400
370492
  if (this.destinations.length > 0) {
370401
370493
  fs17.mkdirSync(destinationRoot, { recursive: true });
370402
370494
  }
370403
- this.destinationMalloyConfig = buildEnvironmentMalloyConfig(this.destinations, destinationRoot);
370495
+ this.destinationMalloyConfig = buildEnvironmentMalloyConfig(this.destinations, destinationRoot, false, { origin: "storage-destination" });
370404
370496
  } catch (error) {
370405
370497
  logger.error(`Failed to assemble storage destinations for environment ${this.environmentName}; serving without them`, { error });
370406
- this.destinationMalloyConfig = buildEnvironmentMalloyConfig([], storageDestinationRoot(this.environmentPath));
370498
+ this.destinationMalloyConfig = buildEnvironmentMalloyConfig([], storageDestinationRoot(this.environmentPath), false, { origin: "storage-destination" });
370407
370499
  }
370408
370500
  if (previous && previous !== this.destinationMalloyConfig) {
370409
370501
  this.retireConnectionGeneration(`environment ${this.environmentName} destinations`, () => previous.releaseConnections());
@@ -377082,7 +377174,7 @@ function readerSql(uri, format, options = {}) {
377082
377174
  return `read_parquet(${path25})`;
377083
377175
  }
377084
377176
  function assertUnderDataPath(uri, dataPath) {
377085
- const root = dataPath.replace(/\/+$/, "");
377177
+ const root = stripTrailingSlashes(dataPath);
377086
377178
  const outside = () => new BadRequestError(`The object must sit under the catalog's data path (${root}/).`);
377087
377179
  if (!root || !uri.startsWith(`${root}/`))
377088
377180
  throw outside();
Binary file
package/package.json CHANGED
@@ -19,7 +19,7 @@
19
19
  "snowflake"
20
20
  ],
21
21
  "homepage": "https://docs.malloydata.dev/documentation/user_guides/publishing/mcp_agents",
22
- "version": "0.10.4",
22
+ "version": "0.10.5",
23
23
  "main": "dist/server.mjs",
24
24
  "bin": {
25
25
  "malloy-publisher": "dist/server.mjs"
Binary file