fix(custom-widgets): simplify error sharing
This commit is contained in:
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", () => {
|
||||
|
||||
Reference in new issue
Block a user