diff --git a/console/src/compose.test.ts b/console/src/compose.test.ts deleted file mode 100644 index fde8570..0000000 --- a/console/src/compose.test.ts +++ /dev/null @@ -1,65 +0,0 @@ -import { describe, it, expect } from "vitest"; -import { renderPreviewHtml, libraryNames, type BundlePreview, type Library } from "./compose"; - -const PREVIEW: BundlePreview = { - image_tag: "ghcr.io/openabdev/openab:0.9.0-claude", - digest: "sha256:abc123", - files: [ - { path: ".claude/skills/memory/SKILL.md", text: "# memory\n", bytes: 9, binary: false }, - { path: "CLAUDE.md", text: "# persona\n", bytes: 10, binary: false }, - ], -}; - -describe("renderPreviewHtml", () => { - it("shows the image tag, digest and file count", () => { - const html = renderPreviewHtml(PREVIEW); - expect(html).toContain("ghcr.io/openabdev/openab:0.9.0-claude"); - expect(html).toContain("sha256:abc123"); - // file count - expect(html).toContain("files 2"); - }); - - it("lists each file path with its content", () => { - const html = renderPreviewHtml(PREVIEW); - expect(html).toContain(".claude/skills/memory/SKILL.md"); - expect(html).toContain("CLAUDE.md"); - expect(html).toContain("# persona\n"); - }); - - it("escapes HTML in paths and content (no injection)", () => { - const html = renderPreviewHtml({ - image_tag: "img", - digest: "sha256:x", - files: [{ path: ".md", text: "", bytes: 5, binary: false }], - }); - expect(html).not.toContain(""); - expect(html).toContain("<script>"); - expect(html).toContain("<evil>.md"); - }); - - it("does not dump content for a binary file", () => { - const html = renderPreviewHtml({ - image_tag: "img", - digest: "sha256:x", - files: [{ path: "blob", text: "�", bytes: 3, binary: true }], - }); - expect(html).toContain("binary"); - expect(html).toContain("not shown"); - }); -}); - -describe("libraryNames", () => { - it("returns sorted template and overlay names", () => { - const lib: Library = { - templates: { zeta: {} as never, alpha: {} as never }, - overlays: { orca: {} as never }, - skills: { skills: {} }, - }; - expect(libraryNames(lib)).toEqual({ templates: ["alpha", "zeta"], overlays: ["orca"] }); - }); - - it("tolerates missing maps", () => { - const lib = { skills: { skills: {} } } as unknown as Library; - expect(libraryNames(lib)).toEqual({ templates: [], overlays: [] }); - }); -}); diff --git a/console/src/compose.ts b/console/src/compose.ts deleted file mode 100644 index 8342e37..0000000 --- a/console/src/compose.ts +++ /dev/null @@ -1,87 +0,0 @@ -// Compose: the template/overlay/skills library types + the composed -// `{path → bytes}` bundle preview (agent-deployment ADR, slice 1). The -// standalone authoring tab this module used to wire (`initComposeTab`) is -// gone — `[+ New fleet]`/`[+ Add instance]` (`deploy.ts`) reach the same -// compose→preview→deploy engine as an action instead, reusing the pure -// helpers below. Everything real runs through the backend: the library is -// persisted by `compose_library_set`, and the preview is composed by -// `compose_preview` (the pure Rust `studio-compose` seam). -// -// These types mirror `studio_compose`'s serde shapes 1:1. - -export interface Template { - name: string; - image_tag: string; - files: Record; - skills: string[]; -} -export interface Overlay { - name: string; - image_tag?: string | null; - files: Record; - skills: string[]; -} -export interface Skill { - files: Record; -} -export interface SkillsLibrary { - skills: Record; -} -export interface Library { - templates: Record; - overlays: Record; - skills: SkillsLibrary; -} -export interface FilePreview { - path: string; - text: string; - bytes: number; - binary: boolean; -} -export interface BundlePreview { - image_tag: string; - digest: string; - files: FilePreview[]; -} - -function escapeHtml(s: string): string { - return s - .replace(/&/g, "&") - .replace(//g, ">") - .replace(/"/g, """); -} - -/** Render the composed-bundle preview to HTML — a pure view (unit-tested). */ -export function renderPreviewHtml(preview: BundlePreview): string { - const rows = preview.files - .map((f) => { - const meta = f.binary ? `${f.bytes} bytes · binary` : `${f.bytes} bytes`; - const body = f.binary - ? `
(binary — ${f.bytes} bytes, not shown)
` - : `
${escapeHtml(f.text)}
`; - return ( - `
` + - `${escapeHtml(f.path)}${escapeHtml(meta)}` + - body + - `
` - ); - }) - .join(""); - const count = preview.files.length; - return ( - `
` + - `
image ${escapeHtml(preview.image_tag)}
` + - `
digest ${escapeHtml(preview.digest)}
` + - `
files ${count}
` + - `
` + - `
${rows}
` - ); -} - -/** Names present in the parsed library, sorted — drives the picker `