@dbx-tools/appkit-mastra 0.1.100 → 0.1.102

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/dist/index.d.ts CHANGED
@@ -1106,6 +1106,8 @@ declare function buildRenderDataTool(config: MastraPluginConfig): _mastra_core_t
1106
1106
  }, unknown, unknown, _mastra_core_tools0.ToolExecutionContext<unknown, unknown, unknown>, "render_data", unknown>;
1107
1107
  //#endregion
1108
1108
  //#region packages/appkit-mastra/src/filesystems.d.ts
1109
+ /** How {@link DatabricksWorkspaceFilesystem.init} handles a missing {@link basePath}. */
1110
+ type DatabricksMkdirsMode = boolean | "try";
1109
1111
  /** Options for {@link DatabricksWorkspaceFilesystem}. */
1110
1112
  interface DatabricksWorkspaceFilesystemOptions extends MastraFilesystemOptions {
1111
1113
  /** Unique identifier for this filesystem instance. */
@@ -1118,12 +1120,16 @@ interface DatabricksWorkspaceFilesystemOptions extends MastraFilesystemOptions {
1118
1120
  */
1119
1121
  basePath: string;
1120
1122
  /**
1121
- * When `true`, {@link init} and file operations fail if {@link basePath}
1122
- * is missing. When `false` (default), a missing base path yields an empty
1123
- * read-only namespace (only `/` exists).
1123
+ * When the {@link basePath} is missing at {@link init}, create it with the
1124
+ * matching Databricks `mkdirs` API. `"try"` (default) logs at debug and
1125
+ * falls back to an empty read-only namespace on failure; `true` fails init;
1126
+ * `false` skips creation and uses the empty namespace.
1127
+ *
1128
+ * A successful mkdir also satisfies the write-access probe when
1129
+ * {@link readOnly} is omitted.
1124
1130
  */
1125
- requireBasePath?: boolean;
1126
- /** Block writes while still allowing reads. When omitted, {@link DatabricksWorkspaceFilesystem.init} probes write access. */
1131
+ mkdirs?: DatabricksMkdirsMode;
1132
+ /** Block writes while still allowing reads. When omitted, {@link init} probes write access. */
1127
1133
  readOnly?: boolean;
1128
1134
  }
1129
1135
  /** Normalize a Databricks base path (POSIX, no trailing slash). */
@@ -1154,15 +1160,14 @@ declare class DatabricksWorkspaceFilesystem extends MastraFilesystem {
1154
1160
  readonly basePath: string;
1155
1161
  status: ProviderStatus;
1156
1162
  private readonly client;
1157
- private readonly requireBasePath;
1163
+ private readonly mkdirs;
1158
1164
  private _readOnly;
1159
1165
  private _basePathMissing;
1160
1166
  get readOnly(): boolean | undefined;
1161
1167
  /**
1162
1168
  * @param options.client - Defaults to the AppKit execution-context client.
1169
+ * @param options.mkdirs - Default `"try"`; see {@link DatabricksMkdirsMode}.
1163
1170
  * @param options.readOnly - When omitted, {@link init} probes write access.
1164
- * @param options.requireBasePath - When `false` (default), a missing
1165
- * {@link basePath} yields an empty namespace instead of errors.
1166
1171
  */
1167
1172
  constructor(options: DatabricksWorkspaceFilesystemOptions);
1168
1173
  /** Resolve and sandbox a workspace-relative path under {@link basePath}. */
@@ -1174,20 +1179,13 @@ declare class DatabricksWorkspaceFilesystem extends MastraFilesystem {
1174
1179
  /** Map SDK / HTTP errors to Mastra workspace filesystem errors. */
1175
1180
  private rethrow;
1176
1181
  /**
1177
- * When {@link requireBasePath} is `false`, probe whether {@link basePath}
1178
- * exists and cache the result on {@link _basePathMissing}.
1182
+ * Probe whether {@link basePath} exists and cache the result on
1183
+ * {@link _basePathMissing}.
1179
1184
  */
1180
1185
  private resolveBasePathStatus;
1181
- /** Delegate to {@link emptyFilesystem} when the optional base path is missing. */
1186
+ /** Delegate to {@link emptyFilesystem} when the base path is missing. */
1182
1187
  private emptyFallback;
1183
1188
  init(): Promise<void>;
1184
- /**
1185
- * Create the base directory when missing and writes are allowed.
1186
- *
1187
- * Uses raw SDK errors for the not-found branch so {@link apiUtils.isNotFoundError}
1188
- * still matches before {@link rethrow} wraps them as Mastra errors.
1189
- */
1190
- private ensureBaseExists;
1191
1189
  /**
1192
1190
  * Write and delete a ephemeral probe file to detect read-only access when
1193
1191
  * {@link DatabricksWorkspaceFilesystemOptions.readOnly} was not set.
@@ -1675,4 +1673,4 @@ declare function buildSummarizeTool(config: MastraPluginConfig): _mastra_core_to
1675
1673
  maxWords?: number | undefined;
1676
1674
  }, unknown, unknown, unknown, _mastra_core_tools0.ToolExecutionContext<unknown, unknown, unknown>, "summarize", unknown>;
1677
1675
  //#endregion
1678
- export { AppKitToolOptions, BuiltAgents, ChartPlannerRequest, DEFAULT_AGENT_MAX_STEPS, DEFAULT_GENIE_ALIAS, DEFAULT_STYLE_INSTRUCTIONS, DatabricksWorkspaceFilesystem, type DatabricksWorkspaceFilesystemOptions, FALLBACK_AGENT_ID, FetchChartOptions, GENIE_INSTRUCTIONS, GenieSpaceConfig, GenieSpacesConfig, MASTRA_MODEL_OVERRIDE_KEY, MASTRA_REQUEST_ID_KEY, MASTRA_SCOPES_KEY, MASTRA_USER_EMAIL_KEY, MASTRA_USER_KEY, MASTRA_USER_NAME_KEY, MastraAgentDefinition, MastraAgentWorkspaceResolver, MastraMcpConfig, MastraMemoryConfig, MastraMemoryConfigOverride, MastraPlugin, MastraPluginConfig, MastraPluginToolkitProvider, MastraPlugins, MastraStorageConfigOverride, MastraTools, MastraToolsFn, type ModelOverrideRequest, PrepareChartOptions, ResolvedMcp, type SummarizeOptions, TRACE_REQUEST_CONTEXT_KEYS, ToolkitOptions, User, approvalGatedToolIds, buildAgents, buildGenieToolkitProvider, buildGenieTools, buildMcpServer, buildRenderDataTool, buildSummarizeTool, chartPlannerRequestSchema, collectSpaceSuggestions, createAgent, createTool, createWorkspace, emptyFilesystem, extractModelOverride, fetchChart, isDbfsPath, isWorkspaceFilesPath, mastra, normalizeDatabricksBasePath, normalizeGenieSpaces, prepareChart, resolveDatabricksAbsolutePath, resolveGenieSpaces, summarizeText, toDatabricksWorkspacePath, tool };
1676
+ export { AppKitToolOptions, BuiltAgents, ChartPlannerRequest, DEFAULT_AGENT_MAX_STEPS, DEFAULT_GENIE_ALIAS, DEFAULT_STYLE_INSTRUCTIONS, type DatabricksMkdirsMode, DatabricksWorkspaceFilesystem, type DatabricksWorkspaceFilesystemOptions, FALLBACK_AGENT_ID, FetchChartOptions, GENIE_INSTRUCTIONS, GenieSpaceConfig, GenieSpacesConfig, MASTRA_MODEL_OVERRIDE_KEY, MASTRA_REQUEST_ID_KEY, MASTRA_SCOPES_KEY, MASTRA_USER_EMAIL_KEY, MASTRA_USER_KEY, MASTRA_USER_NAME_KEY, MastraAgentDefinition, MastraAgentWorkspaceResolver, MastraMcpConfig, MastraMemoryConfig, MastraMemoryConfigOverride, MastraPlugin, MastraPluginConfig, MastraPluginToolkitProvider, MastraPlugins, MastraStorageConfigOverride, MastraTools, MastraToolsFn, type ModelOverrideRequest, PrepareChartOptions, ResolvedMcp, type SummarizeOptions, TRACE_REQUEST_CONTEXT_KEYS, ToolkitOptions, User, approvalGatedToolIds, buildAgents, buildGenieToolkitProvider, buildGenieTools, buildMcpServer, buildRenderDataTool, buildSummarizeTool, chartPlannerRequestSchema, collectSpaceSuggestions, createAgent, createTool, createWorkspace, emptyFilesystem, extractModelOverride, fetchChart, isDbfsPath, isWorkspaceFilesPath, mastra, normalizeDatabricksBasePath, normalizeGenieSpaces, prepareChart, resolveDatabricksAbsolutePath, resolveGenieSpaces, summarizeText, toDatabricksWorkspacePath, tool };
package/dist/index.js CHANGED
@@ -3,7 +3,7 @@ import { DEFAULT_FUZZY_THRESHOLD, DEFAULT_MODEL_CACHE_TTL_MS, FALLBACK_MODEL_IDS
3
3
  import { CompositeFilesystem, DirectoryNotEmptyError, DirectoryNotFoundError, FileExistsError, FileNotFoundError, IsDirectoryError, MastraFilesystem, NotDirectoryError, PermissionError, Workspace, WorkspaceReadOnlyError } from "@mastra/core/workspace";
4
4
  import { apiUtils, appkitUtils, commonUtils, httpUtils, logUtils, netUtils, projectUtils, stringUtils, tokenUtils } from "@dbx-tools/shared";
5
5
  import { MASTRA_RESOURCE_ID_KEY, MASTRA_THREAD_ID_KEY } from "@mastra/core/request-context";
6
- import { ApiError, WorkspaceClient } from "@databricks/sdk-experimental";
6
+ import { WorkspaceClient } from "@databricks/sdk-experimental";
7
7
  import { posix } from "node:path";
8
8
  import { randomUUID } from "node:crypto";
9
9
  import { CacheManager, Plugin, genie, getExecutionContext, getUsernameWithApiLookup, lakebase, toPlugin } from "@databricks/appkit";
@@ -87,21 +87,20 @@ const TRACE_REQUEST_CONTEXT_KEYS = [
87
87
 
88
88
  //#endregion
89
89
  //#region packages/appkit-mastra/src/filesystems.ts
90
- /**
91
- * Mastra {@link WorkspaceFilesystem} implementations for Databricks Apps.
92
- *
93
- * {@link DatabricksWorkspaceFilesystem} maps a Mastra workspace namespace onto
94
- * an absolute Databricks path (Unity Catalog volume, workspace object tree, or
95
- * DBFS). {@link emptyFilesystem} is a read-only no-op mount used when no
96
- * dynamic mounts resolve for a request.
97
- *
98
- * Path helpers ({@link normalizeDatabricksBasePath}, {@link isDbfsPath}, …)
99
- * are exported for tests and callers that need to reason about Databricks
100
- * paths without constructing a filesystem.
101
- */
102
90
  const DBFS_READ_CHUNK_BYTES = 1024 * 1024;
103
91
  const DBFS_PUT_MAX_BYTES = 1024 * 1024;
104
92
  const EMPTY_FILESYSTEM_EPOCH = /* @__PURE__ */ new Date(0);
93
+ /** Mastra error constructor for a known SDK filesystem failure, if any. */
94
+ function filesystemSdkErrorType(err) {
95
+ if (err) {
96
+ const ctx = apiUtils.errorContext(err);
97
+ if (ctx.notAccessible) return FileNotFoundError;
98
+ else if (ctx.hasMessage("already", "exists")) return FileExistsError;
99
+ else if (ctx.hasMessage("not", "directory")) return NotDirectoryError;
100
+ else if (ctx.hasMessage("not", "file")) return IsDirectoryError;
101
+ }
102
+ }
103
+ const log$9 = logUtils.logger("mastra/filesystems");
105
104
  /** Normalize a Databricks base path (POSIX, no trailing slash). */
106
105
  function normalizeDatabricksBasePath(basePath) {
107
106
  const trimmed = basePath.trim();
@@ -203,7 +202,7 @@ var DatabricksWorkspaceFilesystem = class extends MastraFilesystem {
203
202
  basePath;
204
203
  status = "pending";
205
204
  client;
206
- requireBasePath;
205
+ mkdirs;
207
206
  _readOnly;
208
207
  _basePathMissing;
209
208
  get readOnly() {
@@ -211,9 +210,8 @@ var DatabricksWorkspaceFilesystem = class extends MastraFilesystem {
211
210
  }
212
211
  /**
213
212
  * @param options.client - Defaults to the AppKit execution-context client.
213
+ * @param options.mkdirs - Default `"try"`; see {@link DatabricksMkdirsMode}.
214
214
  * @param options.readOnly - When omitted, {@link init} probes write access.
215
- * @param options.requireBasePath - When `false` (default), a missing
216
- * {@link basePath} yields an empty namespace instead of errors.
217
215
  */
218
216
  constructor(options) {
219
217
  super({
@@ -223,7 +221,7 @@ var DatabricksWorkspaceFilesystem = class extends MastraFilesystem {
223
221
  this.id = options.id ?? `databricks-fs-${commonUtils.fnvHash(options.basePath)}`;
224
222
  this.client = options.client ?? getExecutionContext().client;
225
223
  this.basePath = normalizeDatabricksBasePath(options.basePath);
226
- this.requireBasePath = options.requireBasePath ?? false;
224
+ this.mkdirs = options.mkdirs ?? "try";
227
225
  this._readOnly = options.readOnly;
228
226
  }
229
227
  /** Resolve and sandbox a workspace-relative path under {@link basePath}. */
@@ -243,16 +241,14 @@ var DatabricksWorkspaceFilesystem = class extends MastraFilesystem {
243
241
  /** Map SDK / HTTP errors to Mastra workspace filesystem errors. */
244
242
  rethrow(err, inputPath) {
245
243
  const workspacePath = inputPath.startsWith("/") ? inputPath : this.workspacePath(this.resolvePath(inputPath));
246
- if (apiUtils.isNotFoundError(err)) throw new FileNotFoundError(workspacePath);
247
- if (err instanceof ApiError && err.errorCode === "RESOURCE_ALREADY_EXISTS") throw new FileExistsError(workspacePath);
248
- if (err instanceof ApiError && err.errorCode === "NOT_DIRECTORY") throw new NotDirectoryError(workspacePath);
249
- if (err instanceof ApiError && err.errorCode === "NOT_A_FILE") throw new IsDirectoryError(workspacePath);
244
+ const ErrorType = filesystemSdkErrorType(err);
245
+ if (ErrorType) throw new ErrorType(workspacePath);
250
246
  const message = commonUtils.errorMessage(err);
251
247
  throw new Error(`Databricks filesystem ${workspacePath}: ${message}`);
252
248
  }
253
249
  /**
254
- * When {@link requireBasePath} is `false`, probe whether {@link basePath}
255
- * exists and cache the result on {@link _basePathMissing}.
250
+ * Probe whether {@link basePath} exists and cache the result on
251
+ * {@link _basePathMissing}.
256
252
  */
257
253
  async resolveBasePathStatus() {
258
254
  if (this._basePathMissing !== void 0) return;
@@ -260,38 +256,36 @@ var DatabricksWorkspaceFilesystem = class extends MastraFilesystem {
260
256
  await this.assertAbsoluteReadable(this.basePath);
261
257
  this._basePathMissing = false;
262
258
  } catch (err) {
263
- if (!this.requireBasePath && apiUtils.isNotFoundError(err)) {
259
+ if (apiUtils.errorContext(err).notAccessible) {
264
260
  this._basePathMissing = true;
265
261
  return;
266
262
  }
267
263
  this.rethrow(err, "/");
268
264
  }
269
265
  }
270
- /** Delegate to {@link emptyFilesystem} when the optional base path is missing. */
266
+ /** Delegate to {@link emptyFilesystem} when the base path is missing. */
271
267
  async emptyFallback() {
272
- if (this.requireBasePath) return void 0;
273
268
  await this.resolveBasePathStatus();
274
269
  return this._basePathMissing ? emptyFilesystem() : void 0;
275
270
  }
276
271
  async init() {
277
272
  await this.resolveBasePathStatus();
278
- if (this._basePathMissing) return;
279
- if (!this.readOnly) await this.ensureBaseExists();
280
- if (this._readOnly === void 0) await this.probeReadOnly();
281
- }
282
- /**
283
- * Create the base directory when missing and writes are allowed.
284
- *
285
- * Uses raw SDK errors for the not-found branch so {@link apiUtils.isNotFoundError}
286
- * still matches before {@link rethrow} wraps them as Mastra errors.
287
- */
288
- async ensureBaseExists() {
289
- try {
290
- await this.assertAbsoluteReadable(this.basePath);
291
- } catch (err) {
292
- if (!apiUtils.isNotFoundError(err)) this.rethrow(err, "/");
293
- await this.mkdirAbsolute(this.basePath);
273
+ if (this._basePathMissing) {
274
+ if (this.mkdirs === false) return;
275
+ try {
276
+ await this.mkdirAbsolute(this.basePath);
277
+ this._basePathMissing = false;
278
+ if (this._readOnly === void 0) this._readOnly = false;
279
+ } catch (err) {
280
+ if (this.mkdirs === true) this.rethrow(err, "/");
281
+ log$9.debug("mkdirs:try-failed", {
282
+ basePath: this.basePath,
283
+ error: commonUtils.errorMessage(err)
284
+ });
285
+ }
286
+ return;
294
287
  }
288
+ if (this._readOnly === void 0) await this.probeReadOnly();
295
289
  }
296
290
  /**
297
291
  * Write and delete a ephemeral probe file to detect read-only access when
@@ -751,7 +745,7 @@ var DatabricksWorkspaceFilesystem = class extends MastraFilesystem {
751
745
  mimeType: metadata["content-type"]
752
746
  };
753
747
  } catch (fileErr) {
754
- if (!apiUtils.isNotFoundError(fileErr)) this.rethrow(fileErr, inputPath);
748
+ if (!apiUtils.errorContext(fileErr).notAccessible) this.rethrow(fileErr, inputPath);
755
749
  await this.client.files.getDirectoryMetadata({ directory_path: absolutePath });
756
750
  return {
757
751
  name: posix.basename(absolutePath),
@@ -983,9 +977,9 @@ function resolveAssistantSkillsMounts(context) {
983
977
  skillPaths: []
984
978
  };
985
979
  }
986
- mounts[ASSISTANT_WORKSPACE_SKILLS_MOUNT] = readOnlyDatabricksFilesystem(client, ASSISTANT_SHARED_SKILLS_PATH);
980
+ mounts[ASSISTANT_WORKSPACE_SKILLS_MOUNT] = databricksFilesystem(client, ASSISTANT_SHARED_SKILLS_PATH);
987
981
  const email = resolveScopedEmail(requestContext);
988
- if (email) mounts[ASSISTANT_USER_SKILLS_MOUNT] = readOnlyDatabricksFilesystem(client, userAssistantSkillsPath(email));
982
+ if (email) mounts[ASSISTANT_USER_SKILLS_MOUNT] = databricksFilesystem(client, userAssistantSkillsPath(email), false);
989
983
  log$8.debug("assistant-skills:mounted", {
990
984
  sharedPath: ASSISTANT_SHARED_SKILLS_PATH,
991
985
  sharedMount: ASSISTANT_WORKSPACE_SKILLS_MOUNT,
@@ -1042,11 +1036,11 @@ function resolveScopedEmail(requestContext) {
1042
1036
  return (requestContext?.get(MASTRA_USER_EMAIL_KEY))?.trim() || void 0;
1043
1037
  }
1044
1038
  /** Construct a read-only {@link DatabricksWorkspaceFilesystem} for `basePath`. */
1045
- function readOnlyDatabricksFilesystem(client, basePath) {
1039
+ function databricksFilesystem(client, basePath, readOnly = true) {
1046
1040
  return new DatabricksWorkspaceFilesystem({
1047
1041
  client,
1048
1042
  basePath,
1049
- readOnly: true
1043
+ readOnly
1050
1044
  });
1051
1045
  }
1052
1046
  /**
@@ -2344,7 +2338,7 @@ function buildAskGenieTool(opts) {
2344
2338
  finalMessage = await runTurn();
2345
2339
  } catch (err) {
2346
2340
  const seeded = readContextConversationId(requestContext, spaceId);
2347
- if (seeded && apiUtils.isNotFoundError(err)) {
2341
+ if (seeded && apiUtils.errorContext(err).notAccessible) {
2348
2342
  log$6.warn("conversation-cache:stale, resetting", {
2349
2343
  spaceId,
2350
2344
  conversationId: seeded,
@@ -5215,7 +5209,7 @@ var MastraPlugin = class MastraPlugin extends Plugin {
5215
5209
  truncated: data.rows.length < data.rowCount
5216
5210
  };
5217
5211
  } catch (err) {
5218
- if (apiUtils.isNotFoundError(err)) return void 0;
5212
+ if (apiUtils.errorContext(err).notAccessible) return void 0;
5219
5213
  throw err;
5220
5214
  }
5221
5215
  }
package/package.json CHANGED
@@ -1,13 +1,13 @@
1
1
  {
2
2
  "name": "@dbx-tools/appkit-mastra",
3
- "version": "0.1.100",
3
+ "version": "0.1.102",
4
4
  "dependencies": {
5
5
  "@databricks/sdk-experimental": "^0.17.0",
6
- "@dbx-tools/appkit-mastra-shared": "0.1.100",
7
- "@dbx-tools/genie": "0.1.100",
8
- "@dbx-tools/genie-shared": "0.1.100",
9
- "@dbx-tools/model": "0.1.100",
10
- "@dbx-tools/shared": "0.1.100",
6
+ "@dbx-tools/appkit-mastra-shared": "0.1.102",
7
+ "@dbx-tools/genie": "0.1.102",
8
+ "@dbx-tools/genie-shared": "0.1.102",
9
+ "@dbx-tools/model": "0.1.102",
10
+ "@dbx-tools/shared": "0.1.102",
11
11
  "@mastra/ai-sdk": "^1.6.0",
12
12
  "@mastra/core": "^1.47.0",
13
13
  "@mastra/express": "^1.4.2",