feat: add custom widget flow workbench

This commit is contained in:
ajnart committed 2026-09-12 22:51:46 +02:00
1 parent c32a5317ca
commit a6bdbbb4ea
259 files changed
+23875 -1293

No files matched your search

@@ -0,0 +1,18 @@
ALTER TABLE `custom_widget_v2_definition` ADD `extensions` longtext;--> statement-breakpoint
ALTER TABLE `custom_widget_v2_definition` ADD `editor_layout` longtext;--> statement-breakpoint
ALTER TABLE `custom_widget_v2_definition` ADD `workshop_origin` longtext;--> statement-breakpoint
ALTER TABLE `custom_widget_v2_definition` ADD `previous_package` longtext;
--> statement-breakpoint
CREATE TABLE `custom_widget_content` (
`item_id` varchar(64) NOT NULL,
`definition_id` varchar(64) NOT NULL,
`key` varchar(64) NOT NULL,
`name` varchar(64) NOT NULL,
`value` longtext NOT NULL,
`revision` int NOT NULL,
`updated_at` timestamp NOT NULL DEFAULT (now()),
CONSTRAINT `custom_widget_content_item_id_definition_id_key_pk` PRIMARY KEY(`item_id`,`definition_id`,`key`)
);
--> statement-breakpoint
ALTER TABLE `custom_widget_content` ADD CONSTRAINT `custom_widget_content_item_id_item_id_fk` FOREIGN KEY (`item_id`) REFERENCES `item`(`id`) ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE `custom_widget_content` ADD CONSTRAINT `custom_widget_content_definition_id_fk` FOREIGN KEY (`definition_id`) REFERENCES `custom_widget_v2_definition`(`id`) ON DELETE cascade ON UPDATE no action;
File diff suppressed because it is too large. Load diff
@@ -351,6 +351,13 @@
"when": 1788763848931,
"tag": "0049_add_byte_unit_system",
"breakpoints": true
},
{
"idx": 50,
"version": "5",
"when": 1789219251749,
"tag": "0050_custom_widget_workbench",
"breakpoints": true
}
]
}
@@ -0,0 +1,18 @@
ALTER TABLE "custom_widget_v2_definition" ADD COLUMN "extensions" text;--> statement-breakpoint
ALTER TABLE "custom_widget_v2_definition" ADD COLUMN "editor_layout" text;--> statement-breakpoint
ALTER TABLE "custom_widget_v2_definition" ADD COLUMN "workshop_origin" text;--> statement-breakpoint
ALTER TABLE "custom_widget_v2_definition" ADD COLUMN "previous_package" text;
--> statement-breakpoint
CREATE TABLE "custom_widget_content" (
"item_id" varchar(64) NOT NULL,
"definition_id" varchar(64) NOT NULL,
"key" varchar(64) NOT NULL,
"name" varchar(64) NOT NULL,
"value" text NOT NULL,
"revision" integer NOT NULL,
"updated_at" timestamp DEFAULT now() NOT NULL,
CONSTRAINT "custom_widget_content_item_id_definition_id_key_pk" PRIMARY KEY("item_id","definition_id","key")
);
--> statement-breakpoint
ALTER TABLE "custom_widget_content" ADD CONSTRAINT "custom_widget_content_item_id_item_id_fk" FOREIGN KEY ("item_id") REFERENCES "public"."item"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE "custom_widget_content" ADD CONSTRAINT "custom_widget_content_definition_id_custom_widget_v2_definition_id_fk" FOREIGN KEY ("definition_id") REFERENCES "public"."custom_widget_v2_definition"("id") ON DELETE cascade ON UPDATE no action;
File diff suppressed because it is too large. Load diff
@@ -113,6 +113,13 @@
"when": 1788763848932,
"tag": "0015_add_byte_unit_system",
"breakpoints": true
},
{
"idx": 16,
"version": "7",
"when": 1789219251320,
"tag": "0016_custom_widget_workbench",
"breakpoints": true
}
]
}
@@ -0,0 +1,17 @@
ALTER TABLE `custom_widget_v2_definition` ADD `extensions` text;--> statement-breakpoint
ALTER TABLE `custom_widget_v2_definition` ADD `editor_layout` text;--> statement-breakpoint
ALTER TABLE `custom_widget_v2_definition` ADD `workshop_origin` text;--> statement-breakpoint
ALTER TABLE `custom_widget_v2_definition` ADD `previous_package` text;
--> statement-breakpoint
CREATE TABLE `custom_widget_content` (
`item_id` text NOT NULL,
`definition_id` text NOT NULL,
`key` text NOT NULL,
`name` text NOT NULL,
`value` text NOT NULL,
`revision` integer NOT NULL,
`updated_at` integer DEFAULT (unixepoch()) NOT NULL,
PRIMARY KEY(`item_id`, `definition_id`, `key`),
FOREIGN KEY (`item_id`) REFERENCES `item`(`id`) ON UPDATE no action ON DELETE cascade,
FOREIGN KEY (`definition_id`) REFERENCES `custom_widget_v2_definition`(`id`) ON UPDATE no action ON DELETE cascade
);
File diff suppressed because it is too large. Load diff
@@ -337,6 +337,13 @@
"when": 1788763848930,
"tag": "0047_add_byte_unit_system",
"breakpoints": true
},
{
"idx": 48,
"version": "6",
"when": 1789219251298,
"tag": "0048_custom_widget_workbench",
"breakpoints": true
}
]
}
+1
View File
@@ -54,6 +54,7 @@ export const {
cronJobConfigurations,
customWidgetDefinitions,
customWidgetSecrets,
customWidgetContent,
legacyCustomWidgetDefinitions,
legacyCustomWidgetSecrets,
widgetSecrets,
+28
View File
@@ -10,6 +10,7 @@ import {
index,
int,
mediumtext,
longtext,
mysqlTable,
primaryKey,
smallint,
@@ -637,6 +638,10 @@ export const customWidgetDefinitions = mysqlTable("custom_widget_v2_definition",
requests: text().notNull(),
options: text().notNull(),
template: text().notNull(),
extensions: longtext(),
editorLayout: longtext(),
workshopOrigin: longtext(),
previousPackage: longtext(),
enabled: boolean().notNull().default(true),
createdAt: timestamp({ mode: "date" }).notNull().defaultNow(),
updatedAt: timestamp({ mode: "date" }).notNull().defaultNow(),
@@ -666,6 +671,29 @@ export const customWidgetSecrets = mysqlTable(
}),
);
export const customWidgetContent = mysqlTable(
"custom_widget_content",
{
itemId: varchar({ length: 64 })
.notNull()
.references(() => items.id, { onDelete: "cascade" }),
definitionId: varchar({ length: 64 }).notNull(),
key: varchar({ length: 64 }).notNull(),
name: varchar({ length: 64 }).notNull(),
value: longtext().notNull(),
revision: int().notNull(),
updatedAt: timestamp({ mode: "date" }).notNull().defaultNow(),
},
(table) => ({
compoundKey: primaryKey({ columns: [table.itemId, table.definitionId, table.key] }),
definitionFk: foreignKey({
columns: [table.definitionId],
foreignColumns: [customWidgetDefinitions.id],
name: "custom_widget_content_definition_id_fk",
}).onDelete("cascade"),
}),
);
export const widgetSecrets = mysqlTable(
"widget_secret",
{
+22
View File
@@ -633,6 +633,10 @@ export const customWidgetDefinitions = pgTable("custom_widget_v2_definition", {
requests: text().notNull(),
options: text().notNull(),
template: text().notNull(),
extensions: text(),
editorLayout: text(),
workshopOrigin: text(),
previousPackage: text(),
enabled: boolean().notNull().default(true),
createdAt: timestamp({ mode: "date" }).notNull().defaultNow(),
updatedAt: timestamp({ mode: "date" }).notNull().defaultNow(),
@@ -662,6 +666,24 @@ export const customWidgetSecrets = pgTable(
}),
);
export const customWidgetContent = pgTable(
"custom_widget_content",
{
itemId: varchar({ length: 64 })
.notNull()
.references(() => items.id, { onDelete: "cascade" }),
definitionId: varchar({ length: 64 })
.notNull()
.references(() => customWidgetDefinitions.id, { onDelete: "cascade" }),
key: varchar({ length: 64 }).notNull(),
name: varchar({ length: 64 }).notNull(),
value: text().notNull(),
revision: integer().notNull(),
updatedAt: timestamp({ mode: "date" }).notNull().defaultNow(),
},
(table) => ({ compoundKey: primaryKey({ columns: [table.itemId, table.definitionId, table.key] }) }),
);
export const widgetSecrets = pgTable(
"widget_secret",
{
+24
View File
@@ -630,6 +630,10 @@ export const customWidgetDefinitions = sqliteTable("custom_widget_v2_definition"
requests: text().notNull(),
options: text().notNull(),
template: text().notNull(),
extensions: text(),
editorLayout: text(),
workshopOrigin: text(),
previousPackage: text(),
enabled: int({ mode: "boolean" }).notNull().default(true),
createdAt: int({ mode: "timestamp" })
.notNull()
@@ -663,6 +667,26 @@ export const customWidgetSecrets = sqliteTable(
}),
);
export const customWidgetContent = sqliteTable(
"custom_widget_content",
{
itemId: text()
.notNull()
.references(() => items.id, { onDelete: "cascade" }),
definitionId: text()
.notNull()
.references(() => customWidgetDefinitions.id, { onDelete: "cascade" }),
key: text().notNull(),
name: text().notNull(),
value: text().notNull(),
revision: int().notNull(),
updatedAt: int({ mode: "timestamp" })
.notNull()
.default(sql`(unixepoch())`),
},
(table) => ({ compoundKey: primaryKey({ columns: [table.itemId, table.definitionId, table.key] }) }),
);
export const widgetSecrets = sqliteTable(
"widget_secret",
{
+1 -1
View File
@@ -55,7 +55,7 @@ describe("Mysql Migration", () => {
const [customWidgetTables] = await connection
.promise()
.query<mysql.RowDataPacket[]>("SHOW TABLES LIKE 'custom_widget%'");
expect(customWidgetTables).toHaveLength(4);
expect(customWidgetTables).toHaveLength(5);
// A single base64 image attachment already exceeds the 64KB that `text` holds on MySQL, so
// `assistant_message.content` has to be `mediumtext`. Everything below 64KB passes either way.
@@ -37,6 +37,7 @@ test("SQLite migrations seed the five disabled bundled custom widgets", async ()
.prepare("SELECT name FROM sqlite_master WHERE type = 'table' AND name LIKE 'custom_widget%' ORDER BY name")
.all(),
).toEqual([
{ name: "custom_widget_content" },
{ name: "custom_widget_definition" },
{ name: "custom_widget_secret" },
{ name: "custom_widget_v2_definition" },