Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,17 @@ pnpm check:fix # Auto-fix with Biome
pnpm typecheck # TypeScript type checking across all packages
```

### AppKit CLI
When using the published SDK or running from the monorepo (after `pnpm build`), the `appkit` CLI is available:

```bash
npx appkit plugin sync --write # Sync plugin manifests into appkit.plugins.json
npx appkit plugin create # Scaffold a new plugin (interactive, uses @clack/prompts)
npx appkit plugin validate # Validate manifest(s) against the JSON schema
npx appkit plugin list # List plugins (from appkit.plugins.json or --dir)
npx appkit plugin add-resource # Add a resource requirement to a plugin (interactive)
```

### Deployment
```bash
pnpm pack:sdk # Package SDK for deployment
Expand Down
90 changes: 89 additions & 1 deletion docs/docs/plugins.md
Original file line number Diff line number Diff line change
Expand Up @@ -205,9 +205,97 @@ const AppKit = await createApp({

For complete configuration options, see [`createApp`](api/appkit/Function.createApp.md).

## Plugin CLI

AppKit includes a CLI for managing plugins. All commands are available under `npx appkit plugin`.

### Create a plugin

Scaffold a new plugin interactively:

```bash
npx appkit plugin create
```

The wizard walks you through:
- **Placement**: In your repository (e.g. `plugins/my-plugin`) or as a standalone package
- **Metadata**: Name, display name, description
- **Resources**: Which Databricks resources the plugin needs (SQL Warehouse, Secret, etc.) and whether each is required or optional
- **Optional fields**: Author, version, license

The command generates a complete plugin scaffold with `manifest.json`, TypeScript class, and barrel exports — ready to register in your app.

### Sync plugin manifests

Scan your project for plugins and generate `appkit.plugins.json`:

```bash
npx appkit plugin sync --write
```

This discovers plugin manifests from installed packages and local imports, then writes a consolidated manifest used by deployment tooling. Plugins referenced in your `createApp({ plugins: [...] })` call are automatically marked as required.

Use the `--silent` flag in build hooks to suppress output:

```json
{
"scripts": {
"sync": "appkit plugin sync --write --silent",
"predev": "npm run sync",
"prebuild": "npm run sync"
}
}
```

### Validate manifests

Check plugin manifests against the JSON schema:

```bash
# Validate manifest.json in the current directory
npx appkit plugin validate

# Validate specific files or directories
npx appkit plugin validate plugins/my-plugin appkit.plugins.json
```

The validator auto-detects whether a file is a plugin manifest or a template manifest (from `$schema`) and reports errors with humanized paths and expected values.

### List plugins

View registered plugins from `appkit.plugins.json` or scan a directory:

```bash
# From appkit.plugins.json (default)
npx appkit plugin list

# Scan a directory for plugin folders
npx appkit plugin list --dir plugins/

# JSON output for scripting
npx appkit plugin list --json
```

### Add a resource to a plugin

Interactively add a new resource requirement to an existing plugin manifest:

```bash
npx appkit plugin add-resource

# Or specify the plugin directory
npx appkit plugin add-resource --path plugins/my-plugin
```

## Creating custom plugins

If you need custom API routes or background logic, implement an AppKit plugin.
If you need custom API routes or background logic, implement an AppKit plugin. The fastest way is to use the CLI:

```bash
npx appkit plugin create
```

For a deeper understanding of the plugin structure, read on.

### Basic plugin example

Expand Down
157 changes: 135 additions & 22 deletions docs/static/schemas/plugin-manifest.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -168,24 +168,6 @@
"enum": ["CAN_USE"],
"description": "Permission for Databricks App resources"
},
"resourcePermission": {
"type": "string",
"description": "Permission level required for the resource. Valid values depend on resource type.",
"oneOf": [
{ "$ref": "#/$defs/secretPermission" },
{ "$ref": "#/$defs/jobPermission" },
{ "$ref": "#/$defs/sqlWarehousePermission" },
{ "$ref": "#/$defs/servingEndpointPermission" },
{ "$ref": "#/$defs/volumePermission" },
{ "$ref": "#/$defs/vectorSearchIndexPermission" },
{ "$ref": "#/$defs/ucFunctionPermission" },
{ "$ref": "#/$defs/ucConnectionPermission" },
{ "$ref": "#/$defs/databasePermission" },
{ "$ref": "#/$defs/genieSpacePermission" },
{ "$ref": "#/$defs/experimentPermission" },
{ "$ref": "#/$defs/appPermission" }
]
},
"resourceFieldEntry": {
"type": "object",
"required": ["env"],
Expand Down Expand Up @@ -219,13 +201,13 @@
},
"alias": {
"type": "string",
"pattern": "^[a-z][a-zA-Z0-9_]*$",
"minLength": 1,
"description": "Human-readable label for UI/display only. Deduplication uses resourceKey, not alias.",
"examples": ["SQL Warehouse", "Secret", "Vector search index"]
},
"resourceKey": {
"type": "string",
"pattern": "^[a-z][a-zA-Z0-9_]*$",
"pattern": "^[a-z][a-z0-9-]*$",
"description": "Stable key for machine use: deduplication, env naming, composite keys, app.yaml. Required for registry lookup.",
"examples": ["sql-warehouse", "database", "secret"]
},
Expand All @@ -235,7 +217,8 @@
"description": "Human-readable description of why this resource is needed"
},
"permission": {
"$ref": "#/$defs/resourcePermission"
"type": "string",
"description": "Required permission level. Validated per resource type by the allOf/if-then rules below."
},
"fields": {
"type": "object",
Expand All @@ -246,7 +229,137 @@
"description": "Map of field name to env and optional description. Single-value types use one key (e.g. id); multi-value (database, secret) use multiple (e.g. instance_name, database_name or scope, key)."
}
},
"additionalProperties": false
"additionalProperties": false,
"allOf": [
{
"if": {
"properties": { "type": { "const": "secret" } },
"required": ["type"]
},
"then": {
"properties": {
"permission": { "$ref": "#/$defs/secretPermission" }
}
}
},
{
"if": {
"properties": { "type": { "const": "job" } },
"required": ["type"]
},
"then": {
"properties": { "permission": { "$ref": "#/$defs/jobPermission" } }
}
},
{
"if": {
"properties": { "type": { "const": "sql_warehouse" } },
"required": ["type"]
},
"then": {
"properties": {
"permission": { "$ref": "#/$defs/sqlWarehousePermission" }
}
}
},
{
"if": {
"properties": { "type": { "const": "serving_endpoint" } },
"required": ["type"]
},
"then": {
"properties": {
"permission": { "$ref": "#/$defs/servingEndpointPermission" }
}
}
},
{
"if": {
"properties": { "type": { "const": "volume" } },
"required": ["type"]
},
"then": {
"properties": {
"permission": { "$ref": "#/$defs/volumePermission" }
}
}
},
{
"if": {
"properties": { "type": { "const": "vector_search_index" } },
"required": ["type"]
},
"then": {
"properties": {
"permission": { "$ref": "#/$defs/vectorSearchIndexPermission" }
}
}
},
{
"if": {
"properties": { "type": { "const": "uc_function" } },
"required": ["type"]
},
"then": {
"properties": {
"permission": { "$ref": "#/$defs/ucFunctionPermission" }
}
}
},
{
"if": {
"properties": { "type": { "const": "uc_connection" } },
"required": ["type"]
},
"then": {
"properties": {
"permission": { "$ref": "#/$defs/ucConnectionPermission" }
}
}
},
{
"if": {
"properties": { "type": { "const": "database" } },
"required": ["type"]
},
"then": {
"properties": {
"permission": { "$ref": "#/$defs/databasePermission" }
}
}
},
{
"if": {
"properties": { "type": { "const": "genie_space" } },
"required": ["type"]
},
"then": {
"properties": {
"permission": { "$ref": "#/$defs/genieSpacePermission" }
}
}
},
{
"if": {
"properties": { "type": { "const": "experiment" } },
"required": ["type"]
},
"then": {
"properties": {
"permission": { "$ref": "#/$defs/experimentPermission" }
}
}
},
{
"if": {
"properties": { "type": { "const": "app" } },
"required": ["type"]
},
"then": {
"properties": { "permission": { "$ref": "#/$defs/appPermission" } }
}
}
]
},
"configSchemaProperty": {
"type": "object",
Expand Down
82 changes: 3 additions & 79 deletions docs/static/schemas/template-plugins.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -91,89 +91,13 @@
"additionalProperties": false
},
"resourceType": {
"type": "string",
"enum": [
"secret",
"job",
"sql_warehouse",
"serving_endpoint",
"volume",
"vector_search_index",
"uc_function",
"uc_connection",
"database",
"genie_space",
"experiment",
"app"
],
"description": "Type of Databricks resource"
},
"resourcePermission": {
"type": "string",
"description": "Permission level required for the resource. Valid values depend on resource type.",
"examples": ["CAN_USE", "CAN_MANAGE", "READ", "WRITE", "EXECUTE"]
"$ref": "plugin-manifest.schema.json#/$defs/resourceType"
},
"resourceFieldEntry": {
"type": "object",
"required": ["env"],
"properties": {
"env": {
"type": "string",
"pattern": "^[A-Z][A-Z0-9_]*$",
"description": "Environment variable name for this field",
"examples": ["DATABRICKS_CACHE_INSTANCE", "SECRET_SCOPE"]
},
"description": {
"type": "string",
"description": "Human-readable description for this field"
}
},
"additionalProperties": false
"$ref": "plugin-manifest.schema.json#/$defs/resourceFieldEntry"
},
"resourceRequirement": {
"type": "object",
"required": [
"type",
"alias",
"resourceKey",
"description",
"permission",
"fields"
],
"properties": {
"type": {
"$ref": "#/$defs/resourceType"
},
"alias": {
"type": "string",
"pattern": "^[a-z][a-zA-Z0-9_]*$",
"description": "Unique alias for this resource within the plugin (UI/display)",
"examples": ["SQL Warehouse", "Secret", "Vector search index"]
},
"resourceKey": {
"type": "string",
"pattern": "^[a-z][a-zA-Z0-9_]*$",
"description": "Stable key for machine use (env naming, composite keys, app.yaml).",
"examples": ["sql-warehouse", "database", "secret"]
},
"description": {
"type": "string",
"minLength": 1,
"description": "Human-readable description of why this resource is needed"
},
"permission": {
"$ref": "#/$defs/resourcePermission"
},
"fields": {
"type": "object",
"additionalProperties": {
"$ref": "#/$defs/resourceFieldEntry"
},
"minProperties": 1,
"description": "Map of field name to env and optional description. Single-value types use one key (e.g. id); multi-value (database, secret) use multiple (e.g. instance_name, database_name or scope, key)."
}
},
"additionalProperties": false
"$ref": "plugin-manifest.schema.json#/$defs/resourceRequirement"
}
}
}
Loading