From 1aba876dea943e00f852a7ae22854c6b489c0947 Mon Sep 17 00:00:00 2001 From: Gio Della-Libera Date: Thu, 16 Jul 2026 10:48:37 -0700 Subject: [PATCH 01/17] feat(claws): add gated package publication --- bun.lock | 3 + convex/httpApiV1.handlers.test.ts | 185 ++++ convex/httpApiV1/packagesV1.ts | 38 +- convex/lib/clawpack.test.ts | 55 +- convex/lib/clawpack.ts | 98 +- convex/lib/experimentalClaws.test.ts | 16 + convex/lib/experimentalClaws.ts | 10 + convex/lib/packageRegistry.ts | 33 +- convex/packages.public.test.ts | 240 +++++ convex/packages.ts | 162 +++- docs/README.md | 9 +- docs/claws.md | 99 ++ packages/clawhub/package.json | 4 +- packages/clawhub/src/clawpack.ts | 107 ++- packages/clawhub/src/cli.ts | 4 +- .../clawhub/src/cli/commands/packages.test.ts | 132 ++- packages/clawhub/src/cli/commands/packages.ts | 52 +- packages/clawhub/src/schema/clawPackage.ts | 271 ++++++ packages/clawhub/src/schema/claws.ts | 887 ++++++++++++++++++ packages/clawhub/src/schema/packages.ts | 2 +- packages/schema/dist/clawPackage.d.ts | 28 + packages/schema/dist/clawPackage.js | 180 ++++ packages/schema/dist/clawPackage.js.map | 1 + packages/schema/dist/index.d.ts | 1 + packages/schema/dist/index.js | 1 + packages/schema/dist/index.js.map | 1 - packages/schema/dist/packages.d.ts | 6 +- packages/schema/dist/packages.js | 3 +- packages/schema/dist/packages.js.map | 2 +- packages/schema/package.json | 3 +- packages/schema/src/clawPackage.test.ts | 257 +++++ packages/schema/src/clawPackage.ts | 271 ++++++ packages/schema/src/claws.test.ts | 4 +- packages/schema/src/index.ts | 1 + packages/schema/src/packages.ts | 3 +- specs/claws.md | 21 +- 36 files changed, 3050 insertions(+), 140 deletions(-) create mode 100755 convex/lib/experimentalClaws.test.ts create mode 100755 convex/lib/experimentalClaws.ts create mode 100644 docs/claws.md create mode 100644 packages/clawhub/src/schema/clawPackage.ts create mode 100644 packages/clawhub/src/schema/claws.ts create mode 100644 packages/schema/dist/clawPackage.d.ts create mode 100644 packages/schema/dist/clawPackage.js create mode 100644 packages/schema/dist/clawPackage.js.map delete mode 100644 packages/schema/dist/index.js.map create mode 100644 packages/schema/src/clawPackage.test.ts create mode 100644 packages/schema/src/clawPackage.ts diff --git a/bun.lock b/bun.lock index d3373daeae..41a8464a33 100644 --- a/bun.lock +++ b/bun.lock @@ -111,6 +111,7 @@ "@openclaw/plugin-inspector": "0.3.17", "arktype": "2.2.3", "commander": "15.0.0", + "croner": "10.0.1", "fflate": "0.8.3", "ignore": "7.0.6", "json5": "2.2.3", @@ -119,6 +120,7 @@ "p-retry": "8.0.0", "semver": "7.8.5", "undici": "7.28.0", + "yaml": "2.9.0", }, "devDependencies": { "@types/node": "26.1.1", @@ -158,6 +160,7 @@ "arktype": "2.2.3", "croner": "10.0.1", "unicode-case-folding": "1.1.1", + "yaml": "2.9.0", }, "devDependencies": { "@typescript/native": "npm:typescript@7.0.2", diff --git a/convex/httpApiV1.handlers.test.ts b/convex/httpApiV1.handlers.test.ts index 65848cfb89..7d36c0690e 100644 --- a/convex/httpApiV1.handlers.test.ts +++ b/convex/httpApiV1.handlers.test.ts @@ -12191,6 +12191,7 @@ describe("httpApiV1 handlers", () => { artifactKind: "legacy-zip", integritySha256: "a".repeat(64), sha256hash: "b".repeat(64), + clawpackSize: 321, }, }; } @@ -12214,6 +12215,7 @@ describe("httpApiV1 handlers", () => { source: "clawhub", artifactKind: "legacy-zip", artifactSha256: "b".repeat(64), + size: 321, packageName: "demo-plugin", version: "1.0.0", downloadUrl: "https://example.com/api/v1/packages/demo-plugin/download?version=1.0.0", @@ -14444,6 +14446,126 @@ describe("httpApiV1 handlers", () => { expect(storageGet).toHaveBeenCalledWith("storage:1"); }); + it("serves the immutable stored legacy archive without reconstructing it", async () => { + const archive = new Uint8Array([0x50, 0x4b, 0x03, 0x04, 0xaa, 0xbb]); + const runMutation = vi.fn().mockResolvedValue(okRate()); + const runQuery = vi.fn(async (_query: unknown, args: Record) => { + if ("name" in args) { + return { + package: { + _id: "packages:1", + name: "demo-plugin", + displayName: "Demo Plugin", + family: "code-plugin", + tags: {}, + latestReleaseId: "packageReleases:1", + channel: "community", + isOfficial: false, + createdAt: 1, + updatedAt: 1, + }, + latestRelease: null, + owner: null, + }; + } + if ("releaseId" in args) { + return { + _id: "packageReleases:1", + version: "1.0.0", + createdAt: 1, + changelog: "init", + artifactKind: "legacy-zip", + clawpackStorageId: "storage:archive", + files: [ + { + path: "package.json", + size: 2, + sha256: "a".repeat(64), + storageId: "storage:file-that-must-not-be-read", + }, + ], + }; + } + return null; + }); + const storageGet = vi.fn(async (id: string) => + id === "storage:archive" ? new Blob([archive], { type: "application/zip" }) : null, + ); + + const response = await __handlers.packagesGetRouterV1Handler( + makeCtx({ runQuery, runMutation, storage: { get: storageGet } }), + new Request("https://example.com/api/v1/packages/demo-plugin/download"), + ); + + expect(response.status).toBe(200); + expect(new Uint8Array(await response.arrayBuffer())).toEqual(archive); + expect(storageGet).toHaveBeenCalledTimes(1); + expect(storageGet).toHaveBeenCalledWith("storage:archive"); + }); + + it("reconstructs compatibility ZIP downloads instead of serving stored npm tarballs", async () => { + const storedTarball = gzipSync(new TextEncoder().encode("not a zip")); + const packageJson = new TextEncoder().encode('{"name":"demo-plugin"}'); + const runMutation = vi.fn().mockResolvedValue(okRate()); + const runQuery = vi.fn(async (_query: unknown, args: Record) => { + if ("name" in args) { + return { + package: { + _id: "packages:1", + name: "demo-plugin", + displayName: "Demo Plugin", + family: "code-plugin", + tags: {}, + latestReleaseId: "packageReleases:1", + channel: "community", + isOfficial: false, + createdAt: 1, + updatedAt: 1, + }, + latestRelease: null, + owner: null, + }; + } + if ("releaseId" in args) { + return { + _id: "packageReleases:1", + version: "1.0.0", + createdAt: 1, + changelog: "init", + artifactKind: "npm-pack", + clawpackStorageId: "storage:tarball", + files: [ + { + path: "package.json", + size: packageJson.byteLength, + sha256: "a".repeat(64), + storageId: "storage:package-json", + }, + ], + }; + } + return null; + }); + const storageGet = vi.fn(async (id: string) => { + if (id === "storage:tarball") return new Blob([storedTarball], { type: "application/gzip" }); + if (id === "storage:package-json") return new Blob([packageJson]); + return null; + }); + + const response = await __handlers.packagesGetRouterV1Handler( + makeCtx({ runQuery, runMutation, storage: { get: storageGet } }), + new Request("https://example.com/api/v1/packages/demo-plugin/download"), + ); + + expect(response.status).toBe(200); + expect(response.headers.get("content-type")).toContain("application/zip"); + expect( + strFromU8(unzipSync(new Uint8Array(await response.arrayBuffer()))["package/package.json"]!), + ).toBe('{"name":"demo-plugin"}'); + expect(storageGet).not.toHaveBeenCalledWith("storage:tarball"); + expect(storageGet).toHaveBeenCalledWith("storage:package-json"); + }); + it("allows package downloads when verification is clean even without cached vtAnalysis", async () => { const runMutation = vi.fn().mockResolvedValue(okRate()); const runQuery = vi.fn(async (_query: unknown, args: Record) => { @@ -14921,6 +15043,69 @@ describe("httpApiV1 handlers", () => { expect(payload?.files?.map((file) => file.path)).toContain("dist/index.js"); }); + it("multipart Claw publish accepts an npm pack without a plugin manifest", async () => { + vi.mocked(getOptionalApiTokenUserId).mockResolvedValue("users:1" as never); + vi.mocked(requirePackagePublishAuth).mockResolvedValue({ + kind: "user", + userId: "users:1", + user: { _id: "users:1", handle: "p" }, + } as never); + const runMutation = vi.fn().mockResolvedValue(okRate()); + const runAction = vi + .fn() + .mockResolvedValue({ ok: true, packageId: "pkg:claw", releaseId: "rel:claw" }); + const storageStore = vi.fn(async () => `storage:${storageStore.mock.calls.length}`); + const pack = npmPackFixture({ + "package/package.json": JSON.stringify({ + name: "demo-claw", + version: "1.0.0", + openclaw: { claw: "CLAW.md" }, + }), + "package/CLAW.md": "---\nschemaVersion: 1\nagent:\n id: demo-claw\n---\n", + }); + const form = new FormData(); + form.set( + "payload", + JSON.stringify({ + name: "demo-claw", + family: "claw", + version: "1.0.0", + changelog: "init", + }), + ); + form.append( + "clawpack", + new File([bytesToArrayBuffer(pack)], "demo-claw-1.0.0.tgz", { + type: "application/octet-stream", + }), + ); + + const response = await __handlers.publishPackageV1Handler( + makeCtx({ runAction, runMutation, storage: { store: storageStore } }), + new Request("https://example.com/api/v1/packages", { + method: "POST", + headers: { Authorization: "Bearer clh_test" }, + body: form, + }), + ); + + expect(response.status).toBe(200); + expect(storageStore).toHaveBeenCalledTimes(3); + expect(runAction).toHaveBeenCalledWith( + expect.anything(), + expect.objectContaining({ + payload: expect.objectContaining({ + family: "claw", + artifact: expect.objectContaining({ kind: "npm-pack", npmFileCount: 2 }), + files: [ + expect.objectContaining({ path: "package.json" }), + expect.objectContaining({ path: "CLAW.md" }), + ], + }), + }), + ); + }); + it("staged ClawPack publish derives artifact metadata from stored bytes", async () => { vi.mocked(getOptionalApiTokenUserId).mockResolvedValue("users:1" as never); vi.mocked(requirePackagePublishAuth).mockResolvedValue({ diff --git a/convex/httpApiV1/packagesV1.ts b/convex/httpApiV1/packagesV1.ts index a5eaff8441..e3aad026a3 100644 --- a/convex/httpApiV1/packagesV1.ts +++ b/convex/httpApiV1/packagesV1.ts @@ -650,6 +650,7 @@ function toReleaseArtifact(release: ReleaseLike, packageName?: string) { return { kind: "legacy-zip" as const, ...(sha256 ? { sha256 } : {}), + ...(release.clawpackSize !== undefined ? { size: release.clawpackSize } : {}), format: "zip", source: "clawhub" as const, artifactKind: "legacy-zip" as const, @@ -4137,16 +4138,29 @@ export async function packagesGetRouterV1Handler(ctx: ActionCtx, request: Reques if (!release) return text("Version not found", 404, rate.headers); const securityBlock = getReleaseSecurityBlock(release); if (securityBlock) return text(securityBlock.message, securityBlock.status, rate.headers); - const entries: Array<{ path: string; bytes: Uint8Array }> = []; - for (const file of release.files) { - const blob = await ctx.storage.get(file.storageId); - if (!blob) return text(`Missing stored file: ${file.path}`, 500, rate.headers); - entries.push({ - path: file.path, - bytes: new Uint8Array(await blob.arrayBuffer()), - }); + const storedZip = + release.artifactKind === "legacy-zip" && release.clawpackStorageId + ? await ctx.storage.get(release.clawpackStorageId) + : null; + if (release.artifactKind === "legacy-zip" && release.clawpackStorageId && !storedZip) { + return text("Missing stored package artifact", 500, rate.headers); + } + let zip: Uint8Array; + if (storedZip) { + zip = new Uint8Array(await storedZip.arrayBuffer()); + } else { + // Historical legacy releases and npm packs need a compatibility ZIP projection. + const entries: Array<{ path: string; bytes: Uint8Array }> = []; + for (const file of release.files) { + const blob = await ctx.storage.get(file.storageId); + if (!blob) return text(`Missing stored file: ${file.path}`, 500, rate.headers); + entries.push({ + path: file.path, + bytes: new Uint8Array(await blob.arrayBuffer()), + }); + } + zip = buildDeterministicPackageZip(entries); } - const zip = buildDeterministicPackageZip(entries); const [zipSha256, zipSha256Base64] = await Promise.all([sha256Hex(zip), sha256Base64(zip)]); try { const identity = getDownloadIdentity(request, viewerUserId ? String(viewerUserId) : null); @@ -4164,7 +4178,11 @@ export async function packagesGetRouterV1Handler(ctx: ActionCtx, request: Reques } catch { // Best-effort metric path; never fail package downloads. } - return new Response(new Blob([zip], { type: "application/zip" }), { + const zipBody = zip.buffer.slice( + zip.byteOffset, + zip.byteOffset + zip.byteLength, + ) as ArrayBuffer; + return new Response(new Blob([zipBody], { type: "application/zip" }), { status: 200, headers: mergeHeaders( rate.headers, diff --git a/convex/lib/clawpack.test.ts b/convex/lib/clawpack.test.ts index 33580d5653..a00eae9658 100644 --- a/convex/lib/clawpack.test.ts +++ b/convex/lib/clawpack.test.ts @@ -13,8 +13,8 @@ function writeString(target: Uint8Array, offset: number, width: number, value: s target.set(encoded.subarray(0, width), offset); } -function tarFile(path: string, content: string) { - const bytes = new TextEncoder().encode(content); +function tarFile(path: string, content: string | Uint8Array) { + const bytes = typeof content === "string" ? new TextEncoder().encode(content) : content; const header = new Uint8Array(BLOCK_SIZE); writeString(header, 0, 100, path); writeString(header, 100, 8, octal(0o644, 8)); @@ -37,7 +37,7 @@ function tarFile(path: string, content: string) { return [header, body]; } -function npmPackFixtureEntries(files: Array<[string, string]>) { +function npmPackFixtureEntries(files: Array<[string, string | Uint8Array]>) { const parts: Uint8Array[] = []; for (const [path, content] of files) { parts.push(...tarFile(path, content)); @@ -53,7 +53,7 @@ function npmPackFixtureEntries(files: Array<[string, string]>) { return gzipSync(tar); } -function npmPackFixture(files: Record) { +function npmPackFixture(files: Record) { return npmPackFixtureEntries(Object.entries(files)); } @@ -81,14 +81,53 @@ describe("clawpack", () => { ]); }); - it("rejects plugin tarballs without openclaw.plugin.json", async () => { + it("accepts Claw tarballs without a plugin manifest", async () => { + const pack = npmPackFixture({ + "package/package.json": JSON.stringify({ + name: "demo", + version: "1.0.0", + openclaw: { claw: "CLAW.md" }, + }), + "package/CLAW.md": "---\nschemaVersion: 1\nagent: { id: demo }\n---\n", + }); + + const parsed = await parseClawPack(pack); + expect(parsed.pluginManifest).toBeUndefined(); + expect(parsed.entries.map((entry) => entry.path)).toContain("CLAW.md"); + }); + + it("preserves and rejects padded tar path identity", async () => { const pack = npmPackFixture({ "package/package.json": JSON.stringify({ name: "demo", version: "1.0.0" }), + "package/CLAW.md ": "padded", }); - await expect(parseClawPack(pack)).rejects.toThrow( - "ClawPack must contain package/openclaw.plugin.json", - ); + await expect(parseClawPack(pack)).rejects.toThrow("unsafe tar path"); + }); + + it("rejects empty tar path segments", async () => { + const pack = npmPackFixture({ + "package/package.json": JSON.stringify({ name: "demo", version: "1.0.0" }), + "package/workspace//SOUL.md": "unsafe", + }); + + await expect(parseClawPack(pack)).rejects.toThrow("unsafe tar path"); + }); + + it("rejects oversized package metadata before JSON parsing", async () => { + const pack = npmPackFixture({ + "package/package.json": new Uint8Array(256 * 1024 + 1).fill(0x20), + }); + + await expect(parseClawPack(pack)).rejects.toThrow("package.json exceeds 256KB limit"); + }); + + it("rejects invalid UTF-8 package metadata", async () => { + const pack = npmPackFixture({ + "package/package.json": new Uint8Array([0xc3, 0x28]), + }); + + await expect(parseClawPack(pack)).rejects.toThrow("package.json is invalid JSON"); }); it("rejects archives that are not rooted under package/", async () => { diff --git a/convex/lib/clawpack.ts b/convex/lib/clawpack.ts index d606b0aca2..735e45a4f3 100644 --- a/convex/lib/clawpack.ts +++ b/convex/lib/clawpack.ts @@ -1,4 +1,4 @@ -import { gunzipSync } from "fflate"; +import { Gunzip } from "fflate"; type ClawPackEntry = { path: string; @@ -16,23 +16,29 @@ type ParsedClawPack = { fileCount: number; entries: ClawPackEntry[]; packageJson: Record; - pluginManifest: Record; + pluginManifest?: Record; }; const TAR_BLOCK_SIZE = 512; +const MAX_UNPACKED_BYTES = 50 * 1024 * 1024; +const MAX_TAR_BYTES = 64 * 1024 * 1024; +const MAX_FILE_BYTES = MAX_UNPACKED_BYTES; +const MAX_PACKAGE_JSON_BYTES = 256 * 1024; +const MAX_FILE_COUNT = 10_000; +const GZIP_INPUT_CHUNK_BYTES = 1_024; function isRecord(value: unknown): value is Record { return Boolean(value) && typeof value === "object" && !Array.isArray(value); } function textFromBytes(bytes: Uint8Array) { - return new TextDecoder().decode(bytes); + return new TextDecoder("utf-8", { fatal: true }).decode(bytes); } function readTarString(block: Uint8Array, offset: number, length: number) { const slice = block.subarray(offset, offset + length); const end = slice.indexOf(0); - return textFromBytes(end === -1 ? slice : slice.subarray(0, end)).trim(); + return textFromBytes(end === -1 ? slice : slice.subarray(0, end)); } function readTarSize(block: Uint8Array) { @@ -44,15 +50,49 @@ function readTarSize(block: Uint8Array) { } function normalizeTarPath(path: string) { - const normalized = path.replaceAll("\\", "/").replace(/^\.\/+/, ""); - if (!normalized || normalized.startsWith("/") || normalized.includes("\0")) return null; - const segments = normalized.split("/").filter(Boolean); - if (segments.length === 0 || segments.some((segment) => segment === "." || segment === "..")) { + if ( + !path || + path !== path.trim() || + path.includes("\\") || + path.startsWith("/") || + path.includes("\0") + ) { + return null; + } + const segments = path.split("/"); + if ( + segments.length === 0 || + segments.some( + (segment, index) => + segment === "." || segment === ".." || (segment === "" && index !== segments.length - 1), + ) + ) { return null; } return segments.join("/"); } +function gunzipBounded(bytes: Uint8Array): Uint8Array { + const chunks: Uint8Array[] = []; + let total = 0; + const gunzip = new Gunzip((chunk) => { + total += chunk.byteLength; + if (total > MAX_TAR_BYTES) throw new Error("ClawPack unpacked archive exceeds 64MB limit"); + chunks.push(Uint8Array.from(chunk)); + }); + for (let offset = 0; offset < bytes.byteLength; offset += GZIP_INPUT_CHUNK_BYTES) { + const end = Math.min(offset + GZIP_INPUT_CHUNK_BYTES, bytes.byteLength); + gunzip.push(bytes.subarray(offset, end), end === bytes.byteLength); + } + const result = new Uint8Array(total); + let offset = 0; + for (const chunk of chunks) { + result.set(chunk, offset); + offset += chunk.byteLength; + } + return result; +} + function tarEntryPayload(bytes: Uint8Array, offset: number, size: number) { return bytes.subarray(offset, offset + size); } @@ -80,6 +120,7 @@ function parseTarEntries(bytes: Uint8Array): ClawPackEntry[] { if (!path) throw new Error("ClawPack contains an unsafe tar path"); const size = readTarSize(header); + if (size > MAX_FILE_BYTES) throw new Error("ClawPack file exceeds 50MB limit"); const payloadOffset = offset + TAR_BLOCK_SIZE; const payloadEnd = payloadOffset + size; if (payloadEnd > bytes.byteLength) throw new Error("ClawPack tar entry is truncated"); @@ -90,14 +131,13 @@ function parseTarEntries(bytes: Uint8Array): ClawPackEntry[] { throw new Error("ClawPack entries must be rooted under package/"); } const relPath = path.slice("package/".length); - if (!relPath || relPath.endsWith("/")) { - offset = nextTarOffset(payloadOffset, size); - continue; - } + if (!relPath || relPath.endsWith("/")) + throw new Error("ClawPack contains an unsafe tar path"); if (paths.has(relPath)) { throw new Error(`ClawPack contains duplicate path: ${relPath}`); } paths.add(relPath); + if (entries.length >= MAX_FILE_COUNT) throw new Error("ClawPack exceeds 10000 file limit"); entries.push({ path: relPath, bytes: Uint8Array.from(tarEntryPayload(bytes, payloadOffset, size)), @@ -110,6 +150,8 @@ function parseTarEntries(bytes: Uint8Array): ClawPackEntry[] { } if (entries.length === 0) throw new Error("ClawPack contains no files"); + const unpackedSize = entries.reduce((sum, entry) => sum + entry.bytes.byteLength, 0); + if (unpackedSize > MAX_UNPACKED_BYTES) throw new Error("ClawPack package exceeds 50MB limit"); return entries; } @@ -152,18 +194,19 @@ export async function parseClawPack(bytes: Uint8Array): Promise let tarBytes: Uint8Array; try { - tarBytes = gunzipSync(bytes); - } catch { - throw new Error("ClawPack must be a gzip-compressed npm pack tarball"); + tarBytes = gunzipBounded(bytes); + } catch (error) { + if (error instanceof Error && error.message.startsWith("ClawPack ")) throw error; + throw new Error("ClawPack must be a gzip-compressed npm pack tarball", { cause: error }); } const entries = parseTarEntries(tarBytes); const packageJsonEntry = entries.find((entry) => entry.path === "package.json"); if (!packageJsonEntry) throw new Error("ClawPack must contain package/package.json"); - const pluginManifestEntry = entries.find((entry) => entry.path === "openclaw.plugin.json"); - if (!pluginManifestEntry) { - throw new Error("ClawPack must contain package/openclaw.plugin.json"); + if (packageJsonEntry.bytes.byteLength > MAX_PACKAGE_JSON_BYTES) { + throw new Error("ClawPack package.json exceeds 256KB limit"); } + const pluginManifestEntry = entries.find((entry) => entry.path === "openclaw.plugin.json"); let packageJson: unknown; try { @@ -178,14 +221,15 @@ export async function parseClawPack(bytes: Uint8Array): Promise if (!packageName) throw new Error("ClawPack package.json must declare a name"); if (!packageVersion) throw new Error("ClawPack package.json must declare a version"); - let pluginManifest: unknown; - try { - pluginManifest = JSON.parse(textFromBytes(pluginManifestEntry.bytes)); - } catch { - throw new Error("ClawPack openclaw.plugin.json is invalid JSON"); - } - if (!isRecord(pluginManifest)) { - throw new Error("ClawPack openclaw.plugin.json must be an object"); + let pluginManifest: Record | undefined; + if (pluginManifestEntry) { + try { + const parsed = JSON.parse(textFromBytes(pluginManifestEntry.bytes)) as unknown; + if (!isRecord(parsed)) throw new Error(); + pluginManifest = parsed; + } catch { + throw new Error("ClawPack openclaw.plugin.json is invalid JSON object"); + } } return { @@ -199,6 +243,6 @@ export async function parseClawPack(bytes: Uint8Array): Promise fileCount: entries.length, entries, packageJson, - pluginManifest, + ...(pluginManifest ? { pluginManifest } : {}), }; } diff --git a/convex/lib/experimentalClaws.test.ts b/convex/lib/experimentalClaws.test.ts new file mode 100755 index 0000000000..10b15c0f09 --- /dev/null +++ b/convex/lib/experimentalClaws.test.ts @@ -0,0 +1,16 @@ +import { describe, expect, it } from "vitest"; +import { experimentalClawsEnabled, isClawFamilyPubliclyVisible } from "./experimentalClaws"; + +describe("experimental Claw visibility", () => { + it("requires the exact deployment gate value", () => { + expect(experimentalClawsEnabled({})).toBe(false); + expect(experimentalClawsEnabled({ CLAWHUB_EXPERIMENTAL_CLAWS: "true" })).toBe(false); + expect(experimentalClawsEnabled({ CLAWHUB_EXPERIMENTAL_CLAWS: "1" })).toBe(true); + }); + + it("keeps existing package families visible while gating Claws", () => { + expect(isClawFamilyPubliclyVisible("code-plugin", {})).toBe(true); + expect(isClawFamilyPubliclyVisible("claw", {})).toBe(false); + expect(isClawFamilyPubliclyVisible("claw", { CLAWHUB_EXPERIMENTAL_CLAWS: "1" })).toBe(true); + }); +}); diff --git a/convex/lib/experimentalClaws.ts b/convex/lib/experimentalClaws.ts new file mode 100755 index 0000000000..5dbc75314f --- /dev/null +++ b/convex/lib/experimentalClaws.ts @@ -0,0 +1,10 @@ +export function experimentalClawsEnabled(env: Record = process.env) { + return env.CLAWHUB_EXPERIMENTAL_CLAWS === "1"; +} + +export function isClawFamilyPubliclyVisible( + family: string, + env: Record = process.env, +) { + return family !== "claw" || experimentalClawsEnabled(env); +} diff --git a/convex/lib/packageRegistry.ts b/convex/lib/packageRegistry.ts index 8d4e8b5ca9..b47a715693 100644 --- a/convex/lib/packageRegistry.ts +++ b/convex/lib/packageRegistry.ts @@ -355,13 +355,17 @@ export function normalizePublishFiles(files: PublishFile[]) { } export function assertPackageVersion( - family: "code-plugin" | "bundle-plugin" | "skill", + family: "code-plugin" | "bundle-plugin" | "skill" | "claw", version: string, ) { const trimmed = version.trim(); if (!trimmed) throw new ConvexError("Version required"); - if (family === "code-plugin" && !semver.valid(trimmed)) { - throw new ConvexError("Code plugin versions must be valid semver"); + if ((family === "code-plugin" || family === "claw") && !semver.valid(trimmed)) { + throw new ConvexError( + family === "claw" + ? "Claw versions must be valid semver" + : "Code plugin versions must be valid semver", + ); } return trimmed; } @@ -369,22 +373,39 @@ export function assertPackageVersion( export async function readStorageText( ctx: Pick, storageId: string, + options: { maxBytes?: number; label?: string; strictUtf8?: boolean } = {}, ): Promise { const blob = await ctx.storage.get(storageId as never); if (!blob) throw new ConvexError("Uploaded file no longer exists"); - return await blob.text(); + if (options.maxBytes !== undefined && blob.size > options.maxBytes) { + throw new ConvexError(`${options.label ?? "Uploaded text file"} exceeds byte limit`); + } + if (!options.strictUtf8) return await blob.text(); + try { + return new TextDecoder("utf-8", { fatal: true }).decode(await blob.arrayBuffer()); + } catch { + throw new ConvexError(`${options.label ?? "Uploaded text file"} must be valid UTF-8`); + } } export async function readOptionalTextFile( ctx: Pick, files: PublishFile[], pathMatch: (path: string) => boolean, + options: { + exactPath?: boolean; + maxBytes?: number; + label?: string; + strictUtf8?: boolean; + } = {}, ) { - const file = files.find((entry) => pathMatch(entry.path.toLowerCase())); + const file = files.find((entry) => + pathMatch(options.exactPath ? entry.path : entry.path.toLowerCase()), + ); if (!file) return null; return { file, - text: await readStorageText(ctx, file.storageId), + text: await readStorageText(ctx, file.storageId, options), }; } diff --git a/convex/packages.public.test.ts b/convex/packages.public.test.ts index 514d54e90d..b4ca024b5e 100644 --- a/convex/packages.public.test.ts +++ b/convex/packages.public.test.ts @@ -408,6 +408,7 @@ function makePackageManifestStorage() { get: vi.fn(async (id: string) => id === "storage:manifest" ? new Blob([JSON.stringify({ id: "demo.plugin" })]) : null, ), + store: vi.fn(async () => "storage:legacy-zip"), }; } @@ -8863,6 +8864,168 @@ describe("packages public queries", () => { ).rejects.toThrow("Skill packages must use the skills publish flow"); }); + it("rejects Claw publication before mutation when the experimental gate is disabled", async () => { + const previous = process.env.CLAWHUB_EXPERIMENTAL_CLAWS; + delete process.env.CLAWHUB_EXPERIMENTAL_CLAWS; + try { + await expect( + publishPackageForUserInternalHandler({} as never, { + actorUserId: "users:owner", + payload: { + name: "demo-claw", + family: "claw", + version: "1.0.0", + changelog: "init", + files: [], + }, + }), + ).rejects.toThrow("Experimental Claw publication is disabled"); + } finally { + if (previous === undefined) delete process.env.CLAWHUB_EXPERIMENTAL_CLAWS; + else process.env.CLAWHUB_EXPERIMENTAL_CLAWS = previous; + } + }); + + it("rechecks the Claw gate at release insertion for staged publications", async () => { + const previous = process.env.CLAWHUB_EXPERIMENTAL_CLAWS; + delete process.env.CLAWHUB_EXPERIMENTAL_CLAWS; + const dbGet = vi.fn(); + try { + await expect( + insertReleaseInternalHandler( + { + db: { + system: {}, + get: dbGet, + insert: vi.fn(), + patch: vi.fn(), + replace: vi.fn(), + delete: vi.fn(), + query: vi.fn(), + normalizeId: vi.fn(), + }, + } as never, + { + family: "claw", + } as never, + ), + ).rejects.toThrow("Experimental Claw publication is disabled"); + expect(dbGet).not.toHaveBeenCalled(); + } finally { + if (previous === undefined) delete process.env.CLAWHUB_EXPERIMENTAL_CLAWS; + else process.env.CLAWHUB_EXPERIMENTAL_CLAWS = previous; + } + }); + + it("publishes a validated Claw through the existing release pipeline when enabled", async () => { + const previous = process.env.CLAWHUB_EXPERIMENTAL_CLAWS; + process.env.CLAWHUB_EXPERIMENTAL_CLAWS = "1"; + const storedFiles = new Map([ + [ + "storage:package", + JSON.stringify({ + name: "demo-claw", + version: "1.0.0", + openclaw: { claw: "manifests/CLAW.md" }, + }), + ], + [ + "storage:claw", + "---\nschemaVersion: 1\nagent:\n id: demo-claw\n name: Demo Claw\n description: Runs the demo workflow.\nworkspace:\n bootstrapFiles:\n SOUL.md:\n source: workspace/SOUL.md\n---\n# Demo Claw\n", + ], + ["storage:soul", "Be precise.\n"], + ]); + const runMutation = vi.fn(async (_ref: unknown, args: Record) => { + if (args.minimumRole === "publisher") { + return { publisherId: "publishers:owner", linkedUserId: "users:owner" }; + } + if (args.family === "claw") { + return { ok: true, packageId: "packages:claw", releaseId: "releases:claw-1" }; + } + return null; + }); + const ctx = { + runQuery: vi + .fn() + .mockResolvedValueOnce(null) + .mockResolvedValueOnce({ + _id: "users:owner", + role: "user", + githubCreatedAt: Date.now() - 20 * 24 * 60 * 60 * 1000, + }) + .mockResolvedValueOnce({ + _id: "users:owner", + role: "user", + githubCreatedAt: Date.now() - 20 * 24 * 60 * 60 * 1000, + }) + .mockResolvedValueOnce({ + _id: "publishers:owner", + kind: "user", + handle: "owner", + linkedUserId: "users:owner", + }), + runMutation, + scheduler: { runAfter: vi.fn() }, + storage: { + get: vi.fn(async (storageId: string) => { + const content = storedFiles.get(storageId); + return content === undefined ? null : new Blob([content]); + }), + store: vi.fn(async () => "storage:legacy-zip"), + }, + }; + + try { + await expect( + publishPackageForUserInternalHandler(ctx as never, { + actorUserId: "users:owner", + payload: { + name: "demo-claw", + displayName: "Demo Claw", + family: "claw", + version: "1.0.0", + changelog: "init", + files: [ + { path: "package.json", size: 1, storageId: "storage:package", sha256: "package" }, + { + path: "manifests/CLAW.md", + size: 1, + storageId: "storage:claw", + sha256: "claw", + }, + { path: "workspace/SOUL.md", size: 1, storageId: "storage:soul", sha256: "soul" }, + ], + }, + }), + ).resolves.toEqual({ + ok: true, + packageId: "packages:claw", + releaseId: "releases:claw-1", + publicationStatus: "published", + }); + + expect(runMutation).toHaveBeenCalledWith( + expect.anything(), + expect.objectContaining({ + family: "claw", + summary: "Runs the demo workflow.", + artifactKind: "legacy-zip", + clawpackStorageId: "storage:legacy-zip", + clawpackSha256: expect.stringMatching(/^[a-f0-9]{64}$/), + clawpackSize: expect.any(Number), + clawManifestSummary: expect.objectContaining({ + agent: expect.objectContaining({ id: "demo-claw" }), + }), + pluginManifestSummary: undefined, + }), + ); + expect(runMutation.mock.calls.at(-1)?.[1]).not.toHaveProperty("extractedClawManifest"); + } finally { + if (previous === undefined) delete process.env.CLAWHUB_EXPERIMENTAL_CLAWS; + else process.env.CLAWHUB_EXPERIMENTAL_CLAWS = previous; + } + }); + it("keeps raw package publishes behind the per-file size limit", async () => { const ctx = { runQuery: vi @@ -8958,6 +9121,7 @@ describe("packages public queries", () => { const content = files.get(storageId); return content ? new Blob([content]) : null; }), + store: vi.fn(async () => "storage:legacy-zip"), }, }; @@ -9971,6 +10135,7 @@ describe("packages public queries", () => { const content = storedFiles.get(storageId); return content ? new Blob([content]) : null; }), + store: vi.fn(async () => "storage:legacy-zip"), }, }; @@ -10090,6 +10255,7 @@ describe("packages public queries", () => { const content = storedFiles.get(storageId); return content ? new Blob([content]) : null; }), + store: vi.fn(async () => "storage:legacy-zip"), }, }; @@ -10228,6 +10394,7 @@ describe("packages public queries", () => { const content = storedFiles.get(storageId); return content ? new Blob([content]) : null; }), + store: vi.fn(async () => "storage:legacy-zip"), }, runAction: vi.fn(async () => ({ status: "pass", @@ -10380,6 +10547,7 @@ describe("packages public queries", () => { const content = files.get(storageId); return content ? new Blob([content]) : null; }), + store: vi.fn(async () => "storage:legacy-zip"), }, }; @@ -10504,6 +10672,7 @@ describe("packages public queries", () => { const content = files.get(storageId); return content ? new Blob([content]) : null; }), + store: vi.fn(async () => "storage:legacy-zip"), }, }; @@ -16361,4 +16530,75 @@ describe("restorePackageInternal", () => { ).rejects.toThrow("Forbidden"); expect(patch).not.toHaveBeenCalledWith("packages:demo", expect.anything()); }); + + it("fails closed on Claw reads and never exposes the extracted manifest", async () => { + vi.mocked(getAuthUserId).mockResolvedValue(null); + const previous = process.env.CLAWHUB_EXPERIMENTAL_CLAWS; + const clawManifestSummary = { + schemaVersion: 1, + agent: { id: "demo-claw" }, + workspace: { bootstrapFiles: ["SOUL.md"], fileCount: 0 }, + packages: { skillCount: 0, pluginCount: 0 }, + mcpServerCount: 0, + cronJobCount: 0, + }; + const { ctx } = makePackageCtx({ + pkg: makePackageDoc({ family: "claw" }), + latestRelease: makeReleaseDoc({ + clawManifestSummary, + extractedClawManifest: { + schemaVersion: 1, + agent: { id: "demo-claw" }, + workspace: { bootstrapFiles: { "SOUL.md": { source: "workspace/SOUL.md" } } }, + }, + }), + }); + + try { + delete process.env.CLAWHUB_EXPERIMENTAL_CLAWS; + await expect(getByNameHandler(ctx, { name: "demo-plugin" })).resolves.toBeNull(); + await expect( + getVersionByNameHandler(ctx, { name: "demo-plugin", version: "1.0.0" }), + ).resolves.toBeNull(); + + process.env.CLAWHUB_EXPERIMENTAL_CLAWS = "1"; + const detail = await getByNameHandler(ctx, { name: "demo-plugin" }); + expect(detail?.latestRelease).toMatchObject({ clawManifestSummary }); + expect(detail?.latestRelease).not.toHaveProperty("extractedClawManifest"); + + const version = await getVersionByNameHandler(ctx, { + name: "demo-plugin", + version: "1.0.0", + }); + expect(version?.version).toMatchObject({ clawManifestSummary }); + expect(version?.version).not.toHaveProperty("extractedClawManifest"); + } finally { + if (previous === undefined) delete process.env.CLAWHUB_EXPERIMENTAL_CLAWS; + else process.env.CLAWHUB_EXPERIMENTAL_CLAWS = previous; + } + }); + + it("omits stored Claws from unfiltered public lists while disabled", async () => { + const previous = process.env.CLAWHUB_EXPERIMENTAL_CLAWS; + delete process.env.CLAWHUB_EXPERIMENTAL_CLAWS; + const { ctx } = makeDigestCtx({ + pages: [ + { + page: [makeDigest("demo-claw", { family: "claw" }), makeDigest("demo-plugin")], + isDone: true, + continueCursor: "", + }, + ], + }); + + try { + const result = await listPublicPageHandler(ctx, { + paginationOpts: { cursor: null, numItems: 25 }, + }); + expect(result.page.map((entry) => entry.name)).toEqual(["demo-plugin"]); + } finally { + if (previous === undefined) delete process.env.CLAWHUB_EXPERIMENTAL_CLAWS; + else process.env.CLAWHUB_EXPERIMENTAL_CLAWS = previous; + } + }); }); diff --git a/convex/packages.ts b/convex/packages.ts index fb6c6096a5..1f8e78c677 100644 --- a/convex/packages.ts +++ b/convex/packages.ts @@ -1,6 +1,8 @@ import { ServerPackagePublishRequestSchema, + validateClawPackageContents, derivePluginCategoryTags, + summarizeClawManifest, getCatalogTopicSlugs, getPackageScopeOwnerMismatch, INTERNAL_UNCATEGORIZED_CATEGORY, @@ -60,6 +62,7 @@ import { getActivityTrendRangeForEndDay, } from "./lib/downloadTrend"; import { buildPackageInspectorFindingsEmail } from "./lib/emails"; +import { experimentalClawsEnabled, isClawFamilyPubliclyVisible } from "./lib/experimentalClaws"; import { requireGitHubAccountAge } from "./lib/githubAccount"; import { normalizeGitHubRepository } from "./lib/githubActionsOidc"; import { readGlobalPublicPluginsCount } from "./lib/globalStats"; @@ -1210,8 +1213,13 @@ function toPublicPackage( } function toPublicPackageRelease(release: Doc<"packageReleases">) { - const { capabilities: _capabilities, ...publicRelease } = release as Doc<"packageReleases"> & { + const { + capabilities: _capabilities, + extractedClawManifest: _extractedClawManifest, + ...publicRelease + } = release as Doc<"packageReleases"> & { capabilities?: unknown; + extractedClawManifest?: unknown; }; const sourcePath = release.verification?.sourcePath ?? getReleaseSourcePath(release); if (!release.verification || !sourcePath) return publicRelease; @@ -1319,6 +1327,7 @@ function digestMatchesFilters( excludedScanStatuses?: PackageListScanStatus[]; }, ) { + if (!isClawFamilyPubliclyVisible(digest.family)) return false; if (digest.scanStatus && args.excludedScanStatuses?.includes(digest.scanStatus)) return false; if (args.category) { if (digest.pluginCategory) { @@ -1365,6 +1374,7 @@ function packageMatchesListFilters( excludedScanStatuses?: PackageListScanStatus[]; }, ) { + if (!isClawFamilyPubliclyVisible(pkg.family)) return false; if (pkg.scanStatus && args.excludedScanStatuses?.includes(pkg.scanStatus)) return false; if (args.family && pkg.family !== args.family) return false; if (args.channel && pkg.channel !== args.channel) return false; @@ -2634,6 +2644,7 @@ async function getReadablePackageByName( const normalizedName = normalizePackageName(name); const pkg = await getPackageByNormalizedName(ctx, normalizedName); if (!pkg || pkg.softDeletedAt) return null; + if (!isClawFamilyPubliclyVisible(pkg.family)) return null; if (pkg.channel === "private" || isPackageBlockedFromPublic(pkg.scanStatus)) { const canAccessOwner = await viewerCanAccessPackageOwner(ctx, pkg, viewerUserId); if (pkg.channel === "private" && !canAccessOwner) return null; @@ -2654,6 +2665,7 @@ async function getPackageReadableForPublicTrust( ) { const pkg = await getPackageByNormalizedName(ctx, normalizePackageName(name)); if (!pkg || pkg.softDeletedAt) return null; + if (!isClawFamilyPubliclyVisible(pkg.family)) return null; if (pkg.channel === "private" && !(await viewerCanAccessPackageOwner(ctx, pkg, viewerUserId))) { return null; } @@ -3256,7 +3268,7 @@ export const listAuditPage = query({ const page = []; const membershipCache = new Map>(); for (const pkg of result.page) { - if (pkg.family === "skill") continue; + if (pkg.family !== "code-plugin" && pkg.family !== "bundle-plugin") continue; if (!(await canViewerReadPackage(ctx, pkg, undefined, membershipCache))) continue; const owner = toPublicPublisher( @@ -4311,6 +4323,7 @@ async function searchPackagesImpl( topic, }); const entries = highlightedEntries + .filter(({ digest }) => isClawFamilyPubliclyVisible(digest.family)) .filter( ({ digest }) => !digest.scanStatus || !args.excludedScanStatuses?.includes(digest.scanStatus), @@ -7581,11 +7594,17 @@ async function publishPackageImpl( rawPayload, "Package publish payload", ); + if (payload.family === "claw" && !experimentalClawsEnabled()) { + throw new ConvexError("Experimental Claw publication is disabled"); + } if (payload.family === "skill") { throw new ConvexError("Skill packages must use the skills publish flow"); } const family = payload.family; const name = normalizePackageName(payload.name); + if (payload.family === "claw" && payload.name !== name) { + throw new ConvexError(`Claw package name must use canonical form ${name}`); + } const version = assertPackageVersion(family, payload.version); const existingPackage = await runQueryRef | null>( ctx, @@ -7713,7 +7732,8 @@ async function publishPackageImpl( if (totalBytes > MAX_PUBLISH_TOTAL_BYTES) { throw new ConvexError(getPublishTotalSizeError("package")); } - const legacyZipSha256 = await sha256Hex(buildDeterministicPackageZip(legacyZipEntries)); + const legacyZipBytes = buildDeterministicPackageZip(legacyZipEntries); + const legacyZipSha256 = await sha256Hex(legacyZipBytes); if (family === "code-plugin" && (!effectiveSource?.repo || !effectiveSource?.commit)) { throw new ConvexError("Code plugins require source repo and commit metadata"); @@ -7723,6 +7743,14 @@ async function publishPackageImpl( ctx, files, (path) => path === "package.json", + family === "claw" + ? { + exactPath: true, + maxBytes: 256 * 1024, + label: "Claw package.json", + strictUtf8: true, + } + : undefined, ); const pluginManifestEntry = await readOptionalTextFile( ctx, @@ -7746,6 +7774,23 @@ async function publishPackageImpl( ); const packageJson = maybeParseJson(packageJsonEntry?.text); + const declaredClawManifestPath = + family === "claw" && + packageJson && + typeof packageJson.openclaw === "object" && + packageJson.openclaw !== null && + !Array.isArray(packageJson.openclaw) && + typeof (packageJson.openclaw as Record).claw === "string" + ? ((packageJson.openclaw as Record).claw as string) + : undefined; + const clawManifestEntry = declaredClawManifestPath + ? await readOptionalTextFile(ctx, files, (path) => path === declaredClawManifestPath, { + exactPath: true, + maxBytes: 1024 * 1024, + label: "Claw manifest", + strictUtf8: true, + }) + : null; const pluginManifest = maybeParseJson(pluginManifestEntry?.text); const bundleManifest = maybeParseJson(bundleManifestEntry?.text); const storedPackageJson = toConvexSafeJsonValue(packageJson, { @@ -7757,11 +7802,31 @@ async function publishPackageImpl( const storedBundleManifest = toConvexSafeJsonValue(bundleManifest, { maxDepth: MAX_STORED_PACKAGE_METADATA_DEPTH, }); - if (packageJson) ensurePluginNameMatchesPackage(name, packageJson); - if (!pluginManifest) { + if (family !== "claw" && packageJson) ensurePluginNameMatchesPackage(name, packageJson); + if (family !== "claw" && !pluginManifest) { throw new ConvexError("openclaw.plugin.json is required for plugin packages"); } - const icon = normalizePluginManifestIcon(pluginManifest); + const clawPackage = + family === "claw" + ? validateClawPackageContents({ + packageName: name, + version, + packageJson, + files: files.map((file) => ({ + path: file.path, + ...(clawManifestEntry?.file.path === file.path ? { text: clawManifestEntry.text } : {}), + })), + }) + : null; + if (clawPackage && !clawPackage.ok) { + throw new ConvexError( + `Invalid Claw package: ${clawPackage.issues + .map((entry) => `${entry.path}: ${entry.message}`) + .join(" ")}`, + ); + } + const validatedClaw = clawPackage?.ok ? clawPackage.value : undefined; + const icon = family === "claw" ? undefined : normalizePluginManifestIcon(pluginManifest); if (family === "code-plugin") { const validation = validateOpenClawExternalCodePluginPackageContents( packageJson, @@ -7785,7 +7850,11 @@ async function publishPackageImpl( ? extractBundlePluginArtifacts({ packageName: name, packageJson, - pluginManifest, + pluginManifest: + pluginManifest ?? + (() => { + throw new ConvexError("openclaw.plugin.json is required for plugin packages"); + })(), bundleManifest, bundleMetadata: payload.bundle || detectedBundleFormat @@ -7807,22 +7876,32 @@ async function publishPackageImpl( (() => { throw new ConvexError("package.json is required for code plugins"); })(), - pluginManifest, + pluginManifest: + pluginManifest ?? + (() => { + throw new ConvexError("openclaw.plugin.json is required for plugin packages"); + })(), source: effectiveSource, }) : null; - const summary = summarizePackageForSearch({ - packageName: name, - packageJson, - readmeText: readmeEntry?.text ?? null, - }); + const summary = + validatedClaw?.manifest.agent.description?.trim() || + validatedClaw?.manifest.agent.name?.trim() || + summarizePackageForSearch({ + packageName: name, + packageJson, + readmeText: readmeEntry?.text ?? null, + }); let categories: string[]; let normalizedTopics: string[]; try { const declaredCategories = payload.categories ?? normalizeStoredPluginCategoryOverride(existingPackage?.categories); - categories = resolvePluginCategories({ declared: declaredCategories }); + categories = + family === "claw" + ? (declaredCategories ?? []) + : resolvePluginCategories({ declared: declaredCategories }); normalizedTopics = normalizeCatalogTopics(payload.topics ?? existingPackage?.topics); } catch (error) { throw new ConvexError(error instanceof Error ? error.message : "Invalid catalog metadata"); @@ -7836,6 +7915,7 @@ async function publishPackageImpl( packageJson, pluginManifest, bundleManifest, + clawManifest: validatedClaw?.manifest, source: effectiveSource, }, files, @@ -7870,12 +7950,34 @@ async function publishPackageImpl( const integritySha256 = await hashSkillFiles( files.map((file) => ({ path: file.path, sha256: file.sha256 })), ); - const pluginManifestSummary = derivePluginManifestSummary({ - pluginManifest, - ...(bundleManifest ? { skillManifest: bundleManifest } : {}), - compatibility: codeArtifacts?.compatibility ?? bundleArtifacts?.compatibility, - files: await withSkillMarkdownTextsForManifestSummary(ctx, files), - }); + const pluginManifestSummary = + family === "claw" + ? undefined + : derivePluginManifestSummary({ + pluginManifest: + pluginManifest ?? + (() => { + throw new ConvexError("openclaw.plugin.json is required for plugin packages"); + })(), + ...(bundleManifest ? { skillManifest: bundleManifest } : {}), + compatibility: codeArtifacts?.compatibility ?? bundleArtifacts?.compatibility, + files: await withSkillMarkdownTextsForManifestSummary(ctx, files), + }); + + const legacyZipStorageId = + payload.artifact?.kind === "npm-pack" + ? undefined + : await ctx.storage.store( + new Blob( + [ + legacyZipBytes.buffer.slice( + legacyZipBytes.byteOffset, + legacyZipBytes.byteOffset + legacyZipBytes.byteLength, + ) as ArrayBuffer, + ], + { type: "application/zip" }, + ), + ); const packageInsertArgs = { actorUserId, @@ -7902,9 +8004,10 @@ async function publishPackageImpl( integritySha256, sha256hash: legacyZipSha256, artifactKind: payload.artifact?.kind ?? "legacy-zip", - clawpackStorageId: payload.artifact?.storageId as Id<"_storage"> | undefined, - clawpackSha256: payload.artifact?.sha256, - clawpackSize: payload.artifact?.size, + clawpackStorageId: + (payload.artifact?.storageId as Id<"_storage"> | undefined) ?? legacyZipStorageId, + clawpackSha256: payload.artifact?.sha256 ?? legacyZipSha256, + clawpackSize: payload.artifact?.size ?? legacyZipBytes.byteLength, clawpackFormat: payload.artifact?.format, npmIntegrity: payload.artifact?.npmIntegrity, npmShasum: payload.artifact?.npmShasum, @@ -7918,6 +8021,7 @@ async function publishPackageImpl( extractedPluginManifest: storedPluginManifest, normalizedBundleManifest: family === "bundle-plugin" ? storedBundleManifest : undefined, pluginManifestSummary, + clawManifestSummary: validatedClaw ? summarizeClawManifest(validatedClaw.manifest) : undefined, source: effectiveSource, }; @@ -9807,7 +9911,12 @@ export const insertReleaseInternal = internalMutation({ ), name: v.string(), displayName: v.string(), - family: v.union(v.literal("skill"), v.literal("code-plugin"), v.literal("bundle-plugin")), + family: v.union( + v.literal("skill"), + v.literal("code-plugin"), + v.literal("bundle-plugin"), + v.literal("claw"), + ), version: v.string(), publicationStatus: v.optional(v.union(v.literal("pending"), v.literal("published"))), changelog: v.string(), @@ -9851,9 +9960,13 @@ export const insertReleaseInternal = internalMutation({ extractedPluginManifest: v.optional(v.any()), normalizedBundleManifest: v.optional(v.any()), pluginManifestSummary: v.optional(v.any()), + clawManifestSummary: v.optional(v.any()), source: v.optional(v.any()), }, handler: async (ctx, args) => { + if (args.family === "claw" && !experimentalClawsEnabled()) { + throw new ConvexError("Experimental Claw publication is disabled"); + } const now = Date.now(); const publicationStatus = args.publicationStatus ?? "published"; const pendingPublication = publicationStatus === "pending"; @@ -10086,6 +10199,7 @@ export const insertReleaseInternal = internalMutation({ extractedPluginManifest: args.extractedPluginManifest, normalizedBundleManifest: args.normalizedBundleManifest, pluginManifestSummary: args.pluginManifestSummary, + clawManifestSummary: args.clawManifestSummary, compatibility: args.compatibility, runtimeId: args.runtimeId, sourceRepo: args.sourceRepo, diff --git a/docs/README.md b/docs/README.md index 57d5d4a64a..b7f796284f 100644 --- a/docs/README.md +++ b/docs/README.md @@ -28,10 +28,11 @@ Reading order: 4. `docs/publishing.md`: owner-scoped skill/plugin publishing flow. 5. `docs/cli.md`: ClawHub CLI reference. 6. `docs/skill-format.md`: skill bundle metadata and package shape. -7. `docs/auth.md`: GitHub OAuth, API tokens, and CLI login. -8. `docs/telemetry.md`: install telemetry and how to opt out. -9. `docs/namespace-claims.md`: org, brand, owner-handle, package-scope, skill-slug, and namespace ownership disputes. -10. `docs/troubleshooting.md`: user-facing CLI, install, publish, update, and API fixes. +7. `docs/claws.md`: experimental Claw package authoring and publication. +8. `docs/auth.md`: GitHub OAuth, API tokens, and CLI login. +9. `docs/telemetry.md`: install telemetry and how to opt out. +10. `docs/namespace-claims.md`: org, brand, owner-handle, package-scope, skill-slug, and namespace ownership disputes. +11. `docs/troubleshooting.md`: user-facing CLI, install, publish, update, and API fixes. Policy, API, and trust docs: diff --git a/docs/claws.md b/docs/claws.md new file mode 100644 index 0000000000..f79a3e5876 --- /dev/null +++ b/docs/claws.md @@ -0,0 +1,99 @@ +--- +summary: "How to author, validate, and publish experimental Claw packages." +read_when: + - Authoring a Claw package + - Publishing a Claw to an experimental ClawHub deployment +--- + +# Experimental Claw packages + +A Claw is a versioned package that describes one complete OpenClaw agent and +the reusable resources it needs. ClawHub stores and scans the package; +OpenClaw owns local preview, consent, apply, update, and removal. + +Claw publication is experimental. The ClawHub deployment must set +`CLAWHUB_EXPERIMENTAL_CLAWS=1`; otherwise the server rejects publication. + +## Package shape + +A publishable Claw is a normal package directory with a `package.json` that +points to its manifest: + +```json +{ + "name": "@acme/github-triage", + "version": "1.0.0", + "openclaw": { + "claw": "CLAW.md" + } +} +``` + +`CLAW.md` starts with the grouped Claw manifest as YAML frontmatter. Markdown +after the closing delimiter is author-facing documentation. + +```markdown +--- +schemaVersion: 1 +agent: + id: github-triage + name: GitHub Triage + description: Reviews incoming issues. +workspace: + bootstrapFiles: + SOUL.md: + source: workspace/SOUL.md + files: + - source: workspace/reference.md + path: reference.md +packages: + - kind: skill + source: clawhub + ref: "@acme/triage" + version: 1.2.0 +mcpServers: {} +cronJobs: [] +--- + +# GitHub Triage + +Reviews and classifies incoming GitHub issues. +``` + +JSON manifests remain compatible. Set `openclaw.claw` to a package-relative +JSON file such as `openclaw.claw.json`. + +Every `workspace.*.source` must name a file in the same package. Package names +and versions must match `package.json`, dependency versions must be exact, and +MCP environment values must remain unresolved `${ENV_VAR}` references. + +## Validate and publish + +Preview the package without uploading it: + +```bash +clawhub package publish . --family claw --dry-run +``` + +When the target ClawHub deployment has experimental Claws enabled, publish +through the existing authenticated package flow: + +```bash +clawhub package publish . --family claw +``` + +The CLI detects `family: claw` when `package.json` contains `openclaw.claw`, so +`--family claw` is optional for a well-formed package. + +Publication rejects: + +- a missing, invalid, or escaping `openclaw.claw` path; +- package identity or version mismatches; +- malformed `CLAW.md` frontmatter or manifest fields; +- missing workspace source files or portable path collisions; +- floating skill/plugin versions and resolved MCP credentials. + +Accepted packages continue through ClawHub's existing ownership, moderation, +static scanning, release, and artifact storage pipeline. The stored release +retains the exact artifact plus a non-sensitive summary for later search and +detail surfaces; it does not duplicate the full manifest into Convex storage. diff --git a/packages/clawhub/package.json b/packages/clawhub/package.json index 31c6b4f9a4..aa5784e562 100644 --- a/packages/clawhub/package.json +++ b/packages/clawhub/package.json @@ -41,6 +41,7 @@ "@openclaw/plugin-inspector": "0.3.17", "arktype": "2.2.3", "commander": "15.0.0", + "croner": "10.0.1", "fflate": "0.8.3", "ignore": "7.0.6", "json5": "2.2.3", @@ -48,7 +49,8 @@ "ora": "9.4.1", "p-retry": "8.0.0", "semver": "7.8.5", - "undici": "7.28.0" + "undici": "7.28.0", + "yaml": "2.9.0" }, "devDependencies": { "@types/node": "26.1.1", diff --git a/packages/clawhub/src/clawpack.ts b/packages/clawhub/src/clawpack.ts index 7a78a33a7f..0ccb1b244f 100644 --- a/packages/clawhub/src/clawpack.ts +++ b/packages/clawhub/src/clawpack.ts @@ -1,4 +1,4 @@ -import { gunzipSync } from "fflate"; +import { Gunzip } from "fflate"; type ClawPackEntry = { path: string; @@ -10,23 +10,29 @@ type ParsedClawPack = { packageVersion: string; entries: ClawPackEntry[]; packageJson: Record; - pluginManifest: Record; + pluginManifest?: Record; }; const TAR_BLOCK_SIZE = 512; +const MAX_UNPACKED_BYTES = 50 * 1024 * 1024; +const MAX_TAR_BYTES = 64 * 1024 * 1024; +const MAX_FILE_BYTES = MAX_UNPACKED_BYTES; +const MAX_PACKAGE_JSON_BYTES = 256 * 1024; +const MAX_FILE_COUNT = 10_000; +const GZIP_INPUT_CHUNK_BYTES = 1_024; function isRecord(value: unknown): value is Record { return Boolean(value) && typeof value === "object" && !Array.isArray(value); } function textFromBytes(bytes: Uint8Array) { - return new TextDecoder().decode(bytes); + return new TextDecoder("utf-8", { fatal: true }).decode(bytes); } function readTarString(block: Uint8Array, offset: number, length: number) { const slice = block.subarray(offset, offset + length); const end = slice.indexOf(0); - return textFromBytes(end === -1 ? slice : slice.subarray(0, end)).trim(); + return textFromBytes(end === -1 ? slice : slice.subarray(0, end)); } function readTarSize(block: Uint8Array) { @@ -38,15 +44,49 @@ function readTarSize(block: Uint8Array) { } function normalizeTarPath(path: string) { - const normalized = path.replaceAll("\\", "/").replace(/^\.\/+/, ""); - if (!normalized || normalized.startsWith("/") || normalized.includes("\0")) return null; - const segments = normalized.split("/").filter(Boolean); - if (segments.length === 0 || segments.some((segment) => segment === "." || segment === "..")) { + if ( + !path || + path !== path.trim() || + path.includes("\\") || + path.startsWith("/") || + path.includes("\0") + ) { + return null; + } + const segments = path.split("/"); + if ( + segments.length === 0 || + segments.some( + (segment, index) => + segment === "." || segment === ".." || (segment === "" && index !== segments.length - 1), + ) + ) { return null; } return segments.join("/"); } +function gunzipBounded(bytes: Uint8Array): Uint8Array { + const chunks: Uint8Array[] = []; + let total = 0; + const gunzip = new Gunzip((chunk) => { + total += chunk.byteLength; + if (total > MAX_TAR_BYTES) throw new Error("ClawPack unpacked archive exceeds 64MB limit"); + chunks.push(Uint8Array.from(chunk)); + }); + for (let offset = 0; offset < bytes.byteLength; offset += GZIP_INPUT_CHUNK_BYTES) { + const end = Math.min(offset + GZIP_INPUT_CHUNK_BYTES, bytes.byteLength); + gunzip.push(bytes.subarray(offset, end), end === bytes.byteLength); + } + const result = new Uint8Array(total); + let offset = 0; + for (const chunk of chunks) { + result.set(chunk, offset); + offset += chunk.byteLength; + } + return result; +} + function isZeroBlock(block: Uint8Array) { return block.every((byte) => byte === 0); } @@ -57,6 +97,7 @@ function nextTarOffset(offset: number, size: number) { function parseTarEntries(bytes: Uint8Array): ClawPackEntry[] { const entries: ClawPackEntry[] = []; + const paths = new Set(); let offset = 0; while (offset + TAR_BLOCK_SIZE <= bytes.byteLength) { @@ -69,6 +110,7 @@ function parseTarEntries(bytes: Uint8Array): ClawPackEntry[] { if (!path) throw new Error("ClawPack contains an unsafe tar path"); const size = readTarSize(header); + if (size > MAX_FILE_BYTES) throw new Error("ClawPack file exceeds 50MB limit"); const payloadOffset = offset + TAR_BLOCK_SIZE; const payloadEnd = payloadOffset + size; if (payloadEnd > bytes.byteLength) throw new Error("ClawPack tar entry is truncated"); @@ -79,12 +121,17 @@ function parseTarEntries(bytes: Uint8Array): ClawPackEntry[] { throw new Error("ClawPack entries must be rooted under package/"); } const relPath = path.slice("package/".length); - if (relPath) { - entries.push({ - path: relPath, - bytes: Uint8Array.from(bytes.subarray(payloadOffset, payloadEnd)), - }); + if (!relPath || relPath.endsWith("/")) + throw new Error("ClawPack contains an unsafe tar path"); + if (paths.has(relPath)) { + throw new Error(`ClawPack contains duplicate path: ${relPath}`); } + paths.add(relPath); + if (entries.length >= MAX_FILE_COUNT) throw new Error("ClawPack exceeds 10000 file limit"); + entries.push({ + path: relPath, + bytes: Uint8Array.from(bytes.subarray(payloadOffset, payloadEnd)), + }); } else if (typeflag !== "5") { throw new Error("ClawPack may only contain regular files and directories"); } @@ -93,24 +140,27 @@ function parseTarEntries(bytes: Uint8Array): ClawPackEntry[] { } if (entries.length === 0) throw new Error("ClawPack contains no files"); + const unpackedSize = entries.reduce((sum, entry) => sum + entry.bytes.byteLength, 0); + if (unpackedSize > MAX_UNPACKED_BYTES) throw new Error("ClawPack package exceeds 50MB limit"); return entries; } export function parseClawPack(bytes: Uint8Array): ParsedClawPack { let tarBytes: Uint8Array; try { - tarBytes = gunzipSync(bytes); - } catch { - throw new Error("ClawPack must be a gzip-compressed npm pack tarball"); + tarBytes = gunzipBounded(bytes); + } catch (error) { + if (error instanceof Error && error.message.startsWith("ClawPack ")) throw error; + throw new Error("ClawPack must be a gzip-compressed npm pack tarball", { cause: error }); } const entries = parseTarEntries(tarBytes); const packageJsonEntry = entries.find((entry) => entry.path === "package.json"); if (!packageJsonEntry) throw new Error("ClawPack must contain package/package.json"); - const pluginManifestEntry = entries.find((entry) => entry.path === "openclaw.plugin.json"); - if (!pluginManifestEntry) { - throw new Error("ClawPack must contain package/openclaw.plugin.json"); + if (packageJsonEntry.bytes.byteLength > MAX_PACKAGE_JSON_BYTES) { + throw new Error("ClawPack package.json exceeds 256KB limit"); } + const pluginManifestEntry = entries.find((entry) => entry.path === "openclaw.plugin.json"); let packageJson: unknown; try { @@ -125,14 +175,15 @@ export function parseClawPack(bytes: Uint8Array): ParsedClawPack { if (!packageName) throw new Error("ClawPack package.json must declare a name"); if (!packageVersion) throw new Error("ClawPack package.json must declare a version"); - let pluginManifest: unknown; - try { - pluginManifest = JSON.parse(textFromBytes(pluginManifestEntry.bytes)); - } catch { - throw new Error("ClawPack openclaw.plugin.json is invalid JSON"); - } - if (!isRecord(pluginManifest)) { - throw new Error("ClawPack openclaw.plugin.json must be an object"); + let pluginManifest: Record | undefined; + if (pluginManifestEntry) { + try { + const parsed = JSON.parse(textFromBytes(pluginManifestEntry.bytes)) as unknown; + if (!isRecord(parsed)) throw new Error(); + pluginManifest = parsed; + } catch { + throw new Error("ClawPack openclaw.plugin.json is invalid JSON object"); + } } return { @@ -140,6 +191,6 @@ export function parseClawPack(bytes: Uint8Array): ParsedClawPack { packageVersion, entries, packageJson, - pluginManifest, + ...(pluginManifest ? { pluginManifest } : {}), }; } diff --git a/packages/clawhub/src/cli.ts b/packages/clawhub/src/cli.ts index acdee8242a..bdbf232fdb 100644 --- a/packages/clawhub/src/cli.ts +++ b/packages/clawhub/src/cli.ts @@ -542,7 +542,7 @@ const packageCmd = registerCommandGroup(program, ["package"]).description( registerCommand(packageCmd, ["package", "explore"]) .description("Browse published packages and plugins") .argument("[query...]", "Optional search query") - .option("--family ", "skill|code-plugin|bundle-plugin") + .option("--family ", "skill|code-plugin|bundle-plugin|claw") .option("--official", "Only official packages") .option("--executes-code", "Only packages that execute code") .option("--target ", "Filter by host target, e.g. darwin-arm64") @@ -721,7 +721,7 @@ registerCommand(packageCmd, ["package", "pack"]) registerCommand(packageCmd, ["package", "publish"]) .description("Publish a code plugin or bundle plugin from a folder or GitHub source") .argument("", "Package folder path, GitHub repo (owner/repo[@ref]), or URL") - .option("--family ", "code-plugin|bundle-plugin") + .option("--family ", "code-plugin|bundle-plugin|claw") .option("--name ", "Package name") .option("--display-name ", "Display name") .option("--owner ", "Publish under this owner/publisher handle") diff --git a/packages/clawhub/src/cli/commands/packages.test.ts b/packages/clawhub/src/cli/commands/packages.test.ts index 5d96441677..af0b10263a 100644 --- a/packages/clawhub/src/cli/commands/packages.test.ts +++ b/packages/clawhub/src/cli/commands/packages.test.ts @@ -1946,7 +1946,7 @@ describe("package commands", () => { } }); - it("rejects a ClawPack tarball without openclaw.plugin.json", async () => { + it("rejects a code plugin ClawPack tarball without openclaw.plugin.json", async () => { const workdir = await makeTmpWorkdir(); try { const packName = "demo-plugin-1.0.0.tgz"; @@ -1964,16 +1964,54 @@ describe("package commands", () => { await expect( cmdPublishPackage(makeOpts(workdir), packName, { + family: "code-plugin", sourceRepo: "openclaw/demo-plugin", sourceCommit: "abc123", }), - ).rejects.toThrow("ClawPack must contain package/openclaw.plugin.json"); + ).rejects.toThrow("openclaw.plugin.json required"); expect(httpMocks.apiRequestForm).not.toHaveBeenCalled(); } finally { await rm(workdir, { recursive: true, force: true }); } }); + it("publishes a Claw tarball without a plugin manifest", async () => { + const workdir = await makeTmpWorkdir(); + try { + const packName = "demo-claw-1.0.0.tgz"; + await writeFile( + join(workdir, packName), + npmPackFixture({ + "package/package.json": JSON.stringify({ + name: "demo-claw", + version: "1.0.0", + openclaw: { claw: "CLAW.md" }, + }), + "package/CLAW.md": "---\nschemaVersion: 1\nagent:\n id: demo-claw\n---\n", + }), + ); + httpMocks.apiRequestForm.mockResolvedValueOnce({ + ok: true, + packageId: "pkg_claw", + releaseId: "rel_claw", + }); + + await cmdPublishPackage(makeOpts(workdir), packName); + + expect(getPublishPayload()).toMatchObject({ + name: "demo-claw", + family: "claw", + version: "1.0.0", + }); + const uploaded = getPublishForm().get("clawpack"); + expect(uploaded).toBeInstanceOf(File); + const parsed = parseClawPack(new Uint8Array(await (uploaded as File).arrayBuffer())); + expect(parsed.entries.map((entry) => entry.path)).toContain("CLAW.md"); + } finally { + await rm(workdir, { recursive: true, force: true }); + } + }); + it("mints a short-lived publish token from GitHub Actions OIDC in CI", async () => { const workdir = await makeTmpWorkdir(); try { @@ -2527,6 +2565,96 @@ describe("package commands", () => { } }); + it("detects, validates, and publishes a Claw package", async () => { + const workdir = await makeTmpWorkdir(); + try { + const folder = join(workdir, "github-triage"); + await mkdir(join(folder, "workspace"), { recursive: true }); + await writeFile( + join(folder, "package.json"), + JSON.stringify({ + name: "@acme/github-triage", + displayName: "GitHub Triage", + version: "1.0.0", + openclaw: { claw: "CLAW.md" }, + }), + "utf8", + ); + await writeFile( + join(folder, "CLAW.md"), + [ + "---", + "schemaVersion: 1", + "agent:", + " id: github-triage", + " name: GitHub Triage", + " tools:", + " profile: coding", + " alsoAllow: [cron]", + " fs:", + " workspaceOnly: true", + " memory:", + " search:", + " enabled: true", + " rememberAcrossConversations: true", + " sources: [memory, sessions]", + "workspace:", + " bootstrapFiles:", + " SOUL.md:", + " source: workspace/SOUL.md", + "---", + "# GitHub Triage", + ].join("\n"), + "utf8", + ); + await writeFile(join(folder, "workspace", "SOUL.md"), "Be precise.\n", "utf8"); + httpMocks.apiRequestForm.mockResolvedValueOnce({ + ok: true, + packageId: "pkg_claw", + releaseId: "rel_claw", + }); + + await cmdPublishPackage(makeOpts(workdir), "github-triage"); + + expect(getPublishPayload()).toMatchObject({ + name: "@acme/github-triage", + family: "claw", + version: "1.0.0", + }); + } finally { + await rm(workdir, { recursive: true, force: true }); + } + }); + + it("rejects Claws with missing workspace sources before upload", async () => { + const workdir = await makeTmpWorkdir(); + try { + const folder = join(workdir, "broken-claw"); + await mkdir(folder, { recursive: true }); + await writeFile( + join(folder, "package.json"), + JSON.stringify({ + name: "broken-claw", + version: "1.0.0", + openclaw: { claw: "CLAW.md" }, + }), + "utf8", + ); + await writeFile( + join(folder, "CLAW.md"), + "---\nschemaVersion: 1\nagent:\n id: broken-claw\nworkspace:\n files:\n - source: missing.md\n path: missing.md\n---\n", + "utf8", + ); + + await expect(cmdPublishPackage(makeOpts(workdir), "broken-claw")).rejects.toThrow( + "missing.md: Declared workspace source is missing", + ); + expect(httpMocks.apiRequestForm).not.toHaveBeenCalled(); + } finally { + await rm(workdir, { recursive: true, force: true }); + } + }); + it("respects package ignore rules and built-in ignored directories", async () => { const workdir = await makeTmpWorkdir(); try { diff --git a/packages/clawhub/src/cli/commands/packages.ts b/packages/clawhub/src/cli/commands/packages.ts index 3faba946da..7451a02841 100644 --- a/packages/clawhub/src/cli/commands/packages.ts +++ b/packages/clawhub/src/cli/commands/packages.ts @@ -143,8 +143,10 @@ type PackageExploreOptions = { json?: boolean; }; +type PublishablePackageFamily = "code-plugin" | "bundle-plugin" | "claw"; + type PackagePublishOptions = { - family?: "code-plugin" | "bundle-plugin"; + family?: PublishablePackageFamily; name?: string; displayName?: string; owner?: string; @@ -261,7 +263,7 @@ type PackagePublishPayload = { name: string; displayName: string; ownerHandle?: string; - family: "code-plugin" | "bundle-plugin"; + family: PublishablePackageFamily; version: string; changelog: string; manualOverrideReason?: string; @@ -288,7 +290,7 @@ type PackagePublishPlan = { source: string; name: string; displayName: string; - family: "code-plugin" | "bundle-plugin"; + family: PublishablePackageFamily; version: string; commit?: string; files: number; @@ -2184,9 +2186,23 @@ function hasLooseBundleMarker(fileSet: Set) { function detectPackageFamily( fileSet: Set, - explicit?: "code-plugin" | "bundle-plugin", -): "code-plugin" | "bundle-plugin" { + packageJson: unknown, + explicit?: PublishablePackageFamily, +): PublishablePackageFamily { if (explicit) return explicit; + const packageRecord = + packageJson && typeof packageJson === "object" && !Array.isArray(packageJson) + ? (packageJson as Record) + : undefined; + const openclaw = + packageRecord?.openclaw && + typeof packageRecord.openclaw === "object" && + !Array.isArray(packageRecord.openclaw) + ? (packageRecord.openclaw as Record) + : undefined; + if (typeof openclaw?.claw === "string") { + return "claw"; + } if (hasRealBundleManifest(fileSet)) return "bundle-plugin"; if (fileSet.has("openclaw.plugin.json")) return "code-plugin"; if (hasLooseBundleMarker(fileSet)) return "bundle-plugin"; @@ -2318,7 +2334,7 @@ async function preparePackagePublishPlan( (parsedClawpack ? null : await readJsonFile(join(folder, "openclaw.plugin.json"))); const bundleManifestInfo = await readBundleManifestInfo(filesOnDisk, folder, parsedClawpack); const bundleManifest = bundleManifestInfo.manifest; - const family = detectPackageFamily(fileSet, options.family); + const family = detectPackageFamily(fileSet, packageJson, options.family); const name = options.name?.trim() || parsedClawpack?.packageName || @@ -2345,9 +2361,11 @@ async function preparePackagePublishPlan( if (!name) fail("--name required"); if (!displayName) fail("--display-name required"); if (!version) fail("--version required"); - if (!fileSet.has("openclaw.plugin.json")) fail("openclaw.plugin.json required"); - if (family === "code-plugin" && !semver.valid(version)) { - fail("--version must be valid semver for code plugins"); + if (family !== "claw" && !fileSet.has("openclaw.plugin.json")) { + fail("openclaw.plugin.json required"); + } + if ((family === "code-plugin" || family === "claw") && !semver.valid(version)) { + fail(`--version must be valid semver for ${family === "claw" ? "Claws" : "code plugins"}`); } if (family === "code-plugin") { if (!fileSet.has("package.json")) fail("package.json required"); @@ -2358,6 +2376,22 @@ async function preparePackagePublishPlan( } } + if (family === "claw") { + const { validateClawPackageContents } = await import("../../schema/clawPackage.js"); + const validation = validateClawPackageContents({ + packageName: name, + version, + packageJson, + files: filesOnDisk.map((file) => ({ + path: file.relPath, + text: new TextDecoder().decode(file.bytes), + })), + }); + if (!validation.ok) { + fail(validation.issues.map((issue) => `${issue.path}: ${issue.message}`).join(" ")); + } + } + if (family === "code-plugin" && !clawpackOnDisk) { const packDestination = await mkdtemp(join(tmpdir(), "clawhub-clawpack-")); let packed: PackedClawPack; diff --git a/packages/clawhub/src/schema/clawPackage.ts b/packages/clawhub/src/schema/clawPackage.ts new file mode 100644 index 0000000000..b3ee3a6e47 --- /dev/null +++ b/packages/clawhub/src/schema/clawPackage.ts @@ -0,0 +1,271 @@ +import { isScalar, parseDocument, visit } from "yaml"; +import { + summarizeClawManifest, + validateClawManifest, + type ClawManifest, + type ClawManifestSummary, +} from "./claws.js"; + +export type ClawPackageTextFile = { path: string; text?: string }; +export type ClawPackageValidationIssue = { + code: string; + path: string; + message: string; +}; +export type ValidatedClawPackage = { + manifestPath: string; + manifest: ClawManifest; + summary: ClawManifestSummary; +}; + +const EXACT_VERSION_PATTERN = + /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[A-Za-z-][0-9A-Za-z-]*)(?:\.(?:0|[1-9]\d*|\d*[A-Za-z-][0-9A-Za-z-]*))*))?(?:\+([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?$/; +const WINDOWS_INVALID_PATH_CHARS = /[<>:"|?*]/; +const WINDOWS_RESERVED_PATH_SEGMENT = /^(?:con|prn|aux|nul|com[1-9]|lpt[1-9])(?:\..*)?$/i; + +function isRecord(value: unknown): value is Record { + return Boolean(value) && typeof value === "object" && !Array.isArray(value); +} + +export function isSafeClawPackagePath(value: string): boolean { + const normalized = value.replaceAll("\\", "/"); + if ( + !normalized || + normalized !== value || + normalized !== normalized.trim() || + normalized.startsWith("/") || + /^[A-Za-z]:\//.test(normalized) + ) { + return false; + } + return normalized + .split("/") + .every( + (segment) => + segment !== "" && + segment !== "." && + segment !== ".." && + !WINDOWS_INVALID_PATH_CHARS.test(segment) && + !Array.from(segment).some((character) => character.charCodeAt(0) <= 0x1f) && + !segment.endsWith(".") && + !segment.endsWith(" ") && + !WINDOWS_RESERVED_PATH_SEGMENT.test(segment), + ); +} + +function portablePathKey(value: string): string { + return value.replaceAll("\\", "/").normalize("NFC").toLowerCase(); +} + +function issue(code: string, path: string, message: string): ClawPackageValidationIssue { + return { code, path, message }; +} + +function parseManifestDocument(raw: string, manifestPath: string) { + const filename = manifestPath.replaceAll("\\", "/").split("/").at(-1)?.toLowerCase(); + if (filename === "claw.md") { + const markdown = raw.startsWith("\uFEFF") ? raw.slice(1) : raw; + const match = markdown.match(/^---\r?\n([\s\S]*?)\r?\n---(?:\r?\n|$)/); + if (!match) { + return { + issues: [ + issue( + "missing_claw_frontmatter", + manifestPath, + `${manifestPath} must start with YAML frontmatter delimited by --- lines.`, + ), + ], + }; + } + const document = parseDocument(match[1], { prettyErrors: false, uniqueKeys: true }); + if (document.errors.length > 0) { + return { + issues: document.errors.map((error) => + issue("invalid_claw_frontmatter", manifestPath, error.message), + ), + }; + } + let unsupportedFeature: string | undefined; + visit(document, { + Alias() { + unsupportedFeature ??= "aliases"; + }, + Node(_key, node) { + if (node.anchor) { + unsupportedFeature ??= "anchors"; + } else if (node.tag) { + unsupportedFeature ??= "explicit tags"; + } + }, + Pair(_key, pair) { + if (!isScalar(pair.key) || typeof pair.key.value !== "string") { + unsupportedFeature ??= "non-string mapping keys"; + } else if (pair.key.value === "<<") { + unsupportedFeature ??= "merge keys"; + } + }, + }); + if (unsupportedFeature) { + return { + issues: [ + issue( + "unsupported_claw_yaml_feature", + manifestPath, + `${manifestPath} uses ${unsupportedFeature}; CLAW.md frontmatter must map directly to JSON data.`, + ), + ], + }; + } + try { + return { value: document.toJSON() }; + } catch (error) { + return { + issues: [ + issue( + "invalid_claw_frontmatter", + manifestPath, + error instanceof Error ? error.message : "Could not parse Claw frontmatter.", + ), + ], + }; + } + } + try { + return { value: JSON.parse(raw) as unknown }; + } catch (error) { + return { + issues: [ + issue( + "invalid_claw_json", + manifestPath, + error instanceof Error ? error.message : "Could not parse Claw JSON.", + ), + ], + }; + } +} + +export function validateClawPackageContents(input: { + packageName: string; + version: string; + packageJson: unknown; + files: readonly ClawPackageTextFile[]; +}): + | { ok: true; value: ValidatedClawPackage } + | { ok: false; issues: ClawPackageValidationIssue[] } { + const issues: ClawPackageValidationIssue[] = []; + if (!isRecord(input.packageJson)) { + return { + ok: false, + issues: [ + issue("missing_package_json", "package.json", "Claw packages require package.json."), + ], + }; + } + const declaredName = typeof input.packageJson.name === "string" ? input.packageJson.name : ""; + const declaredVersion = + typeof input.packageJson.version === "string" ? input.packageJson.version : ""; + const openclaw = isRecord(input.packageJson.openclaw) ? input.packageJson.openclaw : undefined; + const manifestPath = typeof openclaw?.claw === "string" ? openclaw.claw : ""; + if (declaredName !== input.packageName) { + issues.push( + issue("package_name_mismatch", "package.json.name", `Must match ${input.packageName}.`), + ); + } + if (!EXACT_VERSION_PATTERN.test(declaredVersion) || declaredVersion !== input.version) { + issues.push( + issue( + "package_version_mismatch", + "package.json.version", + `Must be exact semver ${input.version}.`, + ), + ); + } + if (!manifestPath || !isSafeClawPackagePath(manifestPath)) { + issues.push( + issue( + "invalid_claw_manifest_path", + "package.json.openclaw.claw", + "Must name a safe package-relative Claw manifest path.", + ), + ); + } + if (issues.length > 0) return { ok: false, issues }; + + const fileByPath = new Map(); + const portablePaths = new Set(); + for (const file of input.files) { + if (!isSafeClawPackagePath(file.path)) { + issues.push( + issue( + "invalid_package_path", + file.path, + "Package files must use safe canonical package-relative paths.", + ), + ); + continue; + } + const key = portablePathKey(file.path); + if (portablePaths.has(key)) { + issues.push( + issue( + "duplicate_portable_path", + file.path, + "Package paths must be unique across supported filesystems.", + ), + ); + } else { + portablePaths.add(key); + fileByPath.set(file.path, file); + } + } + const manifestFile = fileByPath.get(manifestPath); + if (!manifestFile || manifestFile.text === undefined) { + issues.push( + issue( + "missing_claw_manifest", + manifestPath, + "The declared Claw manifest is missing or is not UTF-8 text.", + ), + ); + return { ok: false, issues }; + } + const parsed = parseManifestDocument(manifestFile.text, manifestPath); + if (parsed.issues) return { ok: false, issues: parsed.issues }; + const validated = validateClawManifest(parsed.value); + if (!validated.ok) { + return { + ok: false, + issues: validated.issues.map((entry) => + issue("invalid_claw_manifest", entry.path, entry.message), + ), + }; + } + + const sources = [ + ...Object.values(validated.manifest.workspace?.bootstrapFiles ?? {}).map( + (entry) => entry.source, + ), + ...(validated.manifest.workspace?.files ?? []).map((entry) => entry.source), + ]; + for (const source of sources) { + if (!fileByPath.has(source)) { + issues.push( + issue( + "missing_workspace_source", + source, + "Declared workspace source is missing from the package.", + ), + ); + } + } + if (issues.length > 0) return { ok: false, issues }; + return { + ok: true, + value: { + manifestPath, + manifest: validated.manifest, + summary: summarizeClawManifest(validated.manifest), + }, + }; +} diff --git a/packages/clawhub/src/schema/claws.ts b/packages/clawhub/src/schema/claws.ts new file mode 100644 index 0000000000..18e5070bb6 --- /dev/null +++ b/packages/clawhub/src/schema/claws.ts @@ -0,0 +1,887 @@ +import { ArkErrors, type inferred, type } from "arktype"; +import { Cron } from "croner"; + +export const CLAW_SCHEMA_VERSION = 1 as const; +export const CLAW_SUMMARY_AGENT_NAME_MAX_CHARS = 128; +export const CLAW_SUMMARY_AGENT_DESCRIPTION_MAX_CHARS = 1_024; +export const CLAW_BOOTSTRAP_FILE_NAMES = [ + "AGENTS.md", + "SOUL.md", + "IDENTITY.md", + "TOOLS.md", + "HEARTBEAT.md", +] as const; + +const StringArraySchema = type("string[]"); +const WorkspaceSourceSchema = type({ "+": "reject", source: "string" }); +const ToolFilterSchema = type({ + "+": "reject", + include: StringArraySchema.optional(), + exclude: StringArraySchema.optional(), +}); +const StdioMcpServerSchema = type({ + "+": "reject", + command: "string", + transport: '"stdio"?', + args: StringArraySchema.optional(), + env: type({ "[string]": "string" }).optional(), + toolFilter: ToolFilterSchema.optional(), + timeout: "number?", + connectTimeout: "number?", +}); +const RemoteMcpServerSchema = type({ + "+": "reject", + url: "string", + transport: '"sse"|"streamable-http"', + auth: '"oauth"?', + toolFilter: ToolFilterSchema.optional(), + timeout: "number?", + connectTimeout: "number?", +}); + +export const ClawManifestSchema = type({ + "+": "reject", + schemaVersion: "1", + agent: { + "+": "reject", + id: "string", + name: "string?", + description: "string?", + identity: type({ + "+": "reject", + name: "string?", + theme: "string?", + emoji: "string?", + avatar: "string?", + }).optional(), + groupChat: type({ + "+": "reject", + mentionPatterns: StringArraySchema.optional(), + }).optional(), + sandbox: type({ + "+": "reject", + mode: '"off"|"non-main"|"all"?', + scope: '"session"|"agent"|"shared"?', + workspaceAccess: '"none"|"ro"|"rw"?', + }).optional(), + tools: type({ + "+": "reject", + profile: "string?", + allow: StringArraySchema.optional(), + alsoAllow: StringArraySchema.optional(), + deny: StringArraySchema.optional(), + fs: type({ + "+": "reject", + workspaceOnly: "boolean?", + }).optional(), + }).optional(), + memory: type({ + "+": "reject", + search: type({ + "+": "reject", + enabled: "boolean?", + rememberAcrossConversations: "boolean?", + sources: type("('memory' | 'sessions')[]").optional(), + }).optional(), + }).optional(), + heartbeat: type({ + "+": "reject", + every: "string?", + activeHours: type({ + "+": "reject", + start: "string?", + end: "string?", + timezone: "string?", + }).optional(), + lightContext: "boolean?", + isolatedSession: "boolean?", + skipWhenBusy: "boolean?", + timeoutSeconds: "number?", + }).optional(), + humanDelay: type({ + "+": "reject", + mode: '"off"|"natural"|"custom"?', + minMs: "number?", + maxMs: "number?", + }).optional(), + }, + workspace: type({ + "+": "reject", + bootstrapFiles: type({ + "+": "reject", + "AGENTS.md": WorkspaceSourceSchema.optional(), + "SOUL.md": WorkspaceSourceSchema.optional(), + "IDENTITY.md": WorkspaceSourceSchema.optional(), + "TOOLS.md": WorkspaceSourceSchema.optional(), + "HEARTBEAT.md": WorkspaceSourceSchema.optional(), + }).optional(), + files: type({ "+": "reject", source: "string", path: "string" }).array().optional(), + }).optional(), + packages: type({ + "+": "reject", + kind: '"skill"|"plugin"', + source: '"clawhub"', + ref: "string", + version: "string", + }) + .array() + .optional(), + mcpServers: type({ + "[string]": StdioMcpServerSchema.or(RemoteMcpServerSchema), + }).optional(), + cronJobs: type({ + "+": "reject", + id: "string", + name: "string?", + schedule: { "+": "reject", cron: "string", timezone: "string" }, + session: '"main"|"isolated"', + message: "string", + delivery: type({ + "+": "reject", + mode: '"none"|"announce"', + channel: '"last"?', + }).optional(), + }) + .array() + .optional(), +}); +export type ClawManifest = (typeof ClawManifestSchema)[inferred]; + +const ClawSummaryAgentNameSchema = type("string").narrow( + (value) => Array.from(value).length <= CLAW_SUMMARY_AGENT_NAME_MAX_CHARS, +); +const ClawSummaryAgentDescriptionSchema = type("string").narrow( + (value) => Array.from(value).length <= CLAW_SUMMARY_AGENT_DESCRIPTION_MAX_CHARS, +); + +export const ClawManifestSummarySchema = type({ + "+": "reject", + schemaVersion: "1", + agent: { + "+": "reject", + id: "string", + name: ClawSummaryAgentNameSchema.optional(), + description: ClawSummaryAgentDescriptionSchema.optional(), + }, + workspace: { + "+": "reject", + bootstrapFiles: "string[]", + fileCount: "number", + }, + packages: { + "+": "reject", + skillCount: "number", + pluginCount: "number", + }, + mcpServerCount: "number", + cronJobCount: "number", +}); +export type ClawManifestSummary = (typeof ClawManifestSummarySchema)[inferred]; + +export type ClawManifestValidationIssue = { path: string; message: string }; + +const AGENT_ID_PATTERN = /^[a-z][a-z0-9_-]{0,63}$/; +const EXACT_VERSION_PATTERN = + /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[A-Za-z-][0-9A-Za-z-]*)(?:\.(?:0|[1-9]\d*|\d*[A-Za-z-][0-9A-Za-z-]*))*))?(?:\+([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?$/; +const ENV_REFERENCE_PATTERN = /^\$\{[A-Z_][A-Z0-9_]*\}$/; +const PACKAGE_NAME_PATTERN = /^(?:@[a-z0-9][a-z0-9._-]*\/)?[a-z0-9][a-z0-9._-]*$/; +const WINDOWS_INVALID_PATH_CHARS = /[<>:"|?*]/; +const WINDOWS_RESERVED_PATH_SEGMENT = /^(?:con|prn|aux|nul|com[1-9]|lpt[1-9])(?:\..*)?$/i; +const AVATAR_MAX_BYTES = 2 * 1024 * 1024; +const AVATAR_MAX_DATA_URL_CHARS = 2_796_230; +const AVATAR_DATA_URL_PATTERN = /^data:image\/[^,]*,/i; +const AVATAR_EXTENSION_PATTERN = /\.(?:png|jpe?g|gif|webp|svg)$/i; +const BASE64_PAYLOAD_PATTERN = /^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/; +const PORTABLE_ENV_KEY_PATTERN = /^[A-Za-z_][A-Za-z0-9_]*$/; +// Snapshot of OpenClaw's host-wide blocked process environment policy for v1 conformance. +const BLOCKED_PROCESS_ENV_KEYS = new Set([ + "NODE_OPTIONS", + "NODE_PATH", + "NODE_REDIRECT_WARNINGS", + "NODE_REPL_EXTERNAL_MODULE", + "NODE_REPL_HISTORY", + "NODE_V8_COVERAGE", + "PYTHONHOME", + "PYTHONPATH", + "PERL5LIB", + "PERL5OPT", + "RUBYLIB", + "RUBYOPT", + "BASHOPTS", + "BASH_ENV", + "ENV", + "KSH_ENV", + "BROWSER", + "GIT_ALLOW_PROTOCOL", + "GIT_EDITOR", + "GIT_EXTERNAL_DIFF", + "GIT_DIR", + "GIT_WORK_TREE", + "GIT_COMMON_DIR", + "GIT_EXEC_PATH", + "GIT_INDEX_FILE", + "GIT_OBJECT_DIRECTORY", + "GIT_ALTERNATE_OBJECT_DIRECTORIES", + "GIT_NAMESPACE", + "GIT_PROTOCOL_FROM_USER", + "GIT_SEQUENCE_EDITOR", + "GIT_TEMPLATE_DIR", + "GIT_SSL_NO_VERIFY", + "GIT_SSL_CAINFO", + "GIT_SSL_CAPATH", + "CC", + "CXX", + "CARGO_BUILD_RUSTC", + "CARGO_BUILD_RUSTC_WRAPPER", + "CARGO_BUILD_RUSTC_WORKSPACE_WRAPPER", + "CARGO_BUILD_RUSTDOC", + "RUSTC", + "RUSTC_WRAPPER", + "RUSTC_WORKSPACE_WRAPPER", + "RUSTDOC", + "CMAKE_C_COMPILER", + "CMAKE_CXX_COMPILER", + "SHELL", + "SHELLOPTS", + "PS4", + "GCONV_PATH", + "IFS", + "SSLKEYLOGFILE", + "JAVA_OPTS", + "JAVA_TOOL_OPTIONS", + "_JAVA_OPTIONS", + "JDK_JAVA_OPTIONS", + "PYTHONBREAKPOINT", + "DOTNET_STARTUP_HOOKS", + "DOTNET_ADDITIONAL_DEPS", + "FPATH", + "GLIBC_TUNABLES", + "MAVEN_OPTS", + "MAKE", + "MAKEFLAGS", + "MFLAGS", + "SBT_OPTS", + "GRADLE_OPTS", + "ANT_OPTS", + "HGRCPATH", + "HGEDITOR", + "HGMERGE", + "EXINIT", + "VIMINIT", + "MYVIMRC", + "GVIMINIT", + "LUA_INIT", + "LUA_INIT_5_1", + "LUA_INIT_5_2", + "LUA_INIT_5_3", + "LUA_INIT_5_4", + "EMACSLOADPATH", + "RUBYSHELL", + "GIT_HOOK_PATH", + "SVN_EDITOR", + "SVN_SSH", + "BZR_EDITOR", + "BZR_SSH", + "BZR_PLUGIN_PATH", + "SUDO_ASKPASS", + "JULIA_EDITOR", + "CONFIG_SITE", + "CONFIG_SHELL", + "CMAKE_TOOLCHAIN_FILE", + "CATALINA_OPTS", + "CORECLR_PROFILER", + "HELM_PLUGINS", + "PACKER_PLUGIN_PATH", + "VAGRANT_VAGRANTFILE", + "ERL_AFLAGS", + "ERL_FLAGS", + "ERL_ZFLAGS", + "ELIXIR_ERL_OPTIONS", + "R_ENVIRON", + "R_PROFILE", + "R_ENVIRON_USER", + "R_PROFILE_USER", + "TCLLIBPATH", + "HOSTALIASES", +]); +const BLOCKED_PROCESS_ENV_PREFIXES = ["DYLD_", "LD_", "BASH_FUNC_"] as const; + +function isStrictNonEmpty(value: string): boolean { + return value.length > 0 && value === value.trim(); +} + +function isBlockedProcessEnvKey(value: string): boolean { + const upper = value.toUpperCase(); + return ( + BLOCKED_PROCESS_ENV_KEYS.has(upper) || + BLOCKED_PROCESS_ENV_PREFIXES.some((prefix) => upper.startsWith(prefix)) + ); +} + +function isSafePackagePath(value: string): boolean { + const normalized = value.replaceAll("\\", "/"); + if ( + !isStrictNonEmpty(normalized) || + normalized.startsWith("/") || + /^[A-Za-z]:\//.test(normalized) + ) { + return false; + } + return normalized + .split("/") + .every( + (segment) => + segment !== "" && + segment !== "." && + segment !== ".." && + !WINDOWS_INVALID_PATH_CHARS.test(segment) && + !Array.from(segment).some((character) => character.charCodeAt(0) <= 0x1f) && + !segment.endsWith(".") && + !segment.endsWith(" ") && + !WINDOWS_RESERVED_PATH_SEGMENT.test(segment), + ); +} + +function portablePathKey(value: string): string { + return value.replaceAll("\\", "/").normalize("NFC").toLowerCase(); +} + +function isPortableAvatar(value: string): boolean { + if (AVATAR_DATA_URL_PATTERN.test(value)) { + if (value.length > AVATAR_MAX_DATA_URL_CHARS) return false; + const comma = value.indexOf(","); + if (comma < 0) return false; + const metadata = value.slice(0, comma); + const payload = value.slice(comma + 1); + try { + const base64 = /;base64(?:;|$)/i.test(metadata); + if (payload.length === 0 || (base64 && !BASE64_PAYLOAD_PATTERN.test(payload))) return false; + const byteLength = base64 + ? (payload.length / 4) * 3 - (payload.endsWith("==") ? 2 : payload.endsWith("=") ? 1 : 0) + : new TextEncoder().encode(decodeURIComponent(payload)).byteLength; + return byteLength > 0 && byteLength <= AVATAR_MAX_BYTES; + } catch { + return false; + } + } + return isSafePackagePath(value) && AVATAR_EXTENSION_PATTERN.test(value); +} + +function conflictsWithWorkspaceTarget(targets: Set, candidate: string): boolean { + for (const target of targets) { + if ( + target === candidate || + target.startsWith(`${candidate}/`) || + candidate.startsWith(`${target}/`) + ) { + return true; + } + } + return false; +} + +function isValidDuration(value: string): boolean { + const trimmed = value.trim().toLowerCase(); + if (!trimmed) return false; + const multipliers: Record = { + ms: 1, + s: 1_000, + m: 60_000, + h: 3_600_000, + d: 86_400_000, + }; + const single = /^(\d+(?:\.\d+)?)(ms|s|m|h|d)?$/.exec(trimmed); + if (single) { + const durationMs = Math.round(Number(single[1]) * multipliers[single[2] ?? "m"]); + return durationMs >= 0 && Number.isSafeInteger(durationMs); + } + let totalMs = 0; + let consumed = 0; + for (const match of trimmed.matchAll(/(\d+(?:\.\d+)?)(ms|s|m|h|d)/g)) { + if (match.index !== consumed) return false; + totalMs += Number(match[1]) * multipliers[match[2]]; + consumed += match[0].length; + } + return ( + consumed === trimmed.length && + consumed > 0 && + Math.round(totalMs) >= 0 && + Number.isSafeInteger(Math.round(totalMs)) + ); +} + +function packageManagerArtifacts(command: string, args: string[]): string[] | undefined { + const executable = command + .split(/[\\/]/) + .at(-1) + ?.replace(/\.(?:cmd|exe)$/i, "") + .toLowerCase(); + let start = 0; + if (executable === "npm") { + if (args[0] !== "exec" && args[0] !== "x") return [""]; + start = 1; + } else if (executable === "bun") { + if (args[0] !== "x") return [""]; + start = 1; + } else if (executable === "pnpm" || executable === "yarn") { + if (args[0] !== "dlx") return [""]; + start = 1; + } else if (executable !== "npx" && executable !== "pnpx" && executable !== "bunx") { + return undefined; + } + const selected: string[] = []; + let positional: string | undefined; + for (let index = start; index < args.length; index += 1) { + const value = args[index]; + if (!value) continue; + if (value === "--") { + positional = args[index + 1] ?? ""; + break; + } + if (value === "-p" || value === "--package") { + selected.push(args[index + 1] ?? ""); + index += 1; + continue; + } + if (value.startsWith("--package=")) { + selected.push(value.slice("--package=".length)); + continue; + } + if (positional === undefined && !value.startsWith("-")) { + positional = value; + break; + } + } + return selected.length > 0 ? selected : [positional ?? ""]; +} + +function truncateSummaryText(value: string | undefined, maxChars: number): string | undefined { + if (!value) return undefined; + return Array.from(value).slice(0, maxChars).join(""); +} + +function isPackageManagerArtifactPinned(command: string, args: string[]): boolean | undefined { + const artifacts = packageManagerArtifacts(command, args); + if (artifacts === undefined) return undefined; + return artifacts.every((artifact) => { + const separator = artifact.lastIndexOf("@"); + const scopedSlash = artifact.startsWith("@") ? artifact.indexOf("/") : -1; + return ( + separator > 0 && + separator > scopedSlash && + EXACT_VERSION_PATTERN.test(artifact.slice(separator + 1)) + ); + }); +} + +function isValidHeartbeatTime(value: string, allow24: boolean): boolean { + const match = /^([01]\d|2[0-4]):([0-5]\d)$/.exec(value); + if (!match) return false; + const hour = Number(match[1]); + const minute = Number(match[2]); + return hour !== 24 || (allow24 && minute === 0); +} + +function isValidTimezone(value: string): boolean { + try { + new Intl.DateTimeFormat("en-US", { timeZone: value }).format(); + return true; + } catch { + return false; + } +} + +function arkIssues(errors: ArkErrors): ClawManifestValidationIssue[] { + return Array.from(errors, (error) => ({ + path: error.path.length > 0 ? `$.${error.path.join(".")}` : "$", + message: error.description ?? "Invalid value.", + })); +} + +function pushNonEmpty( + issues: ClawManifestValidationIssue[], + path: string, + value: string | undefined, +): void { + if (value !== undefined && !isStrictNonEmpty(value)) { + issues.push({ path, message: "Must be non-empty without leading or trailing whitespace." }); + } +} + +function pushNonEmptyArray( + issues: ClawManifestValidationIssue[], + path: string, + values: string[] | undefined, + requireEntry: boolean, +): void { + if (requireEntry && values !== undefined && values.length === 0) { + issues.push({ path, message: "Must contain at least one value." }); + } + for (const [index, value] of (values ?? []).entries()) { + pushNonEmpty(issues, `${path}.${index}`, value); + } +} + +export function validateClawManifest( + value: unknown, +): { ok: true; manifest: ClawManifest } | { ok: false; issues: ClawManifestValidationIssue[] } { + const parsed = ClawManifestSchema(value); + if (parsed instanceof ArkErrors) return { ok: false, issues: arkIssues(parsed) }; + + const issues: ClawManifestValidationIssue[] = []; + if (!AGENT_ID_PATTERN.test(parsed.agent.id)) { + issues.push({ path: "$.agent.id", message: "Invalid portable agent id." }); + } + pushNonEmpty(issues, "$.agent.name", parsed.agent.name); + pushNonEmpty(issues, "$.agent.description", parsed.agent.description); + for (const field of ["name", "theme", "emoji", "avatar"] as const) { + pushNonEmpty(issues, `$.agent.identity.${field}`, parsed.agent.identity?.[field]); + } + pushNonEmptyArray( + issues, + "$.agent.groupChat.mentionPatterns", + parsed.agent.groupChat?.mentionPatterns, + true, + ); + pushNonEmpty(issues, "$.agent.tools.profile", parsed.agent.tools?.profile); + pushNonEmptyArray(issues, "$.agent.tools.allow", parsed.agent.tools?.allow, true); + pushNonEmptyArray(issues, "$.agent.tools.alsoAllow", parsed.agent.tools?.alsoAllow, true); + pushNonEmptyArray(issues, "$.agent.tools.deny", parsed.agent.tools?.deny, true); + if (parsed.agent.tools?.allow && parsed.agent.tools.alsoAllow) { + issues.push({ + path: "$.agent.tools.alsoAllow", + message: "Must not be combined with tools.allow.", + }); + } + if (parsed.agent.memory?.search?.sources?.length === 0) { + issues.push({ + path: "$.agent.memory.search.sources", + message: "Must contain at least one source.", + }); + } + if ( + parsed.agent.memory?.search?.sources?.includes("sessions") && + parsed.agent.memory.search.rememberAcrossConversations !== true + ) { + issues.push({ + path: "$.agent.memory.search.rememberAcrossConversations", + message: "Must be true when memory.search.sources includes sessions.", + }); + } + const heartbeat = parsed.agent.heartbeat; + pushNonEmpty(issues, "$.agent.heartbeat.every", heartbeat?.every); + pushNonEmpty(issues, "$.agent.heartbeat.activeHours.start", heartbeat?.activeHours?.start); + pushNonEmpty(issues, "$.agent.heartbeat.activeHours.end", heartbeat?.activeHours?.end); + pushNonEmpty(issues, "$.agent.heartbeat.activeHours.timezone", heartbeat?.activeHours?.timezone); + if (heartbeat?.every) { + if (!isValidDuration(heartbeat.every)) { + issues.push({ + path: "$.agent.heartbeat.every", + message: "Must be a valid duration.", + }); + } + } + if ( + heartbeat?.activeHours?.start !== undefined && + !isValidHeartbeatTime(heartbeat.activeHours.start, false) + ) { + issues.push({ + path: "$.agent.heartbeat.activeHours.start", + message: "Must be a valid 24-hour start time.", + }); + } + if ( + heartbeat?.activeHours?.end !== undefined && + !isValidHeartbeatTime(heartbeat.activeHours.end, true) + ) { + issues.push({ + path: "$.agent.heartbeat.activeHours.end", + message: "Must be a valid 24-hour end time.", + }); + } + if ( + heartbeat?.activeHours?.timezone !== undefined && + !isValidTimezone(heartbeat.activeHours.timezone) + ) { + issues.push({ + path: "$.agent.heartbeat.activeHours.timezone", + message: "Must be a valid IANA timezone.", + }); + } + if ( + heartbeat?.timeoutSeconds !== undefined && + (!Number.isInteger(heartbeat.timeoutSeconds) || heartbeat.timeoutSeconds <= 0) + ) { + issues.push({ + path: "$.agent.heartbeat.timeoutSeconds", + message: "Must be a positive integer.", + }); + } + for (const field of ["minMs", "maxMs"] as const) { + const delayValue = parsed.agent.humanDelay?.[field]; + if (delayValue !== undefined && (!Number.isInteger(delayValue) || delayValue < 0)) { + issues.push({ + path: `$.agent.humanDelay.${field}`, + message: "Must be a nonnegative integer.", + }); + } + } + const workspaceTargets = new Set(); + for (const name of CLAW_BOOTSTRAP_FILE_NAMES) { + const source = parsed.workspace?.bootstrapFiles?.[name]?.source; + if (source === undefined) continue; + if (!isSafePackagePath(source)) { + issues.push({ + path: `$.workspace.bootstrapFiles.${name}.source`, + message: "Must be a safe package-relative path.", + }); + } + workspaceTargets.add(portablePathKey(name)); + } + for (const [index, file] of (parsed.workspace?.files ?? []).entries()) { + if (!isSafePackagePath(file.source)) { + issues.push({ + path: `$.workspace.files.${index}.source`, + message: "Must be a safe package-relative path.", + }); + } + if (!isSafePackagePath(file.path)) { + issues.push({ + path: `$.workspace.files.${index}.path`, + message: "Must be a safe package-relative path.", + }); + } + const destinationKey = portablePathKey(file.path); + if (conflictsWithWorkspaceTarget(workspaceTargets, destinationKey)) { + issues.push({ + path: `$.workspace.files.${index}.path`, + message: "Workspace destination is declared more than once.", + }); + } + workspaceTargets.add(destinationKey); + } + const avatar = parsed.agent.identity?.avatar; + if (avatar !== undefined) { + if (!isPortableAvatar(avatar)) { + issues.push({ + path: "$.agent.identity.avatar", + message: "Must be a bounded image data URL or managed workspace-relative image path.", + }); + } else if ( + !AVATAR_DATA_URL_PATTERN.test(avatar) && + !workspaceTargets.has(portablePathKey(avatar)) + ) { + issues.push({ + path: "$.agent.identity.avatar", + message: "Workspace-relative avatar must match a workspace.files destination.", + }); + } + } + + const packageKeys = new Set(); + for (const [index, pkg] of (parsed.packages ?? []).entries()) { + pushNonEmpty(issues, `$.packages.${index}.ref`, pkg.ref); + if (!PACKAGE_NAME_PATTERN.test(pkg.ref)) { + issues.push({ + path: `$.packages.${index}.ref`, + message: "Must be a lowercase ClawHub package name.", + }); + } + if (!EXACT_VERSION_PATTERN.test(pkg.version)) { + issues.push({ + path: `$.packages.${index}.version`, + message: "Must be an exact semantic version.", + }); + } + const key = `${pkg.kind}:${pkg.source}:${pkg.ref.toLowerCase()}`; + if (packageKeys.has(key)) { + issues.push({ + path: `$.packages.${index}`, + message: "Package is declared more than once.", + }); + } + packageKeys.add(key); + } + + for (const [name, server] of Object.entries(parsed.mcpServers ?? {})) { + if (!AGENT_ID_PATTERN.test(name)) { + issues.push({ + path: `$.mcpServers.${name}`, + message: "Invalid MCP server name.", + }); + } + if ("url" in server) { + pushNonEmpty(issues, `$.mcpServers.${name}.url`, server.url); + try { + const url = new URL(server.url); + const loopback = ["localhost", "127.0.0.1", "[::1]"].includes(url.hostname); + if (url.protocol !== "https:" && !(url.protocol === "http:" && loopback)) { + throw new Error("protocol"); + } + if (url.username || url.password || url.hash) { + issues.push({ + path: `$.mcpServers.${name}.url`, + message: "Must not contain embedded credentials or fragments.", + }); + } + } catch { + issues.push({ + path: `$.mcpServers.${name}.url`, + message: "Must use HTTPS, except HTTP on an exact loopback host.", + }); + } + } + if ("command" in server) { + pushNonEmpty(issues, `$.mcpServers.${name}.command`, server.command); + pushNonEmptyArray(issues, `$.mcpServers.${name}.args`, server.args, false); + if (isPackageManagerArtifactPinned(server.command, server.args ?? []) === false) { + issues.push({ + path: `$.mcpServers.${name}.args`, + message: "Package-manager MCP commands must select one exact immutable package version.", + }); + } + } + pushNonEmptyArray( + issues, + `$.mcpServers.${name}.toolFilter.include`, + server.toolFilter?.include, + true, + ); + pushNonEmptyArray( + issues, + `$.mcpServers.${name}.toolFilter.exclude`, + server.toolFilter?.exclude, + true, + ); + for (const field of ["include", "exclude"] as const) { + const seen = new Set(); + for (const [index, entry] of (server.toolFilter?.[field] ?? []).entries()) { + if (entry.includes("?") || entry.includes("[") || entry.includes("]")) { + issues.push({ + path: `$.mcpServers.${name}.toolFilter.${field}.${index}`, + message: "Tool filters support only exact names and * wildcards.", + }); + } + if (seen.has(entry)) { + issues.push({ + path: `$.mcpServers.${name}.toolFilter.${field}.${index}`, + message: "Tool filter entries must be unique.", + }); + } + seen.add(entry); + } + } + if ("env" in server) { + for (const [key, envValue] of Object.entries(server.env ?? {})) { + pushNonEmpty(issues, `$.mcpServers.${name}.env`, key); + if (!PORTABLE_ENV_KEY_PATTERN.test(key)) { + issues.push({ + path: `$.mcpServers.${name}.env.${key}`, + message: "Invalid portable environment key.", + }); + } + if (isBlockedProcessEnvKey(key)) { + issues.push({ + path: `$.mcpServers.${name}.env.${key}`, + message: "Environment key is blocked by the spawned-process safety policy.", + }); + } + if (!ENV_REFERENCE_PATTERN.test(envValue)) { + issues.push({ + path: `$.mcpServers.${name}.env.${key}`, + message: "Must be an unresolved ${ENV_VAR} reference.", + }); + } + } + } + if (server.timeout !== undefined && (!Number.isFinite(server.timeout) || server.timeout <= 0)) { + issues.push({ + path: `$.mcpServers.${name}.timeout`, + message: "Must be positive.", + }); + } + if ( + server.connectTimeout !== undefined && + (!Number.isFinite(server.connectTimeout) || server.connectTimeout <= 0) + ) { + issues.push({ + path: `$.mcpServers.${name}.connectTimeout`, + message: "Must be positive.", + }); + } + } + + const cronIds = new Set(); + for (const [index, job] of (parsed.cronJobs ?? []).entries()) { + if (!AGENT_ID_PATTERN.test(job.id)) { + issues.push({ + path: `$.cronJobs.${index}.id`, + message: "Invalid cron job id.", + }); + } + pushNonEmpty(issues, `$.cronJobs.${index}.name`, job.name); + pushNonEmpty(issues, `$.cronJobs.${index}.schedule.cron`, job.schedule.cron); + pushNonEmpty(issues, `$.cronJobs.${index}.schedule.timezone`, job.schedule.timezone); + pushNonEmpty(issues, `$.cronJobs.${index}.message`, job.message); + try { + if (job.schedule.cron.trim().split(/\s+/).length !== 5) throw new Error("fields"); + const cron = new Cron(job.schedule.cron.trim(), { + timezone: job.schedule.timezone.trim(), + catch: false, + }); + if (!cron.nextRun(new Date())) throw new Error("No future run"); + } catch { + issues.push({ + path: `$.cronJobs.${index}.schedule`, + message: "Must contain a valid cron expression and timezone.", + }); + } + if ( + (job.delivery?.mode === "none" && job.delivery.channel !== undefined) || + (job.delivery?.mode === "announce" && job.delivery.channel !== "last") + ) { + issues.push({ + path: `$.cronJobs.${index}.delivery`, + message: 'Must be { mode: "none" } or { mode: "announce", channel: "last" }.', + }); + } + if (cronIds.has(job.id)) { + issues.push({ + path: `$.cronJobs.${index}.id`, + message: "Cron job id is declared more than once.", + }); + } + cronIds.add(job.id); + } + + return issues.length > 0 ? { ok: false, issues } : { ok: true, manifest: parsed }; +} + +export function summarizeClawManifest(manifest: ClawManifest): ClawManifestSummary { + const packages = manifest.packages ?? []; + const agentName = truncateSummaryText(manifest.agent.name, CLAW_SUMMARY_AGENT_NAME_MAX_CHARS); + const agentDescription = truncateSummaryText( + manifest.agent.description, + CLAW_SUMMARY_AGENT_DESCRIPTION_MAX_CHARS, + ); + return { + schemaVersion: CLAW_SCHEMA_VERSION, + agent: { + id: manifest.agent.id, + ...(agentName ? { name: agentName } : {}), + ...(agentDescription ? { description: agentDescription } : {}), + }, + workspace: { + bootstrapFiles: CLAW_BOOTSTRAP_FILE_NAMES.filter( + (name) => manifest.workspace?.bootstrapFiles?.[name] !== undefined, + ), + fileCount: manifest.workspace?.files?.length ?? 0, + }, + packages: { + skillCount: packages.filter((pkg) => pkg.kind === "skill").length, + pluginCount: packages.filter((pkg) => pkg.kind === "plugin").length, + }, + mcpServerCount: Object.keys(manifest.mcpServers ?? {}).length, + cronJobCount: manifest.cronJobs?.length ?? 0, + }; +} diff --git a/packages/clawhub/src/schema/packages.ts b/packages/clawhub/src/schema/packages.ts index 8cd71f9c8e..fb8eba35d0 100644 --- a/packages/clawhub/src/schema/packages.ts +++ b/packages/clawhub/src/schema/packages.ts @@ -1,7 +1,7 @@ import { type inferred, type } from "arktype"; import { CliPublishFileSchema, PublishSourceSchema } from "./schemas.js"; -export const PackageFamilySchema = type('"skill"|"code-plugin"|"bundle-plugin"'); +export const PackageFamilySchema = type('"skill"|"code-plugin"|"bundle-plugin"|"claw"'); export type PackageFamily = (typeof PackageFamilySchema)[inferred]; export const PackageChannelSchema = type('"official"|"community"|"private"'); diff --git a/packages/schema/dist/clawPackage.d.ts b/packages/schema/dist/clawPackage.d.ts new file mode 100644 index 0000000000..4121d79711 --- /dev/null +++ b/packages/schema/dist/clawPackage.d.ts @@ -0,0 +1,28 @@ +import { type ClawManifest, type ClawManifestSummary } from "./claws.js"; +export type ClawPackageTextFile = { + path: string; + text?: string; +}; +export type ClawPackageValidationIssue = { + code: string; + path: string; + message: string; +}; +export type ValidatedClawPackage = { + manifestPath: string; + manifest: ClawManifest; + summary: ClawManifestSummary; +}; +export declare function isSafeClawPackagePath(value: string): boolean; +export declare function validateClawPackageContents(input: { + packageName: string; + version: string; + packageJson: unknown; + files: readonly ClawPackageTextFile[]; +}): { + ok: true; + value: ValidatedClawPackage; +} | { + ok: false; + issues: ClawPackageValidationIssue[]; +}; diff --git a/packages/schema/dist/clawPackage.js b/packages/schema/dist/clawPackage.js new file mode 100644 index 0000000000..4aa038def9 --- /dev/null +++ b/packages/schema/dist/clawPackage.js @@ -0,0 +1,180 @@ +import { isScalar, parseDocument, visit } from "yaml"; +import { summarizeClawManifest, validateClawManifest, } from "./claws.js"; +const EXACT_VERSION_PATTERN = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[A-Za-z-][0-9A-Za-z-]*)(?:\.(?:0|[1-9]\d*|\d*[A-Za-z-][0-9A-Za-z-]*))*))?(?:\+([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?$/; +const WINDOWS_INVALID_PATH_CHARS = /[<>:"|?*]/; +const WINDOWS_RESERVED_PATH_SEGMENT = /^(?:con|prn|aux|nul|com[1-9]|lpt[1-9])(?:\..*)?$/i; +function isRecord(value) { + return Boolean(value) && typeof value === "object" && !Array.isArray(value); +} +export function isSafeClawPackagePath(value) { + const normalized = value.replaceAll("\\", "/"); + if (!normalized || + normalized !== value || + normalized !== normalized.trim() || + normalized.startsWith("/") || + /^[A-Za-z]:\//.test(normalized)) { + return false; + } + return normalized + .split("/") + .every((segment) => segment !== "" && + segment !== "." && + segment !== ".." && + !WINDOWS_INVALID_PATH_CHARS.test(segment) && + !Array.from(segment).some((character) => character.charCodeAt(0) <= 0x1f) && + !segment.endsWith(".") && + !segment.endsWith(" ") && + !WINDOWS_RESERVED_PATH_SEGMENT.test(segment)); +} +function portablePathKey(value) { + return value.replaceAll("\\", "/").normalize("NFC").toLowerCase(); +} +function issue(code, path, message) { + return { code, path, message }; +} +function parseManifestDocument(raw, manifestPath) { + const filename = manifestPath.replaceAll("\\", "/").split("/").at(-1)?.toLowerCase(); + if (filename === "claw.md") { + const markdown = raw.startsWith("\uFEFF") ? raw.slice(1) : raw; + const match = markdown.match(/^---\r?\n([\s\S]*?)\r?\n---(?:\r?\n|$)/); + if (!match) { + return { + issues: [ + issue("missing_claw_frontmatter", manifestPath, `${manifestPath} must start with YAML frontmatter delimited by --- lines.`), + ], + }; + } + const document = parseDocument(match[1], { prettyErrors: false, uniqueKeys: true }); + if (document.errors.length > 0) { + return { + issues: document.errors.map((error) => issue("invalid_claw_frontmatter", manifestPath, error.message)), + }; + } + let unsupportedFeature; + visit(document, { + Alias() { + unsupportedFeature ??= "aliases"; + }, + Node(_key, node) { + if (node.anchor) { + unsupportedFeature ??= "anchors"; + } + else if (node.tag) { + unsupportedFeature ??= "explicit tags"; + } + }, + Pair(_key, pair) { + if (!isScalar(pair.key) || typeof pair.key.value !== "string") { + unsupportedFeature ??= "non-string mapping keys"; + } + else if (pair.key.value === "<<") { + unsupportedFeature ??= "merge keys"; + } + }, + }); + if (unsupportedFeature) { + return { + issues: [ + issue("unsupported_claw_yaml_feature", manifestPath, `${manifestPath} uses ${unsupportedFeature}; CLAW.md frontmatter must map directly to JSON data.`), + ], + }; + } + try { + return { value: document.toJSON() }; + } + catch (error) { + return { + issues: [ + issue("invalid_claw_frontmatter", manifestPath, error instanceof Error ? error.message : "Could not parse Claw frontmatter."), + ], + }; + } + } + try { + return { value: JSON.parse(raw) }; + } + catch (error) { + return { + issues: [ + issue("invalid_claw_json", manifestPath, error instanceof Error ? error.message : "Could not parse Claw JSON."), + ], + }; + } +} +export function validateClawPackageContents(input) { + const issues = []; + if (!isRecord(input.packageJson)) { + return { + ok: false, + issues: [ + issue("missing_package_json", "package.json", "Claw packages require package.json."), + ], + }; + } + const declaredName = typeof input.packageJson.name === "string" ? input.packageJson.name : ""; + const declaredVersion = typeof input.packageJson.version === "string" ? input.packageJson.version : ""; + const openclaw = isRecord(input.packageJson.openclaw) ? input.packageJson.openclaw : undefined; + const manifestPath = typeof openclaw?.claw === "string" ? openclaw.claw : ""; + if (declaredName !== input.packageName) { + issues.push(issue("package_name_mismatch", "package.json.name", `Must match ${input.packageName}.`)); + } + if (!EXACT_VERSION_PATTERN.test(declaredVersion) || declaredVersion !== input.version) { + issues.push(issue("package_version_mismatch", "package.json.version", `Must be exact semver ${input.version}.`)); + } + if (!manifestPath || !isSafeClawPackagePath(manifestPath)) { + issues.push(issue("invalid_claw_manifest_path", "package.json.openclaw.claw", "Must name a safe package-relative Claw manifest path.")); + } + if (issues.length > 0) + return { ok: false, issues }; + const fileByPath = new Map(); + const portablePaths = new Set(); + for (const file of input.files) { + if (!isSafeClawPackagePath(file.path)) { + issues.push(issue("invalid_package_path", file.path, "Package files must use safe canonical package-relative paths.")); + continue; + } + const key = portablePathKey(file.path); + if (portablePaths.has(key)) { + issues.push(issue("duplicate_portable_path", file.path, "Package paths must be unique across supported filesystems.")); + } + else { + portablePaths.add(key); + fileByPath.set(file.path, file); + } + } + const manifestFile = fileByPath.get(manifestPath); + if (!manifestFile || manifestFile.text === undefined) { + issues.push(issue("missing_claw_manifest", manifestPath, "The declared Claw manifest is missing or is not UTF-8 text.")); + return { ok: false, issues }; + } + const parsed = parseManifestDocument(manifestFile.text, manifestPath); + if (parsed.issues) + return { ok: false, issues: parsed.issues }; + const validated = validateClawManifest(parsed.value); + if (!validated.ok) { + return { + ok: false, + issues: validated.issues.map((entry) => issue("invalid_claw_manifest", entry.path, entry.message)), + }; + } + const sources = [ + ...Object.values(validated.manifest.workspace?.bootstrapFiles ?? {}).map((entry) => entry.source), + ...(validated.manifest.workspace?.files ?? []).map((entry) => entry.source), + ]; + for (const source of sources) { + if (!fileByPath.has(source)) { + issues.push(issue("missing_workspace_source", source, "Declared workspace source is missing from the package.")); + } + } + if (issues.length > 0) + return { ok: false, issues }; + return { + ok: true, + value: { + manifestPath, + manifest: validated.manifest, + summary: summarizeClawManifest(validated.manifest), + }, + }; +} +//# sourceMappingURL=clawPackage.js.map \ No newline at end of file diff --git a/packages/schema/dist/clawPackage.js.map b/packages/schema/dist/clawPackage.js.map new file mode 100644 index 0000000000..5321cbcdac --- /dev/null +++ b/packages/schema/dist/clawPackage.js.map @@ -0,0 +1 @@ +{"version":3,"file":"clawPackage.js","sourceRoot":"","sources":["../src/clawPackage.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/packages/schema/dist/index.d.ts b/packages/schema/dist/index.d.ts index d03a08a7d4..8d58b85d55 100644 --- a/packages/schema/dist/index.d.ts +++ b/packages/schema/dist/index.d.ts @@ -1,6 +1,7 @@ export type { ArkValidator } from "./ark.js"; export { formatArkErrors, parseArk } from "./ark.js"; export * from "./claws.js"; +export * from "./clawPackage.js"; export * from "./catalogFeed.js"; export * from "./catalogMetadata.js"; export * from "./docsLinks.js"; diff --git a/packages/schema/dist/index.js b/packages/schema/dist/index.js index 2ded7f40c9..6633f43e07 100644 --- a/packages/schema/dist/index.js +++ b/packages/schema/dist/index.js @@ -1,5 +1,6 @@ export { formatArkErrors, parseArk } from "./ark.js"; export * from "./claws.js"; +export * from "./clawPackage.js"; export * from "./catalogFeed.js"; export * from "./catalogMetadata.js"; export * from "./docsLinks.js"; diff --git a/packages/schema/dist/index.js.map b/packages/schema/dist/index.js.map deleted file mode 100644 index b5e68b2e75..0000000000 --- a/packages/schema/dist/index.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,eAAe,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;AACrD,cAAc,YAAY,CAAC;AAC3B,cAAc,kBAAkB,CAAC;AACjC,cAAc,sBAAsB,CAAC;AACrC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,cAAc,CAAC;AAC7B,cAAc,uBAAuB,CAAC;AACtC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,eAAe,CAAC;AAC9B,cAAc,uBAAuB,CAAC;AACtC,cAAc,qBAAqB,CAAC;AACpC,cAAc,0BAA0B,CAAC;AACzC,OAAO,EAAE,SAAS,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AACzD,cAAc,cAAc,CAAC;AAC7B,cAAc,gBAAgB,CAAC"} \ No newline at end of file diff --git a/packages/schema/dist/packages.d.ts b/packages/schema/dist/packages.d.ts index f8acebafe5..2741577011 100644 --- a/packages/schema/dist/packages.d.ts +++ b/packages/schema/dist/packages.d.ts @@ -10,7 +10,7 @@ export declare function getPackageScopeOwnerMismatch(name: string, ownerHandle: } | null; export declare const PackageFamilySchema: import("arktype/internal/variants/string.ts").StringType<"bundle-plugin" | "claw" | "code-plugin" | "skill", {}>; export type PackageFamily = (typeof PackageFamilySchema)[inferred]; -export declare const PackagePublishFamilySchema: import("arktype/internal/variants/string.ts").StringType<"bundle-plugin" | "code-plugin" | "skill", {}>; +export declare const PackagePublishFamilySchema: import("arktype/internal/variants/string.ts").StringType<"bundle-plugin" | "claw" | "code-plugin" | "skill", {}>; export type PackagePublishFamily = (typeof PackagePublishFamilySchema)[inferred]; export declare const PackageChannelSchema: import("arktype/internal/variants/string.ts").StringType<"community" | "official" | "private", {}>; export type PackageChannel = (typeof PackageChannelSchema)[inferred]; @@ -265,7 +265,7 @@ export declare const PackagePublishMetadataSchema: import("arktype/internal/vari name: string; displayName?: string | undefined; ownerHandle?: string | undefined; - family: "bundle-plugin" | "code-plugin" | "skill"; + family: "bundle-plugin" | "claw" | "code-plugin" | "skill"; version: string; changelog: string; manualOverrideReason?: string | undefined; @@ -293,7 +293,7 @@ export declare const ServerPackagePublishRequestSchema: import("arktype/internal name: string; displayName?: string | undefined; ownerHandle?: string | undefined; - family: "bundle-plugin" | "code-plugin" | "skill"; + family: "bundle-plugin" | "claw" | "code-plugin" | "skill"; version: string; changelog: string; manualOverrideReason?: string | undefined; diff --git a/packages/schema/dist/packages.js b/packages/schema/dist/packages.js index b3f1a5caa9..00a0aeb56f 100644 --- a/packages/schema/dist/packages.js +++ b/packages/schema/dist/packages.js @@ -23,8 +23,7 @@ export function getPackageScopeOwnerMismatch(name, ownerHandle) { }; } export const PackageFamilySchema = type('"skill"|"code-plugin"|"bundle-plugin"|"claw"'); -// Publication stays narrower until a family has a complete validation path. -export const PackagePublishFamilySchema = type('"skill"|"code-plugin"|"bundle-plugin"'); +export const PackagePublishFamilySchema = type('"skill"|"code-plugin"|"bundle-plugin"|"claw"'); export const PackageChannelSchema = type('"official"|"community"|"private"'); export const PackageVerificationTierSchema = type('"structural"|"source-linked"|"provenance-verified"|"rebuild-verified"'); export const PackageVerificationScopeSchema = type('"artifact-only"|"dependency-graph-aware"'); diff --git a/packages/schema/dist/packages.js.map b/packages/schema/dist/packages.js.map index fc72befb17..6fca5eaa48 100644 --- a/packages/schema/dist/packages.js.map +++ b/packages/schema/dist/packages.js.map @@ -1 +1 @@ -{"version":3,"file":"packages.js","sourceRoot":"","sources":["../src/packages.ts"],"names":[],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"packages.js","sourceRoot":"","sources":["../src/packages.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/packages/schema/package.json b/packages/schema/package.json index c86b85ba01..8c9d5847d7 100644 --- a/packages/schema/package.json +++ b/packages/schema/package.json @@ -32,7 +32,8 @@ "dependencies": { "arktype": "2.2.3", "croner": "10.0.1", - "unicode-case-folding": "1.1.1" + "unicode-case-folding": "1.1.1", + "yaml": "2.9.0" }, "devDependencies": { "@typescript/native": "npm:typescript@7.0.2", diff --git a/packages/schema/src/clawPackage.test.ts b/packages/schema/src/clawPackage.test.ts new file mode 100644 index 0000000000..bd37682e1c --- /dev/null +++ b/packages/schema/src/clawPackage.test.ts @@ -0,0 +1,257 @@ +import { describe, expect, it } from "vitest"; +import { validateClawPackageContents } from "./clawPackage.js"; + +const manifest = { + schemaVersion: 1, + agent: { id: "github-triage", name: "GitHub Triage", description: "Reviews issues." }, + workspace: { + bootstrapFiles: { "SOUL.md": { source: "workspace/SOUL.md" } }, + files: [{ source: "workspace/reference.md", path: "reference.md" }], + }, + packages: [{ kind: "skill", source: "clawhub", ref: "@acme/triage", version: "1.2.0" }], + mcpServers: {}, + cronJobs: [], +}; + +function packageJson(claw = "CLAW.md") { + return { + name: "@acme/github-triage", + version: "1.0.0", + openclaw: { claw }, + }; +} + +function files(manifestText = `---\n${JSON.stringify(manifest)}\n---\n# GitHub Triage\n`) { + return [ + { path: "package.json", text: JSON.stringify(packageJson()) }, + { path: "CLAW.md", text: manifestText }, + { path: "workspace/SOUL.md", text: "Be precise.\n" }, + { path: "workspace/reference.md", text: "Reference\n" }, + ]; +} + +describe("validateClawPackageContents", () => { + it("validates CLAW.md and derives its safe summary", () => { + const result = validateClawPackageContents({ + packageName: "@acme/github-triage", + version: "1.0.0", + packageJson: packageJson(), + files: files(), + }); + + expect(result).toEqual({ + ok: true, + value: expect.objectContaining({ + manifestPath: "CLAW.md", + summary: expect.objectContaining({ + agent: { + id: "github-triage", + name: "GitHub Triage", + description: "Reviews issues.", + }, + packages: { skillCount: 1, pluginCount: 0 }, + }), + }), + }); + }); + + it("accepts one UTF-8 BOM before CLAW.md frontmatter", () => { + const result = validateClawPackageContents({ + packageName: "@acme/github-triage", + version: "1.0.0", + packageJson: packageJson(), + files: files(`\uFEFF---\n${JSON.stringify(manifest)}\n---\n# GitHub Triage\n`), + }); + + expect(result.ok).toBe(true); + }); + + it.each([ + ["anchor", "agent: &agent { id: github-triage }"], + ["alias", "agent: { id: &id github-triage, name: *id }"], + ["merge key", "agent: { <<: { id: github-triage } }"], + ["explicit tag", "agent: { id: !!str github-triage }"], + ["non-string mapping key", "agent: { id: github-triage, true: nope }"], + ])("rejects CLAW.md YAML %s", (_label, declaration) => { + const result = validateClawPackageContents({ + packageName: "@acme/github-triage", + version: "1.0.0", + packageJson: packageJson(), + files: files( + [ + "---", + "schemaVersion: 1", + declaration, + "workspace: {}", + "packages: []", + "mcpServers: {}", + "cronJobs: []", + "---", + ].join("\n"), + ), + }); + + expect(result).toEqual({ + ok: false, + issues: [expect.objectContaining({ code: "unsupported_claw_yaml_feature" })], + }); + }); + + it("does not treat a suffix-only filename as CLAW.md", () => { + const result = validateClawPackageContents({ + packageName: "@acme/github-triage", + version: "1.0.0", + packageJson: packageJson("NOTCLAW.md"), + files: [ + ...files().filter((file) => file.path !== "CLAW.md"), + { path: "NOTCLAW.md", text: `---\n${JSON.stringify(manifest)}\n---\n` }, + ], + }); + + expect(result).toEqual({ + ok: false, + issues: [expect.objectContaining({ code: "invalid_claw_json" })], + }); + }); + + it("accepts the JSON compatibility manifest", () => { + const result = validateClawPackageContents({ + packageName: "@acme/github-triage", + version: "1.0.0", + packageJson: packageJson("openclaw.claw.json"), + files: [ + ...files().filter((file) => file.path !== "CLAW.md"), + { path: "openclaw.claw.json", text: JSON.stringify(manifest) }, + ], + }); + + expect(result.ok).toBe(true); + }); + + it("requires package identity to match the publication", () => { + const result = validateClawPackageContents({ + packageName: "@acme/other", + version: "2.0.0", + packageJson: packageJson(), + files: files(), + }); + + expect(result).toEqual({ + ok: false, + issues: expect.arrayContaining([ + expect.objectContaining({ code: "package_name_mismatch" }), + expect.objectContaining({ code: "package_version_mismatch" }), + ]), + }); + }); + + it.each([ + ["name", { ...packageJson(), name: " @acme/github-triage" }], + ["version", { ...packageJson(), version: "1.0.0 " }], + ["manifest path", packageJson(" CLAW.md")], + ])("does not trim padded package %s into validity", (_label, metadata) => { + const result = validateClawPackageContents({ + packageName: "@acme/github-triage", + version: "1.0.0", + packageJson: metadata, + files: files(), + }); + + expect(result.ok).toBe(false); + }); + + it("rejects missing declared workspace sources", () => { + const result = validateClawPackageContents({ + packageName: "@acme/github-triage", + version: "1.0.0", + packageJson: packageJson(), + files: files().filter((file) => file.path !== "workspace/reference.md"), + }); + + expect(result).toEqual({ + ok: false, + issues: [ + expect.objectContaining({ + code: "missing_workspace_source", + path: "workspace/reference.md", + }), + ], + }); + }); + + it("rejects duplicate paths that collide on portable filesystems", () => { + const result = validateClawPackageContents({ + packageName: "@acme/github-triage", + version: "1.0.0", + packageJson: packageJson(), + files: [...files(), { path: "claw.md", text: "duplicate" }], + }); + + expect(result).toEqual({ + ok: false, + issues: expect.arrayContaining([ + expect.objectContaining({ code: "duplicate_portable_path" }), + ]), + }); + }); + + it.each(["CON", "workspace/trailing.", "workspace\\backslash.md"])( + "rejects unsafe unreferenced package path %s", + (path) => { + const result = validateClawPackageContents({ + packageName: "@acme/github-triage", + version: "1.0.0", + packageJson: packageJson(), + files: [...files(), { path, text: "unused" }], + }); + + expect(result).toEqual({ + ok: false, + issues: expect.arrayContaining([expect.objectContaining({ code: "invalid_package_path" })]), + }); + }, + ); + + it("requires exact path spelling for manifests and workspace sources", () => { + const result = validateClawPackageContents({ + packageName: "@acme/github-triage", + version: "1.0.0", + packageJson: packageJson(), + files: files().map((file) => + file.path === "CLAW.md" + ? { ...file, path: "claw.md" } + : file.path === "workspace/reference.md" + ? { ...file, path: "workspace/REFERENCE.md" } + : file, + ), + }); + + expect(result).toEqual({ + ok: false, + issues: [expect.objectContaining({ code: "missing_claw_manifest" })], + }); + }); + + it("rejects resolved credentials through the shared manifest validator", () => { + const unsafe = { + ...manifest, + mcpServers: { github: { command: "npx", env: { GITHUB_TOKEN: "secret" } } }, + }; + const result = validateClawPackageContents({ + packageName: "@acme/github-triage", + version: "1.0.0", + packageJson: packageJson(), + files: files(`---\n${JSON.stringify(unsafe)}\n---\n`), + }); + + expect(result).toEqual({ + ok: false, + issues: expect.arrayContaining([ + expect.objectContaining({ + code: "invalid_claw_manifest", + path: "$.mcpServers.github.env.GITHUB_TOKEN", + }), + ]), + }); + }); +}); diff --git a/packages/schema/src/clawPackage.ts b/packages/schema/src/clawPackage.ts new file mode 100644 index 0000000000..b3ee3a6e47 --- /dev/null +++ b/packages/schema/src/clawPackage.ts @@ -0,0 +1,271 @@ +import { isScalar, parseDocument, visit } from "yaml"; +import { + summarizeClawManifest, + validateClawManifest, + type ClawManifest, + type ClawManifestSummary, +} from "./claws.js"; + +export type ClawPackageTextFile = { path: string; text?: string }; +export type ClawPackageValidationIssue = { + code: string; + path: string; + message: string; +}; +export type ValidatedClawPackage = { + manifestPath: string; + manifest: ClawManifest; + summary: ClawManifestSummary; +}; + +const EXACT_VERSION_PATTERN = + /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[A-Za-z-][0-9A-Za-z-]*)(?:\.(?:0|[1-9]\d*|\d*[A-Za-z-][0-9A-Za-z-]*))*))?(?:\+([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?$/; +const WINDOWS_INVALID_PATH_CHARS = /[<>:"|?*]/; +const WINDOWS_RESERVED_PATH_SEGMENT = /^(?:con|prn|aux|nul|com[1-9]|lpt[1-9])(?:\..*)?$/i; + +function isRecord(value: unknown): value is Record { + return Boolean(value) && typeof value === "object" && !Array.isArray(value); +} + +export function isSafeClawPackagePath(value: string): boolean { + const normalized = value.replaceAll("\\", "/"); + if ( + !normalized || + normalized !== value || + normalized !== normalized.trim() || + normalized.startsWith("/") || + /^[A-Za-z]:\//.test(normalized) + ) { + return false; + } + return normalized + .split("/") + .every( + (segment) => + segment !== "" && + segment !== "." && + segment !== ".." && + !WINDOWS_INVALID_PATH_CHARS.test(segment) && + !Array.from(segment).some((character) => character.charCodeAt(0) <= 0x1f) && + !segment.endsWith(".") && + !segment.endsWith(" ") && + !WINDOWS_RESERVED_PATH_SEGMENT.test(segment), + ); +} + +function portablePathKey(value: string): string { + return value.replaceAll("\\", "/").normalize("NFC").toLowerCase(); +} + +function issue(code: string, path: string, message: string): ClawPackageValidationIssue { + return { code, path, message }; +} + +function parseManifestDocument(raw: string, manifestPath: string) { + const filename = manifestPath.replaceAll("\\", "/").split("/").at(-1)?.toLowerCase(); + if (filename === "claw.md") { + const markdown = raw.startsWith("\uFEFF") ? raw.slice(1) : raw; + const match = markdown.match(/^---\r?\n([\s\S]*?)\r?\n---(?:\r?\n|$)/); + if (!match) { + return { + issues: [ + issue( + "missing_claw_frontmatter", + manifestPath, + `${manifestPath} must start with YAML frontmatter delimited by --- lines.`, + ), + ], + }; + } + const document = parseDocument(match[1], { prettyErrors: false, uniqueKeys: true }); + if (document.errors.length > 0) { + return { + issues: document.errors.map((error) => + issue("invalid_claw_frontmatter", manifestPath, error.message), + ), + }; + } + let unsupportedFeature: string | undefined; + visit(document, { + Alias() { + unsupportedFeature ??= "aliases"; + }, + Node(_key, node) { + if (node.anchor) { + unsupportedFeature ??= "anchors"; + } else if (node.tag) { + unsupportedFeature ??= "explicit tags"; + } + }, + Pair(_key, pair) { + if (!isScalar(pair.key) || typeof pair.key.value !== "string") { + unsupportedFeature ??= "non-string mapping keys"; + } else if (pair.key.value === "<<") { + unsupportedFeature ??= "merge keys"; + } + }, + }); + if (unsupportedFeature) { + return { + issues: [ + issue( + "unsupported_claw_yaml_feature", + manifestPath, + `${manifestPath} uses ${unsupportedFeature}; CLAW.md frontmatter must map directly to JSON data.`, + ), + ], + }; + } + try { + return { value: document.toJSON() }; + } catch (error) { + return { + issues: [ + issue( + "invalid_claw_frontmatter", + manifestPath, + error instanceof Error ? error.message : "Could not parse Claw frontmatter.", + ), + ], + }; + } + } + try { + return { value: JSON.parse(raw) as unknown }; + } catch (error) { + return { + issues: [ + issue( + "invalid_claw_json", + manifestPath, + error instanceof Error ? error.message : "Could not parse Claw JSON.", + ), + ], + }; + } +} + +export function validateClawPackageContents(input: { + packageName: string; + version: string; + packageJson: unknown; + files: readonly ClawPackageTextFile[]; +}): + | { ok: true; value: ValidatedClawPackage } + | { ok: false; issues: ClawPackageValidationIssue[] } { + const issues: ClawPackageValidationIssue[] = []; + if (!isRecord(input.packageJson)) { + return { + ok: false, + issues: [ + issue("missing_package_json", "package.json", "Claw packages require package.json."), + ], + }; + } + const declaredName = typeof input.packageJson.name === "string" ? input.packageJson.name : ""; + const declaredVersion = + typeof input.packageJson.version === "string" ? input.packageJson.version : ""; + const openclaw = isRecord(input.packageJson.openclaw) ? input.packageJson.openclaw : undefined; + const manifestPath = typeof openclaw?.claw === "string" ? openclaw.claw : ""; + if (declaredName !== input.packageName) { + issues.push( + issue("package_name_mismatch", "package.json.name", `Must match ${input.packageName}.`), + ); + } + if (!EXACT_VERSION_PATTERN.test(declaredVersion) || declaredVersion !== input.version) { + issues.push( + issue( + "package_version_mismatch", + "package.json.version", + `Must be exact semver ${input.version}.`, + ), + ); + } + if (!manifestPath || !isSafeClawPackagePath(manifestPath)) { + issues.push( + issue( + "invalid_claw_manifest_path", + "package.json.openclaw.claw", + "Must name a safe package-relative Claw manifest path.", + ), + ); + } + if (issues.length > 0) return { ok: false, issues }; + + const fileByPath = new Map(); + const portablePaths = new Set(); + for (const file of input.files) { + if (!isSafeClawPackagePath(file.path)) { + issues.push( + issue( + "invalid_package_path", + file.path, + "Package files must use safe canonical package-relative paths.", + ), + ); + continue; + } + const key = portablePathKey(file.path); + if (portablePaths.has(key)) { + issues.push( + issue( + "duplicate_portable_path", + file.path, + "Package paths must be unique across supported filesystems.", + ), + ); + } else { + portablePaths.add(key); + fileByPath.set(file.path, file); + } + } + const manifestFile = fileByPath.get(manifestPath); + if (!manifestFile || manifestFile.text === undefined) { + issues.push( + issue( + "missing_claw_manifest", + manifestPath, + "The declared Claw manifest is missing or is not UTF-8 text.", + ), + ); + return { ok: false, issues }; + } + const parsed = parseManifestDocument(manifestFile.text, manifestPath); + if (parsed.issues) return { ok: false, issues: parsed.issues }; + const validated = validateClawManifest(parsed.value); + if (!validated.ok) { + return { + ok: false, + issues: validated.issues.map((entry) => + issue("invalid_claw_manifest", entry.path, entry.message), + ), + }; + } + + const sources = [ + ...Object.values(validated.manifest.workspace?.bootstrapFiles ?? {}).map( + (entry) => entry.source, + ), + ...(validated.manifest.workspace?.files ?? []).map((entry) => entry.source), + ]; + for (const source of sources) { + if (!fileByPath.has(source)) { + issues.push( + issue( + "missing_workspace_source", + source, + "Declared workspace source is missing from the package.", + ), + ); + } + } + if (issues.length > 0) return { ok: false, issues }; + return { + ok: true, + value: { + manifestPath, + manifest: validated.manifest, + summary: summarizeClawManifest(validated.manifest), + }, + }; +} diff --git a/packages/schema/src/claws.test.ts b/packages/schema/src/claws.test.ts index e591ae0207..23c5969ca0 100644 --- a/packages/schema/src/claws.test.ts +++ b/packages/schema/src/claws.test.ts @@ -50,7 +50,7 @@ const fixture = { } as const; describe("Claw manifest contract", () => { - it("allows Claws in storage contracts without opening generic publication", () => { + it("allows Claws in storage and publication metadata contracts", () => { expect(PackageFamilySchema("claw")).toBe("claw"); expect( PackagePublishMetadataSchema({ @@ -59,7 +59,7 @@ describe("Claw manifest contract", () => { version: "1.0.0", changelog: "Initial release", }), - ).toBeInstanceOf(ArkErrors); + ).not.toBeInstanceOf(ArkErrors); }); it("accepts the grouped OpenClaw v1 fixture and derives a safe summary", () => { diff --git a/packages/schema/src/index.ts b/packages/schema/src/index.ts index d03a08a7d4..8d58b85d55 100644 --- a/packages/schema/src/index.ts +++ b/packages/schema/src/index.ts @@ -1,6 +1,7 @@ export type { ArkValidator } from "./ark.js"; export { formatArkErrors, parseArk } from "./ark.js"; export * from "./claws.js"; +export * from "./clawPackage.js"; export * from "./catalogFeed.js"; export * from "./catalogMetadata.js"; export * from "./docsLinks.js"; diff --git a/packages/schema/src/packages.ts b/packages/schema/src/packages.ts index 13e838df05..b4ddf1f38a 100644 --- a/packages/schema/src/packages.ts +++ b/packages/schema/src/packages.ts @@ -29,8 +29,7 @@ export function getPackageScopeOwnerMismatch(name: string, ownerHandle: string | export const PackageFamilySchema = type('"skill"|"code-plugin"|"bundle-plugin"|"claw"'); export type PackageFamily = (typeof PackageFamilySchema)[inferred]; -// Publication stays narrower until a family has a complete validation path. -export const PackagePublishFamilySchema = type('"skill"|"code-plugin"|"bundle-plugin"'); +export const PackagePublishFamilySchema = type('"skill"|"code-plugin"|"bundle-plugin"|"claw"'); export type PackagePublishFamily = (typeof PackagePublishFamilySchema)[inferred]; export const PackageChannelSchema = type('"official"|"community"|"private"'); diff --git a/specs/claws.md b/specs/claws.md index ea8634f1a6..f144b11dd9 100644 --- a/specs/claws.md +++ b/specs/claws.md @@ -36,16 +36,15 @@ OpenClaw's namespaced key or define their own profile-pointer contract. ## Staged implementation -1. Add the shared grouped manifest contract, safe summary, and storage model. -2. Add feature-gated authenticated publication, validation, and authoring docs. +1. Add the shared grouped manifest contract, safe summary, and storage model + ([PR #3089](https://github.com/openclaw/clawhub/pull/3089)). +2. Add feature-gated authenticated publication, package-content validation, + CLI authoring support, and authoring docs + ([PR #3090](https://github.com/openclaw/clawhub/pull/3090)). 3. Add feature-gated search, detail, and API surfaces. 4. Add hosted feed export and a published-package end-to-end proof through OpenClaw `claws add --dry-run`. -The publish schema intentionally remains narrower than the storage family in -the first slice. This prevents existing generic package endpoints from -accepting Claws before their dedicated validation and gate are in place. - The shared validator follows the RFC's strict v1 contract: strings are not trimmed into validity, MCP package selectors must resolve exact versions, process environment keys follow OpenClaw's host-wide safety policy, and tool @@ -68,3 +67,13 @@ field structure is built from `createClawManifestSummarySchema` in both the public ArkType contract and the Convex storage validator. Convex cannot express the summary text-length caps, so publication must validate or derive summaries through the shared schema before storage. + +Claws use the existing package publication pipeline. `package.json` declares +the package identity, version, and package-relative `openclaw.claw` manifest +path. Publication parses `CLAW.md` YAML frontmatter or the JSON compatibility +form and validates the grouped manifest plus referenced workspace files. The +release retains the exact artifact and a bounded derived summary rather than +duplicating the full manifest into Convex storage. The server rejects +`family: claw` before mutation when the experimental gate is disabled; the gate +does not bypass ownership, moderation, scanning, or release invariants when +enabled. From 346241f550e6f9130d3e824a1525edd312672bb4 Mon Sep 17 00:00:00 2001 From: Gio Della-Libera Date: Thu, 23 Jul 2026 14:33:39 -0700 Subject: [PATCH 02/17] feat(claws): validate package-local OpenClaw profiles --- .../clawhub/src/cli/commands/packages.test.ts | 23 +- packages/clawhub/src/schema/clawPackage.ts | 261 ++++++++++++++++++ packages/clawhub/src/schema/claws.ts | 187 +------------ packages/schema/dist/clawPackage.js | 222 +++++++++++++++ packages/schema/dist/clawPackage.js.map | 2 +- packages/schema/dist/index.js.map | 1 + packages/schema/src/clawPackage.test.ts | 175 ++++++++++++ packages/schema/src/clawPackage.ts | 261 ++++++++++++++++++ specs/claws.md | 17 +- 9 files changed, 954 insertions(+), 195 deletions(-) create mode 100644 packages/schema/dist/index.js.map diff --git a/packages/clawhub/src/cli/commands/packages.test.ts b/packages/clawhub/src/cli/commands/packages.test.ts index af0b10263a..578e494e47 100644 --- a/packages/clawhub/src/cli/commands/packages.test.ts +++ b/packages/clawhub/src/cli/commands/packages.test.ts @@ -2570,6 +2570,7 @@ describe("package commands", () => { try { const folder = join(workdir, "github-triage"); await mkdir(join(folder, "workspace"), { recursive: true }); + await mkdir(join(folder, "profiles"), { recursive: true }); await writeFile( join(folder, "package.json"), JSON.stringify({ @@ -2588,6 +2589,22 @@ describe("package commands", () => { "agent:", " id: github-triage", " name: GitHub Triage", + "metadata:", + " openclaw.config: profiles/openclaw.yml", + "workspace:", + " bootstrapFiles:", + " SOUL.md:", + " source: workspace/SOUL.md", + "---", + "# GitHub Triage", + ].join("\n"), + "utf8", + ); + await writeFile( + join(folder, "profiles", "openclaw.yml"), + [ + "schemaVersion: 1", + "agent:", " tools:", " profile: coding", " alsoAllow: [cron]", @@ -2598,12 +2615,6 @@ describe("package commands", () => { " enabled: true", " rememberAcrossConversations: true", " sources: [memory, sessions]", - "workspace:", - " bootstrapFiles:", - " SOUL.md:", - " source: workspace/SOUL.md", - "---", - "# GitHub Triage", ].join("\n"), "utf8", ); diff --git a/packages/clawhub/src/schema/clawPackage.ts b/packages/clawhub/src/schema/clawPackage.ts index b3ee3a6e47..b4eadedbad 100644 --- a/packages/clawhub/src/schema/clawPackage.ts +++ b/packages/clawhub/src/schema/clawPackage.ts @@ -1,3 +1,4 @@ +import { ArkErrors, type } from "arktype"; import { isScalar, parseDocument, visit } from "yaml"; import { summarizeClawManifest, @@ -22,6 +23,65 @@ const EXACT_VERSION_PATTERN = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[A-Za-z-][0-9A-Za-z-]*)(?:\.(?:0|[1-9]\d*|\d*[A-Za-z-][0-9A-Za-z-]*))*))?(?:\+([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?$/; const WINDOWS_INVALID_PATH_CHARS = /[<>:"|?*]/; const WINDOWS_RESERVED_PATH_SEGMENT = /^(?:con|prn|aux|nul|com[1-9]|lpt[1-9])(?:\..*)?$/i; +const MAX_OPENCLAW_PROFILE_BYTES = 256 * 1024; +const StrictStringArraySchema = type("string[]"); +const OpenClawProfileSchema = type({ + "+": "reject", + schemaVersion: "1", + agent: { + "+": "reject", + groupChat: type({ + "+": "reject", + mentionPatterns: StrictStringArraySchema.optional(), + }).optional(), + sandbox: type({ + "+": "reject", + mode: '"off"|"non-main"|"all"?', + scope: '"session"|"agent"|"shared"?', + workspaceAccess: '"none"|"ro"|"rw"?', + }).optional(), + tools: type({ + "+": "reject", + profile: "string?", + allow: StrictStringArraySchema.optional(), + alsoAllow: StrictStringArraySchema.optional(), + deny: StrictStringArraySchema.optional(), + fs: type({ + "+": "reject", + workspaceOnly: "true?", + }).optional(), + }).optional(), + memory: type({ + "+": "reject", + search: type({ + "+": "reject", + enabled: "boolean?", + rememberAcrossConversations: "boolean?", + sources: type("('memory' | 'sessions')[]").optional(), + }).optional(), + }).optional(), + heartbeat: type({ + "+": "reject", + every: "string?", + activeHours: type({ + "+": "reject", + start: "string?", + end: "string?", + timezone: "string?", + }).optional(), + lightContext: "boolean?", + isolatedSession: "boolean?", + skipWhenBusy: "boolean?", + timeoutSeconds: "number?", + }).optional(), + humanDelay: type({ + "+": "reject", + mode: '"off"|"natural"|"custom"?', + minMs: "number?", + maxMs: "number?", + }).optional(), + }, +}); function isRecord(value: unknown): value is Record { return Boolean(value) && typeof value === "object" && !Array.isArray(value); @@ -61,6 +121,178 @@ function issue(code: string, path: string, message: string): ClawPackageValidati return { code, path, message }; } +function parseJsonCompatibleYaml(raw: string, path: string) { + const document = parseDocument(raw.startsWith("\uFEFF") ? raw.slice(1) : raw, { + prettyErrors: false, + uniqueKeys: true, + }); + if (document.errors.length > 0) { + return { + issues: document.errors.map((error) => + issue("invalid_openclaw_profile", path, error.message), + ), + }; + } + let unsupportedFeature: string | undefined; + visit(document, { + Alias() { + unsupportedFeature ??= "aliases"; + }, + Node(_key, node) { + if (node.anchor) { + unsupportedFeature ??= "anchors"; + } else if (node.tag) { + unsupportedFeature ??= "explicit tags"; + } + }, + Pair(_key, pair) { + if (!isScalar(pair.key) || typeof pair.key.value !== "string") { + unsupportedFeature ??= "non-string mapping keys"; + } else if (pair.key.value === "<<") { + unsupportedFeature ??= "merge keys"; + } + }, + }); + if (unsupportedFeature) { + return { + issues: [ + issue( + "unsupported_openclaw_profile_yaml_feature", + path, + `${path} uses ${unsupportedFeature}; OpenClaw profile YAML must map directly to JSON data.`, + ), + ], + }; + } + try { + return { value: document.toJSON() }; + } catch (error) { + return { + issues: [ + issue( + "invalid_openclaw_profile", + path, + error instanceof Error ? error.message : "Could not parse OpenClaw profile.", + ), + ], + }; + } +} + +function isStrictNonEmpty(value: string): boolean { + return value.length > 0 && value === value.trim(); +} + +function isValidDuration(value: string): boolean { + if (!isStrictNonEmpty(value)) return false; + const multipliers: Record = { + ms: 1, + s: 1_000, + m: 60_000, + h: 3_600_000, + d: 86_400_000, + }; + const normalized = value.toLowerCase(); + const single = /^(\d+(?:\.\d+)?)(ms|s|m|h|d)?$/.exec(normalized); + if (single) { + return Number.isSafeInteger(Math.round(Number(single[1]) * multipliers[single[2] ?? "m"])); + } + let totalMs = 0; + let consumed = 0; + for (const match of normalized.matchAll(/(\d+(?:\.\d+)?)(ms|s|m|h|d)/g)) { + if (match.index !== consumed) return false; + totalMs += Number(match[1]) * multipliers[match[2]]; + consumed += match[0].length; + } + return ( + consumed === normalized.length && consumed > 0 && Number.isSafeInteger(Math.round(totalMs)) + ); +} + +function validateOpenClawProfile( + value: unknown, + profilePath: string, +): ClawPackageValidationIssue[] { + const parsed = OpenClawProfileSchema(value); + if (parsed instanceof ArkErrors) { + return Array.from(parsed, (error) => + issue( + "invalid_openclaw_profile", + `${profilePath}${error.path.length > 0 ? `.${error.path.join(".")}` : ""}`, + error.description ?? "Invalid value.", + ), + ); + } + const issues: ClawPackageValidationIssue[] = []; + const add = (path: string, message: string) => + issues.push(issue("invalid_openclaw_profile", `${profilePath}.${path}`, message)); + const requireNonEmpty = (path: string, values: string[] | undefined) => { + if (values !== undefined && values.length === 0) add(path, "Must contain at least one value."); + for (const [index, entry] of (values ?? []).entries()) { + if (!isStrictNonEmpty(entry)) { + add(`${path}.${index}`, "Must be non-empty without leading or trailing whitespace."); + } + } + }; + + requireNonEmpty("agent.groupChat.mentionPatterns", parsed.agent.groupChat?.mentionPatterns); + if (parsed.agent.tools?.profile !== undefined && !isStrictNonEmpty(parsed.agent.tools.profile)) { + add("agent.tools.profile", "Must be non-empty without leading or trailing whitespace."); + } + requireNonEmpty("agent.tools.allow", parsed.agent.tools?.allow); + requireNonEmpty("agent.tools.alsoAllow", parsed.agent.tools?.alsoAllow); + requireNonEmpty("agent.tools.deny", parsed.agent.tools?.deny); + if (parsed.agent.tools?.allow && parsed.agent.tools.alsoAllow) { + add("agent.tools.alsoAllow", "Must not be combined with tools.allow."); + } + if (parsed.agent.memory?.search?.sources?.length === 0) { + add("agent.memory.search.sources", "Must contain at least one source."); + } + if ( + parsed.agent.memory?.search?.sources?.includes("sessions") && + parsed.agent.memory.search.rememberAcrossConversations !== true + ) { + add( + "agent.memory.search.rememberAcrossConversations", + "Must be true when memory.search.sources includes sessions.", + ); + } + const heartbeat = parsed.agent.heartbeat; + if (heartbeat?.every !== undefined && !isValidDuration(heartbeat.every)) { + add("agent.heartbeat.every", "Must be a valid duration."); + } + const activeHours = heartbeat?.activeHours; + if (activeHours?.start !== undefined && !/^(?:[01]\d|2[0-3]):[0-5]\d$/.test(activeHours.start)) { + add("agent.heartbeat.activeHours.start", "Must be a valid 24-hour start time."); + } + if ( + activeHours?.end !== undefined && + !/^(?:(?:[01]\d|2[0-3]):[0-5]\d|24:00)$/.test(activeHours.end) + ) { + add("agent.heartbeat.activeHours.end", "Must be a valid 24-hour end time."); + } + if (activeHours?.timezone !== undefined) { + try { + new Intl.DateTimeFormat("en-US", { timeZone: activeHours.timezone }).format(); + } catch { + add("agent.heartbeat.activeHours.timezone", "Must be a valid IANA timezone."); + } + } + if ( + heartbeat?.timeoutSeconds !== undefined && + (!Number.isInteger(heartbeat.timeoutSeconds) || heartbeat.timeoutSeconds <= 0) + ) { + add("agent.heartbeat.timeoutSeconds", "Must be a positive integer."); + } + for (const field of ["minMs", "maxMs"] as const) { + const delay = parsed.agent.humanDelay?.[field]; + if (delay !== undefined && (!Number.isInteger(delay) || delay < 0)) { + add(`agent.humanDelay.${field}`, "Must be a nonnegative integer."); + } + } + return issues; +} + function parseManifestDocument(raw: string, manifestPath: string) { const filename = manifestPath.replaceAll("\\", "/").split("/").at(-1)?.toLowerCase(); if (filename === "claw.md") { @@ -242,6 +474,35 @@ export function validateClawPackageContents(input: { }; } + const openClawProfilePath = validated.manifest.metadata?.["openclaw.config"]; + if (openClawProfilePath !== undefined) { + const profileFile = fileByPath.get(openClawProfilePath); + if (!profileFile || profileFile.text === undefined) { + issues.push( + issue( + "missing_openclaw_profile", + openClawProfilePath, + "The declared OpenClaw profile is missing or is not UTF-8 text.", + ), + ); + } else if (new TextEncoder().encode(profileFile.text).byteLength > MAX_OPENCLAW_PROFILE_BYTES) { + issues.push( + issue( + "openclaw_profile_too_large", + openClawProfilePath, + `The OpenClaw profile exceeds ${MAX_OPENCLAW_PROFILE_BYTES} bytes.`, + ), + ); + } else { + const profile = parseJsonCompatibleYaml(profileFile.text, openClawProfilePath); + if (profile.issues) { + issues.push(...profile.issues); + } else { + issues.push(...validateOpenClawProfile(profile.value, openClawProfilePath)); + } + } + } + const sources = [ ...Object.values(validated.manifest.workspace?.bootstrapFiles ?? {}).map( (entry) => entry.source, diff --git a/packages/clawhub/src/schema/claws.ts b/packages/clawhub/src/schema/claws.ts index 18e5070bb6..9d66441051 100644 --- a/packages/clawhub/src/schema/claws.ts +++ b/packages/clawhub/src/schema/claws.ts @@ -54,57 +54,8 @@ export const ClawManifestSchema = type({ emoji: "string?", avatar: "string?", }).optional(), - groupChat: type({ - "+": "reject", - mentionPatterns: StringArraySchema.optional(), - }).optional(), - sandbox: type({ - "+": "reject", - mode: '"off"|"non-main"|"all"?', - scope: '"session"|"agent"|"shared"?', - workspaceAccess: '"none"|"ro"|"rw"?', - }).optional(), - tools: type({ - "+": "reject", - profile: "string?", - allow: StringArraySchema.optional(), - alsoAllow: StringArraySchema.optional(), - deny: StringArraySchema.optional(), - fs: type({ - "+": "reject", - workspaceOnly: "boolean?", - }).optional(), - }).optional(), - memory: type({ - "+": "reject", - search: type({ - "+": "reject", - enabled: "boolean?", - rememberAcrossConversations: "boolean?", - sources: type("('memory' | 'sessions')[]").optional(), - }).optional(), - }).optional(), - heartbeat: type({ - "+": "reject", - every: "string?", - activeHours: type({ - "+": "reject", - start: "string?", - end: "string?", - timezone: "string?", - }).optional(), - lightContext: "boolean?", - isolatedSession: "boolean?", - skipWhenBusy: "boolean?", - timeoutSeconds: "number?", - }).optional(), - humanDelay: type({ - "+": "reject", - mode: '"off"|"natural"|"custom"?', - minMs: "number?", - maxMs: "number?", - }).optional(), }, + metadata: type({ "[string]": "string" }).optional(), workspace: type({ "+": "reject", bootstrapFiles: type({ @@ -380,36 +331,6 @@ function conflictsWithWorkspaceTarget(targets: Set, candidate: string): return false; } -function isValidDuration(value: string): boolean { - const trimmed = value.trim().toLowerCase(); - if (!trimmed) return false; - const multipliers: Record = { - ms: 1, - s: 1_000, - m: 60_000, - h: 3_600_000, - d: 86_400_000, - }; - const single = /^(\d+(?:\.\d+)?)(ms|s|m|h|d)?$/.exec(trimmed); - if (single) { - const durationMs = Math.round(Number(single[1]) * multipliers[single[2] ?? "m"]); - return durationMs >= 0 && Number.isSafeInteger(durationMs); - } - let totalMs = 0; - let consumed = 0; - for (const match of trimmed.matchAll(/(\d+(?:\.\d+)?)(ms|s|m|h|d)/g)) { - if (match.index !== consumed) return false; - totalMs += Number(match[1]) * multipliers[match[2]]; - consumed += match[0].length; - } - return ( - consumed === trimmed.length && - consumed > 0 && - Math.round(totalMs) >= 0 && - Number.isSafeInteger(Math.round(totalMs)) - ); -} - function packageManagerArtifacts(command: string, args: string[]): string[] | undefined { const executable = command .split(/[\\/]/) @@ -474,23 +395,6 @@ function isPackageManagerArtifactPinned(command: string, args: string[]): boolea }); } -function isValidHeartbeatTime(value: string, allow24: boolean): boolean { - const match = /^([01]\d|2[0-4]):([0-5]\d)$/.exec(value); - if (!match) return false; - const hour = Number(match[1]); - const minute = Number(match[2]); - return hour !== 24 || (allow24 && minute === 0); -} - -function isValidTimezone(value: string): boolean { - try { - new Intl.DateTimeFormat("en-US", { timeZone: value }).format(); - return true; - } catch { - return false; - } -} - function arkIssues(errors: ArkErrors): ClawManifestValidationIssue[] { return Array.from(errors, (error) => ({ path: error.path.length > 0 ? `$.${error.path.join(".")}` : "$", @@ -537,95 +441,16 @@ export function validateClawManifest( for (const field of ["name", "theme", "emoji", "avatar"] as const) { pushNonEmpty(issues, `$.agent.identity.${field}`, parsed.agent.identity?.[field]); } - pushNonEmptyArray( - issues, - "$.agent.groupChat.mentionPatterns", - parsed.agent.groupChat?.mentionPatterns, - true, - ); - pushNonEmpty(issues, "$.agent.tools.profile", parsed.agent.tools?.profile); - pushNonEmptyArray(issues, "$.agent.tools.allow", parsed.agent.tools?.allow, true); - pushNonEmptyArray(issues, "$.agent.tools.alsoAllow", parsed.agent.tools?.alsoAllow, true); - pushNonEmptyArray(issues, "$.agent.tools.deny", parsed.agent.tools?.deny, true); - if (parsed.agent.tools?.allow && parsed.agent.tools.alsoAllow) { - issues.push({ - path: "$.agent.tools.alsoAllow", - message: "Must not be combined with tools.allow.", - }); - } - if (parsed.agent.memory?.search?.sources?.length === 0) { - issues.push({ - path: "$.agent.memory.search.sources", - message: "Must contain at least one source.", - }); - } - if ( - parsed.agent.memory?.search?.sources?.includes("sessions") && - parsed.agent.memory.search.rememberAcrossConversations !== true - ) { - issues.push({ - path: "$.agent.memory.search.rememberAcrossConversations", - message: "Must be true when memory.search.sources includes sessions.", - }); - } - const heartbeat = parsed.agent.heartbeat; - pushNonEmpty(issues, "$.agent.heartbeat.every", heartbeat?.every); - pushNonEmpty(issues, "$.agent.heartbeat.activeHours.start", heartbeat?.activeHours?.start); - pushNonEmpty(issues, "$.agent.heartbeat.activeHours.end", heartbeat?.activeHours?.end); - pushNonEmpty(issues, "$.agent.heartbeat.activeHours.timezone", heartbeat?.activeHours?.timezone); - if (heartbeat?.every) { - if (!isValidDuration(heartbeat.every)) { - issues.push({ - path: "$.agent.heartbeat.every", - message: "Must be a valid duration.", - }); - } - } - if ( - heartbeat?.activeHours?.start !== undefined && - !isValidHeartbeatTime(heartbeat.activeHours.start, false) - ) { - issues.push({ - path: "$.agent.heartbeat.activeHours.start", - message: "Must be a valid 24-hour start time.", - }); - } - if ( - heartbeat?.activeHours?.end !== undefined && - !isValidHeartbeatTime(heartbeat.activeHours.end, true) - ) { - issues.push({ - path: "$.agent.heartbeat.activeHours.end", - message: "Must be a valid 24-hour end time.", - }); - } + const openClawProfilePath = parsed.metadata?.["openclaw.config"]; if ( - heartbeat?.activeHours?.timezone !== undefined && - !isValidTimezone(heartbeat.activeHours.timezone) + openClawProfilePath !== undefined && + (!isSafePackagePath(openClawProfilePath) || !/\.ya?ml$/i.test(openClawProfilePath)) ) { issues.push({ - path: "$.agent.heartbeat.activeHours.timezone", - message: "Must be a valid IANA timezone.", + path: "$.metadata.openclaw.config", + message: "Must reference a safe package-relative .yml or .yaml file.", }); } - if ( - heartbeat?.timeoutSeconds !== undefined && - (!Number.isInteger(heartbeat.timeoutSeconds) || heartbeat.timeoutSeconds <= 0) - ) { - issues.push({ - path: "$.agent.heartbeat.timeoutSeconds", - message: "Must be a positive integer.", - }); - } - for (const field of ["minMs", "maxMs"] as const) { - const delayValue = parsed.agent.humanDelay?.[field]; - if (delayValue !== undefined && (!Number.isInteger(delayValue) || delayValue < 0)) { - issues.push({ - path: `$.agent.humanDelay.${field}`, - message: "Must be a nonnegative integer.", - }); - } - } const workspaceTargets = new Set(); for (const name of CLAW_BOOTSTRAP_FILE_NAMES) { const source = parsed.workspace?.bootstrapFiles?.[name]?.source; diff --git a/packages/schema/dist/clawPackage.js b/packages/schema/dist/clawPackage.js index 4aa038def9..4327d10d93 100644 --- a/packages/schema/dist/clawPackage.js +++ b/packages/schema/dist/clawPackage.js @@ -1,8 +1,68 @@ +import { ArkErrors, type } from "arktype"; import { isScalar, parseDocument, visit } from "yaml"; import { summarizeClawManifest, validateClawManifest, } from "./claws.js"; const EXACT_VERSION_PATTERN = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[A-Za-z-][0-9A-Za-z-]*)(?:\.(?:0|[1-9]\d*|\d*[A-Za-z-][0-9A-Za-z-]*))*))?(?:\+([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?$/; const WINDOWS_INVALID_PATH_CHARS = /[<>:"|?*]/; const WINDOWS_RESERVED_PATH_SEGMENT = /^(?:con|prn|aux|nul|com[1-9]|lpt[1-9])(?:\..*)?$/i; +const MAX_OPENCLAW_PROFILE_BYTES = 256 * 1024; +const StrictStringArraySchema = type("string[]"); +const OpenClawProfileSchema = type({ + "+": "reject", + schemaVersion: "1", + agent: { + "+": "reject", + groupChat: type({ + "+": "reject", + mentionPatterns: StrictStringArraySchema.optional(), + }).optional(), + sandbox: type({ + "+": "reject", + mode: '"off"|"non-main"|"all"?', + scope: '"session"|"agent"|"shared"?', + workspaceAccess: '"none"|"ro"|"rw"?', + }).optional(), + tools: type({ + "+": "reject", + profile: "string?", + allow: StrictStringArraySchema.optional(), + alsoAllow: StrictStringArraySchema.optional(), + deny: StrictStringArraySchema.optional(), + fs: type({ + "+": "reject", + workspaceOnly: "true?", + }).optional(), + }).optional(), + memory: type({ + "+": "reject", + search: type({ + "+": "reject", + enabled: "boolean?", + rememberAcrossConversations: "boolean?", + sources: type("('memory' | 'sessions')[]").optional(), + }).optional(), + }).optional(), + heartbeat: type({ + "+": "reject", + every: "string?", + activeHours: type({ + "+": "reject", + start: "string?", + end: "string?", + timezone: "string?", + }).optional(), + lightContext: "boolean?", + isolatedSession: "boolean?", + skipWhenBusy: "boolean?", + timeoutSeconds: "number?", + }).optional(), + humanDelay: type({ + "+": "reject", + mode: '"off"|"natural"|"custom"?', + minMs: "number?", + maxMs: "number?", + }).optional(), + }, +}); function isRecord(value) { return Boolean(value) && typeof value === "object" && !Array.isArray(value); } @@ -32,6 +92,149 @@ function portablePathKey(value) { function issue(code, path, message) { return { code, path, message }; } +function parseJsonCompatibleYaml(raw, path) { + const document = parseDocument(raw.startsWith("\uFEFF") ? raw.slice(1) : raw, { + prettyErrors: false, + uniqueKeys: true, + }); + if (document.errors.length > 0) { + return { + issues: document.errors.map((error) => issue("invalid_openclaw_profile", path, error.message)), + }; + } + let unsupportedFeature; + visit(document, { + Alias() { + unsupportedFeature ??= "aliases"; + }, + Node(_key, node) { + if (node.anchor) { + unsupportedFeature ??= "anchors"; + } + else if (node.tag) { + unsupportedFeature ??= "explicit tags"; + } + }, + Pair(_key, pair) { + if (!isScalar(pair.key) || typeof pair.key.value !== "string") { + unsupportedFeature ??= "non-string mapping keys"; + } + else if (pair.key.value === "<<") { + unsupportedFeature ??= "merge keys"; + } + }, + }); + if (unsupportedFeature) { + return { + issues: [ + issue("unsupported_openclaw_profile_yaml_feature", path, `${path} uses ${unsupportedFeature}; OpenClaw profile YAML must map directly to JSON data.`), + ], + }; + } + try { + return { value: document.toJSON() }; + } + catch (error) { + return { + issues: [ + issue("invalid_openclaw_profile", path, error instanceof Error ? error.message : "Could not parse OpenClaw profile."), + ], + }; + } +} +function isStrictNonEmpty(value) { + return value.length > 0 && value === value.trim(); +} +function isValidDuration(value) { + if (!isStrictNonEmpty(value)) + return false; + const multipliers = { + ms: 1, + s: 1_000, + m: 60_000, + h: 3_600_000, + d: 86_400_000, + }; + const normalized = value.toLowerCase(); + const single = /^(\d+(?:\.\d+)?)(ms|s|m|h|d)?$/.exec(normalized); + if (single) { + return Number.isSafeInteger(Math.round(Number(single[1]) * multipliers[single[2] ?? "m"])); + } + let totalMs = 0; + let consumed = 0; + for (const match of normalized.matchAll(/(\d+(?:\.\d+)?)(ms|s|m|h|d)/g)) { + if (match.index !== consumed) + return false; + totalMs += Number(match[1]) * multipliers[match[2]]; + consumed += match[0].length; + } + return (consumed === normalized.length && consumed > 0 && Number.isSafeInteger(Math.round(totalMs))); +} +function validateOpenClawProfile(value, profilePath) { + const parsed = OpenClawProfileSchema(value); + if (parsed instanceof ArkErrors) { + return Array.from(parsed, (error) => issue("invalid_openclaw_profile", `${profilePath}${error.path.length > 0 ? `.${error.path.join(".")}` : ""}`, error.description ?? "Invalid value.")); + } + const issues = []; + const add = (path, message) => issues.push(issue("invalid_openclaw_profile", `${profilePath}.${path}`, message)); + const requireNonEmpty = (path, values) => { + if (values !== undefined && values.length === 0) + add(path, "Must contain at least one value."); + for (const [index, entry] of (values ?? []).entries()) { + if (!isStrictNonEmpty(entry)) { + add(`${path}.${index}`, "Must be non-empty without leading or trailing whitespace."); + } + } + }; + requireNonEmpty("agent.groupChat.mentionPatterns", parsed.agent.groupChat?.mentionPatterns); + if (parsed.agent.tools?.profile !== undefined && !isStrictNonEmpty(parsed.agent.tools.profile)) { + add("agent.tools.profile", "Must be non-empty without leading or trailing whitespace."); + } + requireNonEmpty("agent.tools.allow", parsed.agent.tools?.allow); + requireNonEmpty("agent.tools.alsoAllow", parsed.agent.tools?.alsoAllow); + requireNonEmpty("agent.tools.deny", parsed.agent.tools?.deny); + if (parsed.agent.tools?.allow && parsed.agent.tools.alsoAllow) { + add("agent.tools.alsoAllow", "Must not be combined with tools.allow."); + } + if (parsed.agent.memory?.search?.sources?.length === 0) { + add("agent.memory.search.sources", "Must contain at least one source."); + } + if (parsed.agent.memory?.search?.sources?.includes("sessions") && + parsed.agent.memory.search.rememberAcrossConversations !== true) { + add("agent.memory.search.rememberAcrossConversations", "Must be true when memory.search.sources includes sessions."); + } + const heartbeat = parsed.agent.heartbeat; + if (heartbeat?.every !== undefined && !isValidDuration(heartbeat.every)) { + add("agent.heartbeat.every", "Must be a valid duration."); + } + const activeHours = heartbeat?.activeHours; + if (activeHours?.start !== undefined && !/^(?:[01]\d|2[0-3]):[0-5]\d$/.test(activeHours.start)) { + add("agent.heartbeat.activeHours.start", "Must be a valid 24-hour start time."); + } + if (activeHours?.end !== undefined && + !/^(?:(?:[01]\d|2[0-3]):[0-5]\d|24:00)$/.test(activeHours.end)) { + add("agent.heartbeat.activeHours.end", "Must be a valid 24-hour end time."); + } + if (activeHours?.timezone !== undefined) { + try { + new Intl.DateTimeFormat("en-US", { timeZone: activeHours.timezone }).format(); + } + catch { + add("agent.heartbeat.activeHours.timezone", "Must be a valid IANA timezone."); + } + } + if (heartbeat?.timeoutSeconds !== undefined && + (!Number.isInteger(heartbeat.timeoutSeconds) || heartbeat.timeoutSeconds <= 0)) { + add("agent.heartbeat.timeoutSeconds", "Must be a positive integer."); + } + for (const field of ["minMs", "maxMs"]) { + const delay = parsed.agent.humanDelay?.[field]; + if (delay !== undefined && (!Number.isInteger(delay) || delay < 0)) { + add(`agent.humanDelay.${field}`, "Must be a nonnegative integer."); + } + } + return issues; +} function parseManifestDocument(raw, manifestPath) { const filename = manifestPath.replaceAll("\\", "/").split("/").at(-1)?.toLowerCase(); if (filename === "claw.md") { @@ -157,6 +360,25 @@ export function validateClawPackageContents(input) { issues: validated.issues.map((entry) => issue("invalid_claw_manifest", entry.path, entry.message)), }; } + const openClawProfilePath = validated.manifest.metadata?.["openclaw.config"]; + if (openClawProfilePath !== undefined) { + const profileFile = fileByPath.get(openClawProfilePath); + if (!profileFile || profileFile.text === undefined) { + issues.push(issue("missing_openclaw_profile", openClawProfilePath, "The declared OpenClaw profile is missing or is not UTF-8 text.")); + } + else if (new TextEncoder().encode(profileFile.text).byteLength > MAX_OPENCLAW_PROFILE_BYTES) { + issues.push(issue("openclaw_profile_too_large", openClawProfilePath, `The OpenClaw profile exceeds ${MAX_OPENCLAW_PROFILE_BYTES} bytes.`)); + } + else { + const profile = parseJsonCompatibleYaml(profileFile.text, openClawProfilePath); + if (profile.issues) { + issues.push(...profile.issues); + } + else { + issues.push(...validateOpenClawProfile(profile.value, openClawProfilePath)); + } + } + } const sources = [ ...Object.values(validated.manifest.workspace?.bootstrapFiles ?? {}).map((entry) => entry.source), ...(validated.manifest.workspace?.files ?? []).map((entry) => entry.source), diff --git a/packages/schema/dist/clawPackage.js.map b/packages/schema/dist/clawPackage.js.map index 5321cbcdac..982908df71 100644 --- a/packages/schema/dist/clawPackage.js.map +++ b/packages/schema/dist/clawPackage.js.map @@ -1 +1 @@ -{"version":3,"file":"clawPackage.js","sourceRoot":"","sources":["../src/clawPackage.ts"],"names":[],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"clawPackage.js","sourceRoot":"","sources":["../src/clawPackage.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/packages/schema/dist/index.js.map b/packages/schema/dist/index.js.map new file mode 100644 index 0000000000..bb84e201e1 --- /dev/null +++ b/packages/schema/dist/index.js.map @@ -0,0 +1 @@ +{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,eAAe,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;AACrD,cAAc,YAAY,CAAC;AAC3B,cAAc,kBAAkB,CAAC;AACjC,cAAc,kBAAkB,CAAC;AACjC,cAAc,sBAAsB,CAAC;AACrC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,cAAc,CAAC;AAC7B,cAAc,uBAAuB,CAAC;AACtC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,eAAe,CAAC;AAC9B,cAAc,uBAAuB,CAAC;AACtC,cAAc,qBAAqB,CAAC;AACpC,cAAc,0BAA0B,CAAC;AACzC,OAAO,EAAE,SAAS,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AACzD,cAAc,cAAc,CAAC;AAC7B,cAAc,gBAAgB,CAAC"} \ No newline at end of file diff --git a/packages/schema/src/clawPackage.test.ts b/packages/schema/src/clawPackage.test.ts index bd37682e1c..cb2daf096a 100644 --- a/packages/schema/src/clawPackage.test.ts +++ b/packages/schema/src/clawPackage.test.ts @@ -12,6 +12,31 @@ const manifest = { mcpServers: {}, cronJobs: [], }; +const openClawProfile = [ + "schemaVersion: 1", + "agent:", + " groupChat:", + " mentionPatterns: ['@triage']", + " sandbox: { mode: non-main, scope: agent, workspaceAccess: rw }", + " tools:", + " profile: future-profile", + " alsoAllow: [cron]", + " deny: [gateway]", + " fs: { workspaceOnly: true }", + " memory:", + " search:", + " enabled: true", + " rememberAcrossConversations: true", + " sources: [memory, sessions]", + " heartbeat:", + " every: 30m", + " activeHours: { start: '09:00', end: '24:00', timezone: America/Los_Angeles }", + " lightContext: true", + " isolatedSession: false", + " skipWhenBusy: true", + " timeoutSeconds: 30", + " humanDelay: { mode: custom, minMs: 0, maxMs: 2000 }", +].join("\n"); function packageJson(claw = "CLAW.md") { return { @@ -66,6 +91,156 @@ describe("validateClawPackageContents", () => { expect(result.ok).toBe(true); }); + it("validates a package-local OpenClaw profile without returning it", () => { + const profiledManifest = { + ...manifest, + metadata: { "openclaw.config": "profiles/openclaw.yml" }, + }; + const result = validateClawPackageContents({ + packageName: "@acme/github-triage", + version: "1.0.0", + packageJson: packageJson(), + files: [ + ...files(`---\n${JSON.stringify(profiledManifest)}\n---\n`), + { path: "profiles/openclaw.yml", text: openClawProfile }, + ], + }); + + expect(result.ok).toBe(true); + if (result.ok) { + expect(result.value).not.toHaveProperty("profile"); + expect(result.value.manifest.agent).toEqual(profiledManifest.agent); + } + }); + + it("requires the exact UTF-8 profile target", () => { + const profiledManifest = { + ...manifest, + metadata: { "openclaw.config": "profiles/openclaw.yml" }, + }; + const result = validateClawPackageContents({ + packageName: "@acme/github-triage", + version: "1.0.0", + packageJson: packageJson(), + files: [ + ...files(`---\n${JSON.stringify(profiledManifest)}\n---\n`), + { path: "profiles/OPENCLAW.yml", text: openClawProfile }, + ], + }); + + expect(result).toEqual({ + ok: false, + issues: [ + expect.objectContaining({ + code: "missing_openclaw_profile", + path: "profiles/openclaw.yml", + }), + ], + }); + }); + + it.each([ + ["malformed YAML", "schemaVersion: [", "invalid_openclaw_profile"], + ["duplicate keys", "schemaVersion: 1\nschemaVersion: 1\nagent: {}", "invalid_openclaw_profile"], + [ + "aliases", + "schemaVersion: 1\nagent: &agent {}\ncopy: *agent", + "unsupported_openclaw_profile_yaml_feature", + ], + ["anchors", "schemaVersion: 1\nagent: &agent {}", "unsupported_openclaw_profile_yaml_feature"], + [ + "merge keys", + "schemaVersion: 1\nagent:\n <<: { tools: {} }", + "unsupported_openclaw_profile_yaml_feature", + ], + [ + "explicit tags", + "schemaVersion: 1\nagent: !!map {}", + "unsupported_openclaw_profile_yaml_feature", + ], + [ + "non-string mapping keys", + "schemaVersion: 1\nagent:\n ? [tools]\n : {}", + "unsupported_openclaw_profile_yaml_feature", + ], + ])("rejects OpenClaw profile %s", (_label, text, code) => { + const profiledManifest = { + ...manifest, + metadata: { "openclaw.config": "profiles/openclaw.yml" }, + }; + const result = validateClawPackageContents({ + packageName: "@acme/github-triage", + version: "1.0.0", + packageJson: packageJson(), + files: [ + ...files(`---\n${JSON.stringify(profiledManifest)}\n---\n`), + { path: "profiles/openclaw.yml", text }, + ], + }); + + expect(result).toEqual({ + ok: false, + issues: [expect.objectContaining({ code })], + }); + }); + + it("rejects an oversized OpenClaw profile", () => { + const profiledManifest = { + ...manifest, + metadata: { "openclaw.config": "profiles/openclaw.yml" }, + }; + const result = validateClawPackageContents({ + packageName: "@acme/github-triage", + version: "1.0.0", + packageJson: packageJson(), + files: [ + ...files(`---\n${JSON.stringify(profiledManifest)}\n---\n`), + { path: "profiles/openclaw.yml", text: `#${"x".repeat(256 * 1024)}` }, + ], + }); + + expect(result).toEqual({ + ok: false, + issues: [expect.objectContaining({ code: "openclaw_profile_too_large" })], + }); + }); + + it.each([ + ["unknown version", "schemaVersion: 2\nagent: {}"], + ["unknown field", "schemaVersion: 1\nagent:\n model: gpt-5"], + [ + "workspaceOnly false", + "schemaVersion: 1\nagent:\n tools:\n fs:\n workspaceOnly: false", + ], + [ + "allow conflict", + "schemaVersion: 1\nagent:\n tools:\n allow: [read]\n alsoAllow: [write]", + ], + [ + "sessions without consent", + "schemaVersion: 1\nagent:\n memory:\n search:\n sources: [sessions]", + ], + ])("rejects OpenClaw profile with %s", (_label, text) => { + const profiledManifest = { + ...manifest, + metadata: { "openclaw.config": "profiles/openclaw.yml" }, + }; + const result = validateClawPackageContents({ + packageName: "@acme/github-triage", + version: "1.0.0", + packageJson: packageJson(), + files: [ + ...files(`---\n${JSON.stringify(profiledManifest)}\n---\n`), + { path: "profiles/openclaw.yml", text }, + ], + }); + + expect(result).toEqual({ + ok: false, + issues: [expect.objectContaining({ code: "invalid_openclaw_profile" })], + }); + }); + it.each([ ["anchor", "agent: &agent { id: github-triage }"], ["alias", "agent: { id: &id github-triage, name: *id }"], diff --git a/packages/schema/src/clawPackage.ts b/packages/schema/src/clawPackage.ts index b3ee3a6e47..b4eadedbad 100644 --- a/packages/schema/src/clawPackage.ts +++ b/packages/schema/src/clawPackage.ts @@ -1,3 +1,4 @@ +import { ArkErrors, type } from "arktype"; import { isScalar, parseDocument, visit } from "yaml"; import { summarizeClawManifest, @@ -22,6 +23,65 @@ const EXACT_VERSION_PATTERN = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[A-Za-z-][0-9A-Za-z-]*)(?:\.(?:0|[1-9]\d*|\d*[A-Za-z-][0-9A-Za-z-]*))*))?(?:\+([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?$/; const WINDOWS_INVALID_PATH_CHARS = /[<>:"|?*]/; const WINDOWS_RESERVED_PATH_SEGMENT = /^(?:con|prn|aux|nul|com[1-9]|lpt[1-9])(?:\..*)?$/i; +const MAX_OPENCLAW_PROFILE_BYTES = 256 * 1024; +const StrictStringArraySchema = type("string[]"); +const OpenClawProfileSchema = type({ + "+": "reject", + schemaVersion: "1", + agent: { + "+": "reject", + groupChat: type({ + "+": "reject", + mentionPatterns: StrictStringArraySchema.optional(), + }).optional(), + sandbox: type({ + "+": "reject", + mode: '"off"|"non-main"|"all"?', + scope: '"session"|"agent"|"shared"?', + workspaceAccess: '"none"|"ro"|"rw"?', + }).optional(), + tools: type({ + "+": "reject", + profile: "string?", + allow: StrictStringArraySchema.optional(), + alsoAllow: StrictStringArraySchema.optional(), + deny: StrictStringArraySchema.optional(), + fs: type({ + "+": "reject", + workspaceOnly: "true?", + }).optional(), + }).optional(), + memory: type({ + "+": "reject", + search: type({ + "+": "reject", + enabled: "boolean?", + rememberAcrossConversations: "boolean?", + sources: type("('memory' | 'sessions')[]").optional(), + }).optional(), + }).optional(), + heartbeat: type({ + "+": "reject", + every: "string?", + activeHours: type({ + "+": "reject", + start: "string?", + end: "string?", + timezone: "string?", + }).optional(), + lightContext: "boolean?", + isolatedSession: "boolean?", + skipWhenBusy: "boolean?", + timeoutSeconds: "number?", + }).optional(), + humanDelay: type({ + "+": "reject", + mode: '"off"|"natural"|"custom"?', + minMs: "number?", + maxMs: "number?", + }).optional(), + }, +}); function isRecord(value: unknown): value is Record { return Boolean(value) && typeof value === "object" && !Array.isArray(value); @@ -61,6 +121,178 @@ function issue(code: string, path: string, message: string): ClawPackageValidati return { code, path, message }; } +function parseJsonCompatibleYaml(raw: string, path: string) { + const document = parseDocument(raw.startsWith("\uFEFF") ? raw.slice(1) : raw, { + prettyErrors: false, + uniqueKeys: true, + }); + if (document.errors.length > 0) { + return { + issues: document.errors.map((error) => + issue("invalid_openclaw_profile", path, error.message), + ), + }; + } + let unsupportedFeature: string | undefined; + visit(document, { + Alias() { + unsupportedFeature ??= "aliases"; + }, + Node(_key, node) { + if (node.anchor) { + unsupportedFeature ??= "anchors"; + } else if (node.tag) { + unsupportedFeature ??= "explicit tags"; + } + }, + Pair(_key, pair) { + if (!isScalar(pair.key) || typeof pair.key.value !== "string") { + unsupportedFeature ??= "non-string mapping keys"; + } else if (pair.key.value === "<<") { + unsupportedFeature ??= "merge keys"; + } + }, + }); + if (unsupportedFeature) { + return { + issues: [ + issue( + "unsupported_openclaw_profile_yaml_feature", + path, + `${path} uses ${unsupportedFeature}; OpenClaw profile YAML must map directly to JSON data.`, + ), + ], + }; + } + try { + return { value: document.toJSON() }; + } catch (error) { + return { + issues: [ + issue( + "invalid_openclaw_profile", + path, + error instanceof Error ? error.message : "Could not parse OpenClaw profile.", + ), + ], + }; + } +} + +function isStrictNonEmpty(value: string): boolean { + return value.length > 0 && value === value.trim(); +} + +function isValidDuration(value: string): boolean { + if (!isStrictNonEmpty(value)) return false; + const multipliers: Record = { + ms: 1, + s: 1_000, + m: 60_000, + h: 3_600_000, + d: 86_400_000, + }; + const normalized = value.toLowerCase(); + const single = /^(\d+(?:\.\d+)?)(ms|s|m|h|d)?$/.exec(normalized); + if (single) { + return Number.isSafeInteger(Math.round(Number(single[1]) * multipliers[single[2] ?? "m"])); + } + let totalMs = 0; + let consumed = 0; + for (const match of normalized.matchAll(/(\d+(?:\.\d+)?)(ms|s|m|h|d)/g)) { + if (match.index !== consumed) return false; + totalMs += Number(match[1]) * multipliers[match[2]]; + consumed += match[0].length; + } + return ( + consumed === normalized.length && consumed > 0 && Number.isSafeInteger(Math.round(totalMs)) + ); +} + +function validateOpenClawProfile( + value: unknown, + profilePath: string, +): ClawPackageValidationIssue[] { + const parsed = OpenClawProfileSchema(value); + if (parsed instanceof ArkErrors) { + return Array.from(parsed, (error) => + issue( + "invalid_openclaw_profile", + `${profilePath}${error.path.length > 0 ? `.${error.path.join(".")}` : ""}`, + error.description ?? "Invalid value.", + ), + ); + } + const issues: ClawPackageValidationIssue[] = []; + const add = (path: string, message: string) => + issues.push(issue("invalid_openclaw_profile", `${profilePath}.${path}`, message)); + const requireNonEmpty = (path: string, values: string[] | undefined) => { + if (values !== undefined && values.length === 0) add(path, "Must contain at least one value."); + for (const [index, entry] of (values ?? []).entries()) { + if (!isStrictNonEmpty(entry)) { + add(`${path}.${index}`, "Must be non-empty without leading or trailing whitespace."); + } + } + }; + + requireNonEmpty("agent.groupChat.mentionPatterns", parsed.agent.groupChat?.mentionPatterns); + if (parsed.agent.tools?.profile !== undefined && !isStrictNonEmpty(parsed.agent.tools.profile)) { + add("agent.tools.profile", "Must be non-empty without leading or trailing whitespace."); + } + requireNonEmpty("agent.tools.allow", parsed.agent.tools?.allow); + requireNonEmpty("agent.tools.alsoAllow", parsed.agent.tools?.alsoAllow); + requireNonEmpty("agent.tools.deny", parsed.agent.tools?.deny); + if (parsed.agent.tools?.allow && parsed.agent.tools.alsoAllow) { + add("agent.tools.alsoAllow", "Must not be combined with tools.allow."); + } + if (parsed.agent.memory?.search?.sources?.length === 0) { + add("agent.memory.search.sources", "Must contain at least one source."); + } + if ( + parsed.agent.memory?.search?.sources?.includes("sessions") && + parsed.agent.memory.search.rememberAcrossConversations !== true + ) { + add( + "agent.memory.search.rememberAcrossConversations", + "Must be true when memory.search.sources includes sessions.", + ); + } + const heartbeat = parsed.agent.heartbeat; + if (heartbeat?.every !== undefined && !isValidDuration(heartbeat.every)) { + add("agent.heartbeat.every", "Must be a valid duration."); + } + const activeHours = heartbeat?.activeHours; + if (activeHours?.start !== undefined && !/^(?:[01]\d|2[0-3]):[0-5]\d$/.test(activeHours.start)) { + add("agent.heartbeat.activeHours.start", "Must be a valid 24-hour start time."); + } + if ( + activeHours?.end !== undefined && + !/^(?:(?:[01]\d|2[0-3]):[0-5]\d|24:00)$/.test(activeHours.end) + ) { + add("agent.heartbeat.activeHours.end", "Must be a valid 24-hour end time."); + } + if (activeHours?.timezone !== undefined) { + try { + new Intl.DateTimeFormat("en-US", { timeZone: activeHours.timezone }).format(); + } catch { + add("agent.heartbeat.activeHours.timezone", "Must be a valid IANA timezone."); + } + } + if ( + heartbeat?.timeoutSeconds !== undefined && + (!Number.isInteger(heartbeat.timeoutSeconds) || heartbeat.timeoutSeconds <= 0) + ) { + add("agent.heartbeat.timeoutSeconds", "Must be a positive integer."); + } + for (const field of ["minMs", "maxMs"] as const) { + const delay = parsed.agent.humanDelay?.[field]; + if (delay !== undefined && (!Number.isInteger(delay) || delay < 0)) { + add(`agent.humanDelay.${field}`, "Must be a nonnegative integer."); + } + } + return issues; +} + function parseManifestDocument(raw: string, manifestPath: string) { const filename = manifestPath.replaceAll("\\", "/").split("/").at(-1)?.toLowerCase(); if (filename === "claw.md") { @@ -242,6 +474,35 @@ export function validateClawPackageContents(input: { }; } + const openClawProfilePath = validated.manifest.metadata?.["openclaw.config"]; + if (openClawProfilePath !== undefined) { + const profileFile = fileByPath.get(openClawProfilePath); + if (!profileFile || profileFile.text === undefined) { + issues.push( + issue( + "missing_openclaw_profile", + openClawProfilePath, + "The declared OpenClaw profile is missing or is not UTF-8 text.", + ), + ); + } else if (new TextEncoder().encode(profileFile.text).byteLength > MAX_OPENCLAW_PROFILE_BYTES) { + issues.push( + issue( + "openclaw_profile_too_large", + openClawProfilePath, + `The OpenClaw profile exceeds ${MAX_OPENCLAW_PROFILE_BYTES} bytes.`, + ), + ); + } else { + const profile = parseJsonCompatibleYaml(profileFile.text, openClawProfilePath); + if (profile.issues) { + issues.push(...profile.issues); + } else { + issues.push(...validateOpenClawProfile(profile.value, openClawProfilePath)); + } + } + } + const sources = [ ...Object.values(validated.manifest.workspace?.bootstrapFiles ?? {}).map( (entry) => entry.source, diff --git a/specs/claws.md b/specs/claws.md index f144b11dd9..686197432d 100644 --- a/specs/claws.md +++ b/specs/claws.md @@ -18,10 +18,12 @@ metadata. OpenClaw recognizes `metadata.openclaw.config`; export conventionally uses `profiles/openclaw.yml`, but the pointer is normative and authors may use another safe package-relative YAML path. -That profile exists only inside the Claw package. It is validated during -publication and application, participates in package integrity, and is never -copied into ordinary OpenClaw configuration. Other harnesses may ignore -OpenClaw's namespaced key or define their own profile-pointer contract. +That profile exists only inside the Claw package. ClawHub requires the pointer +to resolve to an exact, bounded UTF-8 YAML package file and validates the +profile's strict v1 OpenClaw policy during publication. OpenClaw validates it +again during application, includes it in package integrity, and never copies it +into ordinary OpenClaw configuration. Other harnesses may ignore OpenClaw's +namespaced key or define their own profile-pointer contract. ## Experimental contract @@ -71,9 +73,10 @@ through the shared schema before storage. Claws use the existing package publication pipeline. `package.json` declares the package identity, version, and package-relative `openclaw.claw` manifest path. Publication parses `CLAW.md` YAML frontmatter or the JSON compatibility -form and validates the grouped manifest plus referenced workspace files. The -release retains the exact artifact and a bounded derived summary rather than -duplicating the full manifest into Convex storage. The server rejects +form and validates the grouped manifest, referenced workspace files, and any +declared package-local OpenClaw profile. The release retains the exact artifact +and a bounded derived summary rather than duplicating the full manifest or +OpenClaw profile into Convex storage. The server rejects `family: claw` before mutation when the experimental gate is disabled; the gate does not bypass ownership, moderation, scanning, or release invariants when enabled. From d70681e50d4ac48999871001122d54603c5b3ecf Mon Sep 17 00:00:00 2001 From: Gio Della-Libera Date: Thu, 23 Jul 2026 16:53:26 -0700 Subject: [PATCH 03/17] fix(claws): align publication profile contract --- packages/clawhub/src/schema/clawPackage.ts | 1 - packages/clawhub/src/schema/claws.ts | 6 ++++-- packages/schema/dist/clawPackage.js | 1 - packages/schema/dist/clawPackage.js.map | 2 +- packages/schema/src/clawPackage.test.ts | 5 ++++- packages/schema/src/clawPackage.ts | 1 - 6 files changed, 9 insertions(+), 7 deletions(-) diff --git a/packages/clawhub/src/schema/clawPackage.ts b/packages/clawhub/src/schema/clawPackage.ts index b4eadedbad..c67b371a41 100644 --- a/packages/clawhub/src/schema/clawPackage.ts +++ b/packages/clawhub/src/schema/clawPackage.ts @@ -71,7 +71,6 @@ const OpenClawProfileSchema = type({ }).optional(), lightContext: "boolean?", isolatedSession: "boolean?", - skipWhenBusy: "boolean?", timeoutSeconds: "number?", }).optional(), humanDelay: type({ diff --git a/packages/clawhub/src/schema/claws.ts b/packages/clawhub/src/schema/claws.ts index 9d66441051..f21f8766f0 100644 --- a/packages/clawhub/src/schema/claws.ts +++ b/packages/clawhub/src/schema/claws.ts @@ -444,11 +444,13 @@ export function validateClawManifest( const openClawProfilePath = parsed.metadata?.["openclaw.config"]; if ( openClawProfilePath !== undefined && - (!isSafePackagePath(openClawProfilePath) || !/\.ya?ml$/i.test(openClawProfilePath)) + (openClawProfilePath.includes("\\") || + !isSafePackagePath(openClawProfilePath) || + !/\.ya?ml$/i.test(openClawProfilePath)) ) { issues.push({ path: "$.metadata.openclaw.config", - message: "Must reference a safe package-relative .yml or .yaml file.", + message: "Must reference a forward-slash package-relative .yml or .yaml file.", }); } const workspaceTargets = new Set(); diff --git a/packages/schema/dist/clawPackage.js b/packages/schema/dist/clawPackage.js index 4327d10d93..d2877d3a06 100644 --- a/packages/schema/dist/clawPackage.js +++ b/packages/schema/dist/clawPackage.js @@ -52,7 +52,6 @@ const OpenClawProfileSchema = type({ }).optional(), lightContext: "boolean?", isolatedSession: "boolean?", - skipWhenBusy: "boolean?", timeoutSeconds: "number?", }).optional(), humanDelay: type({ diff --git a/packages/schema/dist/clawPackage.js.map b/packages/schema/dist/clawPackage.js.map index 982908df71..ad837444b1 100644 --- a/packages/schema/dist/clawPackage.js.map +++ b/packages/schema/dist/clawPackage.js.map @@ -1 +1 @@ -{"version":3,"file":"clawPackage.js","sourceRoot":"","sources":["../src/clawPackage.ts"],"names":[],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"clawPackage.js","sourceRoot":"","sources":["../src/clawPackage.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/packages/schema/src/clawPackage.test.ts b/packages/schema/src/clawPackage.test.ts index cb2daf096a..f7269a1629 100644 --- a/packages/schema/src/clawPackage.test.ts +++ b/packages/schema/src/clawPackage.test.ts @@ -33,7 +33,6 @@ const openClawProfile = [ " activeHours: { start: '09:00', end: '24:00', timezone: America/Los_Angeles }", " lightContext: true", " isolatedSession: false", - " skipWhenBusy: true", " timeoutSeconds: 30", " humanDelay: { mode: custom, minMs: 0, maxMs: 2000 }", ].join("\n"); @@ -208,6 +207,10 @@ describe("validateClawPackageContents", () => { it.each([ ["unknown version", "schemaVersion: 2\nagent: {}"], ["unknown field", "schemaVersion: 1\nagent:\n model: gpt-5"], + [ + "unsupported heartbeat field", + "schemaVersion: 1\nagent:\n heartbeat:\n skipWhenBusy: true", + ], [ "workspaceOnly false", "schemaVersion: 1\nagent:\n tools:\n fs:\n workspaceOnly: false", diff --git a/packages/schema/src/clawPackage.ts b/packages/schema/src/clawPackage.ts index b4eadedbad..c67b371a41 100644 --- a/packages/schema/src/clawPackage.ts +++ b/packages/schema/src/clawPackage.ts @@ -71,7 +71,6 @@ const OpenClawProfileSchema = type({ }).optional(), lightContext: "boolean?", isolatedSession: "boolean?", - skipWhenBusy: "boolean?", timeoutSeconds: "number?", }).optional(), humanDelay: type({ From ff9f42e4c8026cc32cc9706c7e478066ece45a93 Mon Sep 17 00:00:00 2001 From: Patrick Erichsen Date: Fri, 24 Jul 2026 16:45:55 -0500 Subject: [PATCH 04/17] fix(claws): harden package envelope validation --- convex/lib/clawpack.test.ts | 24 +++ convex/lib/clawpack.ts | 9 + convex/lib/skillZip.test.ts | 9 + convex/lib/skillZip.ts | 9 + packages/clawhub/src/clawpack.ts | 9 + .../clawhub/src/cli/commands/packages.test.ts | 10 ++ packages/clawhub/src/schema/clawPackage.ts | 93 +++++++++- packages/schema/dist/clawPackage.d.ts | 8 + packages/schema/dist/clawPackage.js | 69 +++++++- packages/schema/dist/clawPackage.js.map | 2 +- packages/schema/src/clawPackage.test.ts | 165 ++++++++++++++++-- packages/schema/src/clawPackage.ts | 93 +++++++++- 12 files changed, 483 insertions(+), 17 deletions(-) diff --git a/convex/lib/clawpack.test.ts b/convex/lib/clawpack.test.ts index a00eae9658..f387aee0ae 100644 --- a/convex/lib/clawpack.test.ts +++ b/convex/lib/clawpack.test.ts @@ -150,6 +150,30 @@ describe("clawpack", () => { ); }); + it.each([ + [ + "ancestor first", + [ + ["package/a", "file"], + ["package/a/b", "child"], + ], + ], + [ + "descendant first", + [ + ["package/a/b", "child"], + ["package/a", "file"], + ], + ], + ] as const)("rejects tar file/ancestor collisions with %s", async (_label, entries) => { + const pack = npmPackFixtureEntries([ + ["package/package.json", JSON.stringify({ name: "demo", version: "1.0.0" })], + ...entries.map(([path, content]) => [path, content] as [string, string]), + ]); + + await expect(parseClawPack(pack)).rejects.toThrow("file/ancestor path collision"); + }); + it("uses npm-style tarball names", () => { expect(npmTarballName("demo", "1.0.0")).toBe("demo-1.0.0.tgz"); expect(npmTarballName("@scope/demo", "1.0.0")).toBe("scope-demo-1.0.0.tgz"); diff --git a/convex/lib/clawpack.ts b/convex/lib/clawpack.ts index 735e45a4f3..f2aa99dd79 100644 --- a/convex/lib/clawpack.ts +++ b/convex/lib/clawpack.ts @@ -1,3 +1,4 @@ +import { findClawPackagePathHierarchyCollision } from "clawhub-schema"; import { Gunzip } from "fflate"; type ClawPackEntry = { @@ -150,6 +151,14 @@ function parseTarEntries(bytes: Uint8Array): ClawPackEntry[] { } if (entries.length === 0) throw new Error("ClawPack contains no files"); + const hierarchyCollision = findClawPackagePathHierarchyCollision( + entries.map((entry) => entry.path), + ); + if (hierarchyCollision) { + throw new Error( + `ClawPack contains file/ancestor path collision: ${hierarchyCollision.ancestor} and ${hierarchyCollision.descendant}`, + ); + } const unpackedSize = entries.reduce((sum, entry) => sum + entry.bytes.byteLength, 0); if (unpackedSize > MAX_UNPACKED_BYTES) throw new Error("ClawPack package exceeds 50MB limit"); return entries; diff --git a/convex/lib/skillZip.test.ts b/convex/lib/skillZip.test.ts index f529887bd4..d23cad92c2 100644 --- a/convex/lib/skillZip.test.ts +++ b/convex/lib/skillZip.test.ts @@ -156,5 +156,14 @@ describe("skillZip", () => { ]); expect(unzipped["_meta.json"]).toBeUndefined(); }); + + it("rejects file/ancestor collisions before creating the legacy ZIP", () => { + expect(() => + buildDeterministicPackageZip([ + { path: "workspace", bytes: new TextEncoder().encode("file") }, + { path: "workspace/SOUL.md", bytes: new TextEncoder().encode("child") }, + ]), + ).toThrow("file/ancestor path collision"); + }); }); }); diff --git a/convex/lib/skillZip.ts b/convex/lib/skillZip.ts index 87093689f5..04bfe81d93 100644 --- a/convex/lib/skillZip.ts +++ b/convex/lib/skillZip.ts @@ -1,3 +1,4 @@ +import { findClawPackagePathHierarchyCollision } from "clawhub-schema"; import { zipSync } from "fflate"; type ZipEntry = { @@ -68,6 +69,14 @@ export function buildDeterministicZip(entries: ZipEntry[], meta?: SkillZipMeta) } export function buildDeterministicPackageZip(entries: ZipEntry[]) { + const hierarchyCollision = findClawPackagePathHierarchyCollision( + entries.map((entry) => entry.path), + ); + if (hierarchyCollision) { + throw new Error( + `Package contains file/ancestor path collision: ${hierarchyCollision.ancestor} and ${hierarchyCollision.descendant}`, + ); + } const sorted = [...entries].sort((a, b) => a.path.localeCompare(b.path)); const zipData: ZipInput = {}; diff --git a/packages/clawhub/src/clawpack.ts b/packages/clawhub/src/clawpack.ts index 0ccb1b244f..486417ca89 100644 --- a/packages/clawhub/src/clawpack.ts +++ b/packages/clawhub/src/clawpack.ts @@ -1,4 +1,5 @@ import { Gunzip } from "fflate"; +import { findClawPackagePathHierarchyCollision } from "./schema/clawPackage.js"; type ClawPackEntry = { path: string; @@ -140,6 +141,14 @@ function parseTarEntries(bytes: Uint8Array): ClawPackEntry[] { } if (entries.length === 0) throw new Error("ClawPack contains no files"); + const hierarchyCollision = findClawPackagePathHierarchyCollision( + entries.map((entry) => entry.path), + ); + if (hierarchyCollision) { + throw new Error( + `ClawPack contains file/ancestor path collision: ${hierarchyCollision.ancestor} and ${hierarchyCollision.descendant}`, + ); + } const unpackedSize = entries.reduce((sum, entry) => sum + entry.bytes.byteLength, 0); if (unpackedSize > MAX_UNPACKED_BYTES) throw new Error("ClawPack package exceeds 50MB limit"); return entries; diff --git a/packages/clawhub/src/cli/commands/packages.test.ts b/packages/clawhub/src/cli/commands/packages.test.ts index 578e494e47..6558de19ae 100644 --- a/packages/clawhub/src/cli/commands/packages.test.ts +++ b/packages/clawhub/src/cli/commands/packages.test.ts @@ -227,6 +227,16 @@ afterEach(() => { }); describe("package commands", () => { + it("rejects npm pack file/ancestor collisions before extraction", () => { + const bytes = npmPackFixture({ + "package/package.json": JSON.stringify({ name: "demo", version: "1.0.0" }), + "package/a": "file", + "package/a/b": "child", + }); + + expect(() => parseClawPack(bytes)).toThrow("file/ancestor path collision"); + }); + it("validates a local plugin package with bundled Plugin Inspector offline", async () => { const workdir = await makeTmpWorkdir(); try { diff --git a/packages/clawhub/src/schema/clawPackage.ts b/packages/clawhub/src/schema/clawPackage.ts index c67b371a41..53ae9efb7d 100644 --- a/packages/clawhub/src/schema/clawPackage.ts +++ b/packages/clawhub/src/schema/clawPackage.ts @@ -16,6 +16,7 @@ export type ClawPackageValidationIssue = { export type ValidatedClawPackage = { manifestPath: string; manifest: ClawManifest; + implicitWorkspaceFile?: { path: "SOUL.md"; text: string }; summary: ClawManifestSummary; }; @@ -23,7 +24,9 @@ const EXACT_VERSION_PATTERN = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[A-Za-z-][0-9A-Za-z-]*)(?:\.(?:0|[1-9]\d*|\d*[A-Za-z-][0-9A-Za-z-]*))*))?(?:\+([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?$/; const WINDOWS_INVALID_PATH_CHARS = /[<>:"|?*]/; const WINDOWS_RESERVED_PATH_SEGMENT = /^(?:con|prn|aux|nul|com[1-9]|lpt[1-9])(?:\..*)?$/i; +const MAX_CLAW_MANIFEST_BYTES = 1024 * 1024; const MAX_OPENCLAW_PROFILE_BYTES = 256 * 1024; +const OPENCLAW_TOOL_PROFILE_IDS = new Set(["minimal", "coding", "messaging", "full"]); const StrictStringArraySchema = type("string[]"); const OpenClawProfileSchema = type({ "+": "reject", @@ -116,6 +119,22 @@ function portablePathKey(value: string): string { return value.replaceAll("\\", "/").normalize("NFC").toLowerCase(); } +export function findClawPackagePathHierarchyCollision( + paths: readonly string[], +): { ancestor: string; descendant: string } | null { + const pathByKey = new Map(paths.map((path) => [portablePathKey(path), path])); + for (const descendant of paths) { + const segments = portablePathKey(descendant).split("/"); + for (let length = 1; length < segments.length; length += 1) { + const ancestor = pathByKey.get(segments.slice(0, length).join("/")); + if (ancestor !== undefined) { + return { ancestor, descendant }; + } + } + } + return null; +} + function issue(code: string, path: string, message: string): ClawPackageValidationIssue { return { code, path, message }; } @@ -237,6 +256,11 @@ function validateOpenClawProfile( requireNonEmpty("agent.groupChat.mentionPatterns", parsed.agent.groupChat?.mentionPatterns); if (parsed.agent.tools?.profile !== undefined && !isStrictNonEmpty(parsed.agent.tools.profile)) { add("agent.tools.profile", "Must be non-empty without leading or trailing whitespace."); + } else if ( + parsed.agent.tools?.profile !== undefined && + !OPENCLAW_TOOL_PROFILE_IDS.has(parsed.agent.tools.profile) + ) { + add("agent.tools.profile", "Must name a registered OpenClaw built-in profile."); } requireNonEmpty("agent.tools.allow", parsed.agent.tools?.allow); requireNonEmpty("agent.tools.alsoAllow", parsed.agent.tools?.alsoAllow); @@ -348,7 +372,10 @@ function parseManifestDocument(raw: string, manifestPath: string) { }; } try { - return { value: document.toJSON() }; + return { + value: document.toJSON(), + markdownBody: markdown.slice(match[0].length), + }; } catch (error) { return { issues: [ @@ -450,6 +477,16 @@ export function validateClawPackageContents(input: { fileByPath.set(file.path, file); } } + const hierarchyCollision = findClawPackagePathHierarchyCollision([...fileByPath.keys()]); + if (hierarchyCollision) { + issues.push( + issue( + "portable_path_hierarchy_collision", + hierarchyCollision.descendant, + `Package file ${hierarchyCollision.ancestor} cannot also be an ancestor of ${hierarchyCollision.descendant}.`, + ), + ); + } const manifestFile = fileByPath.get(manifestPath); if (!manifestFile || manifestFile.text === undefined) { issues.push( @@ -461,8 +498,28 @@ export function validateClawPackageContents(input: { ); return { ok: false, issues }; } + if (new TextEncoder().encode(manifestFile.text).byteLength > MAX_CLAW_MANIFEST_BYTES) { + return { + ok: false, + issues: [ + issue( + "claw_manifest_too_large", + manifestPath, + `The Claw manifest exceeds ${MAX_CLAW_MANIFEST_BYTES} bytes.`, + ), + ], + }; + } const parsed = parseManifestDocument(manifestFile.text, manifestPath); if (parsed.issues) return { ok: false, issues: parsed.issues }; + if (parsed.markdownBody !== undefined && parsed.markdownBody.trim().length === 0) { + return { + ok: false, + issues: [ + issue("empty_claw_body", manifestPath, "CLAW.md must contain a non-empty Markdown body."), + ], + }; + } const validated = validateClawManifest(parsed.value); if (!validated.ok) { return { @@ -472,6 +529,24 @@ export function validateClawPackageContents(input: { ), }; } + if ( + parsed.markdownBody !== undefined && + (validated.manifest.workspace?.bootstrapFiles?.["SOUL.md"] !== undefined || + (validated.manifest.workspace?.files ?? []).some( + (entry) => portablePathKey(entry.path) === portablePathKey("SOUL.md"), + )) + ) { + return { + ok: false, + issues: [ + issue( + "duplicate_claw_body_destination", + "SOUL.md", + "The CLAW.md body is the managed SOUL.md source and cannot be combined with an explicit SOUL.md declaration.", + ), + ], + }; + } const openClawProfilePath = validated.manifest.metadata?.["openclaw.config"]; if (openClawProfilePath !== undefined) { @@ -520,12 +595,26 @@ export function validateClawPackageContents(input: { } } if (issues.length > 0) return { ok: false, issues }; + const summary = summarizeClawManifest(validated.manifest); + const implicitWorkspaceFile = + parsed.markdownBody === undefined + ? undefined + : { path: "SOUL.md" as const, text: parsed.markdownBody }; return { ok: true, value: { manifestPath, manifest: validated.manifest, - summary: summarizeClawManifest(validated.manifest), + ...(implicitWorkspaceFile ? { implicitWorkspaceFile } : {}), + summary: implicitWorkspaceFile + ? { + ...summary, + workspace: { + ...summary.workspace, + bootstrapFiles: [...summary.workspace.bootstrapFiles, "SOUL.md"], + }, + } + : summary, }, }; } diff --git a/packages/schema/dist/clawPackage.d.ts b/packages/schema/dist/clawPackage.d.ts index 4121d79711..cc486899b7 100644 --- a/packages/schema/dist/clawPackage.d.ts +++ b/packages/schema/dist/clawPackage.d.ts @@ -11,9 +11,17 @@ export type ClawPackageValidationIssue = { export type ValidatedClawPackage = { manifestPath: string; manifest: ClawManifest; + implicitWorkspaceFile?: { + path: "SOUL.md"; + text: string; + }; summary: ClawManifestSummary; }; export declare function isSafeClawPackagePath(value: string): boolean; +export declare function findClawPackagePathHierarchyCollision(paths: readonly string[]): { + ancestor: string; + descendant: string; +} | null; export declare function validateClawPackageContents(input: { packageName: string; version: string; diff --git a/packages/schema/dist/clawPackage.js b/packages/schema/dist/clawPackage.js index d2877d3a06..af3961233b 100644 --- a/packages/schema/dist/clawPackage.js +++ b/packages/schema/dist/clawPackage.js @@ -4,7 +4,9 @@ import { summarizeClawManifest, validateClawManifest, } from "./claws.js"; const EXACT_VERSION_PATTERN = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[A-Za-z-][0-9A-Za-z-]*)(?:\.(?:0|[1-9]\d*|\d*[A-Za-z-][0-9A-Za-z-]*))*))?(?:\+([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?$/; const WINDOWS_INVALID_PATH_CHARS = /[<>:"|?*]/; const WINDOWS_RESERVED_PATH_SEGMENT = /^(?:con|prn|aux|nul|com[1-9]|lpt[1-9])(?:\..*)?$/i; +const MAX_CLAW_MANIFEST_BYTES = 1024 * 1024; const MAX_OPENCLAW_PROFILE_BYTES = 256 * 1024; +const OPENCLAW_TOOL_PROFILE_IDS = new Set(["minimal", "coding", "messaging", "full"]); const StrictStringArraySchema = type("string[]"); const OpenClawProfileSchema = type({ "+": "reject", @@ -88,6 +90,19 @@ export function isSafeClawPackagePath(value) { function portablePathKey(value) { return value.replaceAll("\\", "/").normalize("NFC").toLowerCase(); } +export function findClawPackagePathHierarchyCollision(paths) { + const pathByKey = new Map(paths.map((path) => [portablePathKey(path), path])); + for (const descendant of paths) { + const segments = portablePathKey(descendant).split("/"); + for (let length = 1; length < segments.length; length += 1) { + const ancestor = pathByKey.get(segments.slice(0, length).join("/")); + if (ancestor !== undefined) { + return { ancestor, descendant }; + } + } + } + return null; +} function issue(code, path, message) { return { code, path, message }; } @@ -189,6 +204,10 @@ function validateOpenClawProfile(value, profilePath) { if (parsed.agent.tools?.profile !== undefined && !isStrictNonEmpty(parsed.agent.tools.profile)) { add("agent.tools.profile", "Must be non-empty without leading or trailing whitespace."); } + else if (parsed.agent.tools?.profile !== undefined && + !OPENCLAW_TOOL_PROFILE_IDS.has(parsed.agent.tools.profile)) { + add("agent.tools.profile", "Must name a registered OpenClaw built-in profile."); + } requireNonEmpty("agent.tools.allow", parsed.agent.tools?.allow); requireNonEmpty("agent.tools.alsoAllow", parsed.agent.tools?.alsoAllow); requireNonEmpty("agent.tools.deny", parsed.agent.tools?.deny); @@ -282,7 +301,10 @@ function parseManifestDocument(raw, manifestPath) { }; } try { - return { value: document.toJSON() }; + return { + value: document.toJSON(), + markdownBody: markdown.slice(match[0].length), + }; } catch (error) { return { @@ -344,14 +366,34 @@ export function validateClawPackageContents(input) { fileByPath.set(file.path, file); } } + const hierarchyCollision = findClawPackagePathHierarchyCollision([...fileByPath.keys()]); + if (hierarchyCollision) { + issues.push(issue("portable_path_hierarchy_collision", hierarchyCollision.descendant, `Package file ${hierarchyCollision.ancestor} cannot also be an ancestor of ${hierarchyCollision.descendant}.`)); + } const manifestFile = fileByPath.get(manifestPath); if (!manifestFile || manifestFile.text === undefined) { issues.push(issue("missing_claw_manifest", manifestPath, "The declared Claw manifest is missing or is not UTF-8 text.")); return { ok: false, issues }; } + if (new TextEncoder().encode(manifestFile.text).byteLength > MAX_CLAW_MANIFEST_BYTES) { + return { + ok: false, + issues: [ + issue("claw_manifest_too_large", manifestPath, `The Claw manifest exceeds ${MAX_CLAW_MANIFEST_BYTES} bytes.`), + ], + }; + } const parsed = parseManifestDocument(manifestFile.text, manifestPath); if (parsed.issues) return { ok: false, issues: parsed.issues }; + if (parsed.markdownBody !== undefined && parsed.markdownBody.trim().length === 0) { + return { + ok: false, + issues: [ + issue("empty_claw_body", manifestPath, "CLAW.md must contain a non-empty Markdown body."), + ], + }; + } const validated = validateClawManifest(parsed.value); if (!validated.ok) { return { @@ -359,6 +401,16 @@ export function validateClawPackageContents(input) { issues: validated.issues.map((entry) => issue("invalid_claw_manifest", entry.path, entry.message)), }; } + if (parsed.markdownBody !== undefined && + (validated.manifest.workspace?.bootstrapFiles?.["SOUL.md"] !== undefined || + (validated.manifest.workspace?.files ?? []).some((entry) => portablePathKey(entry.path) === portablePathKey("SOUL.md")))) { + return { + ok: false, + issues: [ + issue("duplicate_claw_body_destination", "SOUL.md", "The CLAW.md body is the managed SOUL.md source and cannot be combined with an explicit SOUL.md declaration."), + ], + }; + } const openClawProfilePath = validated.manifest.metadata?.["openclaw.config"]; if (openClawProfilePath !== undefined) { const profileFile = fileByPath.get(openClawProfilePath); @@ -389,12 +441,25 @@ export function validateClawPackageContents(input) { } if (issues.length > 0) return { ok: false, issues }; + const summary = summarizeClawManifest(validated.manifest); + const implicitWorkspaceFile = parsed.markdownBody === undefined + ? undefined + : { path: "SOUL.md", text: parsed.markdownBody }; return { ok: true, value: { manifestPath, manifest: validated.manifest, - summary: summarizeClawManifest(validated.manifest), + ...(implicitWorkspaceFile ? { implicitWorkspaceFile } : {}), + summary: implicitWorkspaceFile + ? { + ...summary, + workspace: { + ...summary.workspace, + bootstrapFiles: [...summary.workspace.bootstrapFiles, "SOUL.md"], + }, + } + : summary, }, }; } diff --git a/packages/schema/dist/clawPackage.js.map b/packages/schema/dist/clawPackage.js.map index ad837444b1..a505289a61 100644 --- a/packages/schema/dist/clawPackage.js.map +++ b/packages/schema/dist/clawPackage.js.map @@ -1 +1 @@ -{"version":3,"file":"clawPackage.js","sourceRoot":"","sources":["../src/clawPackage.ts"],"names":[],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"clawPackage.js","sourceRoot":"","sources":["../src/clawPackage.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/packages/schema/src/clawPackage.test.ts b/packages/schema/src/clawPackage.test.ts index f7269a1629..f04673fe57 100644 --- a/packages/schema/src/clawPackage.test.ts +++ b/packages/schema/src/clawPackage.test.ts @@ -1,11 +1,14 @@ import { describe, expect, it } from "vitest"; -import { validateClawPackageContents } from "./clawPackage.js"; +import { + findClawPackagePathHierarchyCollision, + validateClawPackageContents, +} from "./clawPackage.js"; const manifest = { schemaVersion: 1, agent: { id: "github-triage", name: "GitHub Triage", description: "Reviews issues." }, workspace: { - bootstrapFiles: { "SOUL.md": { source: "workspace/SOUL.md" } }, + bootstrapFiles: {}, files: [{ source: "workspace/reference.md", path: "reference.md" }], }, packages: [{ kind: "skill", source: "clawhub", ref: "@acme/triage", version: "1.2.0" }], @@ -19,7 +22,7 @@ const openClawProfile = [ " mentionPatterns: ['@triage']", " sandbox: { mode: non-main, scope: agent, workspaceAccess: rw }", " tools:", - " profile: future-profile", + " profile: coding", " alsoAllow: [cron]", " deny: [gateway]", " fs: { workspaceOnly: true }", @@ -55,6 +58,13 @@ function files(manifestText = `---\n${JSON.stringify(manifest)}\n---\n# GitHub T } describe("validateClawPackageContents", () => { + it("finds hierarchy collisions even when another path sorts between them", () => { + expect(findClawPackagePathHierarchyCollision(["a", "a-0", "a/child"])).toEqual({ + ancestor: "a", + descendant: "a/child", + }); + }); + it("validates CLAW.md and derives its safe summary", () => { const result = validateClawPackageContents({ packageName: "@acme/github-triage", @@ -67,6 +77,10 @@ describe("validateClawPackageContents", () => { ok: true, value: expect.objectContaining({ manifestPath: "CLAW.md", + implicitWorkspaceFile: { + path: "SOUL.md", + text: "# GitHub Triage\n", + }, summary: expect.objectContaining({ agent: { id: "github-triage", @@ -74,6 +88,7 @@ describe("validateClawPackageContents", () => { description: "Reviews issues.", }, packages: { skillCount: 1, pluginCount: 0 }, + workspace: { bootstrapFiles: ["SOUL.md"], fileCount: 1 }, }), }), }); @@ -90,6 +105,71 @@ describe("validateClawPackageContents", () => { expect(result.ok).toBe(true); }); + it.each(["", "\n \t\n"])("rejects an empty CLAW.md body", (body) => { + const result = validateClawPackageContents({ + packageName: "@acme/github-triage", + version: "1.0.0", + packageJson: packageJson(), + files: files(`---\n${JSON.stringify(manifest)}\n---\n${body}`), + }); + + expect(result).toEqual({ + ok: false, + issues: [expect.objectContaining({ code: "empty_claw_body", path: "CLAW.md" })], + }); + }); + + it("rejects a CLAW.md envelope larger than 1 MiB", () => { + const result = validateClawPackageContents({ + packageName: "@acme/github-triage", + version: "1.0.0", + packageJson: packageJson(), + files: files(`---\n${JSON.stringify(manifest)}\n---\n${"x".repeat(1024 * 1024)}`), + }); + + expect(result).toEqual({ + ok: false, + issues: [expect.objectContaining({ code: "claw_manifest_too_large", path: "CLAW.md" })], + }); + }); + + it.each([ + [ + "bootstrap source", + { + ...manifest, + workspace: { + ...manifest.workspace, + bootstrapFiles: { "SOUL.md": { source: "workspace/SOUL.md" } }, + }, + }, + ], + [ + "case-folded supporting-file destination", + { + ...manifest, + workspace: { + ...manifest.workspace, + files: [{ source: "workspace/reference.md", path: "soul.md" }], + }, + }, + ], + ])("rejects an explicit SOUL.md %s beside the CLAW.md body", (_label, bodyManifest) => { + const result = validateClawPackageContents({ + packageName: "@acme/github-triage", + version: "1.0.0", + packageJson: packageJson(), + files: files(`---\n${JSON.stringify(bodyManifest)}\n---\n# Prompt\n`), + }); + + expect(result).toEqual({ + ok: false, + issues: [ + expect.objectContaining({ code: "duplicate_claw_body_destination", path: "SOUL.md" }), + ], + }); + }); + it("validates a package-local OpenClaw profile without returning it", () => { const profiledManifest = { ...manifest, @@ -100,7 +180,7 @@ describe("validateClawPackageContents", () => { version: "1.0.0", packageJson: packageJson(), files: [ - ...files(`---\n${JSON.stringify(profiledManifest)}\n---\n`), + ...files(`---\n${JSON.stringify(profiledManifest)}\n---\n# Prompt\n`), { path: "profiles/openclaw.yml", text: openClawProfile }, ], }); @@ -112,6 +192,35 @@ describe("validateClawPackageContents", () => { } }); + it("rejects an unregistered OpenClaw tool profile", () => { + const profiledManifest = { + ...manifest, + metadata: { "openclaw.config": "profiles/openclaw.yml" }, + }; + const result = validateClawPackageContents({ + packageName: "@acme/github-triage", + version: "1.0.0", + packageJson: packageJson(), + files: [ + ...files(`---\n${JSON.stringify(profiledManifest)}\n---\n# Prompt\n`), + { + path: "profiles/openclaw.yml", + text: "schemaVersion: 1\nagent:\n tools:\n profile: future-profile", + }, + ], + }); + + expect(result).toEqual({ + ok: false, + issues: [ + expect.objectContaining({ + code: "invalid_openclaw_profile", + path: "profiles/openclaw.yml.agent.tools.profile", + }), + ], + }); + }); + it("requires the exact UTF-8 profile target", () => { const profiledManifest = { ...manifest, @@ -122,7 +231,7 @@ describe("validateClawPackageContents", () => { version: "1.0.0", packageJson: packageJson(), files: [ - ...files(`---\n${JSON.stringify(profiledManifest)}\n---\n`), + ...files(`---\n${JSON.stringify(profiledManifest)}\n---\n# Prompt\n`), { path: "profiles/OPENCLAW.yml", text: openClawProfile }, ], }); @@ -172,7 +281,7 @@ describe("validateClawPackageContents", () => { version: "1.0.0", packageJson: packageJson(), files: [ - ...files(`---\n${JSON.stringify(profiledManifest)}\n---\n`), + ...files(`---\n${JSON.stringify(profiledManifest)}\n---\n# Prompt\n`), { path: "profiles/openclaw.yml", text }, ], }); @@ -193,7 +302,7 @@ describe("validateClawPackageContents", () => { version: "1.0.0", packageJson: packageJson(), files: [ - ...files(`---\n${JSON.stringify(profiledManifest)}\n---\n`), + ...files(`---\n${JSON.stringify(profiledManifest)}\n---\n# Prompt\n`), { path: "profiles/openclaw.yml", text: `#${"x".repeat(256 * 1024)}` }, ], }); @@ -233,7 +342,7 @@ describe("validateClawPackageContents", () => { version: "1.0.0", packageJson: packageJson(), files: [ - ...files(`---\n${JSON.stringify(profiledManifest)}\n---\n`), + ...files(`---\n${JSON.stringify(profiledManifest)}\n---\n# Prompt\n`), { path: "profiles/openclaw.yml", text }, ], }); @@ -293,17 +402,25 @@ describe("validateClawPackageContents", () => { }); it("accepts the JSON compatibility manifest", () => { + const jsonManifest = { + ...manifest, + workspace: { + ...manifest.workspace, + bootstrapFiles: { "SOUL.md": { source: "workspace/SOUL.md" } }, + }, + }; const result = validateClawPackageContents({ packageName: "@acme/github-triage", version: "1.0.0", packageJson: packageJson("openclaw.claw.json"), files: [ ...files().filter((file) => file.path !== "CLAW.md"), - { path: "openclaw.claw.json", text: JSON.stringify(manifest) }, + { path: "openclaw.claw.json", text: JSON.stringify(jsonManifest) }, ], }); expect(result.ok).toBe(true); + if (result.ok) expect(result.value).not.toHaveProperty("implicitWorkspaceFile"); }); it("requires package identity to match the publication", () => { @@ -373,6 +490,34 @@ describe("validateClawPackageContents", () => { }); }); + it.each([ + ["ancestor first", [{ path: "workspace", text: "file" }, ...files()]], + ["descendant first", [...files(), { path: "workspace", text: "file" }]], + ["case-folded ancestor", [...files(), { path: "WORKSPACE", text: "file" }]], + [ + "Unicode-normalized ancestor", + [ + ...files(), + { path: "cafe\u0301/reference.md", text: "child" }, + { path: "caf\u00e9", text: "file" }, + ], + ], + ])("rejects package file/ancestor collisions with %s", (_label, packageFiles) => { + const result = validateClawPackageContents({ + packageName: "@acme/github-triage", + version: "1.0.0", + packageJson: packageJson(), + files: packageFiles, + }); + + expect(result).toEqual({ + ok: false, + issues: expect.arrayContaining([ + expect.objectContaining({ code: "portable_path_hierarchy_collision" }), + ]), + }); + }); + it.each(["CON", "workspace/trailing.", "workspace\\backslash.md"])( "rejects unsafe unreferenced package path %s", (path) => { @@ -419,7 +564,7 @@ describe("validateClawPackageContents", () => { packageName: "@acme/github-triage", version: "1.0.0", packageJson: packageJson(), - files: files(`---\n${JSON.stringify(unsafe)}\n---\n`), + files: files(`---\n${JSON.stringify(unsafe)}\n---\n# Prompt\n`), }); expect(result).toEqual({ diff --git a/packages/schema/src/clawPackage.ts b/packages/schema/src/clawPackage.ts index c67b371a41..53ae9efb7d 100644 --- a/packages/schema/src/clawPackage.ts +++ b/packages/schema/src/clawPackage.ts @@ -16,6 +16,7 @@ export type ClawPackageValidationIssue = { export type ValidatedClawPackage = { manifestPath: string; manifest: ClawManifest; + implicitWorkspaceFile?: { path: "SOUL.md"; text: string }; summary: ClawManifestSummary; }; @@ -23,7 +24,9 @@ const EXACT_VERSION_PATTERN = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[A-Za-z-][0-9A-Za-z-]*)(?:\.(?:0|[1-9]\d*|\d*[A-Za-z-][0-9A-Za-z-]*))*))?(?:\+([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?$/; const WINDOWS_INVALID_PATH_CHARS = /[<>:"|?*]/; const WINDOWS_RESERVED_PATH_SEGMENT = /^(?:con|prn|aux|nul|com[1-9]|lpt[1-9])(?:\..*)?$/i; +const MAX_CLAW_MANIFEST_BYTES = 1024 * 1024; const MAX_OPENCLAW_PROFILE_BYTES = 256 * 1024; +const OPENCLAW_TOOL_PROFILE_IDS = new Set(["minimal", "coding", "messaging", "full"]); const StrictStringArraySchema = type("string[]"); const OpenClawProfileSchema = type({ "+": "reject", @@ -116,6 +119,22 @@ function portablePathKey(value: string): string { return value.replaceAll("\\", "/").normalize("NFC").toLowerCase(); } +export function findClawPackagePathHierarchyCollision( + paths: readonly string[], +): { ancestor: string; descendant: string } | null { + const pathByKey = new Map(paths.map((path) => [portablePathKey(path), path])); + for (const descendant of paths) { + const segments = portablePathKey(descendant).split("/"); + for (let length = 1; length < segments.length; length += 1) { + const ancestor = pathByKey.get(segments.slice(0, length).join("/")); + if (ancestor !== undefined) { + return { ancestor, descendant }; + } + } + } + return null; +} + function issue(code: string, path: string, message: string): ClawPackageValidationIssue { return { code, path, message }; } @@ -237,6 +256,11 @@ function validateOpenClawProfile( requireNonEmpty("agent.groupChat.mentionPatterns", parsed.agent.groupChat?.mentionPatterns); if (parsed.agent.tools?.profile !== undefined && !isStrictNonEmpty(parsed.agent.tools.profile)) { add("agent.tools.profile", "Must be non-empty without leading or trailing whitespace."); + } else if ( + parsed.agent.tools?.profile !== undefined && + !OPENCLAW_TOOL_PROFILE_IDS.has(parsed.agent.tools.profile) + ) { + add("agent.tools.profile", "Must name a registered OpenClaw built-in profile."); } requireNonEmpty("agent.tools.allow", parsed.agent.tools?.allow); requireNonEmpty("agent.tools.alsoAllow", parsed.agent.tools?.alsoAllow); @@ -348,7 +372,10 @@ function parseManifestDocument(raw: string, manifestPath: string) { }; } try { - return { value: document.toJSON() }; + return { + value: document.toJSON(), + markdownBody: markdown.slice(match[0].length), + }; } catch (error) { return { issues: [ @@ -450,6 +477,16 @@ export function validateClawPackageContents(input: { fileByPath.set(file.path, file); } } + const hierarchyCollision = findClawPackagePathHierarchyCollision([...fileByPath.keys()]); + if (hierarchyCollision) { + issues.push( + issue( + "portable_path_hierarchy_collision", + hierarchyCollision.descendant, + `Package file ${hierarchyCollision.ancestor} cannot also be an ancestor of ${hierarchyCollision.descendant}.`, + ), + ); + } const manifestFile = fileByPath.get(manifestPath); if (!manifestFile || manifestFile.text === undefined) { issues.push( @@ -461,8 +498,28 @@ export function validateClawPackageContents(input: { ); return { ok: false, issues }; } + if (new TextEncoder().encode(manifestFile.text).byteLength > MAX_CLAW_MANIFEST_BYTES) { + return { + ok: false, + issues: [ + issue( + "claw_manifest_too_large", + manifestPath, + `The Claw manifest exceeds ${MAX_CLAW_MANIFEST_BYTES} bytes.`, + ), + ], + }; + } const parsed = parseManifestDocument(manifestFile.text, manifestPath); if (parsed.issues) return { ok: false, issues: parsed.issues }; + if (parsed.markdownBody !== undefined && parsed.markdownBody.trim().length === 0) { + return { + ok: false, + issues: [ + issue("empty_claw_body", manifestPath, "CLAW.md must contain a non-empty Markdown body."), + ], + }; + } const validated = validateClawManifest(parsed.value); if (!validated.ok) { return { @@ -472,6 +529,24 @@ export function validateClawPackageContents(input: { ), }; } + if ( + parsed.markdownBody !== undefined && + (validated.manifest.workspace?.bootstrapFiles?.["SOUL.md"] !== undefined || + (validated.manifest.workspace?.files ?? []).some( + (entry) => portablePathKey(entry.path) === portablePathKey("SOUL.md"), + )) + ) { + return { + ok: false, + issues: [ + issue( + "duplicate_claw_body_destination", + "SOUL.md", + "The CLAW.md body is the managed SOUL.md source and cannot be combined with an explicit SOUL.md declaration.", + ), + ], + }; + } const openClawProfilePath = validated.manifest.metadata?.["openclaw.config"]; if (openClawProfilePath !== undefined) { @@ -520,12 +595,26 @@ export function validateClawPackageContents(input: { } } if (issues.length > 0) return { ok: false, issues }; + const summary = summarizeClawManifest(validated.manifest); + const implicitWorkspaceFile = + parsed.markdownBody === undefined + ? undefined + : { path: "SOUL.md" as const, text: parsed.markdownBody }; return { ok: true, value: { manifestPath, manifest: validated.manifest, - summary: summarizeClawManifest(validated.manifest), + ...(implicitWorkspaceFile ? { implicitWorkspaceFile } : {}), + summary: implicitWorkspaceFile + ? { + ...summary, + workspace: { + ...summary.workspace, + bootstrapFiles: [...summary.workspace.bootstrapFiles, "SOUL.md"], + }, + } + : summary, }, }; } From 61ff8a126d0ce3a9fc0ac8233da2ada50bdf3660 Mon Sep 17 00:00:00 2001 From: Patrick Erichsen Date: Fri, 24 Jul 2026 16:46:00 -0500 Subject: [PATCH 05/17] fix(claws): enforce gated publication boundaries --- convex/httpApiV1.handlers.test.ts | 65 ++++- convex/httpApiV1/packagesV1.ts | 4 + convex/packages.public.test.ts | 191 ++++++++++++- convex/packages.ts | 456 +++++++++++++++++++++++++----- convex/schema.ts | 10 +- 5 files changed, 642 insertions(+), 84 deletions(-) diff --git a/convex/httpApiV1.handlers.test.ts b/convex/httpApiV1.handlers.test.ts index 7d36c0690e..dffaa8dc55 100644 --- a/convex/httpApiV1.handlers.test.ts +++ b/convex/httpApiV1.handlers.test.ts @@ -14872,6 +14872,65 @@ describe("httpApiV1 handlers", () => { expect(runAction).not.toHaveBeenCalled(); }); + it.each(["loose", "direct-tgz", "staged-tgz"] as const)( + "disabled Claw publication rejects %s before multipart storage or ticket mutation", + async (mode) => { + vi.stubEnv("CLAWHUB_EXPERIMENTAL_CLAWS", "0"); + vi.mocked(getOptionalApiTokenUserId).mockResolvedValue("users:1" as never); + vi.mocked(requirePackagePublishAuth).mockResolvedValue({ + kind: "user", + userId: "users:1", + user: { _id: "users:1", handle: "p" }, + } as never); + const runMutation = vi.fn().mockResolvedValue(okRate()); + const runAction = vi.fn(); + const storageGet = vi.fn(); + const storageStore = vi.fn(); + const form = packagePublishForm(packagePublishMetadata({ family: "claw" })); + if (mode === "loose") { + form.append("files", new File(["manifest"], "CLAW.md", { type: "text/markdown" })); + } else if (mode === "direct-tgz") { + const pack = npmPackFixture({ + "package/package.json": JSON.stringify({ name: "demo-claw", version: "1.0.0" }), + "package/CLAW.md": "manifest", + }); + form.append( + "clawpack", + new File([bytesToArrayBuffer(pack)], "demo-claw-1.0.0.tgz", { + type: "application/octet-stream", + }), + ); + } else { + form.set("clawpack", "storage:clawpack"); + form.set("clawpackUploadTicket", "packagePublishUploadTickets:1"); + } + + const response = await __handlers.publishPackageV1Handler( + makeCtx({ + runAction, + runMutation, + storage: { get: storageGet, store: storageStore }, + }), + new Request("https://example.com/api/v1/packages", { + method: "POST", + headers: { Authorization: "Bearer clh_test" }, + body: form, + }), + ); + + expect(response.status).toBe(400); + expect(await response.text()).toBe("Experimental Claw publication is disabled"); + expect(storageGet).not.toHaveBeenCalled(); + expect(storageStore).not.toHaveBeenCalled(); + expect(runAction).not.toHaveBeenCalled(); + expect( + runMutation.mock.calls.some(([, args]) => + Boolean(args && typeof args === "object" && "uploadTicket" in args), + ), + ).toBe(false); + }, + ); + it("package publish rejects browser session auth when token auth is not an API token", async () => { vi.mocked(getAuthUserId).mockResolvedValue("users:session" as never); vi.mocked(requirePackagePublishAuth).mockRejectedValue(new Error("Unauthorized")); @@ -15044,6 +15103,7 @@ describe("httpApiV1 handlers", () => { }); it("multipart Claw publish accepts an npm pack without a plugin manifest", async () => { + vi.stubEnv("CLAWHUB_EXPERIMENTAL_CLAWS", "1"); vi.mocked(getOptionalApiTokenUserId).mockResolvedValue("users:1" as never); vi.mocked(requirePackagePublishAuth).mockResolvedValue({ kind: "user", @@ -15061,7 +15121,8 @@ describe("httpApiV1 handlers", () => { version: "1.0.0", openclaw: { claw: "CLAW.md" }, }), - "package/CLAW.md": "---\nschemaVersion: 1\nagent:\n id: demo-claw\n---\n", + "package/CLAW.md": + "---\nschemaVersion: 1\nagent:\n id: demo-claw\n---\nYou are a focused demo agent.\n", }); const form = new FormData(); form.set( @@ -15089,7 +15150,7 @@ describe("httpApiV1 handlers", () => { }), ); - expect(response.status).toBe(200); + expect(response.status, await response.clone().text()).toBe(200); expect(storageStore).toHaveBeenCalledTimes(3); expect(runAction).toHaveBeenCalledWith( expect.anything(), diff --git a/convex/httpApiV1/packagesV1.ts b/convex/httpApiV1/packagesV1.ts index e3aad026a3..7291d9f359 100644 --- a/convex/httpApiV1/packagesV1.ts +++ b/convex/httpApiV1/packagesV1.ts @@ -34,6 +34,7 @@ import { buildDownloadMetricArgs, getDownloadIdentity } from "../downloadMetrics import { getOptionalActiveAuthUserIdFromAction } from "../lib/access"; import { getOptionalApiTokenUserId, requireApiTokenUser } from "../lib/apiTokenAuth"; import { parseClawPack, sha256Base64, sha256Hex } from "../lib/clawpack"; +import { experimentalClawsEnabled } from "../lib/experimentalClaws"; import { fetchGitHubRepositoryIdentity, verifyGitHubActionsTrustedPublishJwt, @@ -1462,6 +1463,9 @@ async function parseMultipartPackagePublish( parsedPayload, "Package publish payload", ); + if (metadata.family === "claw" && !experimentalClawsEnabled()) { + throw new Error("Experimental Claw publication is disabled"); + } const tarballPart = getTarballPart(form); const fileParts = getFileParts( diff --git a/convex/packages.public.test.ts b/convex/packages.public.test.ts index b4ca024b5e..71ed753e9f 100644 --- a/convex/packages.public.test.ts +++ b/convex/packages.public.test.ts @@ -1,7 +1,7 @@ /* @vitest-environment node */ import { getAuthUserId } from "@convex-dev/auth/server"; -import { afterEach, describe, expect, it, vi } from "vitest"; +import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import { sha256Hex } from "./lib/clawpack"; import { MAX_PUBLISH_FILE_BYTES } from "./lib/publishLimits"; import { @@ -976,6 +976,10 @@ const repairPackageIdentityInternalHandler = ( > )._handler; +beforeEach(() => { + process.env.CLAWHUB_EXPERIMENTAL_CLAWS = "1"; +}); + afterEach(() => { vi.mocked(getAuthUserId).mockReset(); vi.mocked(getAuthUserId).mockResolvedValue(null); @@ -1549,6 +1553,32 @@ function makeDigestCtx(options: { take: vi.fn().mockResolvedValue(matches), }; } + if (indexName.includes("_family_")) { + indexNames.push(indexName); + let family = ""; + const queryBuilder = { + eq: (field: string, value: string | undefined) => { + if (field === "family") family = value ?? ""; + return queryBuilder; + }, + gte: () => queryBuilder, + lt: () => queryBuilder, + }; + builder?.(queryBuilder); + const takeFamilyRows = async (limit: number) => { + take(limit); + return (rowsByTable.get(table) ?? []) + .filter((row) => row.family === family) + .slice(0, limit); + }; + return { + take: vi.fn(takeFamilyRows), + order: vi.fn(() => ({ + paginate: getPaginate(table), + take: vi.fn(takeFamilyRows), + })), + }; + } return withIndex(table, indexName); }, withSearchIndex: ( @@ -1566,12 +1596,11 @@ function makeDigestCtx(options: { let searchField = ""; let query = ""; const queryBuilder = { + eq: () => queryBuilder, search: (field: string, value: string) => { searchField = field; query = value; - return { - eq: () => queryBuilder, - }; + return queryBuilder; }, }; builder?.(queryBuilder); @@ -1596,6 +1625,52 @@ function makeDigestCtx(options: { lt: (field: string, value: string) => unknown; }) => unknown, ) => { + if (indexName.includes("_family_")) { + indexNames.push(indexName); + let family = ""; + let exactTopic = ""; + let exactCategory = ""; + let lowerTopic = ""; + let upperTopic = ""; + const queryBuilder = { + eq: (field: string, value: unknown) => { + if (field === "family" && typeof value === "string") family = value; + if (field === "topic" && typeof value === "string") exactTopic = value; + if (field === "pluginCategory" && typeof value === "string") { + exactCategory = value; + } + return queryBuilder; + }, + gte: (field: string, value: string) => { + if (field === "topic") lowerTopic = value; + return queryBuilder; + }, + lt: (field: string, value: string) => { + if (field === "topic") upperTopic = value; + return queryBuilder; + }, + }; + builder?.(queryBuilder); + const takeFamilyRows = async (limit: number) => { + take(limit); + return (rowsByTable.get(table) ?? []) + .filter((row) => row.family === family) + .filter((row) => !exactTopic || row.topic === exactTopic) + .filter((row) => !exactCategory || row.pluginCategory === exactCategory) + .filter((row) => { + if (!lowerTopic && !upperTopic) return true; + const topic = typeof row.topic === "string" ? row.topic : ""; + return topic >= lowerTopic && topic < upperTopic; + }) + .slice(0, limit); + }; + return { + order: vi.fn(() => ({ + paginate: getPaginate(table), + take: vi.fn(takeFamilyRows), + })), + }; + } if (table !== "packageTopicSearchDigest" || indexName !== "by_active_topic_updated") { return withIndex(table, indexName); } @@ -4212,7 +4287,7 @@ describe("packages public queries", () => { expect(result.map((entry) => entry.package.name)).toEqual(["needle-plugin"]); expect(paginate).not.toHaveBeenCalled(); - expect(take).toHaveBeenCalledTimes(3); + expect(take.mock.calls.length).toBeLessThanOrEqual(8); expect(take).toHaveBeenCalledWith(20); expect(take).toHaveBeenCalledWith(50); }); @@ -8917,7 +8992,7 @@ describe("packages public queries", () => { } }); - it("publishes a validated Claw through the existing release pipeline when enabled", async () => { + it("publishes a profile-bearing Claw through the existing release pipeline when enabled", async () => { const previous = process.env.CLAWHUB_EXPERIMENTAL_CLAWS; process.env.CLAWHUB_EXPERIMENTAL_CLAWS = "1"; const storedFiles = new Map([ @@ -8931,9 +9006,9 @@ describe("packages public queries", () => { ], [ "storage:claw", - "---\nschemaVersion: 1\nagent:\n id: demo-claw\n name: Demo Claw\n description: Runs the demo workflow.\nworkspace:\n bootstrapFiles:\n SOUL.md:\n source: workspace/SOUL.md\n---\n# Demo Claw\n", + "---\nschemaVersion: 1\nagent:\n id: demo-claw\n name: Demo Claw\n description: Runs the demo workflow.\nmetadata:\n openclaw.config: profiles/openclaw.yml\n---\n# Demo Claw\n", ], - ["storage:soul", "Be precise.\n"], + ["storage:profile", "schemaVersion: 1\nagent:\n tools:\n profile: coding\n"], ]); const runMutation = vi.fn(async (_ref: unknown, args: Record) => { if (args.minimumRole === "publisher") { @@ -8993,7 +9068,12 @@ describe("packages public queries", () => { storageId: "storage:claw", sha256: "claw", }, - { path: "workspace/SOUL.md", size: 1, storageId: "storage:soul", sha256: "soul" }, + { + path: "profiles/openclaw.yml", + size: 1, + storageId: "storage:profile", + sha256: "profile", + }, ], }, }), @@ -16581,10 +16661,95 @@ describe("restorePackageInternal", () => { it("omits stored Claws from unfiltered public lists while disabled", async () => { const previous = process.env.CLAWHUB_EXPERIMENTAL_CLAWS; delete process.env.CLAWHUB_EXPERIMENTAL_CLAWS; + const ctx = makePluginExportCtx([ + makeDigest("demo-claw", { family: "claw", updatedAt: 2, _creationTime: 2 }), + makeDigest("demo-plugin", { updatedAt: 1, _creationTime: 1 }), + ]); + + try { + const result = await listPublicPageHandler(ctx, { + paginationOpts: { cursor: null, numItems: 25 }, + }); + expect(result.page.map((entry) => entry.name)).toEqual(["demo-plugin"]); + } finally { + if (previous === undefined) delete process.env.CLAWHUB_EXPERIMENTAL_CLAWS; + else process.env.CLAWHUB_EXPERIMENTAL_CLAWS = previous; + } + }); + + it("does not let disabled Claws starve unfiltered public list pages", async () => { + const previous = process.env.CLAWHUB_EXPERIMENTAL_CLAWS; + delete process.env.CLAWHUB_EXPERIMENTAL_CLAWS; + const hiddenClaws = Array.from({ length: 50 }, (_, index) => + makeDigest(`demo-claw-${index}`, { + family: "claw", + updatedAt: 100 - index, + _creationTime: 100 - index, + }), + ); + const ctx = makePluginExportCtx([ + ...hiddenClaws, + makeDigest("visible-plugin", { updatedAt: 1, _creationTime: 1 }), + ]); + + try { + const result = await listPublicPageHandler(ctx, { + paginationOpts: { cursor: null, numItems: 1 }, + }); + expect(result.page.map((entry) => entry.name)).toEqual(["visible-plugin"]); + } finally { + if (previous === undefined) delete process.env.CLAWHUB_EXPERIMENTAL_CLAWS; + else process.env.CLAWHUB_EXPERIMENTAL_CLAWS = previous; + } + }); + + it("paginates stable families beyond the per-query read window while Claws are disabled", async () => { + const previous = process.env.CLAWHUB_EXPERIMENTAL_CLAWS; + delete process.env.CLAWHUB_EXPERIMENTAL_CLAWS; + const digests = Array.from({ length: 250 }, (_, index) => + makeDigest(`stable-${index.toString().padStart(3, "0")}`, { + updatedAt: 250 - index, + _creationTime: 250 - index, + }), + ); + const ctx = makePluginExportCtx(digests); + + try { + const first = await listPublicPageHandler(ctx, { + paginationOpts: { cursor: null, numItems: 200 }, + }); + const second = await listPublicPageHandler(ctx, { + paginationOpts: { cursor: first.continueCursor, numItems: 200 }, + }); + expect(first.page).toHaveLength(200); + expect(first.isDone).toBe(false); + expect(second.page).toHaveLength(50); + expect(second.isDone).toBe(true); + } finally { + if (previous === undefined) delete process.env.CLAWHUB_EXPERIMENTAL_CLAWS; + else process.env.CLAWHUB_EXPERIMENTAL_CLAWS = previous; + } + }); + + it("does not let disabled Claws starve unfiltered public search", async () => { + const previous = process.env.CLAWHUB_EXPERIMENTAL_CLAWS; + delete process.env.CLAWHUB_EXPERIMENTAL_CLAWS; + const hiddenClaws = Array.from({ length: 50 }, (_, index) => + makeDigest(`matching-claw-${index}`, { + family: "claw", + displayName: `Matching Claw ${index}`, + updatedAt: 100 - index, + }), + ); const { ctx } = makeDigestCtx({ pages: [ { - page: [makeDigest("demo-claw", { family: "claw" }), makeDigest("demo-plugin")], + page: hiddenClaws, + isDone: false, + continueCursor: "after-claws", + }, + { + page: [makeDigest("matching-plugin", { displayName: "Matching Plugin" })], isDone: true, continueCursor: "", }, @@ -16592,10 +16757,8 @@ describe("restorePackageInternal", () => { }); try { - const result = await listPublicPageHandler(ctx, { - paginationOpts: { cursor: null, numItems: 25 }, - }); - expect(result.page.map((entry) => entry.name)).toEqual(["demo-plugin"]); + const result = await searchPublicHandler(ctx, { query: "matching", limit: 1 }); + expect(result.map((entry) => entry.package.name)).toEqual(["matching-plugin"]); } finally { if (previous === undefined) delete process.env.CLAWHUB_EXPERIMENTAL_CLAWS; else process.env.CLAWHUB_EXPERIMENTAL_CLAWS = previous; diff --git a/convex/packages.ts b/convex/packages.ts index 1f8e78c677..415d4c2230 100644 --- a/convex/packages.ts +++ b/convex/packages.ts @@ -138,6 +138,7 @@ const MAX_PLUGIN_EXPORT_LIST_LIMIT = 250; const MAX_SEARCH_PAGE_SIZE = 200; const MAX_DIRECT_PACKAGE_SEARCH_CANDIDATES = 20; const MAX_DIRECT_PACKAGE_FULL_TEXT_CANDIDATES = 40; +const STABLE_PACKAGE_FAMILIES = ["skill", "code-plugin", "bundle-plugin"] as const; const MAX_PACKAGE_VERSION_DELETE_LOOKUP_CANDIDATES = 4; const MAX_POINTERLESS_RELEASE_SURVIVOR_SCAN = 100; // Release rows can approach 1 MiB, and trigger-wrapped patches materialize full documents. @@ -919,6 +920,7 @@ type PackageDigestLike = Pick< | "pluginCategoryTags" | "verificationTier" | "stats" + | "recommendedScore" | "scanStatus" | "softDeletedAt" > & { pluginCategory?: string }; @@ -943,6 +945,13 @@ type PublicPackageListPage = { isDone: boolean; continueCursor: string; }; +const STABLE_PACKAGE_DISCOVERY_CURSOR_PREFIX = "pkgstable:"; +type StablePackageFamily = (typeof STABLE_PACKAGE_FAMILIES)[number]; +type StablePackageDiscoverySourceState = { key: IndexKey | null; done: boolean }; +type StablePackageDiscoveryCursorState = { + sort: "updated" | "downloads" | "recommended" | "installs"; + sources: Record; +}; const OFFICIAL_FIRST_PACKAGE_CATEGORY_CURSOR_PREFIX = "pkgofficialfirst:"; async function runQueryRef( @@ -1885,6 +1894,7 @@ function maybeNormalizePackageQuery(value: string) { async function resolveDirectPackageSearchDigests( ctx: DbReaderCtx, queryText: string, + family?: PackageFamily, ): Promise { const normalizedQuery = maybeNormalizePackageQuery(queryText); const topicQuery = normalizeCatalogTopic(queryText); @@ -1904,49 +1914,85 @@ async function resolveDirectPackageSearchDigests( categoryDigests, ] = await Promise.all([ normalizedQuery - ? ctx.db - .query("packageSearchDigest") - .withIndex("by_active_normalized_name", (q) => - q - .eq("softDeletedAt", undefined) - .gte("normalizedName", normalizedQuery) - .lt("normalizedName", prefixUpperBound(normalizedQuery)), - ) - .take(MAX_DIRECT_PACKAGE_SEARCH_CANDIDATES) + ? family + ? ctx.db + .query("packageSearchDigest") + .withIndex("by_active_family_normalized_name", (q) => + q + .eq("softDeletedAt", undefined) + .eq("family", family) + .gte("normalizedName", normalizedQuery) + .lt("normalizedName", prefixUpperBound(normalizedQuery)), + ) + .take(MAX_DIRECT_PACKAGE_SEARCH_CANDIDATES) + : ctx.db + .query("packageSearchDigest") + .withIndex("by_active_normalized_name", (q) => + q + .eq("softDeletedAt", undefined) + .gte("normalizedName", normalizedQuery) + .lt("normalizedName", prefixUpperBound(normalizedQuery)), + ) + .take(MAX_DIRECT_PACKAGE_SEARCH_CANDIDATES) : Promise.resolve([]), runtimePrefix - ? ctx.db - .query("packageSearchDigest") - .withIndex("by_active_runtime_id", (q) => - q - .eq("softDeletedAt", undefined) - .gte("runtimeId", runtimePrefix) - .lt("runtimeId", prefixUpperBound(runtimePrefix)), - ) - .take(MAX_DIRECT_PACKAGE_SEARCH_CANDIDATES) + ? family + ? ctx.db + .query("packageSearchDigest") + .withIndex("by_active_family_runtime_id", (q) => + q + .eq("softDeletedAt", undefined) + .eq("family", family) + .gte("runtimeId", runtimePrefix) + .lt("runtimeId", prefixUpperBound(runtimePrefix)), + ) + .take(MAX_DIRECT_PACKAGE_SEARCH_CANDIDATES) + : ctx.db + .query("packageSearchDigest") + .withIndex("by_active_runtime_id", (q) => + q + .eq("softDeletedAt", undefined) + .gte("runtimeId", runtimePrefix) + .lt("runtimeId", prefixUpperBound(runtimePrefix)), + ) + .take(MAX_DIRECT_PACKAGE_SEARCH_CANDIDATES) : Promise.resolve([]), ctx.db .query("packageSearchDigest") - .withSearchIndex("search_by_display_name", (q) => - q.search("displayName", queryText).eq("softDeletedAt", undefined), - ) + .withSearchIndex("search_by_display_name", (q) => { + const search = q.search("displayName", queryText).eq("softDeletedAt", undefined); + return family ? search.eq("family", family) : search; + }) .take(MAX_DIRECT_PACKAGE_FULL_TEXT_CANDIDATES), ownerHandlePrefix - ? ctx.db - .query("packageSearchDigest") - .withIndex("by_active_owner_handle", (q) => - q - .eq("softDeletedAt", undefined) - .gte("ownerHandle", ownerHandlePrefix) - .lt("ownerHandle", prefixUpperBound(ownerHandlePrefix)), - ) - .take(MAX_DIRECT_PACKAGE_SEARCH_CANDIDATES) + ? family + ? ctx.db + .query("packageSearchDigest") + .withIndex("by_active_family_owner_handle", (q) => + q + .eq("softDeletedAt", undefined) + .eq("family", family) + .gte("ownerHandle", ownerHandlePrefix) + .lt("ownerHandle", prefixUpperBound(ownerHandlePrefix)), + ) + .take(MAX_DIRECT_PACKAGE_SEARCH_CANDIDATES) + : ctx.db + .query("packageSearchDigest") + .withIndex("by_active_owner_handle", (q) => + q + .eq("softDeletedAt", undefined) + .gte("ownerHandle", ownerHandlePrefix) + .lt("ownerHandle", prefixUpperBound(ownerHandlePrefix)), + ) + .take(MAX_DIRECT_PACKAGE_SEARCH_CANDIDATES) : Promise.resolve([]), topicQuery ? ctx.db .query("packageTopicSearchDigest") - .withIndex("by_active_topic_updated", (q) => - q.eq("softDeletedAt", undefined).eq("topic", topicQuery), + .withIndex(family ? "by_active_family_topic_updated" : "by_active_topic_updated", (q) => + family + ? q.eq("softDeletedAt", undefined).eq("family", family).eq("topic", topicQuery) + : q.eq("softDeletedAt", undefined).eq("topic", topicQuery), ) .order("desc") .take(MAX_DIRECT_PACKAGE_SEARCH_CANDIDATES) @@ -1954,8 +2000,15 @@ async function resolveDirectPackageSearchDigests( categoryQuery ? ctx.db .query("packagePluginCategorySearchDigest") - .withIndex("by_active_category_updated", (q) => - q.eq("softDeletedAt", undefined).eq("pluginCategory", categoryQuery), + .withIndex( + family ? "by_active_family_category_updated" : "by_active_category_updated", + (q) => + family + ? q + .eq("softDeletedAt", undefined) + .eq("family", family) + .eq("pluginCategory", categoryQuery) + : q.eq("softDeletedAt", undefined).eq("pluginCategory", categoryQuery), ) .order("desc") .take(MAX_DIRECT_PACKAGE_SEARCH_CANDIDATES) @@ -1965,11 +2018,17 @@ async function resolveDirectPackageSearchDigests( topicQuery && exactTopicDigests.length < MAX_DIRECT_PACKAGE_SEARCH_CANDIDATES ? await ctx.db .query("packageTopicSearchDigest") - .withIndex("by_active_topic_updated", (q) => - q - .eq("softDeletedAt", undefined) - .gte("topic", topicQuery) - .lt("topic", prefixUpperBound(topicQuery)), + .withIndex(family ? "by_active_family_topic_updated" : "by_active_topic_updated", (q) => + family + ? q + .eq("softDeletedAt", undefined) + .eq("family", family) + .gte("topic", topicQuery) + .lt("topic", prefixUpperBound(topicQuery)) + : q + .eq("softDeletedAt", undefined) + .gte("topic", topicQuery) + .lt("topic", prefixUpperBound(topicQuery)), ) .order("desc") .take(MAX_DIRECT_PACKAGE_SEARCH_CANDIDATES - exactTopicDigests.length) @@ -3388,6 +3447,74 @@ function decodePackageIndexKeyValue(val: unknown): Value | undefined { return val as Value; } +function emptyStablePackageDiscoveryCursor( + sort: StablePackageDiscoveryCursorState["sort"], +): StablePackageDiscoveryCursorState { + return { + sort, + sources: { + skill: { key: null, done: false }, + "code-plugin": { key: null, done: false }, + "bundle-plugin": { key: null, done: false }, + }, + }; +} + +function encodeStablePackageDiscoveryCursor(state: StablePackageDiscoveryCursorState) { + const sources = Object.fromEntries( + STABLE_PACKAGE_FAMILIES.map((family) => [ + family, + { + key: state.sources[family].key?.map(encodePackageIndexKeyValue) ?? null, + done: state.sources[family].done, + }, + ]), + ); + return `${STABLE_PACKAGE_DISCOVERY_CURSOR_PREFIX}${JSON.stringify({ + v: 1, + sort: state.sort, + sources, + })}`; +} + +function decodeStablePackageDiscoveryCursor( + raw: string | null | undefined, + sort: StablePackageDiscoveryCursorState["sort"], +): StablePackageDiscoveryCursorState { + const fallback = emptyStablePackageDiscoveryCursor(sort); + if (!raw?.startsWith(STABLE_PACKAGE_DISCOVERY_CURSOR_PREFIX)) return fallback; + try { + const parsed = JSON.parse(raw.slice(STABLE_PACKAGE_DISCOVERY_CURSOR_PREFIX.length)) as { + v?: unknown; + sort?: unknown; + sources?: unknown; + }; + if ( + parsed.v !== 1 || + parsed.sort !== sort || + !parsed.sources || + typeof parsed.sources !== "object" + ) { + return fallback; + } + const sources = parsed.sources as Record; + const decoded = emptyStablePackageDiscoveryCursor(sort); + for (const family of STABLE_PACKAGE_FAMILIES) { + const value = sources[family]; + if (!value || typeof value !== "object") return fallback; + const source = value as { key?: unknown; done?: unknown }; + if (source.key !== null && !Array.isArray(source.key)) return fallback; + decoded.sources[family] = { + key: Array.isArray(source.key) ? source.key.map(decodePackageIndexKeyValue) : null, + done: source.done === true, + }; + } + return decoded; + } catch { + return fallback; + } +} + function encodePackageIndexCursor(indexName: string, key: IndexKey): string { return JSON.stringify({ v: 1, @@ -3766,6 +3893,143 @@ export const countPublicPlugins = query({ }, }); +function compareStablePackageDiscoveryCandidates( + a: PackageDigestLike, + b: PackageDigestLike, + sort: "updated" | "downloads" | "recommended" | "installs", +) { + const metric = (candidate: PackageDigestLike) => { + if (sort === "downloads") return candidate.stats?.downloads ?? 0; + if (sort === "installs") return candidate.stats?.installs ?? 0; + if (sort === "recommended") return candidate.recommendedScore ?? 0; + return candidate.updatedAt; + }; + const metricDiff = metric(b) - metric(a); + if (metricDiff !== 0) return metricDiff; + const updatedDiff = b.updatedAt - a.updatedAt; + if (updatedDiff !== 0) return updatedDiff; + const familyDiff = a.family.localeCompare(b.family); + if (familyDiff !== 0) return familyDiff; + return a.name.localeCompare(b.name); +} + +async function listStablePackageDiscoveryPage( + ctx: DbReaderCtx, + args: { + channel?: PackageChannel; + isOfficial?: boolean; + category?: PluginCategorySlug; + topic?: string; + excludedScanStatuses?: PackageListScanStatus[]; + sort?: "updated" | "downloads" | "recommended" | "installs"; + viewerUserId?: Id<"users">; + paginationOpts: { cursor: string | null; numItems: number }; + }, +): Promise { + const targetCount = Math.max( + 1, + Math.min(args.paginationOpts.numItems, MAX_PUBLIC_LIST_PAGE_SIZE), + ); + const requestedSort = args.sort ?? "updated"; + const sort = + requestedSort === "recommended" && + ( + await Promise.all( + STABLE_PACKAGE_FAMILIES.map( + async (family) => await hasMissingPackageRecommendedScore(ctx, family), + ), + ) + ).some(Boolean) + ? "updated" + : requestedSort; + const cursor = decodeStablePackageDiscoveryCursor(args.paginationOpts.cursor, sort); + const scanLimit = Math.min(MAX_PUBLIC_LIST_PAGE_SIZE, Math.max(targetCount * 5, 50)); + const loadFamily = async (family: StablePackageFamily) => { + const state = cursor.sources[family]; + if (state.done) { + return { family, rows: [] as PackageDigestLike[], keys: [] as IndexKey[], hasMore: false }; + } + const eqPrefix: IndexKey = [undefined, family]; + if (sort === "updated") { + const result = await getPage(ctx, { + table: "packageSearchDigest", + index: "by_active_family_updated", + startIndexKey: state.key ?? eqPrefix, + startInclusive: state.key === null, + endIndexKey: eqPrefix, + endInclusive: true, + order: "desc", + absoluteMaxRows: scanLimit, + schema, + }); + return { + family, + rows: result.page as PackageDigestLike[], + keys: result.indexKeys, + hasMore: result.hasMore, + }; + } + const index = + sort === "downloads" + ? "by_active_family_downloads" + : sort === "installs" + ? "by_active_family_installs" + : "by_active_family_recommended_score"; + const result = await getPage(ctx, { + table: "packages", + index, + startIndexKey: state.key ?? eqPrefix, + startInclusive: state.key === null, + endIndexKey: eqPrefix, + endInclusive: true, + order: "desc", + absoluteMaxRows: scanLimit, + schema, + }); + return { + family, + rows: result.page.map(extractPackageDigestFields), + keys: result.indexKeys, + hasMore: result.hasMore, + }; + }; + const familyPages = await Promise.all( + STABLE_PACKAGE_FAMILIES.map(async (family) => await loadFamily(family)), + ); + const candidates = familyPages + .flatMap((source) => + source.rows.map((row, index) => ({ + family: source.family, + row, + key: source.keys[index], + source, + })), + ) + .sort((a, b) => compareStablePackageDiscoveryCandidates(a.row, b.row, sort)); + const membershipCache = new Map>(); + const page: PublicPackageListItem[] = []; + const consumedByFamily = new Map(); + for (const candidate of candidates) { + const digest = candidate.row; + consumedByFamily.set(candidate.family, (consumedByFamily.get(candidate.family) ?? 0) + 1); + cursor.sources[candidate.family].key = candidate.key; + if (!digestMatchesSearchFilters(digest, args)) continue; + if (!(await canViewerReadPackage(ctx, digest, args.viewerUserId, membershipCache))) continue; + page.push(await toPublicPackageListItem(ctx, digest)); + if (page.length >= targetCount) break; + } + for (const source of familyPages) { + const consumed = consumedByFamily.get(source.family) ?? 0; + cursor.sources[source.family].done = consumed === source.rows.length && !source.hasMore; + } + const hasMore = STABLE_PACKAGE_FAMILIES.some((family) => !cursor.sources[family].done); + return { + page, + isDone: !hasMore, + continueCursor: hasMore ? encodeStablePackageDiscoveryCursor(cursor) : "", + }; +} + async function listPackagePageImpl( ctx: DbReaderCtx, args: { @@ -3861,6 +4125,19 @@ async function listPackagePageImpl( return { page, isDone: true, continueCursor: "" }; } + if (!args.family && !experimentalClawsEnabled()) { + return await listStablePackageDiscoveryPage(ctx, { + channel: args.channel, + isOfficial: args.isOfficial, + category, + topic, + excludedScanStatuses: args.excludedScanStatuses, + sort: args.sort, + viewerUserId: args.viewerUserId, + paginationOpts: args.paginationOpts, + }); + } + const collected: PublicPackageListItem[] = []; const family = args.family; const channel = args.channel; @@ -4353,30 +4630,44 @@ async function searchPackagesImpl( return results; } - const buildSearchDigestQuery = () => - topic - ? buildPackageTopicDigestQuery(ctx, { - topic, - family: args.family, - channel: args.channel, - isOfficial: args.isOfficial, - }) - : category - ? buildPackagePluginCategoryDigestQuery(ctx, { - category, - family: args.family, + const searchFamilies = + !args.family && !experimentalClawsEnabled() + ? ([...STABLE_PACKAGE_FAMILIES] as PackageFamily[]) + : [args.family]; + const buildSearchDigestQueries = () => + searchFamilies.map((family) => + topic + ? buildPackageTopicDigestQuery(ctx, { + topic, + family, channel: args.channel, isOfficial: args.isOfficial, }) - : buildPackageDigestQuery(ctx, { - family: args.family, - channel: args.channel, - isOfficial: args.isOfficial, - }); + : category + ? buildPackagePluginCategoryDigestQuery(ctx, { + category, + family, + channel: args.channel, + isOfficial: args.isOfficial, + }) + : buildPackageDigestQuery(ctx, { + family, + channel: args.channel, + isOfficial: args.isOfficial, + }), + ); const matches: Array = []; const seen = new Set(); const directDigests = - category && !topic ? [] : await resolveDirectPackageSearchDigests(ctx, queryText); + category && !topic + ? [] + : ( + await Promise.all( + searchFamilies.map( + async (family) => await resolveDirectPackageSearchDigests(ctx, queryText, family), + ), + ) + ).flat(); for (const digest of directDigests) { if (!(await canViewPackage(digest))) continue; if (!digestMatchesSearchFilters(digest, { ...args, topic })) continue; @@ -4413,7 +4704,8 @@ async function searchPackagesImpl( } }; - if (topic && category) { + const searchDigestQueries = buildSearchDigestQueries(); + if (topic && category && searchDigestQueries.length === 1) { let cursor: string | null = null; let isDone = false; let scanPages = 0; @@ -4429,7 +4721,9 @@ async function searchPackagesImpl( page: PackageDigestLike[]; isDone: boolean; continueCursor: string; - } = await buildSearchDigestQuery().order("desc").paginate({ cursor, numItems: pageSize }); + } = await buildSearchDigestQueries()[0] + .order("desc") + .paginate({ cursor, numItems: pageSize }); scanPages += 1; remainingScanBudget -= pageSize; await collectDigestMatches(page.page); @@ -4437,9 +4731,13 @@ async function searchPackagesImpl( isDone = page.isDone; } } else { - const digests: PackageDigestLike[] = await buildSearchDigestQuery() - .order("desc") - .take(scanLimit); + const digests = ( + await Promise.all( + searchDigestQueries.map( + async (query) => (await query.order("desc").take(scanLimit)) as PackageDigestLike[], + ), + ) + ).flat(); await collectDigestMatches(digests); } } @@ -7806,18 +8104,42 @@ async function publishPackageImpl( if (family !== "claw" && !pluginManifest) { throw new ConvexError("openclaw.plugin.json is required for plugin packages"); } - const clawPackage = + const clawValidationFiles = files.map((file) => ({ + path: file.path, + ...(clawManifestEntry?.file.path === file.path ? { text: clawManifestEntry.text } : {}), + })); + let clawPackage = family === "claw" ? validateClawPackageContents({ packageName: name, version, packageJson, - files: files.map((file) => ({ - path: file.path, - ...(clawManifestEntry?.file.path === file.path ? { text: clawManifestEntry.text } : {}), - })), + files: clawValidationFiles, }) : null; + if (clawPackage && !clawPackage.ok) { + const profilePath = clawPackage.issues.find( + (entry) => entry.code === "missing_openclaw_profile", + )?.path; + if (profilePath) { + const profileEntry = await readOptionalTextFile(ctx, files, (path) => path === profilePath, { + exactPath: true, + maxBytes: 256 * 1024, + label: "OpenClaw profile", + strictUtf8: true, + }); + if (profileEntry) { + clawPackage = validateClawPackageContents({ + packageName: name, + version, + packageJson, + files: clawValidationFiles.map((file) => + file.path === profileEntry.file.path ? { ...file, text: profileEntry.text } : file, + ), + }); + } + } + } if (clawPackage && !clawPackage.ok) { throw new ConvexError( `Invalid Claw package: ${clawPackage.issues diff --git a/convex/schema.ts b/convex/schema.ts index 6184579cbd..f1e52e11d8 100644 --- a/convex/schema.ts +++ b/convex/schema.ts @@ -2167,12 +2167,20 @@ const packageSearchDigest = defineTable({ "updatedAt", ]) .index("by_active_normalized_name", ["softDeletedAt", "normalizedName", "updatedAt"]) + .index("by_active_family_normalized_name", [ + "softDeletedAt", + "family", + "normalizedName", + "updatedAt", + ]) .index("by_active_runtime_id", ["softDeletedAt", "runtimeId", "updatedAt"]) + .index("by_active_family_runtime_id", ["softDeletedAt", "family", "runtimeId", "updatedAt"]) .index("by_active_owner_handle", ["softDeletedAt", "ownerHandle", "updatedAt"]) + .index("by_active_family_owner_handle", ["softDeletedAt", "family", "ownerHandle", "updatedAt"]) .index("by_active_name", ["softDeletedAt", "displayName"]) .searchIndex("search_by_display_name", { searchField: "displayName", - filterFields: ["softDeletedAt"], + filterFields: ["softDeletedAt", "family"], }); const packageTopicSearchDigest = defineTable({ From fbbafd255e8f7999d847a4f5096f3028e96c4ad0 Mon Sep 17 00:00:00 2001 From: Patrick Erichsen Date: Fri, 24 Jul 2026 16:53:45 -0500 Subject: [PATCH 06/17] fix(claws): preserve filtered stable-family recall --- convex/packages.public.test.ts | 48 ++++++++++++++++++ convex/packages.ts | 89 +++++++++++++++++++--------------- 2 files changed, 99 insertions(+), 38 deletions(-) diff --git a/convex/packages.public.test.ts b/convex/packages.public.test.ts index 71ed753e9f..96c48fbe61 100644 --- a/convex/packages.public.test.ts +++ b/convex/packages.public.test.ts @@ -5494,6 +5494,54 @@ describe("packages public queries", () => { expect(paginate).toHaveBeenCalledTimes(2); }); + it("scans each stable family past the first combined-filter window while Claws are disabled", async () => { + const previous = process.env.CLAWHUB_EXPERIMENTAL_CLAWS; + delete process.env.CLAWHUB_EXPERIMENTAL_CLAWS; + const { ctx, paginate } = makeDigestCtx({ + topicPages: [ + { + page: Array.from({ length: 50 }, (_, index) => + makeDigest(`calendar-skill-noise-${index}`, { + family: "skill", + topic: "calendar", + topics: ["calendar"], + pluginCategoryTags: ["channels"], + }), + ), + isDone: false, + continueCursor: "later", + }, + { + page: [ + makeDigest("calendar-skill-api", { + family: "skill", + topic: "calendar", + topics: ["calendar"], + pluginCategoryTags: ["tools"], + }), + ], + isDone: true, + continueCursor: "", + }, + ], + }); + + try { + const result = await searchPublicHandler(ctx, { + query: "calendar", + topic: "calendar", + category: "tools", + limit: 1, + }); + + expect(result.map((entry) => entry.package.name)).toEqual(["calendar-skill-api"]); + expect(paginate).toHaveBeenCalled(); + } finally { + if (previous === undefined) delete process.env.CLAWHUB_EXPERIMENTAL_CLAWS; + else process.env.CLAWHUB_EXPERIMENTAL_CLAWS = previous; + } + }); + it("bounds sparse combined-filter search scans", async () => { const topicPages = Array.from({ length: 7 }, (_page, pageIndex) => ({ page: Array.from({ length: 50 }, (_digest, digestIndex) => diff --git a/convex/packages.ts b/convex/packages.ts index 415d4c2230..fb61cebd29 100644 --- a/convex/packages.ts +++ b/convex/packages.ts @@ -4634,28 +4634,26 @@ async function searchPackagesImpl( !args.family && !experimentalClawsEnabled() ? ([...STABLE_PACKAGE_FAMILIES] as PackageFamily[]) : [args.family]; - const buildSearchDigestQueries = () => - searchFamilies.map((family) => - topic - ? buildPackageTopicDigestQuery(ctx, { - topic, + const buildSearchDigestQuery = (family: PackageFamily | undefined) => + topic + ? buildPackageTopicDigestQuery(ctx, { + topic, + family, + channel: args.channel, + isOfficial: args.isOfficial, + }) + : category + ? buildPackagePluginCategoryDigestQuery(ctx, { + category, family, channel: args.channel, isOfficial: args.isOfficial, }) - : category - ? buildPackagePluginCategoryDigestQuery(ctx, { - category, - family, - channel: args.channel, - isOfficial: args.isOfficial, - }) - : buildPackageDigestQuery(ctx, { - family, - channel: args.channel, - isOfficial: args.isOfficial, - }), - ); + : buildPackageDigestQuery(ctx, { + family, + channel: args.channel, + isOfficial: args.isOfficial, + }); const matches: Array = []; const seen = new Set(); const directDigests = @@ -4704,37 +4702,52 @@ async function searchPackagesImpl( } }; - const searchDigestQueries = buildSearchDigestQueries(); - if (topic && category && searchDigestQueries.length === 1) { - let cursor: string | null = null; - let isDone = false; + if (topic && category) { + const scanStates = searchFamilies.map((family) => ({ + family, + cursor: null as string | null, + isDone: false, + })); let scanPages = 0; let remainingScanBudget = MAX_PUBLIC_LIST_FILTER_SCAN_DOCUMENTS; while ( authoritativeMatchCount() < targetCount && - !isDone && + scanStates.some((state) => !state.isDone) && scanPages < MAX_PUBLIC_LIST_FILTER_SCAN_PAGES && remainingScanBudget > 0 ) { - const pageSize = Math.min(scanLimit, remainingScanBudget); - const page: { - page: PackageDigestLike[]; - isDone: boolean; - continueCursor: string; - } = await buildSearchDigestQueries()[0] - .order("desc") - .paginate({ cursor, numItems: pageSize }); - scanPages += 1; - remainingScanBudget -= pageSize; - await collectDigestMatches(page.page); - cursor = page.continueCursor; - isDone = page.isDone; + for (const state of scanStates) { + if ( + state.isDone || + authoritativeMatchCount() >= targetCount || + scanPages >= MAX_PUBLIC_LIST_FILTER_SCAN_PAGES || + remainingScanBudget <= 0 + ) { + continue; + } + const pageSize = Math.min(scanLimit, remainingScanBudget); + const page: { + page: PackageDigestLike[]; + isDone: boolean; + continueCursor: string; + } = await buildSearchDigestQuery(state.family) + .order("desc") + .paginate({ cursor: state.cursor, numItems: pageSize }); + scanPages += 1; + remainingScanBudget -= pageSize; + await collectDigestMatches(page.page); + state.cursor = page.continueCursor; + state.isDone = page.isDone; + } } } else { const digests = ( await Promise.all( - searchDigestQueries.map( - async (query) => (await query.order("desc").take(scanLimit)) as PackageDigestLike[], + searchFamilies.map( + async (family) => + (await buildSearchDigestQuery(family) + .order("desc") + .take(scanLimit)) as PackageDigestLike[], ), ) ).flat(); From 25131d8f0045053e62ebe676b48a6138518bcd18 Mon Sep 17 00:00:00 2001 From: Patrick Erichsen Date: Fri, 24 Jul 2026 16:59:51 -0500 Subject: [PATCH 07/17] fix(claws): close disabled discovery and summary gaps --- convex/packages.public.test.ts | 31 +++++++++++++++++++++++++++++++ convex/packages.ts | 7 +++++-- 2 files changed, 36 insertions(+), 2 deletions(-) diff --git a/convex/packages.public.test.ts b/convex/packages.public.test.ts index 96c48fbe61..3fbd971a58 100644 --- a/convex/packages.public.test.ts +++ b/convex/packages.public.test.ts @@ -9143,6 +9143,7 @@ describe("packages public queries", () => { clawpackSize: expect.any(Number), clawManifestSummary: expect.objectContaining({ agent: expect.objectContaining({ id: "demo-claw" }), + workspace: expect.objectContaining({ bootstrapFiles: ["SOUL.md"] }), }), pluginManifestSummary: undefined, }), @@ -16725,6 +16726,36 @@ describe("restorePackageInternal", () => { } }); + it("rejects explicit Claw list and search filters before querying while disabled", async () => { + const previous = process.env.CLAWHUB_EXPERIMENTAL_CLAWS; + delete process.env.CLAWHUB_EXPERIMENTAL_CLAWS; + const { ctx } = makeDigestCtx({ + pages: [ + { + page: [makeDigest("demo-claw", { family: "claw" })], + isDone: true, + continueCursor: "", + }, + ], + }); + + try { + await expect( + listPublicPageHandler(ctx, { + family: "claw", + paginationOpts: { cursor: null, numItems: 10 }, + }), + ).resolves.toEqual({ page: [], isDone: true, continueCursor: "" }); + await expect( + searchPublicHandler(ctx, { query: "demo", family: "claw", limit: 10 }), + ).resolves.toEqual([]); + expect(ctx.db.query).not.toHaveBeenCalled(); + } finally { + if (previous === undefined) delete process.env.CLAWHUB_EXPERIMENTAL_CLAWS; + else process.env.CLAWHUB_EXPERIMENTAL_CLAWS = previous; + } + }); + it("does not let disabled Claws starve unfiltered public list pages", async () => { const previous = process.env.CLAWHUB_EXPERIMENTAL_CLAWS; delete process.env.CLAWHUB_EXPERIMENTAL_CLAWS; diff --git a/convex/packages.ts b/convex/packages.ts index fb61cebd29..d0e1c58269 100644 --- a/convex/packages.ts +++ b/convex/packages.ts @@ -2,7 +2,6 @@ import { ServerPackagePublishRequestSchema, validateClawPackageContents, derivePluginCategoryTags, - summarizeClawManifest, getCatalogTopicSlugs, getPackageScopeOwnerMismatch, INTERNAL_UNCATEGORIZED_CATEGORY, @@ -4050,6 +4049,9 @@ async function listPackagePageImpl( if (args.channel === "private" && !args.viewerUserId) { return { page: [], isDone: true, continueCursor: "" }; } + if (args.family && !isClawFamilyPubliclyVisible(args.family)) { + return { page: [], isDone: true, continueCursor: "" }; + } if (args.families?.length && !args.highlightedOnly) { throw new Error("families is only supported for highlighted package pages"); } @@ -4585,6 +4587,7 @@ async function searchPackagesImpl( if (!queryText) return []; if (args.category && !isPluginCategorySlug(args.category)) return []; if (args.channel === "private" && !args.viewerUserId) return []; + if (args.family && !isClawFamilyPubliclyVisible(args.family)) return []; const targetCount = Math.max(1, Math.min(args.limit ?? 20, 100)); const viewerUserId = args.viewerUserId; const membershipCache = new Map>(); @@ -8356,7 +8359,7 @@ async function publishPackageImpl( extractedPluginManifest: storedPluginManifest, normalizedBundleManifest: family === "bundle-plugin" ? storedBundleManifest : undefined, pluginManifestSummary, - clawManifestSummary: validatedClaw ? summarizeClawManifest(validatedClaw.manifest) : undefined, + clawManifestSummary: validatedClaw?.summary, source: effectiveSource, }; From 89eb2da0fea4469ac8d867040f8cd7b09e121b4d Mon Sep 17 00:00:00 2001 From: Patrick Erichsen Date: Fri, 24 Jul 2026 17:07:45 -0500 Subject: [PATCH 08/17] fix(claws): align package portability with parent contract --- bun.lock | 1 + convex/lib/clawpack.test.ts | 9 + convex/lib/clawpack.ts | 2 +- convex/lib/skillZip.test.ts | 17 + convex/lib/skillZip.ts | 6 +- docs/claws.md | 12 +- packages/clawhub/package.json | 1 + packages/clawhub/src/clawpack.ts | 2 +- .../clawhub/src/cli/commands/packages.test.ts | 10 +- packages/clawhub/src/schema/clawPackage.ts | 6 +- packages/clawhub/src/schema/claws.ts | 702 +++++++++++------- packages/schema/dist/clawPackage.js | 6 +- packages/schema/dist/clawPackage.js.map | 2 +- packages/schema/src/clawPackage.test.ts | 50 ++ packages/schema/src/clawPackage.ts | 6 +- specs/claws.md | 9 +- 16 files changed, 558 insertions(+), 283 deletions(-) diff --git a/bun.lock b/bun.lock index 41a8464a33..16dc65d098 100644 --- a/bun.lock +++ b/bun.lock @@ -120,6 +120,7 @@ "p-retry": "8.0.0", "semver": "7.8.5", "undici": "7.28.0", + "unicode-case-folding": "1.1.1", "yaml": "2.9.0", }, "devDependencies": { diff --git a/convex/lib/clawpack.test.ts b/convex/lib/clawpack.test.ts index f387aee0ae..f003a6cd49 100644 --- a/convex/lib/clawpack.test.ts +++ b/convex/lib/clawpack.test.ts @@ -114,6 +114,15 @@ describe("clawpack", () => { await expect(parseClawPack(pack)).rejects.toThrow("unsafe tar path"); }); + it.each(["\u007f", "\u0085"])("rejects control character U+%s in tar paths", async (control) => { + const pack = npmPackFixture({ + "package/package.json": JSON.stringify({ name: "demo", version: "1.0.0" }), + [`package/workspace/a${control}.md`]: "unsafe", + }); + + await expect(parseClawPack(pack)).rejects.toThrow("unsafe tar path"); + }); + it("rejects oversized package metadata before JSON parsing", async () => { const pack = npmPackFixture({ "package/package.json": new Uint8Array(256 * 1024 + 1).fill(0x20), diff --git a/convex/lib/clawpack.ts b/convex/lib/clawpack.ts index f2aa99dd79..66364a8295 100644 --- a/convex/lib/clawpack.ts +++ b/convex/lib/clawpack.ts @@ -56,7 +56,7 @@ function normalizeTarPath(path: string) { path !== path.trim() || path.includes("\\") || path.startsWith("/") || - path.includes("\0") + /\p{Cc}/u.test(path) ) { return null; } diff --git a/convex/lib/skillZip.test.ts b/convex/lib/skillZip.test.ts index d23cad92c2..5c1d19b0fc 100644 --- a/convex/lib/skillZip.test.ts +++ b/convex/lib/skillZip.test.ts @@ -165,5 +165,22 @@ describe("skillZip", () => { ]), ).toThrow("file/ancestor path collision"); }); + + it("rejects Unicode-folded hierarchy collisions before creating the legacy ZIP", () => { + expect(() => + buildDeterministicPackageZip([ + { path: "Straße", bytes: new TextEncoder().encode("file") }, + { path: "STRASSE/child", bytes: new TextEncoder().encode("child") }, + ]), + ).toThrow("file/ancestor path collision"); + }); + + it("rejects unsafe package paths before creating the legacy ZIP", () => { + expect(() => + buildDeterministicPackageZip([ + { path: "workspace/a\u0085.md", bytes: new TextEncoder().encode("unsafe") }, + ]), + ).toThrow("unsafe package path"); + }); }); }); diff --git a/convex/lib/skillZip.ts b/convex/lib/skillZip.ts index 04bfe81d93..386f80b5f8 100644 --- a/convex/lib/skillZip.ts +++ b/convex/lib/skillZip.ts @@ -1,4 +1,4 @@ -import { findClawPackagePathHierarchyCollision } from "clawhub-schema"; +import { findClawPackagePathHierarchyCollision, isSafeClawPackagePath } from "clawhub-schema"; import { zipSync } from "fflate"; type ZipEntry = { @@ -69,6 +69,10 @@ export function buildDeterministicZip(entries: ZipEntry[], meta?: SkillZipMeta) } export function buildDeterministicPackageZip(entries: ZipEntry[]) { + const unsafeEntry = entries.find((entry) => !isSafeClawPackagePath(entry.path)); + if (unsafeEntry) { + throw new Error(`Package contains unsafe package path: ${unsafeEntry.path}`); + } const hierarchyCollision = findClawPackagePathHierarchyCollision( entries.map((entry) => entry.path), ); diff --git a/docs/claws.md b/docs/claws.md index f79a3e5876..c146da0f71 100644 --- a/docs/claws.md +++ b/docs/claws.md @@ -29,8 +29,9 @@ points to its manifest: } ``` -`CLAW.md` starts with the grouped Claw manifest as YAML frontmatter. Markdown -after the closing delimiter is author-facing documentation. +`CLAW.md` starts with the grouped Claw manifest as YAML frontmatter. Its +non-empty Markdown body becomes the managed `SOUL.md` for the new agent, so do +not also declare an explicit `SOUL.md` bootstrap or supporting-file destination. ```markdown --- @@ -40,9 +41,6 @@ agent: name: GitHub Triage description: Reviews incoming issues. workspace: - bootstrapFiles: - SOUL.md: - source: workspace/SOUL.md files: - source: workspace/reference.md path: reference.md @@ -61,7 +59,8 @@ Reviews and classifies incoming GitHub issues. ``` JSON manifests remain compatible. Set `openclaw.claw` to a package-relative -JSON file such as `openclaw.claw.json`. +JSON file such as `openclaw.claw.json`. JSON has no implicit body file and may +declare an explicit `SOUL.md` source. Every `workspace.*.source` must name a file in the same package. Package names and versions must match `package.json`, dependency versions must be exact, and @@ -90,6 +89,7 @@ Publication rejects: - a missing, invalid, or escaping `openclaw.claw` path; - package identity or version mismatches; - malformed `CLAW.md` frontmatter or manifest fields; +- an empty `CLAW.md` body or simultaneous explicit `SOUL.md` destination; - missing workspace source files or portable path collisions; - floating skill/plugin versions and resolved MCP credentials. diff --git a/packages/clawhub/package.json b/packages/clawhub/package.json index aa5784e562..b2f3ce5b63 100644 --- a/packages/clawhub/package.json +++ b/packages/clawhub/package.json @@ -50,6 +50,7 @@ "p-retry": "8.0.0", "semver": "7.8.5", "undici": "7.28.0", + "unicode-case-folding": "1.1.1", "yaml": "2.9.0" }, "devDependencies": { diff --git a/packages/clawhub/src/clawpack.ts b/packages/clawhub/src/clawpack.ts index 486417ca89..4038bd030a 100644 --- a/packages/clawhub/src/clawpack.ts +++ b/packages/clawhub/src/clawpack.ts @@ -50,7 +50,7 @@ function normalizeTarPath(path: string) { path !== path.trim() || path.includes("\\") || path.startsWith("/") || - path.includes("\0") + /\p{Cc}/u.test(path) ) { return null; } diff --git a/packages/clawhub/src/cli/commands/packages.test.ts b/packages/clawhub/src/cli/commands/packages.test.ts index 6558de19ae..193a7bedfe 100644 --- a/packages/clawhub/src/cli/commands/packages.test.ts +++ b/packages/clawhub/src/cli/commands/packages.test.ts @@ -1997,7 +1997,7 @@ describe("package commands", () => { version: "1.0.0", openclaw: { claw: "CLAW.md" }, }), - "package/CLAW.md": "---\nschemaVersion: 1\nagent:\n id: demo-claw\n---\n", + "package/CLAW.md": "---\nschemaVersion: 1\nagent:\n id: demo-claw\n---\n# Demo Claw\n", }), ); httpMocks.apiRequestForm.mockResolvedValueOnce({ @@ -2579,7 +2579,6 @@ describe("package commands", () => { const workdir = await makeTmpWorkdir(); try { const folder = join(workdir, "github-triage"); - await mkdir(join(folder, "workspace"), { recursive: true }); await mkdir(join(folder, "profiles"), { recursive: true }); await writeFile( join(folder, "package.json"), @@ -2601,10 +2600,6 @@ describe("package commands", () => { " name: GitHub Triage", "metadata:", " openclaw.config: profiles/openclaw.yml", - "workspace:", - " bootstrapFiles:", - " SOUL.md:", - " source: workspace/SOUL.md", "---", "# GitHub Triage", ].join("\n"), @@ -2628,7 +2623,6 @@ describe("package commands", () => { ].join("\n"), "utf8", ); - await writeFile(join(folder, "workspace", "SOUL.md"), "Be precise.\n", "utf8"); httpMocks.apiRequestForm.mockResolvedValueOnce({ ok: true, packageId: "pkg_claw", @@ -2663,7 +2657,7 @@ describe("package commands", () => { ); await writeFile( join(folder, "CLAW.md"), - "---\nschemaVersion: 1\nagent:\n id: broken-claw\nworkspace:\n files:\n - source: missing.md\n path: missing.md\n---\n", + "---\nschemaVersion: 1\nagent:\n id: broken-claw\nworkspace:\n files:\n - source: missing.md\n path: missing.md\n---\n# Broken Claw\n", "utf8", ); diff --git a/packages/clawhub/src/schema/clawPackage.ts b/packages/clawhub/src/schema/clawPackage.ts index 53ae9efb7d..fafe5ded14 100644 --- a/packages/clawhub/src/schema/clawPackage.ts +++ b/packages/clawhub/src/schema/clawPackage.ts @@ -1,4 +1,5 @@ import { ArkErrors, type } from "arktype"; +import { caseFold } from "unicode-case-folding"; import { isScalar, parseDocument, visit } from "yaml"; import { summarizeClawManifest, @@ -24,6 +25,7 @@ const EXACT_VERSION_PATTERN = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[A-Za-z-][0-9A-Za-z-]*)(?:\.(?:0|[1-9]\d*|\d*[A-Za-z-][0-9A-Za-z-]*))*))?(?:\+([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?$/; const WINDOWS_INVALID_PATH_CHARS = /[<>:"|?*]/; const WINDOWS_RESERVED_PATH_SEGMENT = /^(?:con|prn|aux|nul|com[1-9]|lpt[1-9])(?:\..*)?$/i; +const UNICODE_CONTROL_CHARACTER = /\p{Cc}/u; const MAX_CLAW_MANIFEST_BYTES = 1024 * 1024; const MAX_OPENCLAW_PROFILE_BYTES = 256 * 1024; const OPENCLAW_TOOL_PROFILE_IDS = new Set(["minimal", "coding", "messaging", "full"]); @@ -108,7 +110,7 @@ export function isSafeClawPackagePath(value: string): boolean { segment !== "." && segment !== ".." && !WINDOWS_INVALID_PATH_CHARS.test(segment) && - !Array.from(segment).some((character) => character.charCodeAt(0) <= 0x1f) && + !UNICODE_CONTROL_CHARACTER.test(segment) && !segment.endsWith(".") && !segment.endsWith(" ") && !WINDOWS_RESERVED_PATH_SEGMENT.test(segment), @@ -116,7 +118,7 @@ export function isSafeClawPackagePath(value: string): boolean { } function portablePathKey(value: string): string { - return value.replaceAll("\\", "/").normalize("NFC").toLowerCase(); + return caseFold(value.replaceAll("\\", "/").normalize("NFC")); } export function findClawPackagePathHierarchyCollision( diff --git a/packages/clawhub/src/schema/claws.ts b/packages/clawhub/src/schema/claws.ts index f21f8766f0..268ae296de 100644 --- a/packages/clawhub/src/schema/claws.ts +++ b/packages/clawhub/src/schema/claws.ts @@ -1,5 +1,6 @@ -import { ArkErrors, type inferred, type } from "arktype"; +import { ArkErrors, type BaseType, type inferred, type } from "arktype"; import { Cron } from "croner"; +import { caseFold } from "unicode-case-folding"; export const CLAW_SCHEMA_VERSION = 1 as const; export const CLAW_SUMMARY_AGENT_NAME_MAX_CHARS = 128; @@ -98,38 +99,101 @@ export const ClawManifestSchema = type({ }); export type ClawManifest = (typeof ClawManifestSchema)[inferred]; -const ClawSummaryAgentNameSchema = type("string").narrow( - (value) => Array.from(value).length <= CLAW_SUMMARY_AGENT_NAME_MAX_CHARS, -); -const ClawSummaryAgentDescriptionSchema = type("string").narrow( - (value) => Array.from(value).length <= CLAW_SUMMARY_AGENT_DESCRIPTION_MAX_CHARS, -); +export type ClawManifestSummary = { + schemaVersion: 1; + agent: { id: string; name?: string; description?: string }; + workspace: { bootstrapFiles: string[]; fileCount: number }; + packages: { skillCount: number; pluginCount: number }; + mcpServerCount: number; + cronJobCount: number; +}; -export const ClawManifestSummarySchema = type({ - "+": "reject", - schemaVersion: "1", - agent: { - "+": "reject", - id: "string", - name: ClawSummaryAgentNameSchema.optional(), - description: ClawSummaryAgentDescriptionSchema.optional(), - }, - workspace: { - "+": "reject", - bootstrapFiles: "string[]", - fileCount: "number", - }, - packages: { - "+": "reject", - skillCount: "number", - pluginCount: "number", +export type ClawManifestSummarySchemaAdapter = { + literalOne: TValue; + string: TValue; + number: TValue; + stringArray: TValue; + boundedString: (maxCharacters: number) => TValue; + optional: (schema: TValue) => TOptional; + object: (fields: Record) => TValue; +}; + +/** Builds the v1 summary structure for both the public schema and durable storage validators. */ +export function createClawManifestSummarySchema( + adapter: ClawManifestSummarySchemaAdapter, +): TValue { + return adapter.object({ + schemaVersion: adapter.literalOne, + agent: adapter.object({ + id: adapter.string, + name: adapter.optional(adapter.boundedString(CLAW_SUMMARY_AGENT_NAME_MAX_CHARS)), + description: adapter.optional( + adapter.boundedString(CLAW_SUMMARY_AGENT_DESCRIPTION_MAX_CHARS), + ), + }), + workspace: adapter.object({ + bootstrapFiles: adapter.stringArray, + fileCount: adapter.number, + }), + packages: adapter.object({ + skillCount: adapter.number, + pluginCount: adapter.number, + }), + mcpServerCount: adapter.number, + cronJobCount: adapter.number, + }); +} + +export const ClawManifestSummarySchema = createClawManifestSummarySchema( + { + literalOne: type("1"), + string: type("string"), + number: type("number"), + stringArray: type("string[]"), + boundedString: (maxCharacters) => + type("string").narrow((value) => Array.from(value).length <= maxCharacters), + optional: (schema) => schema.optional(), + object: (fields) => type({ "+": "reject", ...fields }), }, - mcpServerCount: "number", - cronJobCount: "number", -}); -export type ClawManifestSummary = (typeof ClawManifestSummarySchema)[inferred]; +) as BaseType; -export type ClawManifestValidationIssue = { path: string; message: string }; +export const CLAW_MANIFEST_VALIDATION_PHASE = "schema" as const; +export const CLAW_MANIFEST_VALIDATION_CODES = { + invalidManifestShape: "claw_v1_invalid_manifest_shape", + invalidAgentId: "claw_v1_invalid_agent_id", + nonCanonicalString: "claw_v1_non_canonical_string", + emptyList: "claw_v1_empty_list", + invalidProfilePath: "claw_v1_invalid_profile_path", + unsafePath: "claw_v1_unsafe_path", + duplicateWorkspaceDestination: "claw_v1_duplicate_workspace_destination", + invalidAvatar: "claw_v1_invalid_avatar", + undeclaredAvatar: "claw_v1_undeclared_avatar", + invalidPackageReference: "claw_v1_invalid_package_reference", + invalidPackageVersion: "claw_v1_invalid_package_version", + duplicatePackage: "claw_v1_duplicate_package", + invalidMcpServerId: "claw_v1_invalid_mcp_server_id", + invalidMcpUrl: "claw_v1_invalid_mcp_url", + mcpUrlCredentials: "claw_v1_mcp_url_credentials", + unpinnedMcpPackage: "claw_v1_unpinned_mcp_package", + invalidToolFilter: "claw_v1_invalid_tool_filter", + duplicateToolFilter: "claw_v1_duplicate_tool_filter", + invalidEnvironmentKey: "claw_v1_invalid_environment_key", + blockedEnvironmentKey: "claw_v1_blocked_environment_key", + invalidEnvironmentReference: "claw_v1_invalid_environment_reference", + invalidTimeout: "claw_v1_invalid_timeout", + invalidCronJobId: "claw_v1_invalid_cron_job_id", + invalidCronSchedule: "claw_v1_invalid_cron_schedule", + invalidCronDelivery: "claw_v1_invalid_cron_delivery", + duplicateCronJob: "claw_v1_duplicate_cron_job", +} as const; +export type ClawManifestValidationCode = + (typeof CLAW_MANIFEST_VALIDATION_CODES)[keyof typeof CLAW_MANIFEST_VALIDATION_CODES]; +export type ClawManifestValidationIssue = { + code: ClawManifestValidationCode; + phase: typeof CLAW_MANIFEST_VALIDATION_PHASE; + path: string; + message: string; +}; const AGENT_ID_PATTERN = /^[a-z][a-z0-9_-]{0,63}$/; const EXACT_VERSION_PATTERN = @@ -138,124 +202,138 @@ const ENV_REFERENCE_PATTERN = /^\$\{[A-Z_][A-Z0-9_]*\}$/; const PACKAGE_NAME_PATTERN = /^(?:@[a-z0-9][a-z0-9._-]*\/)?[a-z0-9][a-z0-9._-]*$/; const WINDOWS_INVALID_PATH_CHARS = /[<>:"|?*]/; const WINDOWS_RESERVED_PATH_SEGMENT = /^(?:con|prn|aux|nul|com[1-9]|lpt[1-9])(?:\..*)?$/i; +const UNICODE_CONTROL_CHARACTER = /\p{Cc}/u; const AVATAR_MAX_BYTES = 2 * 1024 * 1024; const AVATAR_MAX_DATA_URL_CHARS = 2_796_230; const AVATAR_DATA_URL_PATTERN = /^data:image\/[^,]*,/i; const AVATAR_EXTENSION_PATTERN = /\.(?:png|jpe?g|gif|webp|svg)$/i; const BASE64_PAYLOAD_PATTERN = /^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/; const PORTABLE_ENV_KEY_PATTERN = /^[A-Za-z_][A-Za-z0-9_]*$/; -// Snapshot of OpenClaw's host-wide blocked process environment policy for v1 conformance. -const BLOCKED_PROCESS_ENV_KEYS = new Set([ - "NODE_OPTIONS", - "NODE_PATH", - "NODE_REDIRECT_WARNINGS", - "NODE_REPL_EXTERNAL_MODULE", - "NODE_REPL_HISTORY", - "NODE_V8_COVERAGE", - "PYTHONHOME", - "PYTHONPATH", - "PERL5LIB", - "PERL5OPT", - "RUBYLIB", - "RUBYOPT", - "BASHOPTS", - "BASH_ENV", - "ENV", - "KSH_ENV", - "BROWSER", - "GIT_ALLOW_PROTOCOL", - "GIT_EDITOR", - "GIT_EXTERNAL_DIFF", - "GIT_DIR", - "GIT_WORK_TREE", - "GIT_COMMON_DIR", - "GIT_EXEC_PATH", - "GIT_INDEX_FILE", - "GIT_OBJECT_DIRECTORY", - "GIT_ALTERNATE_OBJECT_DIRECTORIES", - "GIT_NAMESPACE", - "GIT_PROTOCOL_FROM_USER", - "GIT_SEQUENCE_EDITOR", - "GIT_TEMPLATE_DIR", - "GIT_SSL_NO_VERIFY", - "GIT_SSL_CAINFO", - "GIT_SSL_CAPATH", - "CC", - "CXX", - "CARGO_BUILD_RUSTC", - "CARGO_BUILD_RUSTC_WRAPPER", - "CARGO_BUILD_RUSTC_WORKSPACE_WRAPPER", - "CARGO_BUILD_RUSTDOC", - "RUSTC", - "RUSTC_WRAPPER", - "RUSTC_WORKSPACE_WRAPPER", - "RUSTDOC", - "CMAKE_C_COMPILER", - "CMAKE_CXX_COMPILER", - "SHELL", - "SHELLOPTS", - "PS4", - "GCONV_PATH", - "IFS", - "SSLKEYLOGFILE", - "JAVA_OPTS", - "JAVA_TOOL_OPTIONS", - "_JAVA_OPTIONS", - "JDK_JAVA_OPTIONS", - "PYTHONBREAKPOINT", - "DOTNET_STARTUP_HOOKS", - "DOTNET_ADDITIONAL_DEPS", - "FPATH", - "GLIBC_TUNABLES", - "MAVEN_OPTS", - "MAKE", - "MAKEFLAGS", - "MFLAGS", - "SBT_OPTS", - "GRADLE_OPTS", - "ANT_OPTS", - "HGRCPATH", - "HGEDITOR", - "HGMERGE", - "EXINIT", - "VIMINIT", - "MYVIMRC", - "GVIMINIT", - "LUA_INIT", - "LUA_INIT_5_1", - "LUA_INIT_5_2", - "LUA_INIT_5_3", - "LUA_INIT_5_4", - "EMACSLOADPATH", - "RUBYSHELL", - "GIT_HOOK_PATH", - "SVN_EDITOR", - "SVN_SSH", - "BZR_EDITOR", - "BZR_SSH", - "BZR_PLUGIN_PATH", - "SUDO_ASKPASS", - "JULIA_EDITOR", - "CONFIG_SITE", - "CONFIG_SHELL", - "CMAKE_TOOLCHAIN_FILE", - "CATALINA_OPTS", - "CORECLR_PROFILER", - "HELM_PLUGINS", - "PACKER_PLUGIN_PATH", - "VAGRANT_VAGRANTFILE", - "ERL_AFLAGS", - "ERL_FLAGS", - "ERL_ZFLAGS", - "ELIXIR_ERL_OPTIONS", - "R_ENVIRON", - "R_PROFILE", - "R_ENVIRON_USER", - "R_PROFILE_USER", - "TCLLIBPATH", - "HOSTALIASES", -]); -const BLOCKED_PROCESS_ENV_PREFIXES = ["DYLD_", "LD_", "BASH_FUNC_"] as const; +// Versioned compatibility artifact for the OpenClaw policy applied by the v1 consumer. +export const OPENCLAW_CLAW_HOST_ENV_POLICY_V1 = { + contractVersion: 1, + source: { + repository: "openclaw/openclaw", + commit: "f50a918af102b950721c1b380f7eb8af4e0389d1", + path: "src/infra/host-env-security-policy.json", + sha256: "4639dcfa157d4fa4c2fb84f8c5347ffe2252eb4ddb3d818afd7f87d68173b92b", + }, + blockedKeys: [ + "NODE_OPTIONS", + "NODE_PATH", + "NODE_REDIRECT_WARNINGS", + "NODE_REPL_EXTERNAL_MODULE", + "NODE_REPL_HISTORY", + "NODE_V8_COVERAGE", + "PYTHONHOME", + "PYTHONPATH", + "PERL5LIB", + "PERL5OPT", + "RUBYLIB", + "RUBYOPT", + "BASHOPTS", + "BASH_ENV", + "ENV", + "KSH_ENV", + "BROWSER", + "GIT_ALLOW_PROTOCOL", + "GIT_EDITOR", + "GIT_EXTERNAL_DIFF", + "GIT_DIR", + "GIT_WORK_TREE", + "GIT_COMMON_DIR", + "GIT_EXEC_PATH", + "GIT_INDEX_FILE", + "GIT_OBJECT_DIRECTORY", + "GIT_ALTERNATE_OBJECT_DIRECTORIES", + "GIT_NAMESPACE", + "GIT_PROTOCOL_FROM_USER", + "GIT_SEQUENCE_EDITOR", + "GIT_TEMPLATE_DIR", + "GIT_SSL_NO_VERIFY", + "GIT_SSL_CAINFO", + "GIT_SSL_CAPATH", + "CC", + "CPP", + "CXX", + "CARGO_BUILD_RUSTC", + "CARGO_BUILD_RUSTC_WRAPPER", + "CARGO_BUILD_RUSTC_WORKSPACE_WRAPPER", + "CARGO_BUILD_RUSTDOC", + "RUSTC", + "RUSTC_WRAPPER", + "RUSTC_WORKSPACE_WRAPPER", + "RUSTDOC", + "CMAKE_C_COMPILER", + "CMAKE_CXX_COMPILER", + "SHELL", + "SHELLOPTS", + "PS4", + "GCONV_PATH", + "IFS", + "SSLKEYLOGFILE", + "JAVA_OPTS", + "JAVA_TOOL_OPTIONS", + "_JAVA_OPTIONS", + "JDK_JAVA_OPTIONS", + "PYTHONBREAKPOINT", + "DOTNET_STARTUP_HOOKS", + "DOTNET_ADDITIONAL_DEPS", + "FPATH", + "GLIBC_TUNABLES", + "MAVEN_OPTS", + "MAKE", + "MAKEFLAGS", + "MFLAGS", + "SBT_OPTS", + "GRADLE_OPTS", + "ANT_OPTS", + "HGRCPATH", + "HGEDITOR", + "HGMERGE", + "EXINIT", + "VIMINIT", + "MYVIMRC", + "GVIMINIT", + "LUA_INIT", + "LUA_INIT_5_1", + "LUA_INIT_5_2", + "LUA_INIT_5_3", + "LUA_INIT_5_4", + "EMACSLOADPATH", + "RUBYSHELL", + "GIT_HOOK_PATH", + "SVN_EDITOR", + "SVN_SSH", + "BZR_EDITOR", + "BZR_SSH", + "BZR_PLUGIN_PATH", + "SUDO_ASKPASS", + "JULIA_EDITOR", + "CONFIG_SITE", + "CONFIG_SHELL", + "CMAKE_TOOLCHAIN_FILE", + "CATALINA_OPTS", + "CORECLR_PROFILER", + "HELM_PLUGINS", + "PACKER_PLUGIN_PATH", + "VAGRANT_VAGRANTFILE", + "ERL_AFLAGS", + "ERL_FLAGS", + "ERL_ZFLAGS", + "ELIXIR_ERL_OPTIONS", + "R_ENVIRON", + "R_PROFILE", + "R_ENVIRON_USER", + "R_PROFILE_USER", + "TCLLIBPATH", + "HOSTALIASES", + ], + blockedPrefixes: ["DYLD_", "LD_", "BASH_FUNC_"], +} as const; + +const BLOCKED_PROCESS_ENV_KEYS = new Set(OPENCLAW_CLAW_HOST_ENV_POLICY_V1.blockedKeys); +const BLOCKED_PROCESS_ENV_PREFIXES = OPENCLAW_CLAW_HOST_ENV_POLICY_V1.blockedPrefixes; function isStrictNonEmpty(value: string): boolean { return value.length > 0 && value === value.trim(); @@ -286,7 +364,7 @@ function isSafePackagePath(value: string): boolean { segment !== "." && segment !== ".." && !WINDOWS_INVALID_PATH_CHARS.test(segment) && - !Array.from(segment).some((character) => character.charCodeAt(0) <= 0x1f) && + !UNICODE_CONTROL_CHARACTER.test(segment) && !segment.endsWith(".") && !segment.endsWith(" ") && !WINDOWS_RESERVED_PATH_SEGMENT.test(segment), @@ -294,7 +372,7 @@ function isSafePackagePath(value: string): boolean { } function portablePathKey(value: string): string { - return value.replaceAll("\\", "/").normalize("NFC").toLowerCase(); + return caseFold(value.replaceAll("\\", "/").normalize("NFC")); } function isPortableAvatar(value: string): boolean { @@ -338,14 +416,16 @@ function packageManagerArtifacts(command: string, args: string[]): string[] | un ?.replace(/\.(?:cmd|exe)$/i, "") .toLowerCase(); let start = 0; + let scansOptionsAfterPositional = false; if (executable === "npm") { - if (args[0] !== "exec" && args[0] !== "x") return [""]; + if (args[0] !== "exec" && args[0] !== "x") return undefined; start = 1; + scansOptionsAfterPositional = true; } else if (executable === "bun") { - if (args[0] !== "x") return [""]; + if (args[0] !== "x") return undefined; start = 1; } else if (executable === "pnpm" || executable === "yarn") { - if (args[0] !== "dlx") return [""]; + if (args[0] !== "dlx") return undefined; start = 1; } else if (executable !== "npx" && executable !== "pnpx" && executable !== "bunx") { return undefined; @@ -356,7 +436,7 @@ function packageManagerArtifacts(command: string, args: string[]): string[] | un const value = args[index]; if (!value) continue; if (value === "--") { - positional = args[index + 1] ?? ""; + positional ??= args[index + 1] ?? ""; break; } if (value === "-p" || value === "--package") { @@ -368,9 +448,11 @@ function packageManagerArtifacts(command: string, args: string[]): string[] | un selected.push(value.slice("--package=".length)); continue; } + if (value === "-y" || value === "--yes") continue; + if (value.startsWith("-")) return [""]; if (positional === undefined && !value.startsWith("-")) { positional = value; - break; + if (!scansOptionsAfterPositional) break; } } return selected.length > 0 ? selected : [positional ?? ""]; @@ -395,11 +477,22 @@ function isPackageManagerArtifactPinned(command: string, args: string[]): boolea }); } +function validationIssue( + code: ClawManifestValidationCode, + path: string, + message: string, +): ClawManifestValidationIssue { + return { code, phase: CLAW_MANIFEST_VALIDATION_PHASE, path, message }; +} + function arkIssues(errors: ArkErrors): ClawManifestValidationIssue[] { - return Array.from(errors, (error) => ({ - path: error.path.length > 0 ? `$.${error.path.join(".")}` : "$", - message: error.description ?? "Invalid value.", - })); + return Array.from(errors, (error) => + validationIssue( + CLAW_MANIFEST_VALIDATION_CODES.invalidManifestShape, + error.path.length > 0 ? `$.${error.path.join(".")}` : "$", + error.description ?? "Manifest must match the strict Claw v1 shape.", + ), + ); } function pushNonEmpty( @@ -408,7 +501,13 @@ function pushNonEmpty( value: string | undefined, ): void { if (value !== undefined && !isStrictNonEmpty(value)) { - issues.push({ path, message: "Must be non-empty without leading or trailing whitespace." }); + issues.push( + validationIssue( + CLAW_MANIFEST_VALIDATION_CODES.nonCanonicalString, + path, + "Must be non-empty without leading or trailing whitespace.", + ), + ); } } @@ -419,7 +518,13 @@ function pushNonEmptyArray( requireEntry: boolean, ): void { if (requireEntry && values !== undefined && values.length === 0) { - issues.push({ path, message: "Must contain at least one value." }); + issues.push( + validationIssue( + CLAW_MANIFEST_VALIDATION_CODES.emptyList, + path, + "Must contain at least one value.", + ), + ); } for (const [index, value] of (values ?? []).entries()) { pushNonEmpty(issues, `${path}.${index}`, value); @@ -434,13 +539,22 @@ export function validateClawManifest( const issues: ClawManifestValidationIssue[] = []; if (!AGENT_ID_PATTERN.test(parsed.agent.id)) { - issues.push({ path: "$.agent.id", message: "Invalid portable agent id." }); + issues.push( + validationIssue( + CLAW_MANIFEST_VALIDATION_CODES.invalidAgentId, + "$.agent.id", + "Invalid portable agent id.", + ), + ); } pushNonEmpty(issues, "$.agent.name", parsed.agent.name); pushNonEmpty(issues, "$.agent.description", parsed.agent.description); for (const field of ["name", "theme", "emoji", "avatar"] as const) { pushNonEmpty(issues, `$.agent.identity.${field}`, parsed.agent.identity?.[field]); } + for (const key of Object.keys(parsed.metadata ?? {})) { + pushNonEmpty(issues, `$.metadata.${key}`, key); + } const openClawProfilePath = parsed.metadata?.["openclaw.config"]; if ( openClawProfilePath !== undefined && @@ -448,60 +562,81 @@ export function validateClawManifest( !isSafePackagePath(openClawProfilePath) || !/\.ya?ml$/i.test(openClawProfilePath)) ) { - issues.push({ - path: "$.metadata.openclaw.config", - message: "Must reference a forward-slash package-relative .yml or .yaml file.", - }); + issues.push( + validationIssue( + CLAW_MANIFEST_VALIDATION_CODES.invalidProfilePath, + "$.metadata.openclaw.config", + "Must reference a forward-slash package-relative .yml or .yaml file.", + ), + ); } const workspaceTargets = new Set(); for (const name of CLAW_BOOTSTRAP_FILE_NAMES) { const source = parsed.workspace?.bootstrapFiles?.[name]?.source; if (source === undefined) continue; if (!isSafePackagePath(source)) { - issues.push({ - path: `$.workspace.bootstrapFiles.${name}.source`, - message: "Must be a safe package-relative path.", - }); + issues.push( + validationIssue( + CLAW_MANIFEST_VALIDATION_CODES.unsafePath, + `$.workspace.bootstrapFiles.${name}.source`, + "Must be a safe package-relative path.", + ), + ); } workspaceTargets.add(portablePathKey(name)); } for (const [index, file] of (parsed.workspace?.files ?? []).entries()) { if (!isSafePackagePath(file.source)) { - issues.push({ - path: `$.workspace.files.${index}.source`, - message: "Must be a safe package-relative path.", - }); + issues.push( + validationIssue( + CLAW_MANIFEST_VALIDATION_CODES.unsafePath, + `$.workspace.files.${index}.source`, + "Must be a safe package-relative path.", + ), + ); } if (!isSafePackagePath(file.path)) { - issues.push({ - path: `$.workspace.files.${index}.path`, - message: "Must be a safe package-relative path.", - }); + issues.push( + validationIssue( + CLAW_MANIFEST_VALIDATION_CODES.unsafePath, + `$.workspace.files.${index}.path`, + "Must be a safe package-relative path.", + ), + ); } const destinationKey = portablePathKey(file.path); if (conflictsWithWorkspaceTarget(workspaceTargets, destinationKey)) { - issues.push({ - path: `$.workspace.files.${index}.path`, - message: "Workspace destination is declared more than once.", - }); + issues.push( + validationIssue( + CLAW_MANIFEST_VALIDATION_CODES.duplicateWorkspaceDestination, + `$.workspace.files.${index}.path`, + "Workspace destination is declared more than once.", + ), + ); } workspaceTargets.add(destinationKey); } const avatar = parsed.agent.identity?.avatar; if (avatar !== undefined) { if (!isPortableAvatar(avatar)) { - issues.push({ - path: "$.agent.identity.avatar", - message: "Must be a bounded image data URL or managed workspace-relative image path.", - }); + issues.push( + validationIssue( + CLAW_MANIFEST_VALIDATION_CODES.invalidAvatar, + "$.agent.identity.avatar", + "Must be a bounded image data URL or managed workspace-relative image path.", + ), + ); } else if ( !AVATAR_DATA_URL_PATTERN.test(avatar) && !workspaceTargets.has(portablePathKey(avatar)) ) { - issues.push({ - path: "$.agent.identity.avatar", - message: "Workspace-relative avatar must match a workspace.files destination.", - }); + issues.push( + validationIssue( + CLAW_MANIFEST_VALIDATION_CODES.undeclaredAvatar, + "$.agent.identity.avatar", + "Workspace-relative avatar must match a workspace.files destination.", + ), + ); } } @@ -509,33 +644,45 @@ export function validateClawManifest( for (const [index, pkg] of (parsed.packages ?? []).entries()) { pushNonEmpty(issues, `$.packages.${index}.ref`, pkg.ref); if (!PACKAGE_NAME_PATTERN.test(pkg.ref)) { - issues.push({ - path: `$.packages.${index}.ref`, - message: "Must be a lowercase ClawHub package name.", - }); + issues.push( + validationIssue( + CLAW_MANIFEST_VALIDATION_CODES.invalidPackageReference, + `$.packages.${index}.ref`, + "Must be a lowercase ClawHub package name.", + ), + ); } if (!EXACT_VERSION_PATTERN.test(pkg.version)) { - issues.push({ - path: `$.packages.${index}.version`, - message: "Must be an exact semantic version.", - }); + issues.push( + validationIssue( + CLAW_MANIFEST_VALIDATION_CODES.invalidPackageVersion, + `$.packages.${index}.version`, + "Must be an exact semantic version.", + ), + ); } const key = `${pkg.kind}:${pkg.source}:${pkg.ref.toLowerCase()}`; if (packageKeys.has(key)) { - issues.push({ - path: `$.packages.${index}`, - message: "Package is declared more than once.", - }); + issues.push( + validationIssue( + CLAW_MANIFEST_VALIDATION_CODES.duplicatePackage, + `$.packages.${index}`, + "Package is declared more than once.", + ), + ); } packageKeys.add(key); } for (const [name, server] of Object.entries(parsed.mcpServers ?? {})) { if (!AGENT_ID_PATTERN.test(name)) { - issues.push({ - path: `$.mcpServers.${name}`, - message: "Invalid MCP server name.", - }); + issues.push( + validationIssue( + CLAW_MANIFEST_VALIDATION_CODES.invalidMcpServerId, + `$.mcpServers.${name}`, + "Invalid MCP server name.", + ), + ); } if ("url" in server) { pushNonEmpty(issues, `$.mcpServers.${name}.url`, server.url); @@ -546,26 +693,35 @@ export function validateClawManifest( throw new Error("protocol"); } if (url.username || url.password || url.hash) { - issues.push({ - path: `$.mcpServers.${name}.url`, - message: "Must not contain embedded credentials or fragments.", - }); + issues.push( + validationIssue( + CLAW_MANIFEST_VALIDATION_CODES.mcpUrlCredentials, + `$.mcpServers.${name}.url`, + "Must not contain embedded credentials or fragments.", + ), + ); } } catch { - issues.push({ - path: `$.mcpServers.${name}.url`, - message: "Must use HTTPS, except HTTP on an exact loopback host.", - }); + issues.push( + validationIssue( + CLAW_MANIFEST_VALIDATION_CODES.invalidMcpUrl, + `$.mcpServers.${name}.url`, + "Must use HTTPS, except HTTP on an exact loopback host.", + ), + ); } } if ("command" in server) { pushNonEmpty(issues, `$.mcpServers.${name}.command`, server.command); pushNonEmptyArray(issues, `$.mcpServers.${name}.args`, server.args, false); if (isPackageManagerArtifactPinned(server.command, server.args ?? []) === false) { - issues.push({ - path: `$.mcpServers.${name}.args`, - message: "Package-manager MCP commands must select one exact immutable package version.", - }); + issues.push( + validationIssue( + CLAW_MANIFEST_VALIDATION_CODES.unpinnedMcpPackage, + `$.mcpServers.${name}.args`, + "Package-manager MCP commands must select one exact immutable package version.", + ), + ); } } pushNonEmptyArray( @@ -584,16 +740,22 @@ export function validateClawManifest( const seen = new Set(); for (const [index, entry] of (server.toolFilter?.[field] ?? []).entries()) { if (entry.includes("?") || entry.includes("[") || entry.includes("]")) { - issues.push({ - path: `$.mcpServers.${name}.toolFilter.${field}.${index}`, - message: "Tool filters support only exact names and * wildcards.", - }); + issues.push( + validationIssue( + CLAW_MANIFEST_VALIDATION_CODES.invalidToolFilter, + `$.mcpServers.${name}.toolFilter.${field}.${index}`, + "Tool filters support only exact names and * wildcards.", + ), + ); } if (seen.has(entry)) { - issues.push({ - path: `$.mcpServers.${name}.toolFilter.${field}.${index}`, - message: "Tool filter entries must be unique.", - }); + issues.push( + validationIssue( + CLAW_MANIFEST_VALIDATION_CODES.duplicateToolFilter, + `$.mcpServers.${name}.toolFilter.${field}.${index}`, + "Tool filter entries must be unique.", + ), + ); } seen.add(entry); } @@ -602,49 +764,67 @@ export function validateClawManifest( for (const [key, envValue] of Object.entries(server.env ?? {})) { pushNonEmpty(issues, `$.mcpServers.${name}.env`, key); if (!PORTABLE_ENV_KEY_PATTERN.test(key)) { - issues.push({ - path: `$.mcpServers.${name}.env.${key}`, - message: "Invalid portable environment key.", - }); + issues.push( + validationIssue( + CLAW_MANIFEST_VALIDATION_CODES.invalidEnvironmentKey, + `$.mcpServers.${name}.env.${key}`, + "Invalid portable environment key.", + ), + ); } if (isBlockedProcessEnvKey(key)) { - issues.push({ - path: `$.mcpServers.${name}.env.${key}`, - message: "Environment key is blocked by the spawned-process safety policy.", - }); + issues.push( + validationIssue( + CLAW_MANIFEST_VALIDATION_CODES.blockedEnvironmentKey, + `$.mcpServers.${name}.env.${key}`, + "Environment key is blocked by the spawned-process safety policy.", + ), + ); } if (!ENV_REFERENCE_PATTERN.test(envValue)) { - issues.push({ - path: `$.mcpServers.${name}.env.${key}`, - message: "Must be an unresolved ${ENV_VAR} reference.", - }); + issues.push( + validationIssue( + CLAW_MANIFEST_VALIDATION_CODES.invalidEnvironmentReference, + `$.mcpServers.${name}.env.${key}`, + "Must be an unresolved ${ENV_VAR} reference.", + ), + ); } } } if (server.timeout !== undefined && (!Number.isFinite(server.timeout) || server.timeout <= 0)) { - issues.push({ - path: `$.mcpServers.${name}.timeout`, - message: "Must be positive.", - }); + issues.push( + validationIssue( + CLAW_MANIFEST_VALIDATION_CODES.invalidTimeout, + `$.mcpServers.${name}.timeout`, + "Must be positive.", + ), + ); } if ( server.connectTimeout !== undefined && (!Number.isFinite(server.connectTimeout) || server.connectTimeout <= 0) ) { - issues.push({ - path: `$.mcpServers.${name}.connectTimeout`, - message: "Must be positive.", - }); + issues.push( + validationIssue( + CLAW_MANIFEST_VALIDATION_CODES.invalidTimeout, + `$.mcpServers.${name}.connectTimeout`, + "Must be positive.", + ), + ); } } const cronIds = new Set(); for (const [index, job] of (parsed.cronJobs ?? []).entries()) { if (!AGENT_ID_PATTERN.test(job.id)) { - issues.push({ - path: `$.cronJobs.${index}.id`, - message: "Invalid cron job id.", - }); + issues.push( + validationIssue( + CLAW_MANIFEST_VALIDATION_CODES.invalidCronJobId, + `$.cronJobs.${index}.id`, + "Invalid cron job id.", + ), + ); } pushNonEmpty(issues, `$.cronJobs.${index}.name`, job.name); pushNonEmpty(issues, `$.cronJobs.${index}.schedule.cron`, job.schedule.cron); @@ -655,28 +835,38 @@ export function validateClawManifest( const cron = new Cron(job.schedule.cron.trim(), { timezone: job.schedule.timezone.trim(), catch: false, + paused: true, }); if (!cron.nextRun(new Date())) throw new Error("No future run"); } catch { - issues.push({ - path: `$.cronJobs.${index}.schedule`, - message: "Must contain a valid cron expression and timezone.", - }); + issues.push( + validationIssue( + CLAW_MANIFEST_VALIDATION_CODES.invalidCronSchedule, + `$.cronJobs.${index}.schedule`, + "Must contain a valid cron expression and timezone.", + ), + ); } if ( (job.delivery?.mode === "none" && job.delivery.channel !== undefined) || (job.delivery?.mode === "announce" && job.delivery.channel !== "last") ) { - issues.push({ - path: `$.cronJobs.${index}.delivery`, - message: 'Must be { mode: "none" } or { mode: "announce", channel: "last" }.', - }); + issues.push( + validationIssue( + CLAW_MANIFEST_VALIDATION_CODES.invalidCronDelivery, + `$.cronJobs.${index}.delivery`, + 'Must be { mode: "none" } or { mode: "announce", channel: "last" }.', + ), + ); } if (cronIds.has(job.id)) { - issues.push({ - path: `$.cronJobs.${index}.id`, - message: "Cron job id is declared more than once.", - }); + issues.push( + validationIssue( + CLAW_MANIFEST_VALIDATION_CODES.duplicateCronJob, + `$.cronJobs.${index}.id`, + "Cron job id is declared more than once.", + ), + ); } cronIds.add(job.id); } diff --git a/packages/schema/dist/clawPackage.js b/packages/schema/dist/clawPackage.js index af3961233b..8980f45759 100644 --- a/packages/schema/dist/clawPackage.js +++ b/packages/schema/dist/clawPackage.js @@ -1,9 +1,11 @@ import { ArkErrors, type } from "arktype"; +import { caseFold } from "unicode-case-folding"; import { isScalar, parseDocument, visit } from "yaml"; import { summarizeClawManifest, validateClawManifest, } from "./claws.js"; const EXACT_VERSION_PATTERN = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[A-Za-z-][0-9A-Za-z-]*)(?:\.(?:0|[1-9]\d*|\d*[A-Za-z-][0-9A-Za-z-]*))*))?(?:\+([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?$/; const WINDOWS_INVALID_PATH_CHARS = /[<>:"|?*]/; const WINDOWS_RESERVED_PATH_SEGMENT = /^(?:con|prn|aux|nul|com[1-9]|lpt[1-9])(?:\..*)?$/i; +const UNICODE_CONTROL_CHARACTER = /\p{Cc}/u; const MAX_CLAW_MANIFEST_BYTES = 1024 * 1024; const MAX_OPENCLAW_PROFILE_BYTES = 256 * 1024; const OPENCLAW_TOOL_PROFILE_IDS = new Set(["minimal", "coding", "messaging", "full"]); @@ -82,13 +84,13 @@ export function isSafeClawPackagePath(value) { segment !== "." && segment !== ".." && !WINDOWS_INVALID_PATH_CHARS.test(segment) && - !Array.from(segment).some((character) => character.charCodeAt(0) <= 0x1f) && + !UNICODE_CONTROL_CHARACTER.test(segment) && !segment.endsWith(".") && !segment.endsWith(" ") && !WINDOWS_RESERVED_PATH_SEGMENT.test(segment)); } function portablePathKey(value) { - return value.replaceAll("\\", "/").normalize("NFC").toLowerCase(); + return caseFold(value.replaceAll("\\", "/").normalize("NFC")); } export function findClawPackagePathHierarchyCollision(paths) { const pathByKey = new Map(paths.map((path) => [portablePathKey(path), path])); diff --git a/packages/schema/dist/clawPackage.js.map b/packages/schema/dist/clawPackage.js.map index a505289a61..5422aa324f 100644 --- a/packages/schema/dist/clawPackage.js.map +++ b/packages/schema/dist/clawPackage.js.map @@ -1 +1 @@ -{"version":3,"file":"clawPackage.js","sourceRoot":"","sources":["../src/clawPackage.ts"],"names":[],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"clawPackage.js","sourceRoot":"","sources":["../src/clawPackage.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/packages/schema/src/clawPackage.test.ts b/packages/schema/src/clawPackage.test.ts index f04673fe57..999f808c9a 100644 --- a/packages/schema/src/clawPackage.test.ts +++ b/packages/schema/src/clawPackage.test.ts @@ -65,6 +65,13 @@ describe("validateClawPackageContents", () => { }); }); + it("finds hierarchy collisions after full Unicode case folding", () => { + expect(findClawPackagePathHierarchyCollision(["Straße", "STRASSE/child"])).toEqual({ + ancestor: "Straße", + descendant: "STRASSE/child", + }); + }); + it("validates CLAW.md and derives its safe summary", () => { const result = validateClawPackageContents({ packageName: "@acme/github-triage", @@ -247,6 +254,32 @@ describe("validateClawPackageContents", () => { }); }); + it("enforces the shared OpenClaw host environment policy through package validation", () => { + const result = validateClawPackageContents({ + packageName: "@acme/github-triage", + version: "1.0.0", + packageJson: packageJson(), + files: files( + `---\n${JSON.stringify({ + ...manifest, + mcpServers: { + unsafe: { command: "server", env: { CPP: "\${CPP}" } }, + }, + })}\n---\n# Prompt\n`, + ), + }); + + expect(result).toEqual({ + ok: false, + issues: [ + expect.objectContaining({ + code: "invalid_claw_manifest", + path: "$.mcpServers.unsafe.env.CPP", + }), + ], + }); + }); + it.each([ ["malformed YAML", "schemaVersion: [", "invalid_openclaw_profile"], ["duplicate keys", "schemaVersion: 1\nschemaVersion: 1\nagent: {}", "invalid_openclaw_profile"], @@ -535,6 +568,23 @@ describe("validateClawPackageContents", () => { }, ); + it.each([ + ["DEL", "\u007f"], + ["C1", "\u0085"], + ])("rejects %s control characters in package paths", (_label, controlCharacter) => { + const result = validateClawPackageContents({ + packageName: "@acme/github-triage", + version: "1.0.0", + packageJson: packageJson(), + files: [...files(), { path: `workspace/a${controlCharacter}.md`, text: "unused" }], + }); + + expect(result).toEqual({ + ok: false, + issues: expect.arrayContaining([expect.objectContaining({ code: "invalid_package_path" })]), + }); + }); + it("requires exact path spelling for manifests and workspace sources", () => { const result = validateClawPackageContents({ packageName: "@acme/github-triage", diff --git a/packages/schema/src/clawPackage.ts b/packages/schema/src/clawPackage.ts index 53ae9efb7d..fafe5ded14 100644 --- a/packages/schema/src/clawPackage.ts +++ b/packages/schema/src/clawPackage.ts @@ -1,4 +1,5 @@ import { ArkErrors, type } from "arktype"; +import { caseFold } from "unicode-case-folding"; import { isScalar, parseDocument, visit } from "yaml"; import { summarizeClawManifest, @@ -24,6 +25,7 @@ const EXACT_VERSION_PATTERN = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[A-Za-z-][0-9A-Za-z-]*)(?:\.(?:0|[1-9]\d*|\d*[A-Za-z-][0-9A-Za-z-]*))*))?(?:\+([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?$/; const WINDOWS_INVALID_PATH_CHARS = /[<>:"|?*]/; const WINDOWS_RESERVED_PATH_SEGMENT = /^(?:con|prn|aux|nul|com[1-9]|lpt[1-9])(?:\..*)?$/i; +const UNICODE_CONTROL_CHARACTER = /\p{Cc}/u; const MAX_CLAW_MANIFEST_BYTES = 1024 * 1024; const MAX_OPENCLAW_PROFILE_BYTES = 256 * 1024; const OPENCLAW_TOOL_PROFILE_IDS = new Set(["minimal", "coding", "messaging", "full"]); @@ -108,7 +110,7 @@ export function isSafeClawPackagePath(value: string): boolean { segment !== "." && segment !== ".." && !WINDOWS_INVALID_PATH_CHARS.test(segment) && - !Array.from(segment).some((character) => character.charCodeAt(0) <= 0x1f) && + !UNICODE_CONTROL_CHARACTER.test(segment) && !segment.endsWith(".") && !segment.endsWith(" ") && !WINDOWS_RESERVED_PATH_SEGMENT.test(segment), @@ -116,7 +118,7 @@ export function isSafeClawPackagePath(value: string): boolean { } function portablePathKey(value: string): string { - return value.replaceAll("\\", "/").normalize("NFC").toLowerCase(); + return caseFold(value.replaceAll("\\", "/").normalize("NFC")); } export function findClawPackagePathHierarchyCollision( diff --git a/specs/claws.md b/specs/claws.md index 686197432d..0ea88c8dd7 100644 --- a/specs/claws.md +++ b/specs/claws.md @@ -8,9 +8,12 @@ schema. ClawHub owns publication, ownership, discovery, package detail APIs, and hosted feed export. OpenClaw remains authoritative for local planning, consent, mutation, provenance, update, and removal. -The YAML frontmatter is the portable manifest. The `CLAW.md` body remains -documentation-only in the current contract and may be empty; it does not imply -or replace any managed workspace file. +The YAML frontmatter is the portable manifest. A `CLAW.md` package envelope +must have a non-empty body; its exact body text is the implicit managed +`SOUL.md` workspace file. The manifest must not also declare an explicit +`SOUL.md` destination. The exact `CLAW.md` bytes, including the body, remain in +the immutable artifact digest and provenance input. Grouped JSON has no body, +creates no implicit file, and may declare `SOUL.md` explicitly. The portable agent object carries only identity and purpose. Harness-specific settings live in package-local profiles addressed through opaque string From 2a7a39cdfcfa8427b281c5caf6ea190c8fb5fb77 Mon Sep 17 00:00:00 2001 From: Patrick Erichsen Date: Fri, 24 Jul 2026 17:08:54 -0500 Subject: [PATCH 09/17] fix(claws): keep disabled family filtering within public API --- convex/packages.public.test.ts | 30 ------------------------------ convex/packages.ts | 4 ---- 2 files changed, 34 deletions(-) diff --git a/convex/packages.public.test.ts b/convex/packages.public.test.ts index 3fbd971a58..602f8545ac 100644 --- a/convex/packages.public.test.ts +++ b/convex/packages.public.test.ts @@ -16726,36 +16726,6 @@ describe("restorePackageInternal", () => { } }); - it("rejects explicit Claw list and search filters before querying while disabled", async () => { - const previous = process.env.CLAWHUB_EXPERIMENTAL_CLAWS; - delete process.env.CLAWHUB_EXPERIMENTAL_CLAWS; - const { ctx } = makeDigestCtx({ - pages: [ - { - page: [makeDigest("demo-claw", { family: "claw" })], - isDone: true, - continueCursor: "", - }, - ], - }); - - try { - await expect( - listPublicPageHandler(ctx, { - family: "claw", - paginationOpts: { cursor: null, numItems: 10 }, - }), - ).resolves.toEqual({ page: [], isDone: true, continueCursor: "" }); - await expect( - searchPublicHandler(ctx, { query: "demo", family: "claw", limit: 10 }), - ).resolves.toEqual([]); - expect(ctx.db.query).not.toHaveBeenCalled(); - } finally { - if (previous === undefined) delete process.env.CLAWHUB_EXPERIMENTAL_CLAWS; - else process.env.CLAWHUB_EXPERIMENTAL_CLAWS = previous; - } - }); - it("does not let disabled Claws starve unfiltered public list pages", async () => { const previous = process.env.CLAWHUB_EXPERIMENTAL_CLAWS; delete process.env.CLAWHUB_EXPERIMENTAL_CLAWS; diff --git a/convex/packages.ts b/convex/packages.ts index d0e1c58269..2a550e8989 100644 --- a/convex/packages.ts +++ b/convex/packages.ts @@ -4049,9 +4049,6 @@ async function listPackagePageImpl( if (args.channel === "private" && !args.viewerUserId) { return { page: [], isDone: true, continueCursor: "" }; } - if (args.family && !isClawFamilyPubliclyVisible(args.family)) { - return { page: [], isDone: true, continueCursor: "" }; - } if (args.families?.length && !args.highlightedOnly) { throw new Error("families is only supported for highlighted package pages"); } @@ -4587,7 +4584,6 @@ async function searchPackagesImpl( if (!queryText) return []; if (args.category && !isPluginCategorySlug(args.category)) return []; if (args.channel === "private" && !args.viewerUserId) return []; - if (args.family && !isClawFamilyPubliclyVisible(args.family)) return []; const targetCount = Math.max(1, Math.min(args.limit ?? 20, 100)); const viewerUserId = args.viewerUserId; const membershipCache = new Map>(); From c99dc1ea33de3d56c7cdd05ae974bf31ef349cb3 Mon Sep 17 00:00:00 2001 From: Patrick Erichsen Date: Fri, 24 Jul 2026 17:13:04 -0500 Subject: [PATCH 10/17] chore(convex): refresh generated API declarations --- convex/_generated/api.d.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/convex/_generated/api.d.ts b/convex/_generated/api.d.ts index c2fdc7e236..579baad040 100644 --- a/convex/_generated/api.d.ts +++ b/convex/_generated/api.d.ts @@ -69,6 +69,7 @@ import type * as lib_emailRendering from "../lib/emailRendering.js"; import type * as lib_emails from "../lib/emails.js"; import type * as lib_embeddingVisibility from "../lib/embeddingVisibility.js"; import type * as lib_embeddings from "../lib/embeddings.js"; +import type * as lib_experimentalClaws from "../lib/experimentalClaws.js"; import type * as lib_githubAccount from "../lib/githubAccount.js"; import type * as lib_githubActionsOidc from "../lib/githubActionsOidc.js"; import type * as lib_githubAuth from "../lib/githubAuth.js"; @@ -107,6 +108,7 @@ import type * as lib_publisherAbuseScoring from "../lib/publisherAbuseScoring.js import type * as lib_publisherCatalogDisplay from "../lib/publisherCatalogDisplay.js"; import type * as lib_publisherStats from "../lib/publisherStats.js"; import type * as lib_publishers from "../lib/publishers.js"; +import type * as lib_rankingMetricsImportLock from "../lib/rankingMetricsImportLock.js"; import type * as lib_recommendationScore from "../lib/recommendationScore.js"; import type * as lib_reporting from "../lib/reporting.js"; import type * as lib_reservedHandles from "../lib/reservedHandles.js"; @@ -251,6 +253,7 @@ declare const fullApi: ApiFromModules<{ "lib/emails": typeof lib_emails; "lib/embeddingVisibility": typeof lib_embeddingVisibility; "lib/embeddings": typeof lib_embeddings; + "lib/experimentalClaws": typeof lib_experimentalClaws; "lib/githubAccount": typeof lib_githubAccount; "lib/githubActionsOidc": typeof lib_githubActionsOidc; "lib/githubAuth": typeof lib_githubAuth; @@ -289,6 +292,7 @@ declare const fullApi: ApiFromModules<{ "lib/publisherCatalogDisplay": typeof lib_publisherCatalogDisplay; "lib/publisherStats": typeof lib_publisherStats; "lib/publishers": typeof lib_publishers; + "lib/rankingMetricsImportLock": typeof lib_rankingMetricsImportLock; "lib/recommendationScore": typeof lib_recommendationScore; "lib/reporting": typeof lib_reporting; "lib/reservedHandles": typeof lib_reservedHandles; From e9c030e1d4cb8c7bbb137474dedc7ab68ca53f83 Mon Sep 17 00:00:00 2001 From: Patrick Erichsen Date: Fri, 24 Jul 2026 17:19:39 -0500 Subject: [PATCH 11/17] test(claws): satisfy host-policy fixture lint --- packages/schema/src/clawPackage.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/schema/src/clawPackage.test.ts b/packages/schema/src/clawPackage.test.ts index 999f808c9a..9c9e0e97a3 100644 --- a/packages/schema/src/clawPackage.test.ts +++ b/packages/schema/src/clawPackage.test.ts @@ -263,7 +263,7 @@ describe("validateClawPackageContents", () => { `---\n${JSON.stringify({ ...manifest, mcpServers: { - unsafe: { command: "server", env: { CPP: "\${CPP}" } }, + unsafe: { command: "server", env: { CPP: "${CPP}" } }, }, })}\n---\n# Prompt\n`, ), From 80d6d72256248135a8fc11fc2ff2af950b67d425 Mon Sep 17 00:00:00 2001 From: Patrick Erichsen Date: Fri, 24 Jul 2026 17:21:14 -0500 Subject: [PATCH 12/17] fix(deps): upgrade brace-expansion past denial-of-service advisory --- bun.lock | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/bun.lock b/bun.lock index 16dc65d098..0db2663230 100644 --- a/bun.lock +++ b/bun.lock @@ -171,7 +171,7 @@ }, "overrides": { "ast-v8-to-istanbul": "1.0.4", - "brace-expansion": "5.0.7", + "brace-expansion": "5.0.8", "dompurify": "3.4.12", "esbuild": "0.28.1", "fast-uri": "3.1.4", @@ -1074,7 +1074,7 @@ "bidi-js": ["bidi-js@1.0.3", "", { "dependencies": { "require-from-string": "^2.0.2" } }, "sha512-RKshQI1R3YQ+n9YJz2QQ147P66ELpa1FQEg20Dk8oW9t2KgLbpDLLp9aGZ7y8WHSshDknG0bknqGw5/tyCs5tw=="], - "brace-expansion": ["brace-expansion@5.0.7", "", { "dependencies": { "balanced-match": "^4.0.2" } }, "sha512-7oFy703dxfY3/NLxC1fh2SUCQ0H9rmAY+5EpDVfXjUTTs+HEwR2nYaqLv+GWcTsumwxPfiz6CzCNkwXwBUwqCA=="], + "brace-expansion": ["brace-expansion@5.0.8", "", { "dependencies": { "balanced-match": "^4.0.2" } }, "sha512-JZyDyq3D4AUifKTPOB7DELf6XsB3WdPuNxCtob1vFXPsSXhdAiHBWJ/tJ8HAc9aH84BK+5JFZLNkJKx3G9kzQg=="], "browserslist": ["browserslist@4.28.2", "", { "dependencies": { "baseline-browser-mapping": "^2.10.12", "caniuse-lite": "^1.0.30001782", "electron-to-chromium": "^1.5.328", "node-releases": "^2.0.36", "update-browserslist-db": "^1.2.3" }, "bin": { "browserslist": "cli.js" } }, "sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg=="], diff --git a/package.json b/package.json index b2c17dc0fc..72a353c6d4 100644 --- a/package.json +++ b/package.json @@ -185,7 +185,7 @@ }, "overrides": { "ast-v8-to-istanbul": "1.0.4", - "brace-expansion": "5.0.7", + "brace-expansion": "5.0.8", "dompurify": "3.4.12", "esbuild": "0.28.1", "fast-uri": "3.1.4", From 6351896d1bfdcfd3db676db43363d2e8371a751e Mon Sep 17 00:00:00 2001 From: Patrick Erichsen Date: Fri, 24 Jul 2026 17:32:34 -0500 Subject: [PATCH 13/17] fix(claws): preserve stable-family cursor order --- convex/packages.public.test.ts | 39 +++++++++++++++++++++++++++++++++- convex/packages.ts | 23 ++++++++++---------- 2 files changed, 49 insertions(+), 13 deletions(-) diff --git a/convex/packages.public.test.ts b/convex/packages.public.test.ts index 602f8545ac..5bde5e5a3a 100644 --- a/convex/packages.public.test.ts +++ b/convex/packages.public.test.ts @@ -1833,7 +1833,8 @@ function makePluginExportCtx(digests: Array>) { .sort((a, b) => { const updatedDiff = Number(a.updatedAt) - Number(b.updatedAt); if (updatedDiff !== 0) return order === "desc" ? -updatedDiff : updatedDiff; - return String(a._id).localeCompare(String(b._id)); + const idDiff = String(a._id).localeCompare(String(b._id)); + return order === "desc" ? -idDiff : idDiff; }); return { async *[Symbol.asyncIterator]() { @@ -16780,6 +16781,42 @@ describe("restorePackageInternal", () => { } }); + it("preserves each stable family's index order across tied public list pages", async () => { + const previous = process.env.CLAWHUB_EXPERIMENTAL_CLAWS; + delete process.env.CLAWHUB_EXPERIMENTAL_CLAWS; + const ctx = makePluginExportCtx([ + makeDigest("zulu-plugin", { + _id: "packageSearchDigest:zulu", + packageId: "packages:zulu", + updatedAt: 1, + _creationTime: 1, + }), + makeDigest("alpha-plugin", { + _id: "packageSearchDigest:alpha", + packageId: "packages:alpha", + updatedAt: 1, + _creationTime: 1, + }), + ]); + + try { + const first = await listPublicPageHandler(ctx, { + paginationOpts: { cursor: null, numItems: 1 }, + }); + const second = await listPublicPageHandler(ctx, { + paginationOpts: { cursor: first.continueCursor, numItems: 1 }, + }); + expect([...first.page, ...second.page].map((entry) => entry.name)).toEqual([ + "zulu-plugin", + "alpha-plugin", + ]); + expect(second.isDone).toBe(true); + } finally { + if (previous === undefined) delete process.env.CLAWHUB_EXPERIMENTAL_CLAWS; + else process.env.CLAWHUB_EXPERIMENTAL_CLAWS = previous; + } + }); + it("does not let disabled Claws starve unfiltered public search", async () => { const previous = process.env.CLAWHUB_EXPERIMENTAL_CLAWS; delete process.env.CLAWHUB_EXPERIMENTAL_CLAWS; diff --git a/convex/packages.ts b/convex/packages.ts index 2a550e8989..b7aaf77792 100644 --- a/convex/packages.ts +++ b/convex/packages.ts @@ -3995,27 +3995,26 @@ async function listStablePackageDiscoveryPage( const familyPages = await Promise.all( STABLE_PACKAGE_FAMILIES.map(async (family) => await loadFamily(family)), ); - const candidates = familyPages - .flatMap((source) => - source.rows.map((row, index) => ({ - family: source.family, - row, - key: source.keys[index], - source, - })), - ) - .sort((a, b) => compareStablePackageDiscoveryCandidates(a.row, b.row, sort)); const membershipCache = new Map>(); const page: PublicPackageListItem[] = []; const consumedByFamily = new Map(); - for (const candidate of candidates) { + while (page.length < targetCount) { + // Merge only the current head from each source so every persisted source + // cursor advances through a contiguous prefix of that source's index order. + const candidate = familyPages + .flatMap((source) => { + const index = consumedByFamily.get(source.family) ?? 0; + const row = source.rows[index]; + return row ? [{ family: source.family, row, key: source.keys[index] }] : []; + }) + .sort((a, b) => compareStablePackageDiscoveryCandidates(a.row, b.row, sort))[0]; + if (!candidate) break; const digest = candidate.row; consumedByFamily.set(candidate.family, (consumedByFamily.get(candidate.family) ?? 0) + 1); cursor.sources[candidate.family].key = candidate.key; if (!digestMatchesSearchFilters(digest, args)) continue; if (!(await canViewerReadPackage(ctx, digest, args.viewerUserId, membershipCache))) continue; page.push(await toPublicPackageListItem(ctx, digest)); - if (page.length >= targetCount) break; } for (const source of familyPages) { const consumed = consumedByFamily.get(source.family) ?? 0; From 768b6f354f7d49e5fbcfc82ee60be46bae7e6b70 Mon Sep 17 00:00:00 2001 From: Patrick Erichsen Date: Fri, 24 Jul 2026 17:44:26 -0500 Subject: [PATCH 14/17] fix(claws): preserve fair search ranking --- convex/packages.public.test.ts | 199 +++++++++++++++++++++++++++++++-- convex/packages.ts | 15 +-- 2 files changed, 199 insertions(+), 15 deletions(-) diff --git a/convex/packages.public.test.ts b/convex/packages.public.test.ts index 5bde5e5a3a..6abf37486b 100644 --- a/convex/packages.public.test.ts +++ b/convex/packages.public.test.ts @@ -1189,6 +1189,16 @@ function makeDigestCtx(options: { isDone: boolean; continueCursor: string; }>; + topicPagesByFamily?: Partial< + Record< + "skill" | "code-plugin" | "bundle-plugin", + Array<{ + page: Array>; + isDone: boolean; + continueCursor: string; + }> + > + >; categoryPages?: Array<{ page: Array>; isDone: boolean; @@ -1218,6 +1228,20 @@ function makeDigestCtx(options: { > >(); const rowsByTable = new Map>>(); + const familyPagesByTable = new Map< + string, + Map< + string, + Map< + string | null, + { + page: Array>; + isDone: boolean; + continueCursor: string; + } + > + > + >(); const indexNames: string[] = []; const indexFilters: Array<{ indexName: string; @@ -1257,6 +1281,36 @@ function makeDigestCtx(options: { setPages("packageSearchDigest", options.pages ?? []); setPages("packageCapabilitySearchDigest", options.capabilityPages ?? []); setPages("packageTopicSearchDigest", options.topicPages ?? []); + if (options.topicPagesByFamily) { + const pagesByFamily = new Map< + string, + Map< + string | null, + { + page: Array>; + isDone: boolean; + continueCursor: string; + } + > + >(); + for (const [family, pages] of Object.entries(options.topicPagesByFamily)) { + const pagesByCursor = new Map< + string | null, + { + page: Array>; + isDone: boolean; + continueCursor: string; + } + >(); + let cursor: string | null = null; + for (const page of pages ?? []) { + pagesByCursor.set(cursor, page); + cursor = page.continueCursor || null; + } + pagesByFamily.set(family, pagesByCursor); + } + familyPagesByTable.set("packageTopicSearchDigest", pagesByFamily); + } setPages("packagePluginCategorySearchDigest", options.categoryPages ?? []); if (options.categoryRows) { rowsByTable.set("packagePluginCategorySearchDigest", options.categoryRows); @@ -1265,10 +1319,14 @@ function makeDigestCtx(options: { const paginate = vi.fn(); const take = vi.fn(); - const paginateForTable = (table: string) => + const paginateForTable = (table: string, family?: string) => vi.fn(async (args: { cursor: string | null }) => { paginate(args); return ( + familyPagesByTable + .get(table) + ?.get(family ?? "") + ?.get(args.cursor ?? null) ?? pageByTable.get(table)?.get(args.cursor ?? null) ?? { page: [], isDone: true, @@ -1277,11 +1335,12 @@ function makeDigestCtx(options: { ); }); const paginateByTable = new Map>(); - const getPaginate = (table: string) => { - const existing = paginateByTable.get(table); + const getPaginate = (table: string, family?: string) => { + const key = `${table}:${family ?? ""}`; + const existing = paginateByTable.get(key); if (existing) return existing; - const next = paginateForTable(table); - paginateByTable.set(table, next); + const next = paginateForTable(table, family); + paginateByTable.set(key, next); return next; }; const takeForTable = (table: string) => @@ -1556,19 +1615,38 @@ function makeDigestCtx(options: { if (indexName.includes("_family_")) { indexNames.push(indexName); let family = ""; + let lowerField = ""; + let lowerBound = ""; + let upperBound = ""; const queryBuilder = { eq: (field: string, value: string | undefined) => { if (field === "family") family = value ?? ""; return queryBuilder; }, - gte: () => queryBuilder, - lt: () => queryBuilder, + gte: (field: string, value: string) => { + lowerField = field; + lowerBound = value; + return queryBuilder; + }, + lt: (_field: string, value: string) => { + upperBound = value; + return queryBuilder; + }, }; builder?.(queryBuilder); const takeFamilyRows = async (limit: number) => { take(limit); return (rowsByTable.get(table) ?? []) .filter((row) => row.family === family) + .filter((row) => { + if (!lowerField) return true; + const value = readTestField(row, lowerField); + return ( + typeof value === "string" && + value >= lowerBound && + (!upperBound || value < upperBound) + ); + }) .slice(0, limit); }; return { @@ -1666,7 +1744,7 @@ function makeDigestCtx(options: { }; return { order: vi.fn(() => ({ - paginate: getPaginate(table), + paginate: getPaginate(table, family), take: vi.fn(takeFamilyRows), })), }; @@ -5495,6 +5573,111 @@ describe("packages public queries", () => { expect(paginate).toHaveBeenCalledTimes(2); }); + it("ranks bounded candidates from every stable family before applying the search limit", async () => { + const previous = process.env.CLAWHUB_EXPERIMENTAL_CLAWS; + delete process.env.CLAWHUB_EXPERIMENTAL_CLAWS; + const { ctx } = makeDigestCtx({ + pages: [ + { + page: [ + makeDigest("skill-helper", { + family: "skill", + summary: "Calendar integration", + }), + makeDigest("official-plugin", { + family: "code-plugin", + isOfficial: true, + summary: "Calendar integration", + }), + ], + isDone: true, + continueCursor: "", + }, + ], + }); + + try { + const result = await searchPublicHandler(ctx, { query: "calendar", limit: 1 }); + expect(result.map((entry) => entry.package.name)).toEqual(["official-plugin"]); + } finally { + if (previous === undefined) delete process.env.CLAWHUB_EXPERIMENTAL_CLAWS; + else process.env.CLAWHUB_EXPERIMENTAL_CLAWS = previous; + } + }); + + it("gives every stable family a fair combined-filter scan budget", async () => { + const previous = process.env.CLAWHUB_EXPERIMENTAL_CLAWS; + delete process.env.CLAWHUB_EXPERIMENTAL_CLAWS; + const noisePage = (family: "skill" | "code-plugin" | "bundle-plugin", page: number) => + Array.from({ length: 50 }, (_, index) => + makeDigest(`${family}-noise-${page}-${index}`, { + family, + topic: "calendar", + topics: ["calendar"], + pluginCategoryTags: ["channels"], + }), + ); + const { ctx } = makeDigestCtx({ + topicPagesByFamily: { + skill: [ + { page: noisePage("skill", 1), isDone: false, continueCursor: "skill:2" }, + { page: noisePage("skill", 2), isDone: false, continueCursor: "skill:3" }, + { page: noisePage("skill", 3), isDone: true, continueCursor: "" }, + ], + "code-plugin": [ + { + page: noisePage("code-plugin", 1), + isDone: false, + continueCursor: "code:2", + }, + { + page: noisePage("code-plugin", 2), + isDone: false, + continueCursor: "code:3", + }, + { page: noisePage("code-plugin", 3), isDone: true, continueCursor: "" }, + ], + "bundle-plugin": [ + { + page: noisePage("bundle-plugin", 1), + isDone: false, + continueCursor: "bundle:2", + }, + { + page: noisePage("bundle-plugin", 2), + isDone: false, + continueCursor: "bundle:3", + }, + { + page: [ + makeDigest("calendar-bundle-api", { + family: "bundle-plugin", + topic: "calendar", + topics: ["calendar"], + pluginCategoryTags: ["tools"], + }), + ], + isDone: true, + continueCursor: "", + }, + ], + }, + }); + + try { + const result = await searchPublicHandler(ctx, { + query: "calendar", + topic: "calendar", + category: "tools", + limit: 1, + }); + expect(result.map((entry) => entry.package.name)).toEqual(["calendar-bundle-api"]); + } finally { + if (previous === undefined) delete process.env.CLAWHUB_EXPERIMENTAL_CLAWS; + else process.env.CLAWHUB_EXPERIMENTAL_CLAWS = previous; + } + }); + it("scans each stable family past the first combined-filter window while Claws are disabled", async () => { const previous = process.env.CLAWHUB_EXPERIMENTAL_CLAWS; delete process.env.CLAWHUB_EXPERIMENTAL_CLAWS; diff --git a/convex/packages.ts b/convex/packages.ts index b7aaf77792..ab79882e90 100644 --- a/convex/packages.ts +++ b/convex/packages.ts @@ -4696,7 +4696,6 @@ async function searchPackagesImpl( ...match, package: await toPublicPackageListItem(ctx, digest), }); - if (authoritativeMatchCount() >= targetCount) break; } }; @@ -4705,20 +4704,22 @@ async function searchPackagesImpl( family, cursor: null as string | null, isDone: false, + pagesScanned: 0, })); - let scanPages = 0; let remainingScanBudget = MAX_PUBLIC_LIST_FILTER_SCAN_DOCUMENTS; while ( authoritativeMatchCount() < targetCount && - scanStates.some((state) => !state.isDone) && - scanPages < MAX_PUBLIC_LIST_FILTER_SCAN_PAGES && + scanStates.some( + (state) => !state.isDone && state.pagesScanned < MAX_PUBLIC_LIST_FILTER_SCAN_PAGES, + ) && remainingScanBudget > 0 ) { + // Finish each round before checking the match quota so the fixed family + // order cannot decide global relevance or consume another family's cap. for (const state of scanStates) { if ( state.isDone || - authoritativeMatchCount() >= targetCount || - scanPages >= MAX_PUBLIC_LIST_FILTER_SCAN_PAGES || + state.pagesScanned >= MAX_PUBLIC_LIST_FILTER_SCAN_PAGES || remainingScanBudget <= 0 ) { continue; @@ -4731,7 +4732,7 @@ async function searchPackagesImpl( } = await buildSearchDigestQuery(state.family) .order("desc") .paginate({ cursor: state.cursor, numItems: pageSize }); - scanPages += 1; + state.pagesScanned += 1; remainingScanBudget -= pageSize; await collectDigestMatches(page.page); state.cursor = page.continueCursor; From fb5352f373de2c8dfec6d24b32e1282d3e0b0249 Mon Sep 17 00:00:00 2001 From: Patrick Erichsen Date: Fri, 24 Jul 2026 17:49:19 -0500 Subject: [PATCH 15/17] fix(claws): reject malformed UTF-8 in CLI --- .../clawhub/src/cli/commands/packages.test.ts | 33 +++++++++++++++++++ packages/clawhub/src/cli/commands/packages.ts | 3 +- 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/packages/clawhub/src/cli/commands/packages.test.ts b/packages/clawhub/src/cli/commands/packages.test.ts index 193a7bedfe..bc7129a753 100644 --- a/packages/clawhub/src/cli/commands/packages.test.ts +++ b/packages/clawhub/src/cli/commands/packages.test.ts @@ -2641,6 +2641,39 @@ describe("package commands", () => { } }); + it("rejects Claw manifests with malformed UTF-8 before upload", async () => { + const workdir = await makeTmpWorkdir(); + try { + const folder = join(workdir, "invalid-utf8-claw"); + await mkdir(folder, { recursive: true }); + await writeFile( + join(folder, "package.json"), + JSON.stringify({ + name: "invalid-utf8-claw", + displayName: "Invalid UTF-8 Claw", + version: "1.0.0", + openclaw: { claw: "CLAW.md" }, + }), + "utf8", + ); + await writeFile( + join(folder, "CLAW.md"), + Uint8Array.from([ + ...new TextEncoder().encode( + "---\nschemaVersion: 1\nagent:\n id: invalid-utf8-claw\n---\n# Invalid UTF-8 Claw\n", + ), + 0xff, + ]), + ); + await expect(cmdPublishPackage(makeOpts(workdir), "invalid-utf8-claw")).rejects.toThrow( + "CLAW.md: The declared Claw manifest is missing or is not UTF-8 text.", + ); + expect(httpMocks.apiRequestForm).not.toHaveBeenCalled(); + } finally { + await rm(workdir, { recursive: true, force: true }); + } + }); + it("rejects Claws with missing workspace sources before upload", async () => { const workdir = await makeTmpWorkdir(); try { diff --git a/packages/clawhub/src/cli/commands/packages.ts b/packages/clawhub/src/cli/commands/packages.ts index 7451a02841..0e501f4677 100644 --- a/packages/clawhub/src/cli/commands/packages.ts +++ b/packages/clawhub/src/cli/commands/packages.ts @@ -46,6 +46,7 @@ import { type PackageFamily, type PackageTrustedPublisher, type PackageVerificationSummary, + decodeUtf8Text, validateOpenClawExternalCodePluginPackageContents, validateOpenClawExternalCodePluginPackageJson, } from "../../schema/index.js"; @@ -2384,7 +2385,7 @@ async function preparePackagePublishPlan( packageJson, files: filesOnDisk.map((file) => ({ path: file.relPath, - text: new TextDecoder().decode(file.bytes), + text: decodeUtf8Text(file.bytes) ?? undefined, })), }); if (!validation.ok) { From 5d6bf5cbbab67c31dc49207f3c159c9313b870c6 Mon Sep 17 00:00:00 2001 From: Patrick Erichsen Date: Fri, 24 Jul 2026 17:53:12 -0500 Subject: [PATCH 16/17] fix(claws): bound published search summaries --- convex/packages.public.test.ts | 16 ++++++++++++---- convex/packages.ts | 4 ++-- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/convex/packages.public.test.ts b/convex/packages.public.test.ts index 6abf37486b..717cfe46cf 100644 --- a/convex/packages.public.test.ts +++ b/convex/packages.public.test.ts @@ -9227,6 +9227,7 @@ describe("packages public queries", () => { it("publishes a profile-bearing Claw through the existing release pipeline when enabled", async () => { const previous = process.env.CLAWHUB_EXPERIMENTAL_CLAWS; process.env.CLAWHUB_EXPERIMENTAL_CLAWS = "1"; + const longClawDescription = "x".repeat(1_100); const storedFiles = new Map([ [ "storage:package", @@ -9238,7 +9239,7 @@ describe("packages public queries", () => { ], [ "storage:claw", - "---\nschemaVersion: 1\nagent:\n id: demo-claw\n name: Demo Claw\n description: Runs the demo workflow.\nmetadata:\n openclaw.config: profiles/openclaw.yml\n---\n# Demo Claw\n", + `---\nschemaVersion: 1\nagent:\n id: demo-claw\n name: Demo Claw\n description: ${longClawDescription}\nmetadata:\n openclaw.config: profiles/openclaw.yml\n---\n# Demo Claw\n`, ], ["storage:profile", "schemaVersion: 1\nagent:\n tools:\n profile: coding\n"], ]); @@ -9320,19 +9321,26 @@ describe("packages public queries", () => { expect.anything(), expect.objectContaining({ family: "claw", - summary: "Runs the demo workflow.", + summary: expect.any(String), artifactKind: "legacy-zip", clawpackStorageId: "storage:legacy-zip", clawpackSha256: expect.stringMatching(/^[a-f0-9]{64}$/), clawpackSize: expect.any(Number), clawManifestSummary: expect.objectContaining({ - agent: expect.objectContaining({ id: "demo-claw" }), + agent: expect.objectContaining({ + id: "demo-claw", + description: "x".repeat(1_024), + }), workspace: expect.objectContaining({ bootstrapFiles: ["SOUL.md"] }), }), pluginManifestSummary: undefined, }), ); - expect(runMutation.mock.calls.at(-1)?.[1]).not.toHaveProperty("extractedClawManifest"); + const publishMutationArgs = runMutation.mock.calls.find( + ([, args]) => args.family === "claw", + )?.[1]; + expect(publishMutationArgs?.summary).toBe("x".repeat(1_024)); + expect(publishMutationArgs).not.toHaveProperty("extractedClawManifest"); } finally { if (previous === undefined) delete process.env.CLAWHUB_EXPERIMENTAL_CLAWS; else process.env.CLAWHUB_EXPERIMENTAL_CLAWS = previous; diff --git a/convex/packages.ts b/convex/packages.ts index ab79882e90..bcfd93284e 100644 --- a/convex/packages.ts +++ b/convex/packages.ts @@ -8220,8 +8220,8 @@ async function publishPackageImpl( : null; const summary = - validatedClaw?.manifest.agent.description?.trim() || - validatedClaw?.manifest.agent.name?.trim() || + validatedClaw?.summary.agent.description || + validatedClaw?.summary.agent.name || summarizePackageForSearch({ packageName: name, packageJson, From a9f1bb419fecef3191fbf012b9e2483fc8092b3f Mon Sep 17 00:00:00 2001 From: Patrick Erichsen Date: Fri, 24 Jul 2026 17:58:49 -0500 Subject: [PATCH 17/17] fix(claws): preserve discovery cursor sort --- convex/packages.public.test.ts | 44 +++++++++++++++++++++++++++++++++- convex/packages.ts | 33 ++++++++++++++++++++----- 2 files changed, 70 insertions(+), 7 deletions(-) diff --git a/convex/packages.public.test.ts b/convex/packages.public.test.ts index 717cfe46cf..6afbb9c1ba 100644 --- a/convex/packages.public.test.ts +++ b/convex/packages.public.test.ts @@ -1825,7 +1825,10 @@ function makePluginExportIndexKey(row: Record) { return [row.softDeletedAt, row.family, row.updatedAt, row._creationTime, row._id] as unknown[]; } -function makePluginExportCtx(digests: Array>) { +function makePluginExportCtx( + digests: Array>, + options?: { recommendationScoresMissing?: () => boolean }, +) { const packagesById = new Map( digests.map((digest) => [ String(digest.packageId), @@ -1851,6 +1854,15 @@ function makePluginExportCtx(digests: Array>) { db: { get: vi.fn(async (id: string) => packagesById.get(id) ?? null), query: vi.fn((table: string) => { + if (table === "packages") { + return { + withIndex: vi.fn(() => ({ + first: vi.fn(async () => + options?.recommendationScoresMissing?.() ? { _id: "packages:missing-score" } : null, + ), + })), + }; + } if (table !== "packageSearchDigest") throw new Error(`Unexpected table ${table}`); return { withIndex: vi.fn( @@ -17008,6 +17020,36 @@ describe("restorePackageInternal", () => { } }); + it("keeps the cursor's effective sort when recommendation backfill completes between pages", async () => { + const previous = process.env.CLAWHUB_EXPERIMENTAL_CLAWS; + delete process.env.CLAWHUB_EXPERIMENTAL_CLAWS; + let recommendationScoresMissing = true; + const ctx = makePluginExportCtx( + [ + makeDigest("newer-plugin", { updatedAt: 2, _creationTime: 2 }), + makeDigest("older-plugin", { updatedAt: 1, _creationTime: 1 }), + ], + { recommendationScoresMissing: () => recommendationScoresMissing }, + ); + + try { + const first = await listPublicPageHandler(ctx, { + sort: "recommended", + paginationOpts: { cursor: null, numItems: 1 }, + }); + recommendationScoresMissing = false; + const second = await listPublicPageHandler(ctx, { + sort: "recommended", + paginationOpts: { cursor: first.continueCursor, numItems: 1 }, + }); + expect(first.page.map((entry) => entry.name)).toEqual(["newer-plugin"]); + expect(second.page.map((entry) => entry.name)).toEqual(["older-plugin"]); + } finally { + if (previous === undefined) delete process.env.CLAWHUB_EXPERIMENTAL_CLAWS; + else process.env.CLAWHUB_EXPERIMENTAL_CLAWS = previous; + } + }); + it("does not let disabled Claws starve unfiltered public search", async () => { const previous = process.env.CLAWHUB_EXPERIMENTAL_CLAWS; delete process.env.CLAWHUB_EXPERIMENTAL_CLAWS; diff --git a/convex/packages.ts b/convex/packages.ts index bcfd93284e..f11ba313fa 100644 --- a/convex/packages.ts +++ b/convex/packages.ts @@ -3476,6 +3476,23 @@ function encodeStablePackageDiscoveryCursor(state: StablePackageDiscoveryCursorS })}`; } +function readStablePackageDiscoveryCursorSort( + raw: string | null | undefined, +): "updated" | "recommended" | null { + if (!raw?.startsWith(STABLE_PACKAGE_DISCOVERY_CURSOR_PREFIX)) return null; + try { + const parsed = JSON.parse(raw.slice(STABLE_PACKAGE_DISCOVERY_CURSOR_PREFIX.length)) as { + v?: unknown; + sort?: unknown; + }; + return parsed.v === 1 && (parsed.sort === "updated" || parsed.sort === "recommended") + ? parsed.sort + : null; + } catch { + return null; + } +} + function decodeStablePackageDiscoveryCursor( raw: string | null | undefined, sort: StablePackageDiscoveryCursorState["sort"], @@ -3930,17 +3947,21 @@ async function listStablePackageDiscoveryPage( Math.min(args.paginationOpts.numItems, MAX_PUBLIC_LIST_PAGE_SIZE), ); const requestedSort = args.sort ?? "updated"; - const sort = - requestedSort === "recommended" && - ( + const cursorSort = + requestedSort === "recommended" + ? readStablePackageDiscoveryCursorSort(args.paginationOpts.cursor) + : null; + let sort = cursorSort ?? requestedSort; + if (!cursorSort && requestedSort === "recommended") { + const recommendationScoresMissing = ( await Promise.all( STABLE_PACKAGE_FAMILIES.map( async (family) => await hasMissingPackageRecommendedScore(ctx, family), ), ) - ).some(Boolean) - ? "updated" - : requestedSort; + ).some(Boolean); + if (recommendationScoresMissing) sort = "updated"; + } const cursor = decodeStablePackageDiscoveryCursor(args.paginationOpts.cursor, sort); const scanLimit = Math.min(MAX_PUBLIC_LIST_PAGE_SIZE, Math.max(targetCount * 5, 50)); const loadFamily = async (family: StablePackageFamily) => {