Skip to content

Commit 01b90d3

Browse files
committed
chore: poc manifest 1
1 parent 832111b commit 01b90d3

12 files changed

Lines changed: 834 additions & 25 deletions

File tree

packages/appkit/src/plugins/analytics/manifest.json

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,33 @@
1414
"fields": {
1515
"id": {
1616
"env": "DATABRICKS_WAREHOUSE_ID",
17-
"description": "SQL Warehouse ID"
17+
"description": "SQL Warehouse ID",
18+
"setupHint": "Run 'databricks warehouses list' to find your SQL Warehouse ID.",
19+
"discovery": {
20+
"cliCommand": "databricks warehouses list --profile <PROFILE> -o json",
21+
"selectField": ".id",
22+
"displayField": ".name",
23+
"shortcut": "databricks experimental aitools tools get-default-warehouse --profile <PROFILE>"
24+
}
1825
}
1926
}
2027
}
2128
],
2229
"optional": []
2330
},
31+
"postScaffold": [
32+
{ "step": 1, "instruction": "Create SQL query files in config/queries/" },
33+
{ "step": 2, "instruction": "Run: npm run typegen", "blocking": true },
34+
{
35+
"step": 3,
36+
"instruction": "Read client/src/appKitTypes.d.ts for generated types"
37+
},
38+
{ "step": 4, "instruction": "Write UI code using the generated types" },
39+
{
40+
"step": 5,
41+
"instruction": "Update tests/smoke.spec.ts selectors for your app"
42+
}
43+
],
2444
"config": {
2545
"schema": {
2646
"type": "object",

packages/appkit/src/plugins/files/manifest.json

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,26 @@
1414
"fields": {
1515
"path": {
1616
"env": "DATABRICKS_VOLUME_FILES",
17-
"description": "Volume path for file storage (e.g. /Volumes/catalog/schema/volume_name)"
17+
"description": "Volume path for file storage (e.g. /Volumes/catalog/schema/volume_name)",
18+
"setupHint": "Provide the full volume path, e.g. /Volumes/catalog/schema/volume_name.",
19+
"discovery": {
20+
"cliCommand": "databricks volumes list <catalog>.<schema> --profile <PROFILE> -o json",
21+
"selectField": ".full_name",
22+
"displayField": ".name"
23+
}
1824
}
1925
}
2026
}
2127
],
2228
"optional": []
2329
},
30+
"postScaffold": [
31+
{
32+
"step": 1,
33+
"instruction": "Use the files plugin API to read/write volume files in your tRPC procedures"
34+
},
35+
{ "step": 2, "instruction": "Build UI for file upload/download using tRPC" }
36+
],
2437
"config": {
2538
"schema": {
2639
"type": "object",

packages/appkit/src/plugins/genie/manifest.json

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,28 @@
1313
"fields": {
1414
"id": {
1515
"env": "DATABRICKS_GENIE_SPACE_ID",
16-
"description": "Default Genie Space ID"
16+
"description": "Default Genie Space ID",
17+
"setupHint": "Find your Genie Space ID in the AI/BI Genie UI."
1718
}
1819
}
1920
}
2021
],
2122
"optional": []
2223
},
24+
"postScaffold": [
25+
{
26+
"step": 1,
27+
"instruction": "Configure Genie Space aliases in the plugin config spaces map"
28+
},
29+
{
30+
"step": 2,
31+
"instruction": "Build UI components to send natural language queries via the Genie tRPC procedures"
32+
},
33+
{
34+
"step": 3,
35+
"instruction": "Update tests/smoke.spec.ts selectors for your app"
36+
}
37+
],
2338
"config": {
2439
"schema": {
2540
"type": "object",

packages/appkit/src/plugins/lakebase/manifest.json

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,24 @@
1515
"fields": {
1616
"branch": {
1717
"description": "Full Lakebase Postgres branch resource name. Obtain by running `databricks postgres list-branches projects/{project-id}`, select the desired item from the output array and use its .name value.",
18-
"examples": ["projects/{project-id}/branches/{branch-id}"]
18+
"examples": ["projects/{project-id}/branches/{branch-id}"],
19+
"setupHint": "Run 'databricks postgres list-branches projects/{project-id}' to find your branch.",
20+
"discovery": {
21+
"cliCommand": "databricks postgres list-branches projects/{project-id} --profile <PROFILE> -o json",
22+
"selectField": ".name"
23+
}
1924
},
2025
"database": {
2126
"description": "Full Lakebase Postgres database resource name. Obtain by running `databricks postgres list-databases {branch-name}`, select the desired item from the output array and use its .name value. Requires the branch resource name.",
2227
"examples": [
2328
"projects/{project-id}/branches/{branch-id}/databases/{database-id}"
24-
]
29+
],
30+
"setupHint": "Run 'databricks postgres list-databases {branch-name}' to find your database. Requires the branch first.",
31+
"discovery": {
32+
"cliCommand": "databricks postgres list-databases {branch} --profile <PROFILE> -o json",
33+
"selectField": ".name",
34+
"dependsOn": "branch"
35+
}
2536
},
2637
"host": {
2738
"env": "PGHOST",
@@ -60,5 +71,23 @@
6071
}
6172
],
6273
"optional": []
63-
}
74+
},
75+
"postScaffold": [
76+
{
77+
"step": 1,
78+
"instruction": "Define your schema in server/server.ts startup"
79+
},
80+
{
81+
"step": 2,
82+
"instruction": "Write tRPC procedures using pool.query() in server/server.ts"
83+
},
84+
{
85+
"step": 3,
86+
"instruction": "Build React frontend consuming tRPC procedures"
87+
},
88+
{
89+
"step": 4,
90+
"instruction": "Update tests/smoke.spec.ts selectors for your app"
91+
}
92+
]
6493
}

