@dbx-tools/appkit 0.9.119 → 0.9.121
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/index.ts +3 -1
- package/lib/index.d.ts +3 -1
- package/lib/index.js +3 -2
- package/lib/src/appkit.js +3 -1
- package/lib/src/cache-storage.js +3 -3
- package/lib/src/files-cache.js +2 -2
- package/lib/src/identity.d.ts +12 -0
- package/lib/src/identity.js +69 -3
- package/lib/src/interceptor.js +6 -3
- package/lib/src/lakebase-resolver.d.ts +14 -0
- package/lib/src/lakebase-resolver.js +22 -3
- package/lib/src/migration.js +6 -7
- package/lib/src/plugin-execution.js +3 -3
- package/lib/src/provision.js +2 -2
- package/lib/src/tool-operations.d.ts +33 -0
- package/lib/src/tool-operations.js +43 -0
- package/lib/tsconfig.tsbuildinfo +1 -1
- package/package.json +17 -9
- package/src/appkit.ts +2 -0
- package/src/cache-storage.ts +2 -2
- package/src/files-cache.ts +1 -1
- package/src/identity.ts +67 -2
- package/src/interceptor.ts +5 -2
- package/src/lakebase-resolver.ts +23 -1
- package/src/migration.ts +3 -5
- package/src/plugin-execution.ts +2 -2
- package/src/provision.ts +1 -1
- package/src/tool-operations.ts +80 -0
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/** Zod-owned operation contracts converted into native AppKit agent tools. */
|
|
2
|
+
import { defineTool } from "@databricks/appkit/beta";
|
|
3
|
+
/** Preserve operation inference while declaring an exported operation map. */
|
|
4
|
+
export function defineOperations(operations) {
|
|
5
|
+
return operations;
|
|
6
|
+
}
|
|
7
|
+
/** Convert exported Zod operation contracts into a native AppKit tool registry. */
|
|
8
|
+
export function toolRegistryFromOperations(operations) {
|
|
9
|
+
return Object.fromEntries(Object.entries(operations).flatMap(([name, operation]) => {
|
|
10
|
+
const metadata = operation.schema.meta();
|
|
11
|
+
const tool = metadata?.appkitTool ?? {};
|
|
12
|
+
if (tool.agentTool !== true)
|
|
13
|
+
return [];
|
|
14
|
+
const effect = tool.effect ?? "write";
|
|
15
|
+
const approval = tool.approval === true || effect === "destructive";
|
|
16
|
+
const description = operation.schema.description?.trim();
|
|
17
|
+
if (!description) {
|
|
18
|
+
throw new TypeError(`AppKit operation ${name} requires a Zod schema description`);
|
|
19
|
+
}
|
|
20
|
+
return [
|
|
21
|
+
[
|
|
22
|
+
name,
|
|
23
|
+
defineTool({
|
|
24
|
+
description,
|
|
25
|
+
schema: operation.schema,
|
|
26
|
+
annotations: {
|
|
27
|
+
effect,
|
|
28
|
+
...(approval ? { destructive: true } : {}),
|
|
29
|
+
...(tool.requiresUserContext === undefined
|
|
30
|
+
? {}
|
|
31
|
+
: { requiresUserContext: tool.requiresUserContext }),
|
|
32
|
+
...(tool.idempotent === undefined ? {} : { idempotent: tool.idempotent }),
|
|
33
|
+
},
|
|
34
|
+
...(tool.autoInheritable === undefined
|
|
35
|
+
? {}
|
|
36
|
+
: { autoInheritable: tool.autoInheritable }),
|
|
37
|
+
execute: (input, signal) => operation.execute(operation.schema.parse(input), signal),
|
|
38
|
+
}),
|
|
39
|
+
],
|
|
40
|
+
];
|
|
41
|
+
}));
|
|
42
|
+
}
|
|
43
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidG9vbC1vcGVyYXRpb25zLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL3Rvb2wtb3BlcmF0aW9ucy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSw4RUFBOEU7QUFFOUUsT0FBTyxFQUFFLFVBQVUsRUFBcUIsTUFBTSx5QkFBeUIsQ0FBQztBQThCeEUsOEVBQThFO0FBQzlFLE1BQU0sVUFBVSxnQkFBZ0IsQ0FFOUIsVUFFRDtJQUNDLE9BQU8sVUFBVSxDQUFDO0FBQ3BCLENBQUM7QUFFRCxtRkFBbUY7QUFDbkYsTUFBTSxVQUFVLDBCQUEwQixDQUN4QyxVQUF5RTtJQUV6RSxPQUFPLE1BQU0sQ0FBQyxXQUFXLENBQ3ZCLE1BQU0sQ0FBQyxPQUFPLENBQUMsVUFBVSxDQUFDLENBQUMsT0FBTyxDQUFDLENBQUMsQ0FBQyxJQUFJLEVBQUUsU0FBUyxDQUFDLEVBQUUsRUFBRTtRQUN2RCxNQUFNLFFBQVEsR0FBRyxTQUFTLENBQUMsTUFBTSxDQUFDLElBQUksRUFBMkMsQ0FBQztRQUNsRixNQUFNLElBQUksR0FBRyxRQUFRLEVBQUUsVUFBVSxJQUFJLEVBQUUsQ0FBQztRQUN4QyxJQUFJLElBQUksQ0FBQyxTQUFTLEtBQUssSUFBSTtZQUFFLE9BQU8sRUFBRSxDQUFDO1FBQ3ZDLE1BQU0sTUFBTSxHQUFHLElBQUksQ0FBQyxNQUFNLElBQUksT0FBTyxDQUFDO1FBQ3RDLE1BQU0sUUFBUSxHQUFHLElBQUksQ0FBQyxRQUFRLEtBQUssSUFBSSxJQUFJLE1BQU0sS0FBSyxhQUFhLENBQUM7UUFDcEUsTUFBTSxXQUFXLEdBQUcsU0FBUyxDQUFDLE1BQU0sQ0FBQyxXQUFXLEVBQUUsSUFBSSxFQUFFLENBQUM7UUFDekQsSUFBSSxDQUFDLFdBQVcsRUFBRSxDQUFDO1lBQ2pCLE1BQU0sSUFBSSxTQUFTLENBQUMsb0JBQW9CLElBQUksb0NBQW9DLENBQUMsQ0FBQztRQUNwRixDQUFDO1FBQ0QsT0FBTztZQUNMO2dCQUNFLElBQUk7Z0JBQ0osVUFBVSxDQUFDO29CQUNULFdBQVc7b0JBQ1gsTUFBTSxFQUFFLFNBQVMsQ0FBQyxNQUFNO29CQUN4QixXQUFXLEVBQUU7d0JBQ1gsTUFBTTt3QkFDTixHQUFHLENBQUMsUUFBUSxDQUFDLENBQUMsQ0FBQyxFQUFFLFdBQVcsRUFBRSxJQUFJLEVBQUUsQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDO3dCQUMxQyxHQUFHLENBQUMsSUFBSSxDQUFDLG1CQUFtQixLQUFLLFNBQVM7NEJBQ3hDLENBQUMsQ0FBQyxFQUFFOzRCQUNKLENBQUMsQ0FBQyxFQUFFLG1CQUFtQixFQUFFLElBQUksQ0FBQyxtQkFBbUIsRUFBRSxDQUFDO3dCQUN0RCxHQUFHLENBQUMsSUFBSSxDQUFDLFVBQVUsS0FBSyxTQUFTLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxDQUFDLENBQUMsRUFBRSxVQUFVLEVBQUUsSUFBSSxDQUFDLFVBQVUsRUFBRSxDQUFDO3FCQUMxRTtvQkFDRCxHQUFHLENBQUMsSUFBSSxDQUFDLGVBQWUsS0FBSyxTQUFTO3dCQUNwQyxDQUFDLENBQUMsRUFBRTt3QkFDSixDQUFDLENBQUMsRUFBRSxlQUFlLEVBQUUsSUFBSSxDQUFDLGVBQWUsRUFBRSxDQUFDO29CQUM5QyxPQUFPLEVBQUUsQ0FBQyxLQUFLLEVBQUUsTUFBTSxFQUFFLEVBQUUsQ0FBQyxTQUFTLENBQUMsT0FBTyxDQUFDLFNBQVMsQ0FBQyxNQUFNLENBQUMsS0FBSyxDQUFDLEtBQUssQ0FBQyxFQUFFLE1BQU0sQ0FBQztpQkFDckYsQ0FBQzthQUNIO1NBQ0YsQ0FBQztJQUNKLENBQUMsQ0FBQyxDQUNILENBQUM7QUFDSixDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLyoqIFpvZC1vd25lZCBvcGVyYXRpb24gY29udHJhY3RzIGNvbnZlcnRlZCBpbnRvIG5hdGl2ZSBBcHBLaXQgYWdlbnQgdG9vbHMuICovXG5cbmltcG9ydCB7IGRlZmluZVRvb2wsIHR5cGUgVG9vbFJlZ2lzdHJ5IH0gZnJvbSBcIkBkYXRhYnJpY2tzL2FwcGtpdC9iZXRhXCI7XG5pbXBvcnQgeyB6IH0gZnJvbSBcInpvZFwiO1xuXG4vKiogTWV0YWRhdGEgc3RvcmVkIHVuZGVyIGBhcHBraXRUb29sYCBieSBhIFpvZCBzY2hlbWEncyBgLm1ldGEoKWAgdmFsdWUuICovXG5leHBvcnQgaW50ZXJmYWNlIEFwcEtpdFRvb2xNZXRhIHtcbiAgLyoqIE9wdCBpbiB0byBleHBvc2luZyB0aGlzIG9wZXJhdGlvbiBhcyBhbiBhZ2VudCB0b29sLiAqL1xuICByZWFkb25seSBhZ2VudFRvb2w/OiBib29sZWFuO1xuICAvKiogVG9vbCBzaWRlLWVmZmVjdCBjbGFzc2lmaWNhdGlvbi4gRGVmYXVsdHMgdG8gYHdyaXRlYC4gKi9cbiAgcmVhZG9ubHkgZWZmZWN0PzogXCJyZWFkXCIgfCBcIndyaXRlXCIgfCBcImRlc3RydWN0aXZlXCI7XG4gIC8qKiBSZXF1aXJlIGh1bWFuIGFwcHJvdmFsIGJlZm9yZSBleGVjdXRpb24uICovXG4gIHJlYWRvbmx5IGFwcHJvdmFsPzogYm9vbGVhbjtcbiAgLyoqIFdoZXRoZXIgZXhlY3V0aW9uIHJlcXVpcmVzIGFuIGF1dGhlbnRpY2F0ZWQgdXNlciBjb250ZXh0LiAqL1xuICByZWFkb25seSByZXF1aXJlc1VzZXJDb250ZXh0PzogYm9vbGVhbjtcbiAgLyoqIFdoZXRoZXIgcmVwZWF0ZWQgY2FsbHMgd2l0aCB0aGUgc2FtZSBpbnB1dCBhcmUgc2FmZS4gKi9cbiAgcmVhZG9ubHkgaWRlbXBvdGVudD86IGJvb2xlYW47XG4gIC8qKiBXaGV0aGVyIEFwcEtpdCBtYXkgaW5oZXJpdCB0aGUgdG9vbCBhdXRvbWF0aWNhbGx5LiAqL1xuICByZWFkb25seSBhdXRvSW5oZXJpdGFibGU/OiBib29sZWFuO1xufVxuXG4vKiogU2hhcGUgcGxhY2VkIGluIGEgWm9kIHNjaGVtYSdzIGAubWV0YSgpYCB2YWx1ZS4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgQXBwS2l0T3BlcmF0aW9uU2NoZW1hTWV0YSB7XG4gIHJlYWRvbmx5IGFwcGtpdFRvb2w/OiBBcHBLaXRUb29sTWV0YTtcbn1cblxuLyoqIE9uZSB0eXBlZCBvcGVyYXRpb24gYmFja2VkIGJ5IGEgc2luZ2xlIFpvZCBpbnB1dCBjb250cmFjdC4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgQXBwS2l0T3BlcmF0aW9uPFRTY2hlbWEgZXh0ZW5kcyB6LlpvZFR5cGUgPSB6LlpvZFR5cGUsIFRSZXN1bHQgPSB1bmtub3duPiB7XG4gIHJlYWRvbmx5IHNjaGVtYTogVFNjaGVtYTtcbiAgcmVhZG9ubHkgZXhlY3V0ZTogKGlucHV0OiB6Lm91dHB1dDxUU2NoZW1hPiwgc2lnbmFsPzogQWJvcnRTaWduYWwpID0+IFRSZXN1bHQgfCBQcm9taXNlPFRSZXN1bHQ+O1xufVxuXG4vKiogUHJlc2VydmUgb3BlcmF0aW9uIGluZmVyZW5jZSB3aGlsZSBkZWNsYXJpbmcgYW4gZXhwb3J0ZWQgb3BlcmF0aW9uIG1hcC4gKi9cbmV4cG9ydCBmdW5jdGlvbiBkZWZpbmVPcGVyYXRpb25zPFxuICBjb25zdCBUU2NoZW1hcyBleHRlbmRzIFJlYWRvbmx5PFJlY29yZDxzdHJpbmcsIHouWm9kVHlwZT4+LFxuPihvcGVyYXRpb25zOiB7XG4gIHJlYWRvbmx5IFtUTmFtZSBpbiBrZXlvZiBUU2NoZW1hc106IEFwcEtpdE9wZXJhdGlvbjxUU2NoZW1hc1tUTmFtZV0sIHVua25vd24+O1xufSk6IHR5cGVvZiBvcGVyYXRpb25zIHtcbiAgcmV0dXJuIG9wZXJhdGlvbnM7XG59XG5cbi8qKiBDb252ZXJ0IGV4cG9ydGVkIFpvZCBvcGVyYXRpb24gY29udHJhY3RzIGludG8gYSBuYXRpdmUgQXBwS2l0IHRvb2wgcmVnaXN0cnkuICovXG5leHBvcnQgZnVuY3Rpb24gdG9vbFJlZ2lzdHJ5RnJvbU9wZXJhdGlvbnMoXG4gIG9wZXJhdGlvbnM6IFJlYWRvbmx5PFJlY29yZDxzdHJpbmcsIEFwcEtpdE9wZXJhdGlvbjx6LlpvZFR5cGUsIHVua25vd24+Pj4sXG4pOiBUb29sUmVnaXN0cnkge1xuICByZXR1cm4gT2JqZWN0LmZyb21FbnRyaWVzKFxuICAgIE9iamVjdC5lbnRyaWVzKG9wZXJhdGlvbnMpLmZsYXRNYXAoKFtuYW1lLCBvcGVyYXRpb25dKSA9PiB7XG4gICAgICBjb25zdCBtZXRhZGF0YSA9IG9wZXJhdGlvbi5zY2hlbWEubWV0YSgpIGFzIEFwcEtpdE9wZXJhdGlvblNjaGVtYU1ldGEgfCB1bmRlZmluZWQ7XG4gICAgICBjb25zdCB0b29sID0gbWV0YWRhdGE/LmFwcGtpdFRvb2wgPz8ge307XG4gICAgICBpZiAodG9vbC5hZ2VudFRvb2wgIT09IHRydWUpIHJldHVybiBbXTtcbiAgICAgIGNvbnN0IGVmZmVjdCA9IHRvb2wuZWZmZWN0ID8/IFwid3JpdGVcIjtcbiAgICAgIGNvbnN0IGFwcHJvdmFsID0gdG9vbC5hcHByb3ZhbCA9PT0gdHJ1ZSB8fCBlZmZlY3QgPT09IFwiZGVzdHJ1Y3RpdmVcIjtcbiAgICAgIGNvbnN0IGRlc2NyaXB0aW9uID0gb3BlcmF0aW9uLnNjaGVtYS5kZXNjcmlwdGlvbj8udHJpbSgpO1xuICAgICAgaWYgKCFkZXNjcmlwdGlvbikge1xuICAgICAgICB0aHJvdyBuZXcgVHlwZUVycm9yKGBBcHBLaXQgb3BlcmF0aW9uICR7bmFtZX0gcmVxdWlyZXMgYSBab2Qgc2NoZW1hIGRlc2NyaXB0aW9uYCk7XG4gICAgICB9XG4gICAgICByZXR1cm4gW1xuICAgICAgICBbXG4gICAgICAgICAgbmFtZSxcbiAgICAgICAgICBkZWZpbmVUb29sKHtcbiAgICAgICAgICAgIGRlc2NyaXB0aW9uLFxuICAgICAgICAgICAgc2NoZW1hOiBvcGVyYXRpb24uc2NoZW1hLFxuICAgICAgICAgICAgYW5ub3RhdGlvbnM6IHtcbiAgICAgICAgICAgICAgZWZmZWN0LFxuICAgICAgICAgICAgICAuLi4oYXBwcm92YWwgPyB7IGRlc3RydWN0aXZlOiB0cnVlIH0gOiB7fSksXG4gICAgICAgICAgICAgIC4uLih0b29sLnJlcXVpcmVzVXNlckNvbnRleHQgPT09IHVuZGVmaW5lZFxuICAgICAgICAgICAgICAgID8ge31cbiAgICAgICAgICAgICAgICA6IHsgcmVxdWlyZXNVc2VyQ29udGV4dDogdG9vbC5yZXF1aXJlc1VzZXJDb250ZXh0IH0pLFxuICAgICAgICAgICAgICAuLi4odG9vbC5pZGVtcG90ZW50ID09PSB1bmRlZmluZWQgPyB7fSA6IHsgaWRlbXBvdGVudDogdG9vbC5pZGVtcG90ZW50IH0pLFxuICAgICAgICAgICAgfSxcbiAgICAgICAgICAgIC4uLih0b29sLmF1dG9Jbmhlcml0YWJsZSA9PT0gdW5kZWZpbmVkXG4gICAgICAgICAgICAgID8ge31cbiAgICAgICAgICAgICAgOiB7IGF1dG9Jbmhlcml0YWJsZTogdG9vbC5hdXRvSW5oZXJpdGFibGUgfSksXG4gICAgICAgICAgICBleGVjdXRlOiAoaW5wdXQsIHNpZ25hbCkgPT4gb3BlcmF0aW9uLmV4ZWN1dGUob3BlcmF0aW9uLnNjaGVtYS5wYXJzZShpbnB1dCksIHNpZ25hbCksXG4gICAgICAgICAgfSksXG4gICAgICAgIF0sXG4gICAgICBdO1xuICAgIH0pLFxuICApO1xufVxuIl19
|
package/lib/tsconfig.tsbuildinfo
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"root":["../src/_auto-configure.ts","../src/appkit.ts","../src/brand-context.ts","../src/bundle.ts","../src/cache-storage.ts","../src/child-process.ts","../src/databricks.ts","../src/files-cache.ts","../src/identity.ts","../src/interceptor.ts","../src/lakebase-resolver.ts","../src/migration.ts","../src/pgaddress.ts","../src/plugin-execution.ts","../src/plugin-registry.ts","../src/provision.ts","../src/request-tags.ts","../src/tool-provider.ts","../src/toolkit-entries.ts","../index.ts"],"version":"5.9.3"}
|
|
1
|
+
{"root":["../src/_auto-configure.ts","../src/appkit.ts","../src/brand-context.ts","../src/bundle.ts","../src/cache-storage.ts","../src/child-process.ts","../src/databricks.ts","../src/files-cache.ts","../src/identity.ts","../src/interceptor.ts","../src/lakebase-resolver.ts","../src/migration.ts","../src/pgaddress.ts","../src/plugin-execution.ts","../src/plugin-registry.ts","../src/provision.ts","../src/request-tags.ts","../src/tool-operations.ts","../src/tool-provider.ts","../src/toolkit-entries.ts","../index.ts"],"version":"5.9.3"}
|
package/package.json
CHANGED
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
},
|
|
21
21
|
"devDependencies": {
|
|
22
22
|
"@databricks/appkit": "0.81.0",
|
|
23
|
-
"@dbx-tools/projen": "^0.9.
|
|
23
|
+
"@dbx-tools/projen": "^0.9.121",
|
|
24
24
|
"@types/node": "^24.6.0",
|
|
25
25
|
"typescript": "^5.9.3",
|
|
26
26
|
"vitest": "3.2.4"
|
|
@@ -30,13 +30,13 @@
|
|
|
30
30
|
},
|
|
31
31
|
"dependencies": {
|
|
32
32
|
"@databricks/sdk-experimental": "^0.17.0",
|
|
33
|
-
"@dbx-tools/auth": "^0.9.
|
|
34
|
-
"@dbx-tools/cache": "^0.9.
|
|
35
|
-
"@dbx-tools/core": "^0.9.
|
|
36
|
-
"@dbx-tools/lakebase": "^0.9.
|
|
37
|
-
"@dbx-tools/postgres": "^0.9.
|
|
38
|
-
"@dbx-tools/shared-core": "^0.9.
|
|
39
|
-
"@dbx-tools/shared-fs": "^0.9.
|
|
33
|
+
"@dbx-tools/auth": "^0.9.121",
|
|
34
|
+
"@dbx-tools/cache": "^0.9.121",
|
|
35
|
+
"@dbx-tools/core": "^0.9.121",
|
|
36
|
+
"@dbx-tools/lakebase": "^0.9.121",
|
|
37
|
+
"@dbx-tools/postgres": "^0.9.121",
|
|
38
|
+
"@dbx-tools/shared-core": "^0.9.121",
|
|
39
|
+
"@dbx-tools/shared-fs": "^0.9.121",
|
|
40
40
|
"zod": "4.3.6"
|
|
41
41
|
},
|
|
42
42
|
"main": "./lib/index.js",
|
|
@@ -114,6 +114,10 @@
|
|
|
114
114
|
"types": "./lib/src/request-tags.d.ts",
|
|
115
115
|
"default": "./lib/src/request-tags.js"
|
|
116
116
|
},
|
|
117
|
+
"./tool-operations": {
|
|
118
|
+
"types": "./lib/src/tool-operations.d.ts",
|
|
119
|
+
"default": "./lib/src/tool-operations.js"
|
|
120
|
+
},
|
|
117
121
|
"./tool-provider": {
|
|
118
122
|
"types": "./lib/src/tool-provider.d.ts",
|
|
119
123
|
"default": "./lib/src/tool-provider.js"
|
|
@@ -129,7 +133,7 @@
|
|
|
129
133
|
"./package.json": "./package.json"
|
|
130
134
|
}
|
|
131
135
|
},
|
|
132
|
-
"version": "0.9.
|
|
136
|
+
"version": "0.9.121",
|
|
133
137
|
"types": "./lib/index.d.ts",
|
|
134
138
|
"dbxToolsConfig": {
|
|
135
139
|
"tags": [
|
|
@@ -206,6 +210,10 @@
|
|
|
206
210
|
"types": "./lib/src/request-tags.d.ts",
|
|
207
211
|
"default": "./lib/src/request-tags.js"
|
|
208
212
|
},
|
|
213
|
+
"./tool-operations": {
|
|
214
|
+
"types": "./lib/src/tool-operations.d.ts",
|
|
215
|
+
"default": "./lib/src/tool-operations.js"
|
|
216
|
+
},
|
|
209
217
|
"./tool-provider": {
|
|
210
218
|
"types": "./lib/src/tool-provider.d.ts",
|
|
211
219
|
"default": "./lib/src/tool-provider.js"
|
package/src/appkit.ts
CHANGED
|
@@ -48,6 +48,7 @@ import {
|
|
|
48
48
|
lifecycleBridge,
|
|
49
49
|
type ResolvedAppEnv,
|
|
50
50
|
} from "./interceptor.ts";
|
|
51
|
+
import { installAsUserDevelopmentPatch } from "./identity.ts";
|
|
51
52
|
import { applyLakebaseEnv, type LakebaseConnection } from "./lakebase-resolver.ts";
|
|
52
53
|
import { provisionCacheSchema } from "./provision.ts";
|
|
53
54
|
|
|
@@ -298,6 +299,7 @@ function interceptorList(interceptor: CreateAppConfig["interceptor"]): Intercept
|
|
|
298
299
|
export async function createApp<T extends AppKitPlugins>(
|
|
299
300
|
config?: CreateAppConfig<T>,
|
|
300
301
|
): Promise<PluginMap<T>> {
|
|
302
|
+
installAsUserDevelopmentPatch();
|
|
301
303
|
const plugins = pluginNames(config);
|
|
302
304
|
const interceptors = interceptorList(config?.interceptor);
|
|
303
305
|
logger.debug("createApp: start", {
|
package/src/cache-storage.ts
CHANGED
|
@@ -217,7 +217,7 @@ export function loadPersistentStorage(): PersistentStorageConstructor | undefine
|
|
|
217
217
|
return loaded;
|
|
218
218
|
} catch (err) {
|
|
219
219
|
logger.debug("soft persistent cache skipped (PersistentStorage unavailable)", {
|
|
220
|
-
error: errorUtils.
|
|
220
|
+
error: errorUtils.toError(err),
|
|
221
221
|
});
|
|
222
222
|
persistentStorageCtor = undefined;
|
|
223
223
|
return undefined;
|
|
@@ -330,7 +330,7 @@ async function createPersistentStorage(
|
|
|
330
330
|
return storage;
|
|
331
331
|
} catch (err) {
|
|
332
332
|
logger.debug("soft persistent cache unavailable", {
|
|
333
|
-
error: errorUtils.
|
|
333
|
+
error: errorUtils.toError(err),
|
|
334
334
|
});
|
|
335
335
|
if (pool) {
|
|
336
336
|
await pool.end().catch(() => {});
|
package/src/files-cache.ts
CHANGED
|
@@ -247,7 +247,7 @@ export class FilesCacheManager {
|
|
|
247
247
|
state.group.timer = undefined;
|
|
248
248
|
void this.flushInvalidations(state.group).catch((error) => {
|
|
249
249
|
logger.warn("filesystem cache invalidation batch failed", {
|
|
250
|
-
error: errorUtils.
|
|
250
|
+
error: errorUtils.toError(error),
|
|
251
251
|
});
|
|
252
252
|
});
|
|
253
253
|
}, this.config.invalidationBatchMs);
|
package/src/identity.ts
CHANGED
|
@@ -52,9 +52,9 @@
|
|
|
52
52
|
* @module
|
|
53
53
|
*/
|
|
54
54
|
|
|
55
|
-
import { ConfigurationError } from "@databricks/appkit";
|
|
55
|
+
import { ConfigurationError, Plugin } from "@databricks/appkit";
|
|
56
56
|
import { configUtils } from "@dbx-tools/core";
|
|
57
|
-
import { stringUtils, token } from "@dbx-tools/shared-core";
|
|
57
|
+
import { log, stringUtils, token } from "@dbx-tools/shared-core";
|
|
58
58
|
|
|
59
59
|
/**
|
|
60
60
|
* Header the Databricks Apps front door forwards the caller's OBO token on.
|
|
@@ -158,3 +158,68 @@ export function useServicePrincipal(mode: IdentityMode, req?: HeaderBearing): bo
|
|
|
158
158
|
if (mode === "user") return false;
|
|
159
159
|
return requestAccessToken(req) === undefined;
|
|
160
160
|
}
|
|
161
|
+
|
|
162
|
+
const logger = log.logger("appkit");
|
|
163
|
+
const skippedAsUser = new Set<string>();
|
|
164
|
+
const AS_USER_SKIP_WARN = "Skipping user impersonation";
|
|
165
|
+
const AS_USER_PATCHED = Symbol.for("dbx-tools.appkit.asUserDevLog");
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* In development, keep AppKit `asUser` behavior. The first missing-token skip
|
|
169
|
+
* for a forwarded email or user id uses AppKit's own warn; later skips for
|
|
170
|
+
* that user, and every skip with no forwarded identity, are silent. Request
|
|
171
|
+
* objects are not a stable key: each plugin call typically has its own `req`.
|
|
172
|
+
*
|
|
173
|
+
* No-op outside development, when `Plugin.asUser` is missing or not a
|
|
174
|
+
* function, or if the prototype cannot be rewritten. Failures never throw:
|
|
175
|
+
* AppKit keeps its original `asUser`. Extra arguments are forwarded so a
|
|
176
|
+
* later signature change still reaches the original method.
|
|
177
|
+
*/
|
|
178
|
+
export function installAsUserDevelopmentPatch(): void {
|
|
179
|
+
if (process.env.NODE_ENV !== "development") return;
|
|
180
|
+
try {
|
|
181
|
+
const proto = Plugin.prototype as unknown as Record<PropertyKey, unknown>;
|
|
182
|
+
if (proto[AS_USER_PATCHED] === true) return;
|
|
183
|
+
const original = proto.asUser;
|
|
184
|
+
if (typeof original !== "function") return;
|
|
185
|
+
proto[AS_USER_PATCHED] = true;
|
|
186
|
+
proto.asUser = function patchedAsUser(this: unknown, req: unknown, ...rest: unknown[]) {
|
|
187
|
+
const headers = asHeaderBearing(req);
|
|
188
|
+
const args = [req, ...rest];
|
|
189
|
+
if (!headers || requestAccessToken(headers) !== undefined) {
|
|
190
|
+
return Function.prototype.apply.call(original, this, args);
|
|
191
|
+
}
|
|
192
|
+
return withAsUserSkipWarnSuppressed(headers, () =>
|
|
193
|
+
Function.prototype.apply.call(original, this, args),
|
|
194
|
+
);
|
|
195
|
+
};
|
|
196
|
+
} catch (error) {
|
|
197
|
+
logger.debug("asUser development patch skipped", { error: String(error) });
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
function asHeaderBearing(req: unknown): HeaderBearing | undefined {
|
|
202
|
+
if (req === null || typeof req !== "object") return undefined;
|
|
203
|
+
if (typeof (req as HeaderBearing).header !== "function") return undefined;
|
|
204
|
+
return req as HeaderBearing;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
function withAsUserSkipWarnSuppressed<T>(req: HeaderBearing, fn: () => T): T {
|
|
208
|
+
const user = requestUserEmail(req) ?? requestUserId(req);
|
|
209
|
+
if (user && !skippedAsUser.has(user)) {
|
|
210
|
+
skippedAsUser.add(user);
|
|
211
|
+
return fn();
|
|
212
|
+
}
|
|
213
|
+
const warn = console.warn;
|
|
214
|
+
console.warn = (...args: unknown[]) => {
|
|
215
|
+
if (!log.isLevelEnabled("debug") && args.some((value) => typeof value === "string" && value.includes(AS_USER_SKIP_WARN))) {
|
|
216
|
+
return;
|
|
217
|
+
}
|
|
218
|
+
warn.apply(console, args as Parameters<typeof console.warn>);
|
|
219
|
+
};
|
|
220
|
+
try {
|
|
221
|
+
return fn();
|
|
222
|
+
} finally {
|
|
223
|
+
console.warn = warn;
|
|
224
|
+
}
|
|
225
|
+
}
|
package/src/interceptor.ts
CHANGED
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
*/
|
|
21
21
|
|
|
22
22
|
import { Plugin, toPlugin, type BasePluginConfig, type PluginManifest } from "@databricks/appkit";
|
|
23
|
-
import { log } from "@dbx-tools/shared-core";
|
|
23
|
+
import { errorUtils, log } from "@dbx-tools/shared-core";
|
|
24
24
|
import type { LakebaseConnection } from "./lakebase-resolver.ts";
|
|
25
25
|
|
|
26
26
|
const logger = log.logger("interceptor");
|
|
@@ -120,7 +120,10 @@ export function createInterceptorContext(env: ResolvedAppEnv): InterceptorRuntim
|
|
|
120
120
|
} catch (error) {
|
|
121
121
|
// Match AppKit: a failing lifecycle handler is logged, never fatal, and
|
|
122
122
|
// never blocks its siblings.
|
|
123
|
-
logger.warn("lifecycle handler failed", {
|
|
123
|
+
logger.warn("lifecycle handler failed", {
|
|
124
|
+
event,
|
|
125
|
+
error: errorUtils.toError(error),
|
|
126
|
+
});
|
|
124
127
|
}
|
|
125
128
|
}
|
|
126
129
|
};
|
package/src/lakebase-resolver.ts
CHANGED
|
@@ -40,11 +40,13 @@ import {
|
|
|
40
40
|
ExecutionError,
|
|
41
41
|
getUsernameWithApiLookup,
|
|
42
42
|
getWorkspaceClient,
|
|
43
|
+
type LakebasePool,
|
|
43
44
|
ValidationError,
|
|
44
45
|
} from "@databricks/appkit";
|
|
45
46
|
import { configUtils, projectUtils } from "@dbx-tools/core";
|
|
46
47
|
import {
|
|
47
48
|
asyncUtils,
|
|
49
|
+
errorUtils,
|
|
48
50
|
log,
|
|
49
51
|
object,
|
|
50
52
|
options as sharedOptions,
|
|
@@ -113,6 +115,23 @@ export interface LakebaseConnection extends LakebaseConnectionInputs {
|
|
|
113
115
|
sslMode: SslMode;
|
|
114
116
|
}
|
|
115
117
|
|
|
118
|
+
/**
|
|
119
|
+
* Capture AppKit's existing service-principal Lakebase pool.
|
|
120
|
+
*
|
|
121
|
+
* Call this during `setup:complete`, outside any `asUser()` scope. AppKit's
|
|
122
|
+
* exported pool routes dynamically by request context, but its runtime
|
|
123
|
+
* `activePool()` method returns the already-created underlying pool selected in
|
|
124
|
+
* the current context. Capturing it once at startup pins application-owned
|
|
125
|
+
* storage to the service principal without creating another pool or connection.
|
|
126
|
+
*
|
|
127
|
+
* A future AppKit version may export a fixed pool directly; in that case the
|
|
128
|
+
* pool has no `activePool()` method and is returned unchanged.
|
|
129
|
+
*/
|
|
130
|
+
export function captureServicePrincipalPool(pool: LakebasePool): LakebasePool {
|
|
131
|
+
const routingPool = pool as LakebasePool & { activePool?: () => LakebasePool };
|
|
132
|
+
return routingPool.activePool?.call(pool) ?? pool;
|
|
133
|
+
}
|
|
134
|
+
|
|
116
135
|
/**
|
|
117
136
|
* Every field of the Lakebase REST payloads is modelled as optional and
|
|
118
137
|
* validated at the boundary rather than asserted with a cast: the pickers below
|
|
@@ -778,7 +797,10 @@ function isAlreadyExistsError(error: unknown): boolean {
|
|
|
778
797
|
* echo request context, so it goes to the log instead.
|
|
779
798
|
*/
|
|
780
799
|
function operationFailed(opName: string, opError: unknown): ExecutionError {
|
|
781
|
-
logger.error("autopg: operation failed", {
|
|
800
|
+
logger.error("autopg: operation failed", {
|
|
801
|
+
operation: opName,
|
|
802
|
+
error: errorUtils.toError(opError),
|
|
803
|
+
});
|
|
782
804
|
const code = object.isRecord(opError) ? (opError.code ?? opError.reason) : undefined;
|
|
783
805
|
return new ExecutionError(`Lakebase operation failed: ${opName}`, {
|
|
784
806
|
context: {
|
package/src/migration.ts
CHANGED
|
@@ -31,10 +31,8 @@ export function handleOwnershipMigrationError(
|
|
|
31
31
|
const key = `${options.scope}\0${message}`;
|
|
32
32
|
if (loggedOwnershipErrors.has(key)) return true;
|
|
33
33
|
loggedOwnershipErrors.add(key);
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
options.logger.warn(options.event, { error: message, ...options.context });
|
|
38
|
-
}
|
|
34
|
+
const error = errorUtils.toError(cause);
|
|
35
|
+
if (log.isLevelEnabled("debug")) options.logger.error(options.event, { error });
|
|
36
|
+
else options.logger.warn(options.event, { error, ...options.context });
|
|
39
37
|
return true;
|
|
40
38
|
}
|
package/src/plugin-execution.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/** AppKit-specific adapters over the shared intercepted-execution contract. */
|
|
2
2
|
|
|
3
3
|
import { ExecutionError } from "@databricks/appkit";
|
|
4
|
-
import { execution, type Logger } from "@dbx-tools/shared-core";
|
|
4
|
+
import { errorUtils, execution, type Logger } from "@dbx-tools/shared-core";
|
|
5
5
|
|
|
6
6
|
/** Inputs for one plugin operation routed through AppKit interceptors. */
|
|
7
7
|
export interface RunPluginExecutionOptions<T, Settings> {
|
|
@@ -32,7 +32,7 @@ export function runPluginExecution<T, Settings>(
|
|
|
32
32
|
options.logger.warn("execution-failed", {
|
|
33
33
|
operation: failure.operation,
|
|
34
34
|
status: failure.status,
|
|
35
|
-
error: failure.message,
|
|
35
|
+
error: errorUtils.toError(failure.message),
|
|
36
36
|
});
|
|
37
37
|
return new ExecutionError(`${options.plugin}: ${failure.operation} failed`, {
|
|
38
38
|
context: { operation: failure.operation, status: failure.status },
|
package/src/provision.ts
CHANGED
|
@@ -128,7 +128,7 @@ export async function provisionCacheSchema(
|
|
|
128
128
|
} catch (err) {
|
|
129
129
|
logger.warn("autopg: cache provisioning failed (continuing)", {
|
|
130
130
|
schema: CACHE_SCHEMA,
|
|
131
|
-
error: errorUtils.
|
|
131
|
+
error: errorUtils.toError(err),
|
|
132
132
|
});
|
|
133
133
|
} finally {
|
|
134
134
|
await pool.end().catch(() => {});
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/** Zod-owned operation contracts converted into native AppKit agent tools. */
|
|
2
|
+
|
|
3
|
+
import { defineTool, type ToolRegistry } from "@databricks/appkit/beta";
|
|
4
|
+
import { z } from "zod";
|
|
5
|
+
|
|
6
|
+
/** Metadata stored under `appkitTool` by a Zod schema's `.meta()` value. */
|
|
7
|
+
export interface AppKitToolMeta {
|
|
8
|
+
/** Opt in to exposing this operation as an agent tool. */
|
|
9
|
+
readonly agentTool?: boolean;
|
|
10
|
+
/** Tool side-effect classification. Defaults to `write`. */
|
|
11
|
+
readonly effect?: "read" | "write" | "destructive";
|
|
12
|
+
/** Require human approval before execution. */
|
|
13
|
+
readonly approval?: boolean;
|
|
14
|
+
/** Whether execution requires an authenticated user context. */
|
|
15
|
+
readonly requiresUserContext?: boolean;
|
|
16
|
+
/** Whether repeated calls with the same input are safe. */
|
|
17
|
+
readonly idempotent?: boolean;
|
|
18
|
+
/** Whether AppKit may inherit the tool automatically. */
|
|
19
|
+
readonly autoInheritable?: boolean;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/** Shape placed in a Zod schema's `.meta()` value. */
|
|
23
|
+
export interface AppKitOperationSchemaMeta {
|
|
24
|
+
readonly appkitTool?: AppKitToolMeta;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/** One typed operation backed by a single Zod input contract. */
|
|
28
|
+
export interface AppKitOperation<TSchema extends z.ZodType = z.ZodType, TResult = unknown> {
|
|
29
|
+
readonly schema: TSchema;
|
|
30
|
+
readonly execute: (input: z.output<TSchema>, signal?: AbortSignal) => TResult | Promise<TResult>;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/** Preserve operation inference while declaring an exported operation map. */
|
|
34
|
+
export function defineOperations<
|
|
35
|
+
const TSchemas extends Readonly<Record<string, z.ZodType>>,
|
|
36
|
+
>(operations: {
|
|
37
|
+
readonly [TName in keyof TSchemas]: AppKitOperation<TSchemas[TName], unknown>;
|
|
38
|
+
}): typeof operations {
|
|
39
|
+
return operations;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/** Convert exported Zod operation contracts into a native AppKit tool registry. */
|
|
43
|
+
export function toolRegistryFromOperations(
|
|
44
|
+
operations: Readonly<Record<string, AppKitOperation<z.ZodType, unknown>>>,
|
|
45
|
+
): ToolRegistry {
|
|
46
|
+
return Object.fromEntries(
|
|
47
|
+
Object.entries(operations).flatMap(([name, operation]) => {
|
|
48
|
+
const metadata = operation.schema.meta() as AppKitOperationSchemaMeta | undefined;
|
|
49
|
+
const tool = metadata?.appkitTool ?? {};
|
|
50
|
+
if (tool.agentTool !== true) return [];
|
|
51
|
+
const effect = tool.effect ?? "write";
|
|
52
|
+
const approval = tool.approval === true || effect === "destructive";
|
|
53
|
+
const description = operation.schema.description?.trim();
|
|
54
|
+
if (!description) {
|
|
55
|
+
throw new TypeError(`AppKit operation ${name} requires a Zod schema description`);
|
|
56
|
+
}
|
|
57
|
+
return [
|
|
58
|
+
[
|
|
59
|
+
name,
|
|
60
|
+
defineTool({
|
|
61
|
+
description,
|
|
62
|
+
schema: operation.schema,
|
|
63
|
+
annotations: {
|
|
64
|
+
effect,
|
|
65
|
+
...(approval ? { destructive: true } : {}),
|
|
66
|
+
...(tool.requiresUserContext === undefined
|
|
67
|
+
? {}
|
|
68
|
+
: { requiresUserContext: tool.requiresUserContext }),
|
|
69
|
+
...(tool.idempotent === undefined ? {} : { idempotent: tool.idempotent }),
|
|
70
|
+
},
|
|
71
|
+
...(tool.autoInheritable === undefined
|
|
72
|
+
? {}
|
|
73
|
+
: { autoInheritable: tool.autoInheritable }),
|
|
74
|
+
execute: (input, signal) => operation.execute(operation.schema.parse(input), signal),
|
|
75
|
+
}),
|
|
76
|
+
],
|
|
77
|
+
];
|
|
78
|
+
}),
|
|
79
|
+
);
|
|
80
|
+
}
|