/** * The artifact-editor registry (DES-artifact-editor-plugins §5.2, §8.6, §9.1; EP-C1). * * Two kinds of editor exist: * * - **First-party** editors ship INSIDE studio's bundle (§8.6): `/editors//editor.json` * beside the ONE self-contained `index.html` entry. Crew discovers them at boot, pins each entry's * sha256 and byte size, and serves the entry through the hashed bundle route (`routes.ts`) with * the §8.2 headers. Their ids are `wicked-*` — the reserved first-party namespace every pack is * refused (garden `wicked-pack.schema.json`). * - **Third-party** editors arrive as garden packs (spec 2 `editors[]`), installed by the operator * (`POST /editors {packRoot}`, human-only) once garden's fail-closed `pack check` AND the * install-time conformance run (garden `scripts/editor/conformance.py`, run by crew against its * own conformance host page, §12.1 / EP-C8) both pass (`pack-install.ts`). The registry records * the pack directory and each editor's pinned entry hash; it never writes into the pack. A pack * that also ships skills or blocks is refused: their own "Add these" approval (§3.2) is not built. * * Persisted state is ONE file, `/daemon-editors.json`: the enable/disable flags the * operator sets in Settings (§6.4, human-only) and the installed packs. It sits under the `daemon-` * prefix both crew's state-home registry and the engine's worker fence already classify (crew#756), * so no new top-level entry is introduced. The packs themselves live where the installer staged * them (garden's default `~/.wicked/editors//current`, program-owned, outside the state home); * a bundle is re-hashed against the recorded pin on every serve, so a copy changed after install is * refused, never served. */ import { z } from 'zod'; import type { EditorView } from '../core/types.js'; /** The persisted flags file, under the registered `daemon-` prefix. */ export declare const EDITORS_STATE_FILENAME = "daemon-editors.json"; /** Where first-party manifests live inside the studio bundle. */ export declare const STUDIO_EDITORS_DIRNAME = "editors"; /** The most of an installed pack's `wicked-pack.json` the registry reads (a manifest is a few KB). */ export declare const PACK_MANIFEST_CAP_BYTES: number; /** The host's bundle cap (§8.5); a pack's `limits.bundleBytes` may only be lower. */ export declare const HOST_BUNDLE_CAP_BYTES: number; export declare const EDITOR_PERMISSIONS: readonly ["artifact.read", "artifact.write", "selection.chip", "composer.draft", "checks.read", "checks.contribute", "sources.read", "media.read", "artifact.export", "ui.fullscreen", "network.media", "mcp.session", "artifact.theme"]; /** Editor ids: lowercase dash-words, ≤ 64 (garden's shape); first-party ones start with `wicked-`. */ export declare const EDITOR_ID: RegExp; export declare const FIRST_PARTY_ID: RegExp; /** Anchored SemVer (garden's shape). */ export declare const EDITOR_VERSION: RegExp; declare const EditorManifestFields: z.ZodObject<{ id: z.ZodString; title: z.ZodString; version: z.ZodString; protocol: z.ZodArray; kinds: z.ZodArray; entry: z.ZodDefault; sizes: z.ZodEffects, "many">, ("full" | "inline" | "pane")[], ("full" | "inline" | "pane")[]>; panels: z.ZodOptional, "many">>; permissions: z.ZodArray; why: z.ZodString; }, "strip", z.ZodTypeAny, { id: "artifact.read" | "artifact.write" | "selection.chip" | "composer.draft" | "checks.read" | "checks.contribute" | "sources.read" | "media.read" | "artifact.export" | "ui.fullscreen" | "network.media" | "mcp.session" | "artifact.theme"; why: string; }, { id: "artifact.read" | "artifact.write" | "selection.chip" | "composer.draft" | "checks.read" | "checks.contribute" | "sources.read" | "media.read" | "artifact.export" | "ui.fullscreen" | "network.media" | "mcp.session" | "artifact.theme"; why: string; }>, "many">; limits: z.ZodOptional>; surfaces: z.ZodEffects, import("wicked-crew-api-types").EditorSurfaces | undefined, unknown>; }, "strip", z.ZodTypeAny, { id: string; title: string; version: string; entry: string; protocol: number[]; sizes: ("full" | "inline" | "pane")[]; kinds: string[]; permissions: { id: "artifact.read" | "artifact.write" | "selection.chip" | "composer.draft" | "checks.read" | "checks.contribute" | "sources.read" | "media.read" | "artifact.export" | "ui.fullscreen" | "network.media" | "mcp.session" | "artifact.theme"; why: string; }[]; limits?: { bundleBytes: number; } | undefined; surfaces?: import("wicked-crew-api-types").EditorSurfaces | undefined; panels?: "checks"[] | undefined; }, { id: string; title: string; version: string; protocol: number[]; sizes: ("full" | "inline" | "pane")[]; kinds: string[]; permissions: { id: "artifact.read" | "artifact.write" | "selection.chip" | "composer.draft" | "checks.read" | "checks.contribute" | "sources.read" | "media.read" | "artifact.export" | "ui.fullscreen" | "network.media" | "mcp.session" | "artifact.theme"; why: string; }[]; entry?: string | undefined; limits?: { bundleBytes: number; } | undefined; surfaces?: unknown; panels?: "checks"[] | undefined; }>; export declare const EditorManifestSchema: z.ZodEffects; kinds: z.ZodArray; entry: z.ZodDefault; sizes: z.ZodEffects, "many">, ("full" | "inline" | "pane")[], ("full" | "inline" | "pane")[]>; panels: z.ZodOptional, "many">>; permissions: z.ZodArray; why: z.ZodString; }, "strip", z.ZodTypeAny, { id: "artifact.read" | "artifact.write" | "selection.chip" | "composer.draft" | "checks.read" | "checks.contribute" | "sources.read" | "media.read" | "artifact.export" | "ui.fullscreen" | "network.media" | "mcp.session" | "artifact.theme"; why: string; }, { id: "artifact.read" | "artifact.write" | "selection.chip" | "composer.draft" | "checks.read" | "checks.contribute" | "sources.read" | "media.read" | "artifact.export" | "ui.fullscreen" | "network.media" | "mcp.session" | "artifact.theme"; why: string; }>, "many">; limits: z.ZodOptional>; surfaces: z.ZodEffects, import("wicked-crew-api-types").EditorSurfaces | undefined, unknown>; }, "strip", z.ZodTypeAny, { id: string; title: string; version: string; entry: string; protocol: number[]; sizes: ("full" | "inline" | "pane")[]; kinds: string[]; permissions: { id: "artifact.read" | "artifact.write" | "selection.chip" | "composer.draft" | "checks.read" | "checks.contribute" | "sources.read" | "media.read" | "artifact.export" | "ui.fullscreen" | "network.media" | "mcp.session" | "artifact.theme"; why: string; }[]; limits?: { bundleBytes: number; } | undefined; surfaces?: import("wicked-crew-api-types").EditorSurfaces | undefined; panels?: "checks"[] | undefined; }, { id: string; title: string; version: string; protocol: number[]; sizes: ("full" | "inline" | "pane")[]; kinds: string[]; permissions: { id: "artifact.read" | "artifact.write" | "selection.chip" | "composer.draft" | "checks.read" | "checks.contribute" | "sources.read" | "media.read" | "artifact.export" | "ui.fullscreen" | "network.media" | "mcp.session" | "artifact.theme"; why: string; }[]; entry?: string | undefined; limits?: { bundleBytes: number; } | undefined; surfaces?: unknown; panels?: "checks"[] | undefined; }>, { id: string; title: string; version: string; entry: string; protocol: number[]; sizes: ("full" | "inline" | "pane")[]; kinds: string[]; permissions: { id: "artifact.read" | "artifact.write" | "selection.chip" | "composer.draft" | "checks.read" | "checks.contribute" | "sources.read" | "media.read" | "artifact.export" | "ui.fullscreen" | "network.media" | "mcp.session" | "artifact.theme"; why: string; }[]; limits?: { bundleBytes: number; } | undefined; surfaces?: import("wicked-crew-api-types").EditorSurfaces | undefined; panels?: "checks"[] | undefined; }, { id: string; title: string; version: string; protocol: number[]; sizes: ("full" | "inline" | "pane")[]; kinds: string[]; permissions: { id: "artifact.read" | "artifact.write" | "selection.chip" | "composer.draft" | "checks.read" | "checks.contribute" | "sources.read" | "media.read" | "artifact.export" | "ui.fullscreen" | "network.media" | "mcp.session" | "artifact.theme"; why: string; }[]; entry?: string | undefined; limits?: { bundleBytes: number; } | undefined; surfaces?: unknown; panels?: "checks"[] | undefined; }>; export type EditorManifest = z.infer; export interface EditorRecord { manifest: EditorManifest; /** The full 64-hex sha256 of the entry file, pinned when the registry read it. */ sha256: string; bytes: number; /** Absolute path of the entry file. Never on the wire. */ entryPath: string; firstParty: boolean; /** `studio-bundle`: first-party, shipped in studio's dist. `pack`: a third-party pack the operator installed. */ source: 'studio-bundle' | 'pack'; enabled: boolean; /** For a pack editor: the pack it came from. Never on the wire. */ pack?: InstalledPack; } /** One installed pack editor, as `daemon-editors.json` records it. */ export interface InstalledPack { /** Absolute path of the pack directory (holding `wicked-pack.json`). */ root: string; /** The pack's `name` (the installer's key). */ name: string; /** The editor's entry path, relative to the pack root. */ entry: string; /** The entry's sha256 when it was installed (and passed the conformance run). */ sha256: string; /** {@link editorEntryDigest} of its manifest entry as admitted: permissions, kinds, version, … */ manifestSha256: string; installedAt: string; /** The conformance run that admitted it: when, and which cases ran. */ conformance: { ok: true; at: string; cases: string[]; }; } /** An install the registry refuses: the id is taken, or the pack did not read back. */ export declare class EditorInstallConflict extends Error { constructor(message: string); } export declare function sha256Of(buf: Buffer): string; /** * Read a file holding at most `cap` bytes, never allocating more than `cap + 1` (codex r1 on X3: a * file swapped for a huge one is refused before it is read whole). `null` = over the cap. */ export declare function readCapped(path: string, cap: number): Buffer | null; /** The digest of a pack editor's admitted manifest entry: a manifest edited after install is a new install. */ export declare function editorEntryDigest(entry: unknown): string; /** * A path is served from the bundle only if it IS what it looks like: a real directory or a real * file, never a link (codex r1 — a link under `editors/` could point outside the bundle and be * served as first-party). `null` = fine; else the reason. */ export declare function notRealEntry(path: string, want: 'dir' | 'file'): string | null; /** The entry must be ONE self-contained file: nothing loaded from outside it (§5.2; the CSP would block it anyway). */ export declare function externalReferences(html: string): string[]; /** * Read the first-party editors a studio bundle ships. A malformed manifest, a missing or oversized * entry, an entry with outside references, or a non-`wicked-*` id is skipped with a reason — never * served half-right. */ export declare function discoverStudioEditors(studioRoot: string, log?: (msg: string) => void): Array>; /** A pack editor's manifest entry: the shared shape plus its pinned hash and a pack-relative entry. */ export declare const PackEditorSchema: z.ZodEffects; kinds: z.ZodArray; sizes: z.ZodEffects, "many">, ("full" | "inline" | "pane")[], ("full" | "inline" | "pane")[]>; panels: z.ZodOptional, "many">>; permissions: z.ZodArray; why: z.ZodString; }, "strip", z.ZodTypeAny, { id: "artifact.read" | "artifact.write" | "selection.chip" | "composer.draft" | "checks.read" | "checks.contribute" | "sources.read" | "media.read" | "artifact.export" | "ui.fullscreen" | "network.media" | "mcp.session" | "artifact.theme"; why: string; }, { id: "artifact.read" | "artifact.write" | "selection.chip" | "composer.draft" | "checks.read" | "checks.contribute" | "sources.read" | "media.read" | "artifact.export" | "ui.fullscreen" | "network.media" | "mcp.session" | "artifact.theme"; why: string; }>, "many">; limits: z.ZodOptional>; surfaces: z.ZodEffects, import("wicked-crew-api-types").EditorSurfaces | undefined, unknown>; } & { entry: z.ZodString; sha256: z.ZodString; }, "strip", z.ZodTypeAny, { id: string; title: string; version: string; sha256: string; entry: string; protocol: number[]; sizes: ("full" | "inline" | "pane")[]; kinds: string[]; permissions: { id: "artifact.read" | "artifact.write" | "selection.chip" | "composer.draft" | "checks.read" | "checks.contribute" | "sources.read" | "media.read" | "artifact.export" | "ui.fullscreen" | "network.media" | "mcp.session" | "artifact.theme"; why: string; }[]; limits?: { bundleBytes: number; } | undefined; surfaces?: import("wicked-crew-api-types").EditorSurfaces | undefined; panels?: "checks"[] | undefined; }, { id: string; title: string; version: string; sha256: string; entry: string; protocol: number[]; sizes: ("full" | "inline" | "pane")[]; kinds: string[]; permissions: { id: "artifact.read" | "artifact.write" | "selection.chip" | "composer.draft" | "checks.read" | "checks.contribute" | "sources.read" | "media.read" | "artifact.export" | "ui.fullscreen" | "network.media" | "mcp.session" | "artifact.theme"; why: string; }[]; limits?: { bundleBytes: number; } | undefined; surfaces?: unknown; panels?: "checks"[] | undefined; }>, { id: string; title: string; version: string; sha256: string; entry: string; protocol: number[]; sizes: ("full" | "inline" | "pane")[]; kinds: string[]; permissions: { id: "artifact.read" | "artifact.write" | "selection.chip" | "composer.draft" | "checks.read" | "checks.contribute" | "sources.read" | "media.read" | "artifact.export" | "ui.fullscreen" | "network.media" | "mcp.session" | "artifact.theme"; why: string; }[]; limits?: { bundleBytes: number; } | undefined; surfaces?: import("wicked-crew-api-types").EditorSurfaces | undefined; panels?: "checks"[] | undefined; }, { id: string; title: string; version: string; sha256: string; entry: string; protocol: number[]; sizes: ("full" | "inline" | "pane")[]; kinds: string[]; permissions: { id: "artifact.read" | "artifact.write" | "selection.chip" | "composer.draft" | "checks.read" | "checks.contribute" | "sources.read" | "media.read" | "artifact.export" | "ui.fullscreen" | "network.media" | "mcp.session" | "artifact.theme"; why: string; }[]; limits?: { bundleBytes: number; } | undefined; surfaces?: unknown; panels?: "checks"[] | undefined; }>; /** * Read one pack editor at serve-time quality: the manifest entry for `id` in `/wicked-pack.json`, * its entry a real file inside the pack, within its caps, loading nothing from outside, and hashing to * `pin`. `null` + a logged reason when any of it fails — an installed editor whose pack changed is * skipped until it is installed again, never served half-right. */ export declare function readPackEditor(pack: InstalledPack, id: string, log?: (msg: string) => void): Omit | null; export declare class EditorRegistry { private readonly records; private readonly stateFile; private readonly log; constructor(opts?: { stateHome?: string; studioRoot?: string; log?: (msg: string) => void; }); /** Every installed pack editor as recorded, including one skipped at boot (so an install can replace it). */ private readonly installed; /** The flags as read at boot: kept for an installed editor skipped at boot, so a write never drops its "off". */ private persistedFlags; private readState; private writeState; /** Where the flags live (diagnostics / tests). */ get stateFilePath(): string; list(): EditorRecord[]; get(id: string): EditorRecord | null; /** Enable or disable (§6.4: the operator's click). Returns the record, or `null` for an unknown id. */ setEnabled(id: string, enabled: boolean): EditorRecord | null; /** * Apply `change` to the in-memory maps and persist; a failed write restores the maps as they were * and rethrows, so memory never holds what the file does not (codex r1 on X3). */ private commit; /** * Record a pack's editors, already checked and conformance-run by the caller (`pack-install.ts`): * each is read back from disk exactly as boot reads it, and only then recorded. All or nothing — a * pack editor that does not read back leaves the registry as it was. An editor id another pack * holds is refused; the same pack re-installed (an update) replaces its own editors, keeping the * operator's on/off choice unless `enabled` says otherwise. */ installPack(packs: Array<{ id: string; pack: InstalledPack; }>, enabled: boolean | undefined): { records: EditorRecord[]; updated: boolean; }; /** Remove an installed pack editor (the pack's files are the installer's to delete). `false` when it is not one. */ removeInstalled(id: string): boolean; /** Whether `id` is an installed pack editor (served or skipped at boot). */ isInstalled(id: string): boolean; /** The wire view: no paths, the hashed entry URL instead. */ static view(r: EditorRecord): EditorView; } export {}; //# sourceMappingURL=registry.d.ts.map