packages/appkit/src/registry/types.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,26 @@ export {
5656
* Defines a single field for a resource. Each field has its own environment variable and optional description.
5757
* Single-value types use one key (e.g. id); multi-value types (database, secret) use multiple (e.g. instance_name, database_name or scope, key).
5858
*/
59+
export interface DiscoveryDescriptor {
60+
/** CLI command to list candidate values. Use <PROFILE> as a placeholder for the Databricks CLI profile. */
61+
cliCommand: string;
62+
/** jq-style field to extract the value from each result (e.g. ".id"). */
63+
selectField: string;
64+
/** jq-style field for human-readable display (e.g. ".name"). */
65+
displayField?: string;
66+
/** Field name in the same resource that must be resolved first. */
67+
dependsOn?: string;
68+
/** Optional command that returns a single value directly instead of a list. */
69+
shortcut?: string;
70+
}
71+
72+
export interface PostScaffoldStep {
73+
step: number;
74+
instruction: string;
75+
/** When true, this step must finish before proceeding to the next. */
76+
blocking?: boolean;
77+
}
78+
5979
export interface ResourceFieldEntry {
6080
/** Environment variable name for this field */
6181
env?: string;
@@ -71,6 +91,10 @@ export interface ResourceFieldEntry {
7191
value?: string;
7292
/** Named resolver prefixed by resource type (e.g., 'postgres:host'). The CLI resolves this value during the init prompt flow. */
7393
resolve?: string;
94+
/** How to discover candidate values for this field via CLI commands. */
95+
discovery?: DiscoveryDescriptor;
96+
/** Short actionable instruction for obtaining this field's value. */
97+
setupHint?: string;
7498
}
7599

76100
/**
@@ -182,6 +206,9 @@ export interface PluginManifest<TName extends string = string> {
182206
schema: ConfigSchema;
183207
};
184208

209+
/** Ordered steps to follow after scaffolding. */
210+
postScaffold?: PostScaffoldStep[];
211+
185212
/**
186213
* When true, excluded from the template plugins manifest during sync.
187214
*/

packages/shared/src/cli/commands/plugin/manifest-types.ts

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,20 @@
44
* definitions in sync, validate, list, and add-resource commands.
55
*/
66

7+
export interface DiscoveryDescriptor {
8+
cliCommand: string;
9+
selectField: string;
10+
displayField?: string;
11+
dependsOn?: string;
12+
shortcut?: string;
13+
}
14+
15+
export interface PostScaffoldStep {
16+
step: number;
17+
instruction: string;
18+
blocking?: boolean;
19+
}
20+
721
export interface ResourceFieldEntry {
822
env?: string;
923
description?: string;
@@ -12,6 +26,8 @@ export interface ResourceFieldEntry {
1226
localOnly?: boolean;
1327
value?: string;
1428
resolve?: string;
29+
discovery?: DiscoveryDescriptor;
30+
setupHint?: string;
1531
}
1632

1733
export interface ResourceRequirement {
@@ -33,6 +49,7 @@ export interface PluginManifest {
3349
};
3450
config?: { schema: unknown };
3551
onSetupMessage?: string;
52+
postScaffold?: PostScaffoldStep[];
3653
hidden?: boolean;
3754
}
3855

@@ -42,8 +59,22 @@ export interface TemplatePlugin extends Omit<PluginManifest, "config"> {
4259
requiredByTemplate?: boolean;
4360
}
4461

62+
export interface ScaffoldingFlag {
63+
required: boolean;
64+
description: string;
65+
pattern?: string;
66+
default?: string;
67+
}
68+
69+
export interface Scaffolding {
70+
command: string;
71+
flags: Record<string, ScaffoldingFlag>;
72+
rules: string[];
73+
}
74+
4575
export interface TemplatePluginsManifest {
4676
$schema: string;
4777
version: string;
78+
scaffolding?: Scaffolding;
4879
plugins: Record<string, TemplatePlugin>;
4980
}

packages/shared/src/cli/commands/plugin/sync/sync.test.ts

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
import path from "node:path";
22
import { Lang, parse } from "@ast-grep/napi";
33
import { describe, expect, it } from "vitest";
4+
import type { TemplatePluginsManifest } from "../manifest-types";
45
import {
6+
computeResolution,
7+
enrichPluginsWithResolution,
58
isWithinDirectory,
69
parseImports,
710
parsePluginUsages,
@@ -182,4 +185,121 @@ describe("plugin sync", () => {
182185
expect(shouldAllowJsManifestForPackage("@acme/plugin")).toBe(false);
183186
});
184187
});
188+
189+
describe("computeResolution", () => {
190+
it('returns "platform-injected" when localOnly is true', () => {
191+
expect(computeResolution({ localOnly: true })).toBe("platform-injected");
192+
});
193+
194+
it('returns "platform-injected" when localOnly is true even with value and resolve', () => {
195+
expect(
196+
computeResolution({
197+
localOnly: true,
198+
value: "5432",
199+
resolve: "postgres:host",
200+
}),
201+
).toBe("platform-injected");
202+
});
203+
204+
it('returns "static" when value is set', () => {
205+
expect(computeResolution({ value: "5432" })).toBe("static");
206+
});
207+
208+
it('returns "static" for non-empty value without localOnly', () => {
209+
expect(computeResolution({ value: "require" })).toBe("static");
210+
});
211+
212+
it('returns "cli-resolved" when resolve is set', () => {
213+
expect(computeResolution({ resolve: "postgres:host" })).toBe(
214+
"cli-resolved",
215+
);
216+
});
217+
218+
it('returns "user-provided" when no localOnly, value, or resolve', () => {
219+
expect(computeResolution({})).toBe("user-provided");
220+
expect(
221+
computeResolution({ env: "MY_VAR", description: "Some field" }),
222+
).toBe("user-provided");
223+
});
224+
225+
it('returns "user-provided" for empty value string', () => {
226+
expect(computeResolution({ value: "" })).toBe("user-provided");
227+
});
228+
});
229+
230+
describe("enrichPluginsWithResolution", () => {
231+
it("injects resolution on every resource field", () => {
232+
const plugins: TemplatePluginsManifest["plugins"] = {
233+
analytics: {
234+
name: "analytics",
235+
displayName: "Analytics",
236+
description: "Test",
237+
package: "@databricks/appkit",
238+
resources: {
239+
required: [
240+
{
241+
type: "sql_warehouse",
242+
alias: "SQL Warehouse",
243+
resourceKey: "sql-warehouse",
244+
description: "test",
245+
permission: "CAN_USE",
246+
fields: {
247+
id: { env: "WAREHOUSE_ID", description: "Warehouse ID" },
248+
},
249+
},
250+
],
251+
optional: [],
252+
},
253+
},
254+
lakebase: {
255+
name: "lakebase",
256+
displayName: "Lakebase",
257+
description: "Test",
258+
package: "@databricks/appkit",
259+
resources: {
260+
required: [
261+
{
262+
type: "postgres",
263+
alias: "Postgres",
264+
resourceKey: "postgres",
265+
description: "test",
266+
permission: "CAN_CONNECT_AND_CREATE",
267+
fields: {
268+
branch: { description: "Branch" },
269+
host: {
270+
env: "PGHOST",
271+
localOnly: true,
272+
resolve: "postgres:host",
273+
},
274+
port: { env: "PGPORT", localOnly: true, value: "5432" },
275+
endpoint: { resolve: "postgres:endpointPath" },
276+
},
277+
},
278+
],
279+
optional: [],
280+
},
281+
},
282+
};
283+
284+
enrichPluginsWithResolution(plugins);
285+
286+
const analyticsId = plugins.analytics.resources.required[0].fields
287+
.id as Record<string, unknown>;
288+
expect(analyticsId.resolution).toBe("user-provided");
289+
290+
const lakebaseFields = plugins.lakebase.resources.required[0].fields;
291+
expect(
292+
(lakebaseFields.branch as Record<string, unknown>).resolution,
293+
).toBe("user-provided");
294+
expect((lakebaseFields.host as Record<string, unknown>).resolution).toBe(
295+
"platform-injected",
296+
);
297+
expect((lakebaseFields.port as Record<string, unknown>).resolution).toBe(
298+
"platform-injected",
299+
);
300+
expect(
301+
(lakebaseFields.endpoint as Record<string, unknown>).resolution,
302+
).toBe("cli-resolved");
303+
});
304+
});
185305
});

0 commit comments

Comments
 (0)