diff --git a/.agents/skills/homarr-custom-widget/references/schema.md b/.agents/skills/homarr-custom-widget/references/schema.md index a79b38d3f..2363462b6 100644 --- a/.agents/skills/homarr-custom-widget/references/schema.md +++ b/.agents/skills/homarr-custom-widget/references/schema.md @@ -28,7 +28,14 @@ The object key `default` is the required source ID; `default` is not a property "requests": { "summary": { "path": "/summary" }, "search": { "trigger": "manual", "path": "/search", "query": { "q": { "$param": "query" } } }, - "create": { "kind": "action", "method": "POST", "path": "/items", "body": { "id": { "$param": "id" } }, "confirmation": "Create this item?", "invalidates": ["search"] } + "create": { + "kind": "action", + "method": "POST", + "path": "/items", + "body": { "id": { "$param": "id" } }, + "confirmation": "Create this item?", + "invalidates": ["search"] + } } } ``` diff --git a/apps/nextjs/src/app/api/assistant/chat/assistant-tool-input-repair.spec.ts b/apps/nextjs/src/app/api/assistant/chat/assistant-tool-input-repair.spec.ts index e477385a9..4fa4b8a6a 100644 --- a/apps/nextjs/src/app/api/assistant/chat/assistant-tool-input-repair.spec.ts +++ b/apps/nextjs/src/app/api/assistant/chat/assistant-tool-input-repair.spec.ts @@ -76,7 +76,11 @@ describe("repairAssistantToolInput", () => { }); test.each([ - ["customWidget_previewQuery", { previewId: "preview-1", requestId: "search" }, { sessionId: "preview-1", requestId: "search" }], + [ + "customWidget_previewQuery", + { previewId: "preview-1", requestId: "search" }, + { sessionId: "preview-1", requestId: "search" }, + ], [ "customWidget_previewAction", { previewSessionId: "preview-1", requestId: "requestMovie", params: { mediaId: 603 } }, diff --git a/apps/nextjs/src/app/api/assistant/chat/assistant-tool-input-repair.ts b/apps/nextjs/src/app/api/assistant/chat/assistant-tool-input-repair.ts index 95ceab20a..f84a62c17 100644 --- a/apps/nextjs/src/app/api/assistant/chat/assistant-tool-input-repair.ts +++ b/apps/nextjs/src/app/api/assistant/chat/assistant-tool-input-repair.ts @@ -125,10 +125,7 @@ const repairCustomWidgetLifecycleInput = (tool return null; } if (typeof input !== "object" || input === null || Array.isArray(input)) return null; - const normalized = normalizeCustomWidgetLifecycleToolInput( - toolCall.toolName, - input as Record, - ); + const normalized = normalizeCustomWidgetLifecycleToolInput(toolCall.toolName, input as Record); if (normalized === input) return null; return { ...toolCall, input: JSON.stringify(normalized) }; }; diff --git a/packages/api/src/router/custom-widget/preview-sessions.ts b/packages/api/src/router/custom-widget/preview-sessions.ts index 509a61e35..8bacd2300 100644 --- a/packages/api/src/router/custom-widget/preview-sessions.ts +++ b/packages/api/src/router/custom-widget/preview-sessions.ts @@ -101,12 +101,8 @@ export const createPreviewSession = (input: CreatePreviewSessionInput) => call(( export const getPreviewSession = (id: string, userId: string) => call(() => getService().get(id, userId)); export const setPreviewSessionLiveActions = (id: string, userId: string, enabled: boolean) => call(() => getService().setLiveActions(id, userId, enabled)); -export const revisePreviewSessionTemplate = ( - id: string, - userId: string, - template: string, - expectedRevision?: number, -) => call(() => getService().reviseTemplate(id, userId, template, expectedRevision)); +export const revisePreviewSessionTemplate = (id: string, userId: string, template: string, expectedRevision?: number) => + call(() => getService().reviseTemplate(id, userId, template, expectedRevision)); export const appendPreviewJournal = ( session: CustomWidgetPreviewSession, entry: Omit, diff --git a/packages/api/src/router/test/custom-widget/preview-authoring.spec.ts b/packages/api/src/router/test/custom-widget/preview-authoring.spec.ts index e93b93b2e..59392a411 100644 --- a/packages/api/src/router/test/custom-widget/preview-authoring.spec.ts +++ b/packages/api/src/router/test/custom-widget/preview-authoring.spec.ts @@ -110,7 +110,7 @@ describe("custom widget agent preview workflow", () => { }, secrets: [], }); - const revisedTemplate = '{data.fixtures?.name}Ready'; + const revisedTemplate = "{data.fixtures?.name}Ready"; const revised = await caller.previewReviseTemplate({ sessionId: preview.previewSession.id, diff --git a/packages/custom-widgets/src/core/definition-security.ts b/packages/custom-widgets/src/core/definition-security.ts index 69a8c9aa8..a69c80b6a 100644 --- a/packages/custom-widgets/src/core/definition-security.ts +++ b/packages/custom-widgets/src/core/definition-security.ts @@ -30,8 +30,7 @@ const harmlessCredentialSettings = new Set([ const commonCredentialLiteralPattern = /\b(?:sk|pk|rk)-(?:[A-Za-z0-9][A-Za-z0-9._-]{7,})\b|\b(?:sk_(?:live|test)|github_pat|glpat|gh[pousr]|hf_|xox[baprs])-?[A-Za-z0-9._-]{8,}\b|\b(?:AKIA|ASIA)[A-Z0-9]{16}\b|\bAIza[0-9A-Za-z_-]{20,}\b|\beyJ[A-Za-z0-9_-]{8,}\.eyJ[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}\b/giu; -const authorizationSchemePattern = - /(^|[^A-Za-z0-9_-])(bearer|basic)([\s:_-]+)(["']?)([A-Za-z0-9._~+/%=-]{8,})/giu; +const authorizationSchemePattern = /(^|[^A-Za-z0-9_-])(bearer|basic)([\s:_-]+)(["']?)([A-Za-z0-9._~+/%=-]{8,})/giu; const credentialAssignmentPattern = /\b(authorization|auth(?:entication)?(?:[ _-]?(?:tokens?|keys?|credentials?))?|credentials?|api[ _-]?keys?|passwords?|passwds?|secrets?|tokens?|access[ _-]?(?:tokens?|keys?)|refresh[ _-]?tokens?|client[ _-]?secrets?|private[ _-]?keys?|signing[ _-]?keys?)((?:["']?\s*[:=]\s*["']?))([^\s,;"'}<>]+)/giu; diff --git a/packages/custom-widgets/src/jsx/runtime-component-policy.ts b/packages/custom-widgets/src/jsx/runtime-component-policy.ts index a96ca4a9e..d34ead832 100644 --- a/packages/custom-widgets/src/jsx/runtime-component-policy.ts +++ b/packages/custom-widgets/src/jsx/runtime-component-policy.ts @@ -19,9 +19,7 @@ export function getScopedCustomJsxControlName(scopeId: string, name: string) { export function getInvalidCustomJsxPropValueReason(componentName: string, propName: string, value: unknown) { if (propName === "resetKey" && value !== null) { const scalar = - typeof value === "string" || - typeof value === "boolean" || - (typeof value === "number" && Number.isFinite(value)); + typeof value === "string" || typeof value === "boolean" || (typeof value === "number" && Number.isFinite(value)); if (!scalar) return "resetKey supports only a finite number, string, boolean, or null"; } if ( diff --git a/packages/custom-widgets/src/runtime/refresh-button.tsx b/packages/custom-widgets/src/runtime/refresh-button.tsx index b8233660a..a0f521d01 100644 --- a/packages/custom-widgets/src/runtime/refresh-button.tsx +++ b/packages/custom-widgets/src/runtime/refresh-button.tsx @@ -14,7 +14,13 @@ export interface RefreshButtonProps { size?: string; } -export function RefreshButton({ requestId, label, color = "gray", variant = "subtle", size = "sm" }: RefreshButtonProps) { +export function RefreshButton({ + requestId, + label, + color = "gray", + variant = "subtle", + size = "sm", +}: RefreshButtonProps) { const runtime = useCustomWidgetRuntime(); const [loading, setLoading] = useState(false); const refresh = async () => { diff --git a/packages/custom-widgets/src/test/preview-sessions.spec.ts b/packages/custom-widgets/src/test/preview-sessions.spec.ts index 44366b220..7a6396575 100644 --- a/packages/custom-widgets/src/test/preview-sessions.spec.ts +++ b/packages/custom-widgets/src/test/preview-sessions.spec.ts @@ -228,7 +228,7 @@ describe("preview sessions", () => { const revised = await service.reviseTemplate( "session", "owner", - '{data.data?.name}Ready', + "{data.data?.name}Ready", 0, ); const session = await service.get("session", "owner"); @@ -238,13 +238,11 @@ describe("preview sessions", () => { expect(session.sources).toEqual(original.sources); expect(session.requests).toEqual(original.requests); expect(session.optionDefinitions).toEqual(original.optionDefinitions); - expect(await service.getJournal("session", "owner")).toEqual([ - expect.objectContaining({ sessionRevision: 0 }), - ]); + expect(await service.getJournal("session", "owner")).toEqual([expect.objectContaining({ sessionRevision: 0 })]); await expect(service.reviseTemplate("session", "owner", session.template, 1)).rejects.toThrow("unchanged"); - await expect( - service.reviseTemplate("session", "owner", "Stale edit", 0), - ).rejects.toMatchObject({ code: "CONFLICT" }); + await expect(service.reviseTemplate("session", "owner", "Stale edit", 0)).rejects.toMatchObject({ + code: "CONFLICT", + }); await expect( service.reviseTemplate("session", "owner", 'Run', 1), ).rejects.toMatchObject({ code: "BAD_REQUEST" }); diff --git a/packages/custom-widgets/src/test/schema.spec.ts b/packages/custom-widgets/src/test/schema.spec.ts index 3497f596c..0c561cd5a 100644 --- a/packages/custom-widgets/src/test/schema.spec.ts +++ b/packages/custom-widgets/src/test/schema.spec.ts @@ -91,7 +91,7 @@ describe("lean Custom Widget schema", () => { it("removes model-inserted zero-width spaces before JSX validation and persistence", () => { const candidate = customWidgetDefinitionSchema.parse({ ...CUSTOM_WIDGET_STARTER, - template: '\u200bCafe\u0301 {1 ?? \u03040}\u200b', + template: "\u200bCafe\u0301 {1 ?? \u03040}\u200b", }); expect(candidate.template).toBe("Café {1 ?? 0}"); @@ -319,7 +319,7 @@ describe("lean Custom Widget schema", () => { customWidgetDefinitionSchema.safeParse({ ...CUSTOM_WIDGET_STARTER, template: - 'https://www.themoviedb.org/assets/2/v4/glyphicons/basic/glyphicons-basic-38-picture-grey-c8f8b4d9.svg', + "https://www.themoviedb.org/assets/2/v4/glyphicons/basic/glyphicons-basic-38-picture-grey-c8f8b4d9.svg", }).success, ).toBe(true); });