fix(custom-widgets): simplify error sharing

This commit is contained in:
Thomas Camlong committed 2026-07-20 18:11:10 +02:00
1 parent b509679274
commit 211e059521
10 files changed
+65 -115

No files matched your search

@@ -64,26 +64,6 @@ export function buildCustomWidgetAiPrompt(
return embeddedSkill ? prompt : prompt.slice(0, 8_000);
}
export function buildCustomWidgetFixPrompt(input: {
currentConfig: Partial<HomarrCustomWidgetV2> | Record<string, unknown>;
request?: string | null;
documentationUrl?: string | null;
diagnostics: string;
journal: unknown;
}) {
const sections = [AUTHORING_PROMPT];
if (input.documentationUrl) sections.push(`API documentation: ${redactUrl(input.documentationUrl)}`);
sections.push(`Original request: ${redactText(input.request?.trim() || "Fix the current widget.")}`);
sections.push(`Current widget:\n\n\`\`\`json\n${JSON.stringify(redactValue(input.currentConfig), null, 2)}\n\`\`\``);
sections.push(
`Validation diagnostics:\n\n\`\`\`text\n${redactText(input.diagnostics) || "No static diagnostics."}\n\`\`\``,
);
sections.push(
`Redacted request journal:\n\n\`\`\`json\n${JSON.stringify(redactValue(input.journal), null, 2)}\n\`\`\``,
);
return sections.join("\n\n");
}
const sensitiveKey =
/(^|[-_])(authorization|api[-_]?keys?|passwords?|passwds?|secrets?|tokens?|access[-_]?tokens?|refresh[-_]?tokens?|client[-_]?secrets?)($|[-_])/iu;
@@ -46,7 +46,7 @@ Create one widget at a time. Repeat the workflow when the user asks for several
- Include responsive loading, empty, error, and success states.
- Keep actions explicit, permission-scoped, confirmed when destructive, and followed by targeted invalidation.
Read [references/schema.md](references/schema.md), [references/runtime.md](references/runtime.md), and [references/security.md](references/security.md) before offline authoring. When connected, prefer live resources because installed Mantine components can differ by Homarr release.
When connected through MCP, use the live schema, component, example, and skill resources. If live resources are unavailable, follow this document and the authoring prompt; do not assume access to repository-relative files.
`;
export function getCustomWidgetSkill() {
@@ -1,6 +1,6 @@
import { describe, expect, test } from "vitest";
import { buildCustomWidgetAiPrompt, buildCustomWidgetFixPrompt, buildCustomWidgetMcpPrompt } from "../core/ai-prompt";
import { buildCustomWidgetAiPrompt, buildCustomWidgetMcpPrompt } from "../core/ai-prompt";
import { CUSTOM_WIDGET_SKILL_MD } from "../core/authoring-resources";
import { CUSTOM_WIDGET_STARTER } from "../core/examples";
@@ -17,7 +17,8 @@ describe("buildCustomWidgetAiPrompt", () => {
expect(prompt).toContain("Use this embedded Homarr Custom Widget skill while authoring:");
expect(prompt).toContain("# Homarr Custom Widget");
expect(prompt).toContain("installed Mantine components can differ by Homarr release.");
expect(prompt).toContain("do not assume access to repository-relative files");
expect(prompt).not.toContain("references/schema.md");
expect(prompt.endsWith(CUSTOM_WIDGET_SKILL_MD)).toBe(true);
});
@@ -76,25 +77,6 @@ describe("buildCustomWidgetAiPrompt", () => {
});
});
describe("buildCustomWidgetFixPrompt", () => {
test("includes the complete current widget and redacted iteration evidence", () => {
const secret = "literal-sensitive-value";
const prompt = buildCustomWidgetFixPrompt({
currentConfig: {
name: "Large widget",
template: `<Text>${"x".repeat(9_000)}</Text>`,
secrets: [{ value: secret }],
},
request: `Fix it with password='${secret}'`,
diagnostics: `Header apiKey='${secret}' is invalid`,
journal: [{ requestId: "load", authorization: secret }],
});
expect(prompt).toContain("x".repeat(9_000));
expect(prompt).toContain("Validation diagnostics");
expect(prompt).not.toContain(secret);
});
});
describe("buildCustomWidgetMcpPrompt", () => {
test("describes tool-driven iteration without embedding the copy/paste output contract", () => {
const prompt = buildCustomWidgetMcpPrompt("Create two Portainer widgets", "https://example.com/docs");
@@ -12,6 +12,8 @@ describe("custom widget Agent Skill", () => {
const source = await readFile(resolve(repositoryRoot, ".agents/skills/homarr-custom-widget/SKILL.md"), "utf8");
expect(CUSTOM_WIDGET_SKILL_MD).toBe(source);
expect(getCustomWidgetSkill().skillMd).toBe(source);
expect(source).not.toContain("references/schema.md");
expect(source).toContain("do not assume access to repository-relative files");
});
test("publishes repository-based skills.sh installation metadata", () => {