From 87ca030c30f3cfb78ab15c8e66b5ff1469c8f9c8 Mon Sep 17 00:00:00 2001 From: Patrick Erichsen Date: Mon, 3 Aug 2026 20:37:01 -0700 Subject: [PATCH] fix: upload skill files directly to Convex (#3391) * fix: upload skill files directly to Convex * chore: prepare clawhub CLI 0.23.3 --- CHANGELOG.md | 6 + bun.lock | 2 +- convex/_generated/api.d.ts | 2 + convex/crons.ts | 7 + convex/httpApiV1.handlers.test.ts | 139 +++++++++- convex/httpApiV1/shared.ts | 1 + convex/httpApiV1/skillsV1.ts | 131 +++++++++- convex/lib/retentionPolicy.test.ts | 6 + convex/lib/retentionPolicy.ts | 9 + convex/lib/skillPublish.ts | 2 + convex/retention.test.ts | 50 ++++ convex/retention.ts | 31 +++ convex/schema.ts | 15 ++ convex/skillPublishUploads.test.ts | 122 +++++++++ convex/skillPublishUploads.ts | 170 ++++++++++++ convex/skills.ts | 9 + packages/clawhub/package.json | 2 +- .../clawhub/src/cli/commands/publish.test.ts | 243 +++++++++++------- packages/clawhub/src/cli/commands/publish.ts | 93 +++++-- packages/clawhub/src/http.bun.test.ts | 19 ++ packages/clawhub/src/http.ts | 9 +- packages/clawhub/src/schema/routes.ts | 1 + packages/clawhub/src/schema/schemas.ts | 13 + packages/schema/dist/packages.d.ts | 1 + packages/schema/dist/routes.d.ts | 1 + packages/schema/dist/routes.js | 1 + packages/schema/dist/routes.js.map | 2 +- packages/schema/dist/schemas.d.ts | 12 + packages/schema/dist/schemas.js | 11 + packages/schema/dist/schemas.js.map | 2 +- packages/schema/src/routes.ts | 1 + packages/schema/src/schemas.ts | 13 + .../clawhub-cli-npm-release-check.test.mjs | 2 +- specs/security-moderation.md | 14 + 34 files changed, 1014 insertions(+), 128 deletions(-) create mode 100644 convex/skillPublishUploads.test.ts create mode 100644 convex/skillPublishUploads.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 448ad679..409608ed 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,12 @@ - CLI: accept npm 12's package-keyed `npm pack --json` output when building ClawPacks while retaining compatibility with earlier npm array output. - Web/API: preserve JSON, SSR, and OG responses through the Convex proxy after the H3 response-wrapper update. +## 0.23.3 - 2026-08-03 + +### Fixes + +- CLI/API: stage skill files directly in Convex storage before publishing metadata, so bundles within the documented 50MB total limit no longer hit Vercel's smaller request-body limit. + ## 0.23.2 - 2026-08-03 ### Changes diff --git a/bun.lock b/bun.lock index 05539788..93226bb9 100644 --- a/bun.lock +++ b/bun.lock @@ -101,7 +101,7 @@ }, "packages/clawhub": { "name": "clawhub", - "version": "0.23.2", + "version": "0.23.3", "bin": { "clawdhub": "bin/clawdhub.js", "clawhub": "bin/clawdhub.js", diff --git a/convex/_generated/api.d.ts b/convex/_generated/api.d.ts index d71134dd..26f11d6b 100644 --- a/convex/_generated/api.d.ts +++ b/convex/_generated/api.d.ts @@ -192,6 +192,7 @@ import type * as skillPresentationAssets from "../skillPresentationAssets.js"; import type * as skillPresentationAssetsHttp from "../skillPresentationAssetsHttp.js"; import type * as skillPresentationBackfill from "../skillPresentationBackfill.js"; import type * as skillPresentationImageNode from "../skillPresentationImageNode.js"; +import type * as skillPublishUploads from "../skillPublishUploads.js"; import type * as skillStatEvents from "../skillStatEvents.js"; import type * as skillTransfers from "../skillTransfers.js"; import type * as skills from "../skills.js"; @@ -401,6 +402,7 @@ declare const fullApi: ApiFromModules<{ skillPresentationAssetsHttp: typeof skillPresentationAssetsHttp; skillPresentationBackfill: typeof skillPresentationBackfill; skillPresentationImageNode: typeof skillPresentationImageNode; + skillPublishUploads: typeof skillPublishUploads; skillStatEvents: typeof skillStatEvents; skillTransfers: typeof skillTransfers; skills: typeof skills; diff --git a/convex/crons.ts b/convex/crons.ts index 198aa78d..497029a0 100644 --- a/convex/crons.ts +++ b/convex/crons.ts @@ -245,6 +245,13 @@ if (process.env.CLAWHUB_DISABLE_CRONS !== "1" && process.env.CLAWHUB_PREVIEW !== { batchSize: RETENTION_STANDARD_BATCH_SIZE }, ); + crons.interval( + "skill-publish-upload-retention-prune", + { hours: 1 }, + internal.retention.pruneExpiredSkillPublishUploadsInternal, + { batchSize: RETENTION_STANDARD_BATCH_SIZE }, + ); + crons.interval( "http-rate-limit-keys-prune", { hours: 1 }, diff --git a/convex/httpApiV1.handlers.test.ts b/convex/httpApiV1.handlers.test.ts index ce8d8ca9..3ef0b51b 100644 --- a/convex/httpApiV1.handlers.test.ts +++ b/convex/httpApiV1.handlers.test.ts @@ -7228,6 +7228,124 @@ describe("httpApiV1 handlers", () => { expect(new Uint8Array(await response.arrayBuffer())).toEqual(fileBytes); }); + it("creates a direct skill upload URL for an authenticated API user", async () => { + vi.mocked(requireApiTokenUser).mockResolvedValueOnce({ + userId: "users:1", + user: { handle: "p" }, + } as never); + const runMutation = vi + .fn() + .mockResolvedValueOnce(okRate()) + .mockResolvedValueOnce({ uploadTicket: "skillPublishUploadTickets:1" }); + + const response = await __handlers.skillsPostRouterV1Handler( + makeCtx({ runMutation }), + new Request("https://example.com/api/v1/skills/-/upload-url", { + method: "POST", + headers: { + Authorization: "Bearer clh_test", + "Content-Type": "application/json", + }, + body: JSON.stringify({ + path: "SKILL.md", + size: 5, + sha256: "a".repeat(64), + contentType: "text/markdown", + }), + }), + ); + + expect(response.status).toBe(200); + expect(await response.json()).toEqual({ + uploadUrl: "https://example.com/api/v1/skills/-/upload/skillPublishUploadTickets%3A1", + uploadTicket: "skillPublishUploadTickets:1", + }); + }); + + it("stores a bounded direct skill upload and attaches it to its ticket", async () => { + vi.mocked(requireApiTokenUser).mockResolvedValueOnce({ + userId: "users:1", + user: { handle: "p" }, + } as never); + const bytes = new TextEncoder().encode("hello"); + const runQuery = vi.fn().mockResolvedValue({ + path: "SKILL.md", + size: bytes.byteLength, + sha256: "2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824", + contentType: "text/markdown", + }); + const runMutation = vi.fn().mockResolvedValue(okRate()); + const store = vi.fn().mockResolvedValue("storage:1"); + + const response = await __handlers.skillsPostRouterV1Handler( + makeCtx({ runQuery, runMutation, storage: { store, delete: vi.fn() } }), + new Request( + "https://example.convex.site/api/v1/skills/-/upload/skillPublishUploadTickets%3A1", + { + method: "POST", + headers: { + Authorization: "Bearer clh_test", + "Content-Type": "text/markdown", + }, + body: bytes, + }, + ), + ); + + expect(response.status).toBe(200); + expect(await response.json()).toEqual({ storageId: "storage:1" }); + expect(store).toHaveBeenCalledTimes(1); + expect(runMutation).toHaveBeenCalledWith( + internal.skillPublishUploads.attachSkillPublishUploadInternal, + { + userId: "users:1", + uploadTicket: "skillPublishUploadTickets:1", + storageId: "storage:1", + }, + ); + }); + + it("stops reading a direct skill upload once it exceeds the ticket size", async () => { + vi.mocked(requireApiTokenUser).mockResolvedValueOnce({ + userId: "users:1", + user: { handle: "p" }, + } as never); + const runQuery = vi.fn().mockResolvedValue({ + path: "SKILL.md", + size: 5, + sha256: "a".repeat(64), + contentType: "text/markdown", + }); + const store = vi.fn(); + const body = new ReadableStream({ + start(controller) { + controller.enqueue(new Uint8Array([1, 2, 3])); + controller.enqueue(new Uint8Array([4, 5, 6])); + controller.close(); + }, + }); + + const response = await __handlers.skillsPostRouterV1Handler( + makeCtx({ + runQuery, + runMutation: vi.fn().mockResolvedValue(okRate()), + storage: { store, delete: vi.fn() }, + }), + new Request( + "https://example.convex.site/api/v1/skills/-/upload/skillPublishUploadTickets%3A1", + { + method: "POST", + headers: { Authorization: "Bearer clh_test" }, + body, + duplex: "half", + } as RequestInit & { duplex: "half" }, + ), + ); + + expect(response.status).toBe(413); + expect(store).not.toHaveBeenCalled(); + }); + it("publish json succeeds", async () => { vi.mocked(requireApiTokenUser).mockResolvedValueOnce({ userId: "users:1", @@ -7254,6 +7372,7 @@ describe("httpApiV1 handlers", () => { storageId: "storage:1", sha256: "abc", contentType: "text/plain", + uploadTicket: "skillPublishUploadTickets:1", }, ], }); @@ -7312,6 +7431,7 @@ describe("httpApiV1 handlers", () => { storageId: "storage:1", sha256: "abc", contentType: "text/plain", + uploadTicket: "skillPublishUploadTickets:1", }, ], }); @@ -7376,6 +7496,7 @@ describe("httpApiV1 handlers", () => { storageId: "storage:1", sha256: "abc", contentType: "text/plain", + uploadTicket: "skillPublishUploadTickets:1", }, ], }); @@ -7394,7 +7515,9 @@ describe("httpApiV1 handlers", () => { expect.anything(), "users:1", expect.objectContaining({ source }), - {}, + expect.objectContaining({ + skillPublishUploadTickets: ["skillPublishUploadTickets:1"], + }), ); expect(vi.mocked(publishVersionForUser).mock.calls[0]?.[3]).not.toHaveProperty( "sourceProvenance", @@ -7431,6 +7554,7 @@ describe("httpApiV1 handlers", () => { storageId: "storage:1", sha256: "abc", contentType: "text/plain", + uploadTicket: "skillPublishUploadTickets:1", }, ], }); @@ -7455,7 +7579,10 @@ describe("httpApiV1 handlers", () => { expect.anything(), "users:1", expect.not.objectContaining({ ownerHandle: expect.anything() }), - { ownerPublisherId: "publishers:openclaw" }, + expect.objectContaining({ + ownerPublisherId: "publishers:openclaw", + skillPublishUploadTickets: ["skillPublishUploadTickets:1"], + }), ); }); @@ -7490,6 +7617,7 @@ describe("httpApiV1 handlers", () => { storageId: "storage:1", sha256: "abc", contentType: "text/plain", + uploadTicket: "skillPublishUploadTickets:1", }, ], }); @@ -7506,7 +7634,10 @@ describe("httpApiV1 handlers", () => { expect.anything(), "users:1", expect.not.objectContaining({ ownerHandle: expect.anything() }), - { ownerPublisherId: "publishers:openclaw" }, + expect.objectContaining({ + ownerPublisherId: "publishers:openclaw", + skillPublishUploadTickets: ["skillPublishUploadTickets:1"], + }), ); }); @@ -7533,6 +7664,7 @@ describe("httpApiV1 handlers", () => { storageId: "storage:1", sha256: "abc", contentType: "text/plain", + uploadTicket: "skillPublishUploadTickets:1", }, ], }); @@ -7568,6 +7700,7 @@ describe("httpApiV1 handlers", () => { storageId: "storage:1", sha256: "abc", contentType: "text/plain", + uploadTicket: "skillPublishUploadTickets:1", }, ], }); diff --git a/convex/httpApiV1/shared.ts b/convex/httpApiV1/shared.ts index 5c472ecd..8138f99b 100644 --- a/convex/httpApiV1/shared.ts +++ b/convex/httpApiV1/shared.ts @@ -537,6 +537,7 @@ export function parsePublishBody(body: unknown) { files: parsed.files.map((file) => ({ ...file, storageId: file.storageId as Id<"_storage">, + uploadTicket: file.uploadTicket as Id<"skillPublishUploadTickets"> | undefined, })), }; } diff --git a/convex/httpApiV1/skillsV1.ts b/convex/httpApiV1/skillsV1.ts index fcd642d3..cca61baa 100644 --- a/convex/httpApiV1/skillsV1.ts +++ b/convex/httpApiV1/skillsV1.ts @@ -1,5 +1,6 @@ import { ApiRoutes, + ApiV1SkillUploadUrlRequestSchema, ApiV1SkillBulkRescanBatchRequestSchema, ApiV1SkillBulkRescanStatusRequestSchema, ApiV1SkillHardDeleteRequestSchema, @@ -95,6 +96,30 @@ const DEFAULT_EXPORT_PAGE_LIMIT = 250; const MAX_EXPORT_TOTAL_BYTES = 256 * 1024 * 1024; const MAX_SECURITY_VERDICT_ITEMS = 100; +async function readRequestBodyWithinLimit(request: Request, maxBytes: number) { + if (!request.body) return new Uint8Array(); + const reader = request.body.getReader(); + const chunks: Uint8Array[] = []; + let totalBytes = 0; + while (true) { + const { done, value } = await reader.read(); + if (done) break; + totalBytes += value.byteLength; + if (totalBytes > maxBytes) { + await reader.cancel("Upload exceeds its declared size").catch(() => undefined); + return null; + } + chunks.push(value); + } + const bytes = new Uint8Array(totalBytes); + let offset = 0; + for (const chunk of chunks) { + bytes.set(chunk, offset); + offset += chunk.byteLength; + } + return bytes; +} + type ListSkillsResult = { items: Array<{ skill: { @@ -2625,6 +2650,13 @@ export async function publishSkillV1Handler(ctx: ActionCtx, request: Request) { if (!hasAcceptedLegacyLicenseTerms(payload.acceptLicenseTerms)) { return text("MIT-0 license terms must be accepted to publish skills", 400, rate.headers); } + if (payload.files.some((file) => !file.uploadTicket)) { + return text( + "Every directly uploaded skill file requires an upload ticket", + 400, + rate.headers, + ); + } const result = await publishSkillPayloadForApiUser(ctx, auth.userId, payload); return json({ ok: true, ...result }, 200, rate.headers); } @@ -2651,6 +2683,13 @@ async function publishSkillPayloadForApiUser( payload: ReturnType, ) { const { ownerHandle, sourceOwnerHandle, migrateOwner, ...publishPayload } = payload; + const uploadTickets = publishPayload.files.flatMap((file) => + file.uploadTicket ? [file.uploadTicket] : [], + ); + if (uploadTickets.length > 0 && uploadTickets.length !== publishPayload.files.length) { + throw new Error("Every directly uploaded skill file requires an upload ticket"); + } + const files = publishPayload.files.map(({ uploadTicket: _uploadTicket, ...file }) => file); const target = ownerHandle ? ((await ctx.runMutation(internal.publishers.resolvePublishTargetForUserInternal, { actorUserId: userId, @@ -2667,11 +2706,17 @@ async function publishSkillPayloadForApiUser( })) as { publisherId: Id<"publishers"> }) : null; const shouldMigrateOwner = Boolean(target && source); - return await publishVersionForUser(ctx, userId, publishPayload, { - ...(target ? { ownerPublisherId: target.publisherId } : {}), - ...(source ? { sourceOwnerPublisherId: source.publisherId } : {}), - ...(shouldMigrateOwner ? { migrateOwner: true } : {}), - }); + return await publishVersionForUser( + ctx, + userId, + { ...publishPayload, files }, + { + ...(target ? { ownerPublisherId: target.publisherId } : {}), + ...(source ? { sourceOwnerPublisherId: source.publisherId } : {}), + ...(shouldMigrateOwner ? { migrateOwner: true } : {}), + ...(uploadTickets.length > 0 ? { skillPublishUploadTickets: uploadTickets } : {}), + }, + ); } function hasAcceptedLegacyLicenseTerms(acceptLicenseTerms: boolean | undefined) { @@ -2942,6 +2987,82 @@ export async function skillsPostRouterV1Handler(ctx: ActionCtx, request: Request const action = segments[1] ?? ""; const slug = segments[0]?.trim().toLowerCase() ?? ""; + if (segments.length === 2 && slug === "-" && action === "upload-url") { + const auth = await requireApiTokenUserOrResponse(ctx, request, rate.headers); + if (!auth.ok) return auth.response; + try { + const expected = parseArk( + ApiV1SkillUploadUrlRequestSchema, + await request.json(), + "Skill upload request", + ); + const { uploadTicket } = await ctx.runMutation( + internal.skillPublishUploads.createSkillPublishUploadInternal, + { + userId: auth.userId, + path: expected.path, + size: expected.size, + sha256: expected.sha256, + ...(expected.contentType ? { contentType: expected.contentType } : {}), + }, + ); + // The request reaching this Convex action has the direct Convex origin even + // when clawhub.ai forwarded it. Keep the file body off the Vercel request path. + const uploadUrl = new URL( + `/api/v1/skills/-/upload/${encodeURIComponent(uploadTicket)}`, + request.url, + ).toString(); + return json({ uploadUrl, uploadTicket }, 200, rate.headers); + } catch (error) { + const message = error instanceof Error ? error.message : "Invalid skill upload request"; + return text(message, 400, rate.headers); + } + } + + if (segments.length === 3 && slug === "-" && action === "upload" && segments[2]) { + const auth = await requireApiTokenUserOrResponse(ctx, request, rate.headers); + if (!auth.ok) return auth.response; + const uploadTicket = segments[2] as Id<"skillPublishUploadTickets">; + let storageId: Id<"_storage"> | undefined; + try { + const expected = await ctx.runQuery( + internal.skillPublishUploads.getSkillPublishUploadForUserInternal, + { userId: auth.userId, uploadTicket }, + ); + const declaredLength = Number(request.headers.get("content-length")); + if (Number.isFinite(declaredLength) && declaredLength > expected.size) { + return text("Uploaded file exceeds its declared size", 413, rate.headers); + } + const bytes = await readRequestBodyWithinLimit(request, expected.size); + if (!bytes) { + return text("Uploaded file exceeds its declared size", 413, rate.headers); + } + if (bytes.byteLength !== expected.size) { + return text("Uploaded file size does not match its upload ticket", 400, rate.headers); + } + const digest = await crypto.subtle.digest("SHA-256", bytes); + const sha256 = Array.from(new Uint8Array(digest), (byte) => + byte.toString(16).padStart(2, "0"), + ).join(""); + if (sha256 !== expected.sha256) { + return text("Uploaded file SHA-256 does not match its upload ticket", 400, rate.headers); + } + storageId = await ctx.storage.store( + new Blob([bytes], expected.contentType ? { type: expected.contentType } : undefined), + ); + await ctx.runMutation(internal.skillPublishUploads.attachSkillPublishUploadInternal, { + userId: auth.userId, + uploadTicket, + storageId, + }); + return json({ storageId }, 200, rate.headers); + } catch (error) { + if (storageId) await ctx.storage.delete(storageId).catch(() => undefined); + const message = error instanceof Error ? error.message : "Skill upload failed"; + return text(message, 400, rate.headers); + } + } + if (segments.length === 3 && segments[1] === "tags" && segments[2]) { if (!slug) return text("Slug required", 400, rate.headers); const auth = await requireApiTokenUserOrResponse(ctx, request, rate.headers); diff --git a/convex/lib/retentionPolicy.test.ts b/convex/lib/retentionPolicy.test.ts index 780d6c37..ddc14adf 100644 --- a/convex/lib/retentionPolicy.test.ts +++ b/convex/lib/retentionPolicy.test.ts @@ -45,6 +45,12 @@ describe("retention policies", () => { expirationIndex: "by_expires_at", prune: "retention.pruneExpiredPublisherInvitesInternal", }); + expect(getRetentionPolicy("skillPublishUploadTickets")).toMatchObject({ + classification: "ephemeral", + expirationField: "expiresAt", + expirationIndex: "by_expires_at", + prune: "retention.pruneExpiredSkillPublishUploadsInternal", + }); }); it("documents package daily stats as durable analytics", () => { diff --git a/convex/lib/retentionPolicy.ts b/convex/lib/retentionPolicy.ts index e5120a7e..431edf31 100644 --- a/convex/lib/retentionPolicy.ts +++ b/convex/lib/retentionPolicy.ts @@ -105,6 +105,15 @@ export const RETENTION_POLICIES = { retention: "Organization logo upload ticket TTL.", }, ), + skillPublishUploadTickets: ephemeral( + "Direct skill file upload tickets are deleted after their short staging window.", + { + expirationField: "expiresAt", + expirationIndex: "by_expires_at", + prune: "retention.pruneExpiredSkillPublishUploadsInternal", + retention: "1 hour.", + }, + ), officialPublishers: permanent("Manual official publisher assignments."), githubSkillSources: permanent("Tracked GitHub source configuration."), githubSkillContents: derived("Cached GitHub source content snapshots.", "githubSkillSources"), diff --git a/convex/lib/skillPublish.ts b/convex/lib/skillPublish.ts index 2c3437d1..10c29a6f 100644 --- a/convex/lib/skillPublish.ts +++ b/convex/lib/skillPublish.ts @@ -154,6 +154,7 @@ export type PublishOptions = { // accidentally transfer ownership. migrateOwner?: boolean; stagePrePublicationChecks?: boolean; + skillPublishUploadTickets?: Id<"skillPublishUploadTickets">[]; }; type InternalPublishOptions = PublishOptions; @@ -489,6 +490,7 @@ async function publishVersionForUserInternal( const skillInsertArgs = { userId, + skillPublishUploadTickets: options.skillPublishUploadTickets, ownerPublisherId: options.ownerPublisherId, sourceOwnerPublisherId: options.sourceOwnerPublisherId, migrateOwner: options.migrateOwner, diff --git a/convex/retention.test.ts b/convex/retention.test.ts index fce5f4d8..cf1735e8 100644 --- a/convex/retention.test.ts +++ b/convex/retention.test.ts @@ -5,6 +5,7 @@ const retentionRefs = vi.hoisted(() => ({ pruneExpiredAuthSessionsInternal: Symbol("pruneExpiredAuthSessionsInternal"), pruneExpiredAuthRefreshTokensInternal: Symbol("pruneExpiredAuthRefreshTokensInternal"), pruneExpiredPublisherInvitesInternal: Symbol("pruneExpiredPublisherInvitesInternal"), + pruneExpiredSkillPublishUploadsInternal: Symbol("pruneExpiredSkillPublishUploadsInternal"), })); vi.mock("./_generated/api", () => ({ @@ -17,6 +18,7 @@ const { pruneExpiredAuthRefreshTokensInternal, pruneExpiredAuthSessionsInternal, pruneExpiredPublisherInvitesInternal, + pruneExpiredSkillPublishUploadsInternal, } = await import("./retention"); type WrappedHandler = { @@ -41,6 +43,12 @@ const prunePublisherInvitesHandler = ( { deleted: number; hasMore: boolean } > )._handler; +const pruneSkillUploadsHandler = ( + pruneExpiredSkillPublishUploadsInternal as unknown as WrappedHandler< + { batchSize?: number }, + { deletedTickets: number; deletedStorage: number; hasMore: boolean } + > +)._handler; function makeDb(base: { query: ReturnType; delete: ReturnType }) { return { @@ -233,4 +241,46 @@ describe("auth retention", () => { batchSize: 1, }); }); + + it("deletes expired unconsumed skill upload storage in bounded batches", async () => { + const now = 4_000_000; + vi.spyOn(Date, "now").mockReturnValue(now); + const rows = [ + { + _id: "skillPublishUploadTickets:one", + expiresAt: now - 1, + storageId: "storage:one", + }, + ]; + const deleteDoc = vi.fn(); + const deleteStorage = vi.fn(); + const runAfter = vi.fn(); + const lt = vi.fn(() => ({})); + const ctx = { + db: makeDb({ + query: vi.fn(() => ({ + withIndex: vi.fn((indexName: string, build: (q: unknown) => unknown) => { + expect(indexName).toBe("by_expires_at"); + build({ lt }); + return { take: vi.fn(async () => rows) }; + }), + })), + delete: deleteDoc, + }), + storage: { delete: deleteStorage }, + scheduler: { runAfter }, + }; + + const result = await pruneSkillUploadsHandler(ctx as never, { batchSize: 1 }); + + expect(result).toEqual({ deletedTickets: 1, deletedStorage: 1, hasMore: true }); + expect(lt).toHaveBeenCalledWith("expiresAt", now); + expect(deleteStorage).toHaveBeenCalledWith("storage:one"); + expect(deleteDoc).toHaveBeenCalledWith("skillPublishUploadTickets:one"); + expect(runAfter).toHaveBeenCalledWith( + 0, + retentionRefs.pruneExpiredSkillPublishUploadsInternal, + { batchSize: 1 }, + ); + }); }); diff --git a/convex/retention.ts b/convex/retention.ts index 102ee4b6..c31851a7 100644 --- a/convex/retention.ts +++ b/convex/retention.ts @@ -109,3 +109,34 @@ export const pruneExpiredPublisherInvitesInternal = internalMutation({ return { deleted: stale.length, hasMore }; }, }); + +export const pruneExpiredSkillPublishUploadsInternal = internalMutation({ + args: { + batchSize: v.optional(v.number()), + }, + handler: async (ctx, args) => { + const batchSize = normalizeRetentionBatchSize(args.batchSize); + const stale = await ctx.db + .query("skillPublishUploadTickets") + .withIndex("by_expires_at", (q) => q.lt("expiresAt", Date.now())) + .take(batchSize); + + let deletedStorage = 0; + for (const ticket of stale) { + if (!ticket.usedAt && ticket.storageId) { + await ctx.storage.delete(ticket.storageId); + deletedStorage += 1; + } + await ctx.db.delete(ticket._id); + } + + const hasMore = stale.length === batchSize; + if (hasMore) { + await ctx.scheduler.runAfter(0, internal.retention.pruneExpiredSkillPublishUploadsInternal, { + batchSize, + }); + } + + return { deletedTickets: stale.length, deletedStorage, hasMore }; + }, +}); diff --git a/convex/schema.ts b/convex/schema.ts index 91e1bdbe..84b89043 100644 --- a/convex/schema.ts +++ b/convex/schema.ts @@ -336,6 +336,20 @@ const publisherImageUploadTickets = defineTable({ storageId: v.optional(v.id("_storage")), }).index("by_publisher_user", ["publisherId", "userId"]); +const skillPublishUploadTickets = defineTable({ + userId: v.id("users"), + path: v.string(), + size: v.number(), + sha256: v.string(), + contentType: v.optional(v.string()), + storageId: v.optional(v.id("_storage")), + createdAt: v.number(), + expiresAt: v.number(), + usedAt: v.optional(v.number()), +}) + .index("by_user", ["userId"]) + .index("by_expires_at", ["expiresAt"]); + const officialPublishers = defineTable({ publisherId: v.id("publishers"), reason: v.optional(v.string()), @@ -4266,6 +4280,7 @@ export default defineSchema({ publisherMembers, publisherInvites, publisherImageUploadTickets, + skillPublishUploadTickets, officialPublishers, githubSkillSources, githubSkillContents, diff --git a/convex/skillPublishUploads.test.ts b/convex/skillPublishUploads.test.ts new file mode 100644 index 00000000..403e7367 --- /dev/null +++ b/convex/skillPublishUploads.test.ts @@ -0,0 +1,122 @@ +/* @vitest-environment node */ + +import { afterEach, describe, expect, it, vi } from "vitest"; +import { + cleanupSkillPublishUploadInternal, + consumeSkillPublishUploads, +} from "./skillPublishUploads"; + +type CleanupHandler = { + _handler: ( + ctx: unknown, + args: { uploadTicket: "skillPublishUploadTickets:1" }, + ) => Promise; +}; + +const cleanupHandler = (cleanupSkillPublishUploadInternal as unknown as CleanupHandler)._handler; + +function makeCtx(ticket: Record | null) { + return { + db: { + get: vi.fn(async () => ticket), + patch: vi.fn(), + delete: vi.fn(), + normalizeId: vi.fn(), + query: vi.fn(), + replace: vi.fn(), + insert: vi.fn(), + system: { get: vi.fn(), query: vi.fn() }, + }, + storage: { delete: vi.fn() }, + }; +} + +describe("skill publish upload tickets", () => { + afterEach(() => { + vi.restoreAllMocks(); + }); + + it("atomically consumes a matching staged upload", async () => { + vi.spyOn(Date, "now").mockReturnValue(2_000); + const ctx = makeCtx({ + _id: "skillPublishUploadTickets:1", + userId: "users:1", + path: "SKILL.md", + size: 5, + sha256: "a".repeat(64), + contentType: "text/markdown", + storageId: "storage:1", + expiresAt: 10_000, + }); + + await consumeSkillPublishUploads(ctx as never, { + userId: "users:1" as never, + uploadTickets: ["skillPublishUploadTickets:1" as never], + files: [ + { + path: "SKILL.md", + size: 5, + sha256: "a".repeat(64), + contentType: "text/markdown", + storageId: "storage:1" as never, + }, + ], + }); + + expect(ctx.db.patch).toHaveBeenCalledWith("skillPublishUploadTickets:1", { usedAt: 2_000 }); + }); + + it("rejects a ticket whose staged file does not match the publish", async () => { + vi.spyOn(Date, "now").mockReturnValue(2_000); + const ctx = makeCtx({ + _id: "skillPublishUploadTickets:1", + userId: "users:1", + path: "SKILL.md", + size: 5, + sha256: "a".repeat(64), + storageId: "storage:1", + expiresAt: 10_000, + }); + + await expect( + consumeSkillPublishUploads(ctx as never, { + userId: "users:1" as never, + uploadTickets: ["skillPublishUploadTickets:1" as never], + files: [ + { + path: "SKILL.md", + size: 6, + sha256: "a".repeat(64), + storageId: "storage:1" as never, + }, + ], + }), + ).rejects.toThrow("does not match this publish"); + expect(ctx.db.patch).not.toHaveBeenCalled(); + }); + + it("deletes an abandoned staged file when its ticket expires", async () => { + const ctx = makeCtx({ + _id: "skillPublishUploadTickets:1", + storageId: "storage:1", + }); + + await cleanupHandler(ctx, { uploadTicket: "skillPublishUploadTickets:1" }); + + expect(ctx.storage.delete).toHaveBeenCalledWith("storage:1"); + expect(ctx.db.delete).toHaveBeenCalledWith("skillPublishUploadTickets:1"); + }); + + it("keeps published storage when deleting a consumed ticket", async () => { + const ctx = makeCtx({ + _id: "skillPublishUploadTickets:1", + storageId: "storage:1", + usedAt: 2_000, + }); + + await cleanupHandler(ctx, { uploadTicket: "skillPublishUploadTickets:1" }); + + expect(ctx.storage.delete).not.toHaveBeenCalled(); + expect(ctx.db.delete).toHaveBeenCalledWith("skillPublishUploadTickets:1"); + }); +}); diff --git a/convex/skillPublishUploads.ts b/convex/skillPublishUploads.ts new file mode 100644 index 00000000..14b14a5a --- /dev/null +++ b/convex/skillPublishUploads.ts @@ -0,0 +1,170 @@ +import { normalizeContentType } from "clawhub-schema"; +import { v } from "convex/values"; +import { internal } from "./_generated/api"; +import type { Id } from "./_generated/dataModel"; +import type { MutationCtx } from "./_generated/server"; +import { internalMutation, internalQuery } from "./functions"; +import { MAX_PUBLISH_FILE_BYTES } from "./lib/publishLimits"; +import { validateFilePath } from "./lib/skillZip"; + +const SKILL_PUBLISH_UPLOAD_TTL_MS = 60 * 60_000; + +function assertExpectedUpload(args: { + path: string; + size: number; + sha256: string; + contentType?: string; +}) { + if (!validateFilePath(args.path)) throw new Error("Invalid upload path"); + if (!Number.isSafeInteger(args.size) || args.size < 0 || args.size > MAX_PUBLISH_FILE_BYTES) { + throw new Error(`Upload must be at most ${MAX_PUBLISH_FILE_BYTES} bytes`); + } + if (!/^[a-f0-9]{64}$/i.test(args.sha256)) throw new Error("Invalid upload SHA-256"); +} + +export const createSkillPublishUploadInternal = internalMutation({ + args: { + userId: v.id("users"), + path: v.string(), + size: v.number(), + sha256: v.string(), + contentType: v.optional(v.string()), + }, + handler: async (ctx, args) => { + const user = await ctx.db.get(args.userId); + if (!user || user.deletedAt || user.deactivatedAt) throw new Error("User not found"); + assertExpectedUpload(args); + const now = Date.now(); + const expiresAt = now + SKILL_PUBLISH_UPLOAD_TTL_MS; + const uploadTicket = await ctx.db.insert("skillPublishUploadTickets", { + userId: args.userId, + path: args.path, + size: args.size, + sha256: args.sha256.toLowerCase(), + contentType: normalizeContentType(args.contentType), + createdAt: now, + expiresAt, + }); + await ctx.scheduler.runAt( + expiresAt, + internal.skillPublishUploads.cleanupSkillPublishUploadInternal, + { uploadTicket }, + ); + return { uploadTicket }; + }, +}); + +export const getSkillPublishUploadForUserInternal = internalQuery({ + args: { + userId: v.id("users"), + uploadTicket: v.id("skillPublishUploadTickets"), + }, + handler: async (ctx, args) => { + const ticket = await ctx.db.get(args.uploadTicket); + if ( + !ticket || + ticket.userId !== args.userId || + ticket.usedAt || + ticket.storageId || + ticket.expiresAt <= Date.now() + ) { + throw new Error("Skill upload ticket is missing, used, or expired"); + } + return { + path: ticket.path, + size: ticket.size, + sha256: ticket.sha256, + contentType: ticket.contentType, + }; + }, +}); + +export const attachSkillPublishUploadInternal = internalMutation({ + args: { + userId: v.id("users"), + uploadTicket: v.id("skillPublishUploadTickets"), + storageId: v.id("_storage"), + }, + handler: async (ctx, args) => { + const ticket = await ctx.db.get(args.uploadTicket); + const now = Date.now(); + if ( + !ticket || + ticket.userId !== args.userId || + ticket.usedAt || + ticket.storageId || + ticket.expiresAt <= now + ) { + throw new Error("Skill upload ticket is missing, used, or expired"); + } + const metadata = await ctx.db.system.get("_storage", args.storageId); + if ( + !metadata || + metadata._creationTime < ticket.createdAt || + metadata.size !== ticket.size || + metadata.sha256.toLowerCase() !== ticket.sha256 || + normalizeContentType(metadata.contentType) !== ticket.contentType + ) { + throw new Error("Uploaded file does not match its skill upload ticket"); + } + await ctx.db.patch(ticket._id, { storageId: args.storageId }); + }, +}); + +type SkillPublishFile = { + path: string; + size: number; + storageId: Id<"_storage">; + sha256: string; + contentType?: string; +}; + +export async function consumeSkillPublishUploads( + ctx: MutationCtx, + args: { + userId: Id<"users">; + uploadTickets: Id<"skillPublishUploadTickets">[]; + files: SkillPublishFile[]; + }, +) { + if (args.uploadTickets.length !== args.files.length) { + throw new Error("Every directly uploaded skill file requires an upload ticket"); + } + if (new Set(args.uploadTickets).size !== args.uploadTickets.length) { + throw new Error("Skill upload tickets cannot be reused"); + } + const now = Date.now(); + for (let index = 0; index < args.files.length; index += 1) { + const uploadTicket = args.uploadTickets[index]; + const file = args.files[index]; + if (!uploadTicket || !file) throw new Error("Skill upload ticket mismatch"); + const ticket = await ctx.db.get(uploadTicket); + if ( + !ticket || + ticket.userId !== args.userId || + ticket.usedAt || + ticket.expiresAt <= now || + ticket.storageId !== file.storageId || + ticket.path !== file.path || + ticket.size !== file.size || + ticket.sha256 !== file.sha256.toLowerCase() || + ticket.contentType !== normalizeContentType(file.contentType) + ) { + throw new Error("Skill upload ticket does not match this publish"); + } + await ctx.db.patch(ticket._id, { usedAt: now }); + } +} + +export const cleanupSkillPublishUploadInternal = internalMutation({ + args: { uploadTicket: v.id("skillPublishUploadTickets") }, + handler: async (ctx, args) => { + const ticket = await ctx.db.get(args.uploadTicket); + if (!ticket) return { deleted: false }; + if (!ticket.usedAt && ticket.storageId) { + await ctx.storage.delete(ticket.storageId); + } + await ctx.db.delete(ticket._id); + return { deleted: true }; + }, +}); diff --git a/convex/skills.ts b/convex/skills.ts index c48ffdfe..4faca9de 100644 --- a/convex/skills.ts +++ b/convex/skills.ts @@ -173,6 +173,7 @@ import { normalizeSkillTags } from "./lib/skillTags"; import { runStaticPublishScan } from "./lib/staticPublishScan"; import { adjustUserSkillStatsForSkillChange } from "./lib/userSkillStats"; import schema from "./schema"; +import { consumeSkillPublishUploads } from "./skillPublishUploads"; const MAX_OWNER_SUMMARY_LENGTH = 500; const MAX_POINTERLESS_VERSION_SURVIVOR_SCAN = 100; @@ -12454,6 +12455,7 @@ function stripUndefinedForStoredPublication(value: unknown): unknown { export const insertVersion = internalMutation({ args: { userId: v.id("users"), + skillPublishUploadTickets: v.optional(v.array(v.id("skillPublishUploadTickets"))), ownerPublisherId: v.optional(v.id("publishers")), sourceOwnerPublisherId: v.optional(v.id("publishers")), // Explicit opt-in to owner migration. When an existing skill row already has @@ -12587,6 +12589,13 @@ export const insertVersion = internalMutation({ if (!normalizedSlug) throw new ConvexError("Slug is required."); const user = await ctx.db.get(userId); if (!user || user.deletedAt || user.deactivatedAt) throw new Error("User not found"); + if (args.skillPublishUploadTickets) { + await consumeSkillPublishUploads(ctx, { + userId, + uploadTickets: args.skillPublishUploadTickets, + files: args.files, + }); + } const personalPublisher = await ensurePersonalPublisherForUser(ctx, user, { actorUserId: userId, source: "skill.publish", diff --git a/packages/clawhub/package.json b/packages/clawhub/package.json index 9e488808..01c25d92 100644 --- a/packages/clawhub/package.json +++ b/packages/clawhub/package.json @@ -1,6 +1,6 @@ { "name": "clawhub", - "version": "0.23.2", + "version": "0.23.3", "description": "ClawHub CLI \\u2014 install, update, search, and publish skills plus OpenClaw packages.", "homepage": "https://clawhub.ai", "bugs": { diff --git a/packages/clawhub/src/cli/commands/publish.test.ts b/packages/clawhub/src/cli/commands/publish.test.ts index 9c990aa2..1bbdefdb 100644 --- a/packages/clawhub/src/cli/commands/publish.test.ts +++ b/packages/clawhub/src/cli/commands/publish.test.ts @@ -16,6 +16,7 @@ const authTokenMocks = createAuthTokenModuleMocks(); const registryMocks = createRegistryModuleMocks(); const httpMocks = createHttpModuleMocks(); const uiMocks = createUiModuleMocks(); +let publishResponse: Record; vi.mock("../authToken.js", () => authTokenMocks.moduleFactory()); vi.mock("../registry.js", () => registryMocks.moduleFactory()); @@ -78,7 +79,7 @@ describe("cmdPublish", () => { match: { version: "1.2.3" }, latestVersion: { version: "1.2.3" }, }); - httpMocks.apiRequestForm.mockResolvedValueOnce({ + mockPublishResponse({ ok: true, skillId: "skill_1", versionId: "ver_2", @@ -114,7 +115,7 @@ describe("cmdPublish", () => { httpMocks.apiRequest.mockRejectedValueOnce( new Error("Skill not found or unavailable to this account."), ); - httpMocks.apiRequestForm.mockResolvedValueOnce({ + mockPublishResponse({ ok: true, skillId: "skill_1", versionId: "ver_1", @@ -143,7 +144,7 @@ describe("cmdPublish", () => { httpMocks.apiRequest.mockRejectedValueOnce( new Error("Skill not found or unavailable to this account."), ); - httpMocks.apiRequestForm.mockResolvedValueOnce({ + mockPublishResponse({ ok: true, skillId: "skill_1", versionId: "ver_pending", @@ -179,7 +180,7 @@ describe("cmdPublish", () => { httpMocks.apiRequest.mockRejectedValueOnce( new Error("Skill not found or unavailable to this account."), ); - httpMocks.apiRequestForm.mockResolvedValueOnce({ + mockPublishResponse({ ok: true, skillId: "skill_1", versionId: "ver_pending", @@ -212,7 +213,7 @@ describe("cmdPublish", () => { const folder = join(workdir, "unknown-status-skill"); await mkdir(folder, { recursive: true }); await writeFile(join(folder, "SKILL.md"), "# Skill\n", "utf8"); - httpMocks.apiRequestForm.mockResolvedValueOnce({ + mockPublishResponse({ ok: true, skillId: "skill_1", versionId: "ver_1", @@ -239,7 +240,7 @@ describe("cmdPublish", () => { match: null, latestVersion: { version: "1.2.3" }, }); - httpMocks.apiRequestForm.mockResolvedValueOnce({ + mockPublishResponse({ ok: true, skillId: "skill_1", versionId: "ver_2", @@ -269,7 +270,7 @@ describe("cmdPublish", () => { match: { version: "1.2.3" }, latestVersion: { version: "1.2.3" }, }); - httpMocks.apiRequestForm.mockResolvedValueOnce({ + mockPublishResponse({ ok: true, skillId: "skill_1", versionId: "ver_2", @@ -316,6 +317,85 @@ describe("cmdPublish", () => { } }); + it("uploads each skill file separately before sending the publish metadata", async () => { + const workdir = await makeTmpWorkdir(); + try { + const folder = join(workdir, "staged-skill"); + await mkdir(join(folder, "assets"), { recursive: true }); + await writeFile(join(folder, "SKILL.md"), "# Staged skill\n", "utf8"); + await writeFile(join(folder, "assets", "payload.bin"), Uint8Array.from([0, 1, 2, 255])); + + httpMocks.uploadBinary + .mockResolvedValueOnce({ storageId: "storage:skill" }) + .mockResolvedValueOnce({ storageId: "storage:payload" }); + mockPublishResponse({ + ok: true, + skillId: "skill_1", + versionId: "ver_1", + publicationStatus: "published", + }); + + await cmdPublish(makeOpts(workdir), "staged-skill", { + version: "1.0.0", + }); + + expect(httpMocks.apiRequestForm).not.toHaveBeenCalled(); + expect(httpMocks.uploadBinary).toHaveBeenCalledTimes(2); + const uploadTicketCalls = httpMocks.apiRequest.mock.calls.filter((call) => { + const args = call[1] as { method?: string; path?: string }; + return args.method === "POST" && args.path === "/api/v1/skills/-/upload-url"; + }); + expect(uploadTicketCalls).toHaveLength(2); + expect(uploadTicketCalls[0]?.[1]).toMatchObject({ + token: "tkn", + body: { + path: "SKILL.md", + size: 15, + sha256: "90b735dd867ee1111738fb1397982e1dbf7e6bda451d60d60fc10623926adcfc", + }, + }); + expect(httpMocks.uploadBinary).toHaveBeenNthCalledWith( + 1, + expect.objectContaining({ + url: "https://upload.local/1", + token: "tkn", + }), + expect.anything(), + ); + const publishCall = httpMocks.apiRequest.mock.calls.find((call) => { + const args = call[1] as { method?: string; path?: string }; + return args.method === "POST" && args.path === "/api/v1/skills"; + }); + expect(publishCall?.[1]).toMatchObject({ + method: "POST", + path: "/api/v1/skills", + token: "tkn", + body: { + slug: "staged-skill", + files: [ + expect.objectContaining({ + path: "SKILL.md", + size: 15, + storageId: "storage:skill", + sha256: "90b735dd867ee1111738fb1397982e1dbf7e6bda451d60d60fc10623926adcfc", + uploadTicket: "skillPublishUploadTickets:1", + }), + expect.objectContaining({ + path: "assets/payload.bin", + size: 4, + storageId: "storage:payload", + sha256: "3d1f57c984978ef98a18378c8166c1cb8ede02c03eeb6aee7e2f121dfeee3e56", + contentType: "application/octet-stream", + uploadTicket: "skillPublishUploadTickets:2", + }), + ], + }, + }); + } finally { + await rm(workdir, { recursive: true, force: true }); + } + }); + it("publishes Terraform and opaque files with exact bytes (mocked HTTP)", async () => { const workdir = await makeTmpWorkdir(); try { @@ -333,7 +413,7 @@ describe("cmdPublish", () => { await writeFile(join(folder, "terraform.tfvars"), variablesContent, "utf8"); await writeFile(join(folder, "assets", "payload.bin"), opaqueBytes); - httpMocks.apiRequestForm.mockResolvedValueOnce({ + mockPublishResponse({ ok: true, skillId: "skill_1", versionId: "ver_1", @@ -351,15 +431,7 @@ describe("cmdPublish", () => { await cmdPublish(makeOpts(workdir), "my-skill", options); - const publishCall = httpMocks.apiRequestForm.mock.calls.find((call) => { - const req = call[1] as { path?: string } | undefined; - return req?.path === "/api/v1/skills"; - }); - if (!publishCall) throw new Error("Missing publish call"); - const publishForm = (publishCall[1] as { form?: FormData }).form as FormData; - const payloadEntry = publishForm.get("payload"); - if (typeof payloadEntry !== "string") throw new Error("Missing publish payload"); - const payload = JSON.parse(payloadEntry); + const payload = publishPayload(); expect(payload.slug).toBe("my-skill"); expect(payload.displayName).toBe("My Skill"); expect(payload.ownerHandle).toBe("me"); @@ -369,22 +441,26 @@ describe("cmdPublish", () => { expect(payload.tags).toEqual(["latest"]); expect(payload.categories).toEqual(["automation", "development"]); expect(payload.topics).toEqual(["React", "GPU development"]); - const files = publishForm.getAll("files") as Array; - expect(files.map((file) => file.name ?? "").sort()).toEqual([ + const files = payload.files as Array<{ path: string }>; + expect(files.map((file) => file.path).sort()).toEqual([ "SKILL.md", "assets/payload.bin", "main.tf", "notes.md", "terraform.tfvars", ]); - const byName = new Map(files.map((file) => [file.name ?? "", file])); - expect(await byName.get("main.tf")?.text()).toBe(terraformContent); - expect(await byName.get("terraform.tfvars")?.text()).toBe(variablesContent); - expect( - new Uint8Array( - (await byName.get("assets/payload.bin")?.arrayBuffer()) ?? new ArrayBuffer(0), - ), - ).toEqual(opaqueBytes); + const uploadedBytes = new Map( + files.map((file, index) => { + const uploadCall = httpMocks.uploadBinary.mock.calls[index]; + if (!uploadCall) throw new Error(`Missing upload call for ${file.path}`); + return [file.path, (uploadCall[0] as { bytes: Uint8Array }).bytes] as const; + }), + ); + expect(new TextDecoder().decode(uploadedBytes.get("main.tf"))).toBe(terraformContent); + expect(new TextDecoder().decode(uploadedBytes.get("terraform.tfvars"))).toBe( + variablesContent, + ); + expect(uploadedBytes.get("assets/payload.bin")).toEqual(opaqueBytes); } finally { await rm(workdir, { recursive: true, force: true }); } @@ -397,7 +473,7 @@ describe("cmdPublish", () => { await mkdir(folder, { recursive: true }); await writeFile(join(folder, "SKILL.md"), "# Clear topics\n", "utf8"); - httpMocks.apiRequestForm.mockResolvedValueOnce({ + mockPublishResponse({ ok: true, skillId: "skill_1", versionId: "ver_1", @@ -418,7 +494,7 @@ describe("cmdPublish", () => { await mkdir(folder, { recursive: true }); await writeFile(join(folder, "SKILL.md"), "# Skill\n", "utf8"); - httpMocks.apiRequestForm.mockResolvedValueOnce({ + mockPublishResponse({ ok: true, skillId: "skill_1", versionId: "ver_1", @@ -432,15 +508,7 @@ describe("cmdPublish", () => { forkOf: "@openclaw/demo@1.2.3", }); - const publishCall = httpMocks.apiRequestForm.mock.calls.find((call) => { - const req = call[1] as { path?: string } | undefined; - return req?.path === "/api/v1/skills"; - }); - if (!publishCall) throw new Error("Missing publish call"); - const publishForm = (publishCall[1] as { form?: FormData }).form as FormData; - const payloadEntry = publishForm.get("payload"); - if (typeof payloadEntry !== "string") throw new Error("Missing publish payload"); - expect(JSON.parse(payloadEntry).forkOf).toEqual({ + expect(publishPayload().forkOf).toEqual({ slug: "demo", ownerHandle: "openclaw", version: "1.2.3", @@ -459,7 +527,7 @@ describe("cmdPublish", () => { await writeFile(join(folder, "notes.md"), "notes\n", "utf8"); await writeFile(join(folder, "skill-card.md"), "# Generated card\n", "utf8"); - httpMocks.apiRequestForm.mockResolvedValueOnce({ + mockPublishResponse({ ok: true, skillId: "skill_1", versionId: "ver_1", @@ -473,14 +541,8 @@ describe("cmdPublish", () => { tags: "latest", }); - const publishCall = httpMocks.apiRequestForm.mock.calls.find((call) => { - const req = call[1] as { path?: string } | undefined; - return req?.path === "/api/v1/skills"; - }); - if (!publishCall) throw new Error("Missing publish call"); - const publishForm = (publishCall[1] as { form?: FormData }).form as FormData; - const files = publishForm.getAll("files") as Array; - expect(files.map((file) => file.name ?? "").sort()).toEqual(["SKILL.md", "notes.md"]); + const files = publishPayload().files as Array<{ path: string }>; + expect(files.map((file) => file.path).sort()).toEqual(["SKILL.md", "notes.md"]); } finally { await rm(workdir, { recursive: true, force: true }); } @@ -493,7 +555,7 @@ describe("cmdPublish", () => { await mkdir(folder, { recursive: true }); await writeFile(join(folder, "SKILL.md"), "# Skill\n", "utf8"); - httpMocks.apiRequestForm.mockResolvedValueOnce({ + mockPublishResponse({ ok: true, skillId: "skill_1", versionId: "ver_2", @@ -505,11 +567,7 @@ describe("cmdPublish", () => { tags: "latest", }); - expect(httpMocks.apiRequestForm).toHaveBeenCalledWith( - expect.anything(), - expect.objectContaining({ path: "/api/v1/skills", method: "POST" }), - expect.anything(), - ); + expect(publishPayload()).toMatchObject({ changelog: "" }); } finally { await rm(workdir, { recursive: true, force: true }); } @@ -524,7 +582,7 @@ describe("cmdPublish", () => { await writeFile(join(folder, "SKILL.md"), "# Skill\n", "utf8"); await writeFile(join(folder, "notes.md"), "ignored notes\n", "utf8"); - httpMocks.apiRequestForm.mockResolvedValueOnce({ + mockPublishResponse({ ok: true, skillId: "skill_1", versionId: "ver_1", @@ -538,14 +596,8 @@ describe("cmdPublish", () => { tags: "latest", }); - const publishCall = httpMocks.apiRequestForm.mock.calls.find((call) => { - const req = call[1] as { path?: string } | undefined; - return req?.path === "/api/v1/skills"; - }); - if (!publishCall) throw new Error("Missing publish call"); - const publishForm = (publishCall[1] as { form?: FormData }).form as FormData; - const files = publishForm.getAll("files") as Array; - expect(files.map((file) => file.name ?? "")).toEqual(["SKILL.md"]); + const files = publishPayload().files as Array<{ path: string }>; + expect(files.map((file) => file.path)).toEqual(["SKILL.md"]); } finally { await rm(workdir, { recursive: true, force: true }); } @@ -558,7 +610,7 @@ describe("cmdPublish", () => { await mkdir(folder, { recursive: true }); await writeFile(join(folder, "SKILL.md"), "# Skill\n", "utf8"); - httpMocks.apiRequestForm.mockResolvedValueOnce({ + mockPublishResponse({ ok: true, skillId: "skill_1", versionId: "ver_2", @@ -572,15 +624,7 @@ describe("cmdPublish", () => { tags: "latest", }); - const publishCall = httpMocks.apiRequestForm.mock.calls.find((call) => { - const req = call[1] as { path?: string } | undefined; - return req?.path === "/api/v1/skills"; - }); - if (!publishCall) throw new Error("Missing publish call"); - const publishForm = (publishCall[1] as { form?: FormData }).form as FormData; - const payloadEntry = publishForm.get("payload"); - if (typeof payloadEntry !== "string") throw new Error("Missing publish payload"); - const payload = JSON.parse(payloadEntry); + const payload = publishPayload(); expect(payload.ownerHandle).toBe("openclaw"); expect(payload.sourceOwnerHandle).toBe("me"); expect(payload.migrateOwner).toBe(true); @@ -620,7 +664,7 @@ describe("cmdPublish", () => { await writeFile(join(folder, "SKILL.md"), "# Skill\n", "utf8"); mockDefaultApiRequest("steipete"); - httpMocks.apiRequestForm.mockResolvedValueOnce({ + mockPublishResponse({ ok: true, skillId: "skill_1", versionId: "ver_1", @@ -636,15 +680,7 @@ describe("cmdPublish", () => { sourcePath: "skills/source-skill", }); - const publishCall = httpMocks.apiRequestForm.mock.calls.find((call) => { - const req = call[1] as { path?: string } | undefined; - return req?.path === "/api/v1/skills"; - }); - if (!publishCall) throw new Error("Missing publish call"); - const publishForm = (publishCall[1] as { form?: FormData }).form as FormData; - const payloadEntry = publishForm.get("payload"); - if (typeof payloadEntry !== "string") throw new Error("Missing publish payload"); - const payload = JSON.parse(payloadEntry); + const payload = publishPayload(); expect(payload.source).toEqual({ kind: "github", url: "https://github.com/NVIDIA/skills", @@ -691,28 +727,57 @@ describe("cmdPublish", () => { }); function publishPayload() { - const publishCall = httpMocks.apiRequestForm.mock.calls.find((call) => { - const request = call[1] as { path?: string } | undefined; - return request?.path === "/api/v1/skills"; + const publishCall = httpMocks.apiRequest.mock.calls.find((call) => { + const request = call[1] as { method?: string; path?: string } | undefined; + return request?.method === "POST" && request.path === "/api/v1/skills"; }); if (!publishCall) throw new Error("Missing publish call"); - const form = (publishCall[1] as { form?: FormData }).form; - const payload = form?.get("payload"); - if (typeof payload !== "string") throw new Error("Missing publish payload"); - return JSON.parse(payload) as Record; + const body = (publishCall[1] as { body?: unknown }).body; + if (!body || typeof body !== "object" || Array.isArray(body)) { + throw new Error("Missing publish payload"); + } + return body as Record; +} + +function mockPublishResponse(response: Record) { + publishResponse = response; } function mockDefaultApiRequest(whoamiHandle: string | null = "me") { + publishResponse = { + ok: true, + skillId: "skill_1", + versionId: "ver_1", + publicationStatus: "published", + }; + let uploadIndex = 0; httpMocks.apiRequest.mockReset(); httpMocks.apiRequest.mockImplementation(async (_registry: unknown, request: unknown) => { if (isWhoamiRequest(request)) { return { user: { handle: whoamiHandle } }; } + const args = request as { method?: string; path?: string }; + if (args.method === "POST" && args.path === "/api/v1/skills/-/upload-url") { + uploadIndex += 1; + return { + uploadUrl: `https://upload.local/${uploadIndex}`, + uploadTicket: `skillPublishUploadTickets:${uploadIndex}`, + }; + } + if (args.method === "POST" && args.path === "/api/v1/skills") { + return publishResponse; + } return { match: null, latestVersion: null, }; }); + let storageIndex = 0; + httpMocks.uploadBinary.mockReset(); + httpMocks.uploadBinary.mockImplementation(async () => { + storageIndex += 1; + return { storageId: `storage:${storageIndex}` }; + }); } function isWhoamiRequest(request: unknown) { diff --git a/packages/clawhub/src/cli/commands/publish.ts b/packages/clawhub/src/cli/commands/publish.ts index ac506802..b12a045e 100644 --- a/packages/clawhub/src/cli/commands/publish.ts +++ b/packages/clawhub/src/cli/commands/publish.ts @@ -1,11 +1,13 @@ import { readFile, readdir, stat } from "node:fs/promises"; import { basename, join, resolve } from "node:path"; import semver from "semver"; -import { apiRequest, apiRequestForm, registryUrl } from "../../http.js"; +import { apiRequest, registryUrl, uploadBinary } from "../../http.js"; import { ApiRoutes, + ApiUploadFileResponseSchema, ApiV1PublishResponseSchema, ApiV1SkillResolveResponseSchema, + ApiV1SkillUploadUrlResponseSchema, ApiV1WhoamiResponseSchema, } from "../../schema/index.js"; import { hashSkillFiles, listSkillFiles } from "../../skills.js"; @@ -168,40 +170,81 @@ export async function cmdPublish( options.migrateOwner && publishOwnerHandle ? sourceOwnerHandle || explicitSourceOwnerHandle || (await getDefaultOwnerHandle(token)) : undefined; - const form = new FormData(); - form.set( - "payload", - JSON.stringify({ - slug, - displayName, - ownerHandle: publishOwnerHandle, - ...(publishSourceOwnerHandle ? { sourceOwnerHandle: publishSourceOwnerHandle } : {}), - ...(options.migrateOwner ? { migrateOwner: true } : {}), - version, - changelog, - acceptLicenseTerms: true, - tags, - ...(options.categories !== undefined ? { categories } : {}), - ...(options.topics !== undefined ? { topics } : {}), - ...(source ? { source } : {}), - ...(forkOf ? { forkOf } : {}), - }), - ); + const fileHashes = new Map(hashed.files.map((file) => [file.path, file])); + const uploadedFiles = [] as Array<{ + path: string; + size: number; + storageId: string; + sha256: string; + contentType?: string; + uploadTicket: string; + }>; let index = 0; for (const file of filesOnDisk) { index += 1; if (spinner) spinner.text = `Uploading ${file.relPath} (${index}/${filesOnDisk.length})`; - const blob = new Blob([Buffer.from(file.bytes)], { - type: file.contentType ?? "application/octet-stream", + const hash = fileHashes.get(file.relPath); + if (!hash) fail(`Unable to hash ${file.relPath}`); + const contentType = file.contentType ?? "application/octet-stream"; + const { uploadUrl, uploadTicket } = await apiRequest( + registry, + { + method: "POST", + path: ApiRoutes.skillUploadUrl, + token, + body: { + path: file.relPath, + size: hash.size, + sha256: hash.sha256, + contentType, + }, + }, + ApiV1SkillUploadUrlResponseSchema, + ); + const { storageId } = await uploadBinary( + { + url: uploadUrl, + bytes: file.bytes, + contentType, + token, + }, + ApiUploadFileResponseSchema, + ); + uploadedFiles.push({ + path: file.relPath, + size: hash.size, + storageId, + sha256: hash.sha256, + contentType, + uploadTicket, }); - form.append("files", blob, file.relPath); } if (spinner) spinner.text = `Publishing ${slug}@${version}`; - const result = await apiRequestForm( + const result = await apiRequest( registry, - { method: "POST", path: ApiRoutes.skills, token, form }, + { + method: "POST", + path: ApiRoutes.skills, + token, + body: { + slug, + displayName, + ownerHandle: publishOwnerHandle, + ...(publishSourceOwnerHandle ? { sourceOwnerHandle: publishSourceOwnerHandle } : {}), + ...(options.migrateOwner ? { migrateOwner: true } : {}), + version, + changelog, + acceptLicenseTerms: true, + tags, + ...(options.categories !== undefined ? { categories } : {}), + ...(options.topics !== undefined ? { topics } : {}), + ...(source ? { source } : {}), + ...(forkOf ? { forkOf } : {}), + files: uploadedFiles, + }, + }, ApiV1PublishResponseSchema, ); diff --git a/packages/clawhub/src/http.bun.test.ts b/packages/clawhub/src/http.bun.test.ts index 98764b0b..71122bdb 100644 --- a/packages/clawhub/src/http.bun.test.ts +++ b/packages/clawhub/src/http.bun.test.ts @@ -210,4 +210,23 @@ describe("bun http client", () => { true, ); }); + + it("keeps binary upload bearer tokens out of curl arguments", async () => { + const { client, spawnImpl } = createBunClient({ + spawnImpl: () => ({ status: 0, stdout: '{"storageId":"storage:1"}\n200', stderr: "" }), + mkdtempValue: "/tmp/clawhub-binary-upload", + }); + + await client.uploadBinary({ + url: "https://upload.example/file", + bytes: new Uint8Array([1, 2, 3]), + contentType: "application/octet-stream", + token: "clh_secret", + }); + + const [, args, options] = spawnImpl.mock.calls[0] as [string, string[], { input?: string }]; + expect(args).toContain("@-"); + expect(args.join(" ")).not.toContain("clh_secret"); + expect(options.input).toBe("Authorization: Bearer clh_secret\n"); + }); }); diff --git a/packages/clawhub/src/http.ts b/packages/clawhub/src/http.ts index 99dba4a6..781b572d 100644 --- a/packages/clawhub/src/http.ts +++ b/packages/clawhub/src/http.ts @@ -58,6 +58,7 @@ type BinaryUploadArgs = { url: string; bytes: Uint8Array; contentType?: string; + token?: string; retryCount?: number; }; @@ -310,6 +311,7 @@ export function createHttpClient(options: HttpClientOptions = {}): HttpClient { const headers: Record = {}; if (args.contentType) headers["Content-Type"] = args.contentType; + if (args.token) headers.Authorization = `Bearer ${args.token}`; const response = await fetchWithTimeout( deps, args.url, @@ -842,6 +844,7 @@ async function uploadBinaryViaCurl( >, args: BinaryUploadArgs, ) { + if (args.token && /[\r\n]/.test(args.token)) throw new Error("Invalid API token"); const tempDir = await deps.mkdtempImpl(join(deps.tmpdirPath, "clawhub-upload-")); try { const filePath = join(tempDir, "upload.bin"); @@ -858,9 +861,13 @@ async function uploadBinaryViaCurl( "POST", ]; if (args.contentType) curlArgs.push("-H", `Content-Type: ${args.contentType}`); + if (args.token) curlArgs.push("-H", "@-"); curlArgs.push("--data-binary", `@${filePath}`, args.url); - const result = deps.spawnSyncImpl("curl", curlArgs, { encoding: "utf8" }); + const result = deps.spawnSyncImpl("curl", curlArgs, { + encoding: "utf8", + input: args.token ? `Authorization: Bearer ${args.token}\n` : undefined, + }); if (result.status !== 0) { throw new Error(result.stderr || "curl failed"); } diff --git a/packages/clawhub/src/schema/routes.ts b/packages/clawhub/src/schema/routes.ts index 303477c5..5fd5e48b 100644 --- a/packages/clawhub/src/schema/routes.ts +++ b/packages/clawhub/src/schema/routes.ts @@ -18,6 +18,7 @@ export const ApiRoutes = { publishTokenMint: "/api/v1/publish/token/mint", publishAttempts: "/api/v1/publish/attempts", skills: "/api/v1/skills", + skillUploadUrl: "/api/v1/skills/-/upload-url", trending: "/api/v1/trending", skillsSh: "/api/v1/skills-sh", skillScans: "/api/v1/skills/-/scan", diff --git a/packages/clawhub/src/schema/schemas.ts b/packages/clawhub/src/schema/schemas.ts index fd423ac7..c2c92251 100644 --- a/packages/clawhub/src/schema/schemas.ts +++ b/packages/clawhub/src/schema/schemas.ts @@ -71,12 +71,25 @@ export const ApiUploadFileResponseSchema = type({ storageId: "string", }); +export const ApiV1SkillUploadUrlRequestSchema = type({ + path: "string", + size: "number", + sha256: "string", + contentType: "string?", +}); + +export const ApiV1SkillUploadUrlResponseSchema = type({ + uploadUrl: "string", + uploadTicket: "string", +}); + export const CliPublishFileSchema = type({ path: "string", size: "number", storageId: "string", sha256: "string", contentType: "string?", + uploadTicket: "string?", }); export type CliPublishFile = (typeof CliPublishFileSchema)[inferred]; diff --git a/packages/schema/dist/packages.d.ts b/packages/schema/dist/packages.d.ts index 3b194fd1..fa70fcd6 100644 --- a/packages/schema/dist/packages.d.ts +++ b/packages/schema/dist/packages.d.ts @@ -333,6 +333,7 @@ export declare const ServerPackagePublishRequestSchema: import("arktype/internal storageId: string; sha256: string; contentType?: string | undefined; + uploadTicket?: string | undefined; }[]; }, {}>; export type ServerPackagePublishRequest = (typeof ServerPackagePublishRequestSchema)[inferred]; diff --git a/packages/schema/dist/routes.d.ts b/packages/schema/dist/routes.d.ts index dfcb9318..d92b6302 100644 --- a/packages/schema/dist/routes.d.ts +++ b/packages/schema/dist/routes.d.ts @@ -17,6 +17,7 @@ export declare const ApiRoutes: { readonly publishTokenMint: "/api/v1/publish/token/mint"; readonly publishAttempts: "/api/v1/publish/attempts"; readonly skills: "/api/v1/skills"; + readonly skillUploadUrl: "/api/v1/skills/-/upload-url"; readonly trending: "/api/v1/trending"; readonly skillsSh: "/api/v1/skills-sh"; readonly skillScans: "/api/v1/skills/-/scan"; diff --git a/packages/schema/dist/routes.js b/packages/schema/dist/routes.js index f72b617a..ac1bdbbb 100644 --- a/packages/schema/dist/routes.js +++ b/packages/schema/dist/routes.js @@ -17,6 +17,7 @@ export const ApiRoutes = { publishTokenMint: "/api/v1/publish/token/mint", publishAttempts: "/api/v1/publish/attempts", skills: "/api/v1/skills", + skillUploadUrl: "/api/v1/skills/-/upload-url", trending: "/api/v1/trending", skillsSh: "/api/v1/skills-sh", skillScans: "/api/v1/skills/-/scan", diff --git a/packages/schema/dist/routes.js.map b/packages/schema/dist/routes.js.map index fce71e6a..7f7ceac0 100644 --- a/packages/schema/dist/routes.js.map +++ b/packages/schema/dist/routes.js.map @@ -1 +1 @@ -{"version":3,"file":"routes.js","sourceRoot":"","sources":["../src/routes.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,eAAe,GAAG;IAC7B,QAAQ,EAAE,eAAe;IACzB,MAAM,EAAE,aAAa;IACrB,KAAK,EAAE,YAAY;IACnB,YAAY,EAAE,oBAAoB;IAClC,SAAS,EAAE,iBAAiB;IAC5B,YAAY,EAAE,qBAAqB;IACnC,UAAU,EAAE,kBAAkB;IAC9B,mBAAmB,EAAE,4BAA4B;IACjD,cAAc,EAAE,uBAAuB;IACvC,gBAAgB,EAAE,yBAAyB;CACnC,CAAC;AAEX,MAAM,CAAC,MAAM,SAAS,GAAG;IACvB,MAAM,EAAE,gBAAgB;IACxB,OAAO,EAAE,iBAAiB;IAC1B,QAAQ,EAAE,kBAAkB;IAC5B,gBAAgB,EAAE,4BAA4B;IAC9C,eAAe,EAAE,0BAA0B;IAC3C,MAAM,EAAE,gBAAgB;IACxB,QAAQ,EAAE,kBAAkB;IAC5B,QAAQ,EAAE,mBAAmB;IAC7B,UAAU,EAAE,uBAAuB;IACnC,OAAO,EAAE,iBAAiB;IAC1B,aAAa,EAAE,wBAAwB;IACvC,QAAQ,EAAE,kBAAkB;IAC5B,WAAW,EAAE,sBAAsB;IACnC,aAAa,EAAE,wBAAwB;IACvC,UAAU,EAAE,oBAAoB;IAChC,WAAW,EAAE,uBAAuB;IACpC,iBAAiB,EAAE,sBAAsB;IACzC,gBAAgB,EAAE,qBAAqB;IACvC,cAAc,EAAE,0BAA0B;IAC1C,KAAK,EAAE,eAAe;IACtB,SAAS,EAAE,mBAAmB;IAC9B,UAAU,EAAE,oBAAoB;IAChC,KAAK,EAAE,eAAe;IACtB,aAAa,EAAE,wBAAwB;IACvC,MAAM,EAAE,gBAAgB;IACxB,YAAY,EAAE,uBAAuB;CAC7B,CAAC"} \ No newline at end of file +{"version":3,"file":"routes.js","sourceRoot":"","sources":["../src/routes.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,eAAe,GAAG;IAC7B,QAAQ,EAAE,eAAe;IACzB,MAAM,EAAE,aAAa;IACrB,KAAK,EAAE,YAAY;IACnB,YAAY,EAAE,oBAAoB;IAClC,SAAS,EAAE,iBAAiB;IAC5B,YAAY,EAAE,qBAAqB;IACnC,UAAU,EAAE,kBAAkB;IAC9B,mBAAmB,EAAE,4BAA4B;IACjD,cAAc,EAAE,uBAAuB;IACvC,gBAAgB,EAAE,yBAAyB;CACnC,CAAC;AAEX,MAAM,CAAC,MAAM,SAAS,GAAG;IACvB,MAAM,EAAE,gBAAgB;IACxB,OAAO,EAAE,iBAAiB;IAC1B,QAAQ,EAAE,kBAAkB;IAC5B,gBAAgB,EAAE,4BAA4B;IAC9C,eAAe,EAAE,0BAA0B;IAC3C,MAAM,EAAE,gBAAgB;IACxB,cAAc,EAAE,6BAA6B;IAC7C,QAAQ,EAAE,kBAAkB;IAC5B,QAAQ,EAAE,mBAAmB;IAC7B,UAAU,EAAE,uBAAuB;IACnC,OAAO,EAAE,iBAAiB;IAC1B,aAAa,EAAE,wBAAwB;IACvC,QAAQ,EAAE,kBAAkB;IAC5B,WAAW,EAAE,sBAAsB;IACnC,aAAa,EAAE,wBAAwB;IACvC,UAAU,EAAE,oBAAoB;IAChC,WAAW,EAAE,uBAAuB;IACpC,iBAAiB,EAAE,sBAAsB;IACzC,gBAAgB,EAAE,qBAAqB;IACvC,cAAc,EAAE,0BAA0B;IAC1C,KAAK,EAAE,eAAe;IACtB,SAAS,EAAE,mBAAmB;IAC9B,UAAU,EAAE,oBAAoB;IAChC,KAAK,EAAE,eAAe;IACtB,aAAa,EAAE,wBAAwB;IACvC,MAAM,EAAE,gBAAgB;IACxB,YAAY,EAAE,uBAAuB;CAC7B,CAAC"} \ No newline at end of file diff --git a/packages/schema/dist/schemas.d.ts b/packages/schema/dist/schemas.d.ts index 3a54a88d..2f97fd42 100644 --- a/packages/schema/dist/schemas.d.ts +++ b/packages/schema/dist/schemas.d.ts @@ -55,12 +55,23 @@ export declare const ApiCliUploadUrlResponseSchema: import("arktype/internal/var export declare const ApiUploadFileResponseSchema: import("arktype/internal/variants/object.ts").ObjectType<{ storageId: string; }, {}>; +export declare const ApiV1SkillUploadUrlRequestSchema: import("arktype/internal/variants/object.ts").ObjectType<{ + path: string; + size: number; + sha256: string; + contentType?: string | undefined; +}, {}>; +export declare const ApiV1SkillUploadUrlResponseSchema: import("arktype/internal/variants/object.ts").ObjectType<{ + uploadUrl: string; + uploadTicket: string; +}, {}>; export declare const CliPublishFileSchema: import("arktype/internal/variants/object.ts").ObjectType<{ path: string; size: number; storageId: string; sha256: string; contentType?: string | undefined; + uploadTicket?: string | undefined; }, {}>; export type CliPublishFile = (typeof CliPublishFileSchema)[inferred]; export declare const PublishSourceSchema: import("arktype/internal/variants/object.ts").ObjectType<{ @@ -104,6 +115,7 @@ export declare const CliPublishRequestSchema: import("arktype/internal/variants/ storageId: string; sha256: string; contentType?: string | undefined; + uploadTicket?: string | undefined; }[]; }, {}>; export type CliPublishRequest = (typeof CliPublishRequestSchema)[inferred]; diff --git a/packages/schema/dist/schemas.js b/packages/schema/dist/schemas.js index 9de33b92..b204dd04 100644 --- a/packages/schema/dist/schemas.js +++ b/packages/schema/dist/schemas.js @@ -53,12 +53,23 @@ export const ApiCliUploadUrlResponseSchema = type({ export const ApiUploadFileResponseSchema = type({ storageId: "string", }); +export const ApiV1SkillUploadUrlRequestSchema = type({ + path: "string", + size: "number", + sha256: "string", + contentType: "string?", +}); +export const ApiV1SkillUploadUrlResponseSchema = type({ + uploadUrl: "string", + uploadTicket: "string", +}); export const CliPublishFileSchema = type({ path: "string", size: "number", storageId: "string", sha256: "string", contentType: "string?", + uploadTicket: "string?", }); export const PublishSourceSchema = type({ kind: '"github"', diff --git a/packages/schema/dist/schemas.js.map b/packages/schema/dist/schemas.js.map index 3e07dba5..68714038 100644 --- a/packages/schema/dist/schemas.js.map +++ b/packages/schema/dist/schemas.js.map @@ -1 +1 @@ -{"version":3,"file":"schemas.js","sourceRoot":"","sources":["../src/schemas.ts"],"names":[],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"schemas.js","sourceRoot":"","sources":["../src/schemas.ts"],"names":[],"mappings":"AAAA,OAAO,EAAiB,IAAI,EAAE,MAAM,SAAS,CAAC;AAC9C,OAAO,EAAE,0BAA0B,EAAE,MAAM,cAAc,CAAC;AAE1D,MAAM,CAAC,MAAM,kBAAkB,GAAG,IAAI,CAAC;IACrC,QAAQ,EAAE,QAAQ;IAClB,KAAK,EAAE,SAAS;CACjB,CAAC,CAAC;AAGH,MAAM,CAAC,MAAM,qBAAqB,GAAG,IAAI,CAAC;IACxC,OAAO,EAAE,QAAQ;IACjB,QAAQ,EAAE,SAAS;IACnB,aAAa,EAAE,SAAS;CACzB,CAAC,CAAC,EAAE,CAAC;IACJ,QAAQ,EAAE,QAAQ;IAClB,QAAQ,EAAE,SAAS;IACnB,aAAa,EAAE,SAAS;CACzB,CAAC,CAAC;AAGH,MAAM,CAAC,MAAM,cAAc,GAAG,IAAI,CAAC;IACjC,OAAO,EAAE,GAAG;IACZ,MAAM,EAAE;QACN,UAAU,EAAE;YACV,OAAO,EAAE,aAAa;YACtB,WAAW,EAAE,QAAQ;YACrB,WAAW,EAAE,SAAS;YACtB,SAAS,EAAE,SAAS;YACpB,MAAM,EAAE,UAAU;YAClB,SAAS,EAAE,SAAS;SACrB;KACF;CACF,CAAC,CAAC;AAGH,MAAM,CAAC,MAAM,0BAA0B,GAAG,IAAI,CAAC;IAC7C,IAAI,EAAE;QACJ,MAAM,EAAE,aAAa;KACtB;CACF,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,uBAAuB,GAAG,IAAI,CAAC;IAC1C,OAAO,EAAE,IAAI,CAAC;QACZ,IAAI,EAAE,SAAS;QACf,WAAW,EAAE,cAAc;QAC3B,WAAW,EAAE,SAAS;QACtB,OAAO,EAAE,cAAc;QACvB,KAAK,EAAE,QAAQ;KAChB,CAAC,CAAC,KAAK,EAAE;CACX,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,0BAA0B,GAAG,IAAI,CAAC;IAC7C,aAAa,EAAE,IAAI,CAAC;QAClB,OAAO,EAAE,QAAQ;KAClB,CAAC,CAAC,QAAQ,EAAE;IACb,KAAK,EAAE,eAAe;CACvB,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,6BAA6B,GAAG,IAAI,CAAC;IAChD,SAAS,EAAE,QAAQ;IACnB,YAAY,EAAE,QAAQ;CACvB,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,2BAA2B,GAAG,IAAI,CAAC;IAC9C,SAAS,EAAE,QAAQ;CACpB,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,gCAAgC,GAAG,IAAI,CAAC;IACnD,IAAI,EAAE,QAAQ;IACd,IAAI,EAAE,QAAQ;IACd,MAAM,EAAE,QAAQ;IAChB,WAAW,EAAE,SAAS;CACvB,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,iCAAiC,GAAG,IAAI,CAAC;IACpD,SAAS,EAAE,QAAQ;IACnB,YAAY,EAAE,QAAQ;CACvB,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,oBAAoB,GAAG,IAAI,CAAC;IACvC,IAAI,EAAE,QAAQ;IACd,IAAI,EAAE,QAAQ;IACd,SAAS,EAAE,QAAQ;IACnB,MAAM,EAAE,QAAQ;IAChB,WAAW,EAAE,SAAS;IACtB,YAAY,EAAE,SAAS;CACxB,CAAC,CAAC;AAGH,MAAM,CAAC,MAAM,mBAAmB,GAAG,IAAI,CAAC;IACtC,IAAI,EAAE,UAAU;IAChB,GAAG,EAAE,QAAQ;IACb,IAAI,EAAE,QAAQ;IACd,GAAG,EAAE,QAAQ;IACb,MAAM,EAAE,QAAQ;IAChB,IAAI,EAAE,QAAQ;IACd,UAAU,EAAE,QAAQ;CACrB,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,uBAAuB,GAAG,IAAI,CAAC;IAC1C,IAAI,EAAE,QAAQ;IACd,WAAW,EAAE,QAAQ;IACrB,WAAW,EAAE,SAAS;IACtB,iBAAiB,EAAE,SAAS;IAC5B,YAAY,EAAE,UAAU;IACxB,OAAO,EAAE,QAAQ;IACjB,SAAS,EAAE,QAAQ;IACnB,kBAAkB,EAAE,UAAU;IAC9B,IAAI,EAAE,WAAW;IACjB,UAAU,EAAE,WAAW;IACvB,MAAM,EAAE,WAAW;IACnB,MAAM,EAAE,mBAAmB,CAAC,QAAQ,EAAE;IACtC,MAAM,EAAE,IAAI,CAAC;QACX,IAAI,EAAE,QAAQ;QACd,WAAW,EAAE,SAAS;QACtB,OAAO,EAAE,SAAS;KACnB,CAAC,CAAC,QAAQ,EAAE;IACb,KAAK,EAAE,oBAAoB,CAAC,KAAK,EAAE;CACpC,CAAC,CAAC;AAGH,MAAM,CAAC,MAAM,2BAA2B,GAAG,IAAI,CAAC;IAC9C,EAAE,EAAE,MAAM;IACV,OAAO,EAAE,QAAQ;IACjB,SAAS,EAAE,QAAQ;IACnB,MAAM,EAAE,wBAAwB;IAChC,IAAI,EAAE,SAAS;IACf,OAAO,EAAE,SAAS;IAClB,iBAAiB,EAAE,wBAAwB;IAC3C,SAAS,EAAE,SAAS;CACrB,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,2BAA2B,GAAG,IAAI,CAAC;IAC9C,IAAI,EAAE,QAAQ;IACd,MAAM,EAAE,SAAS;CAClB,CAAC,CAAC;AAGH,MAAM,CAAC,MAAM,+BAA+B,GAAG,IAAI,CAAC;IAClD,EAAE,EAAE,MAAM;IACV,iBAAiB,EAAE,SAAS;CAC7B,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,6BAA6B,GAAG,IAAI,CAAC;IAChD,KAAK,EAAE,IAAI,CAAC,EAAE,OAAO,EAAE,QAAQ,EAAE,CAAC,CAAC,EAAE,CAAC,MAAM,CAAC;IAC7C,aAAa,EAAE,IAAI,CAAC,EAAE,OAAO,EAAE,QAAQ,EAAE,CAAC,CAAC,EAAE,CAAC,MAAM,CAAC;CACtD,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,sCAAsC,GAAG,IAAI,CAAC;IACzD,EAAE,EAAE,MAAM;IACV,IAAI,EAAE,QAAQ;IACd,WAAW,EAAE,WAAW;IACxB,OAAO,EAAE;QACP,OAAO,EAAE,QAAQ;QACjB,WAAW,EAAE,QAAQ;KACtB;CACF,CAAC;KACC,EAAE,CAAC;IACF,EAAE,EAAE,MAAM;IACV,IAAI,EAAE,QAAQ;IACd,WAAW,EAAE,UAAU;IACvB,MAAM,EAAE;QACN,IAAI,EAAE,QAAQ;QACd,IAAI,EAAE,QAAQ;QACd,MAAM,EAAE,QAAQ;QAChB,WAAW,EAAE,QAAQ;QACrB,SAAS,EAAE,QAAQ;KACpB;CACF,CAAC;KACD,EAAE,CAAC;IACF,EAAE,EAAE,OAAO;IACX,IAAI,EAAE,QAAQ;IACd,MAAM,EACJ,oLAAoL;IACtL,OAAO,EAAE,QAAQ;IACjB,MAAM,EAAE,QAAQ;CACjB,CAAC,CAAC;AAIL,MAAM,CAAC,MAAM,+BAA+B,GAAG,IAAI,CAAC;IAClD,GAAG,EAAE,QAAQ;IACb,KAAK,EAAE,QAAQ;IACf,WAAW,EAAE,QAAQ;IACrB,OAAO,EAAE,QAAQ;IACjB,KAAK,EAAE;QACL,MAAM,EAAE,QAAQ;QAChB,SAAS,EAAE,QAAQ;KACpB;IACD,UAAU,EAAE,QAAQ;IACpB,UAAU,EAAE,QAAQ;IACpB,YAAY,EAAE,QAAQ;IACtB,iBAAiB,EAAE,QAAQ;IAC3B,SAAS,EAAE,QAAQ;IACnB,QAAQ,EAAE,QAAQ;IAClB,QAAQ,EAAE;QACR,OAAO,EAAE,sBAAsB;QAC/B,MAAM,EAAE,WAAW;QACnB,SAAS,EAAE,QAAQ;QACnB,SAAS,EAAE,QAAQ;KACpB;IACD,OAAO,EAAE;QACP,EAAE,EAAE,MAAM;QACV,IAAI,EAAE,QAAQ;QACd,WAAW,EAAE,UAAU;QACvB,MAAM,EAAE;YACN,IAAI,EAAE,QAAQ;YACd,IAAI,EAAE,QAAQ;YACd,MAAM,EAAE,QAAQ;YAChB,WAAW,EAAE,QAAQ;YACrB,SAAS,EAAE,QAAQ;SACpB;KACF;CACF,CAAC,CAAC;AAGH,MAAM,CAAC,MAAM,gCAAgC,GAAG,IAAI,CAAC;IACnD,KAAK,EAAE,WAAW;IAClB,IAAI,EAAE,QAAQ;IACd,WAAW,EAAE,SAAS;IACtB,SAAS,EAAE,SAAS;IACpB,UAAU,EAAE,cAAc;IAC1B,gBAAgB,EAAE,SAAS;IAC3B,UAAU,EAAE,SAAS;IACrB,SAAS,EAAE,SAAS;IACpB,YAAY,EAAE,SAAS;IACvB,WAAW,EAAE,wBAAwB;IACrC,UAAU,EAAE,SAAS;IACrB,OAAO,EAAE,SAAS;IAClB,uEAAuE;IACvE,MAAM,EAAE,SAAS;IACjB,SAAS,EAAE,SAAS;CACrB,CAAC;KACC,EAAE,CAAC;IACF,KAAK,EAAE,kBAAkB;IACzB,WAAW,EAAE,QAAQ;IACrB,OAAO,EAAE,SAAS;CACnB,CAAC;KACD,EAAE,CAAC;IACF,6EAA6E;IAC7E,KAAK,EAAE,IAAI,CAAC;QACV,MAAM,EAAE,QAAQ;QAChB,KAAK,EAAE,QAAQ;QACf,MAAM,EAAE,IAAI,CAAC;YACX,IAAI,EAAE,QAAQ;YACd,OAAO,EAAE,cAAc;SACxB,CAAC,CAAC,KAAK,EAAE;KACX,CAAC,CAAC,KAAK,EAAE;CACX,CAAC,CAAC;AAGL,MAAM,CAAC,MAAM,oCAAoC,GAAG,IAAI,CAAC;IACvD,EAAE,EAAE,MAAM;CACX,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,yBAAyB,GAAG,IAAI,CAAC;IAC5C,IAAI,EAAE;QACJ,MAAM,EAAE,aAAa;QACrB,WAAW,EAAE,cAAc;QAC3B,KAAK,EAAE,cAAc;QACrB,IAAI,EAAE,kCAAkC;KACzC;CACF,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,6BAA6B,GAAG,IAAI,CAAC;IAChD,KAAK,EAAE,IAAI,CAAC;QACV,MAAM,EAAE,QAAQ;QAChB,MAAM,EAAE,aAAa;QACrB,WAAW,EAAE,cAAc;QAC3B,IAAI,EAAE,cAAc;QACpB,IAAI,EAAE,kCAAkC;KACzC,CAAC,CAAC,KAAK,EAAE;IACV,KAAK,EAAE,QAAQ;CAChB,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,kCAAkC,GAAG,IAAI,CAAC;IACrD,EAAE,EAAE,MAAM;IACV,WAAW,EAAE,QAAQ;IACrB,MAAM,EAAE,QAAQ;IAChB,OAAO,EAAE,MAAM;IACf,OAAO,EAAE,OAAO;CACjB,CAAC,CAAC;AAGH,MAAM,CAAC,MAAM,kCAAkC,GAAG,IAAI,CAAC;IACrD,EAAE,EAAE,MAAM;IACV,WAAW,EAAE,QAAQ;IACrB,MAAM,EAAE,QAAQ;IAChB,MAAM,EAAE,SAAS;IACjB,OAAO,EAAE,SAAS;IAClB,YAAY,EAAE,QAAQ;IACtB,cAAc,EAAE,QAAQ;IACxB,WAAW,EAAE,QAAQ;CACtB,CAAC,CAAC;AAGH,MAAM,CAAC,MAAM,mCAAmC,GAAG,IAAI,CAAC;IACtD,EAAE,EAAE,MAAM;IACV,WAAW,EAAE,QAAQ;IACrB,MAAM,EAAE,QAAQ;IAChB,MAAM,EAAE,SAAS;IACjB,WAAW,EAAE,SAAS;IACtB,YAAY,EAAE,QAAQ;IACtB,cAAc,EAAE,QAAQ;IACxB,WAAW,EAAE,QAAQ;IACrB,aAAa,EAAE,QAAQ;IACvB,oBAAoB,EAAE,QAAQ;IAC9B,iBAAiB,EAAE,SAAS;IAC5B,iBAAiB,EAAE,QAAQ;CAC5B,CAAC,CAAC;AAGH,MAAM,CAAC,MAAM,iCAAiC,GAAG,IAAI,CAAC;IACpD,EAAE,EAAE,MAAM;IACV,IAAI,EAAE,MAAM;IACZ,SAAS,EAAE,IAAI,CAAC;QACd,KAAK,EAAE,QAAQ;QACf,SAAS,EAAE,QAAQ;QACnB,SAAS,EAAE,aAAa;KACzB,CAAC;IACF,OAAO,EAAE,QAAQ;IACjB,QAAQ,EAAE,QAAQ;IAClB,UAAU,EAAE,aAAa;CAC1B,CAAC,CAAC;AAGH,MAAM,CAAC,MAAM,yBAAyB,GAAG,IAAI,CAAC;IAC5C,OAAO,EAAE,IAAI,CAAC;QACZ,IAAI,EAAE,SAAS;QACf,WAAW,EAAE,cAAc;QAC3B,WAAW,EAAE,SAAS;QACtB,OAAO,EAAE,cAAc;QACvB,OAAO,EAAE,cAAc;QACvB,KAAK,EAAE,QAAQ;QACf,SAAS,EAAE,SAAS;QACpB,SAAS,EAAE,SAAS;QACpB,KAAK,EAAE,IAAI,CAAC;YACV,MAAM,EAAE,cAAc;YACtB,WAAW,EAAE,cAAc;YAC3B,KAAK,EAAE,cAAc;SACtB,CAAC;aACC,EAAE,CAAC,MAAM,CAAC;aACV,QAAQ,EAAE;KACd,CAAC,CAAC,KAAK,EAAE;CACX,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,4BAA4B,GAAG,IAAI,CAAC;IAC/C,KAAK,EAAE,IAAI,CAAC;QACV,IAAI,EAAE,QAAQ;QACd,WAAW,EAAE,QAAQ;QACrB,OAAO,EAAE,cAAc;QACvB,WAAW,EAAE,cAAc;QAC3B,MAAM,EAAE,WAAW;QACnB,IAAI,EAAE,SAAS;QACf,KAAK,EAAE,SAAS;QAChB,SAAS,EAAE,QAAQ;QACnB,SAAS,EAAE,QAAQ;QACnB,aAAa,EAAE,IAAI,CAAC;YAClB,OAAO,EAAE,QAAQ;YACjB,SAAS,EAAE,QAAQ;YACnB,SAAS,EAAE,QAAQ;YACnB,OAAO,EAAE,0BAA0B,CAAC,EAAE,CAAC,MAAM,CAAC,CAAC,QAAQ,EAAE;SAC1D,CAAC,CAAC,QAAQ,EAAE;QACb,QAAQ,EAAE,IAAI,CAAC;YACb,KAAK,EAAE,IAAI,CAAC;gBACV,GAAG,EAAE,QAAQ;gBACb,QAAQ,EAAE,SAAS;aACpB,CAAC,CAAC,KAAK,EAAE;YACV,EAAE,EAAE,gBAAgB;YACpB,OAAO,EAAE,gBAAgB;SAC1B,CAAC;aACC,EAAE,CAAC,MAAM,CAAC;aACV,QAAQ,EAAE;KACd,CAAC,CAAC,KAAK,EAAE;IACV,UAAU,EAAE,aAAa;CAC1B,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,wBAAwB,GAAG,IAAI,CAAC;IAC3C,KAAK,EAAE,IAAI,CAAC;QACV,IAAI,EAAE,QAAQ;QACd,WAAW,EAAE,QAAQ;QACrB,OAAO,EAAE,cAAc;QACvB,WAAW,EAAE,cAAc;QAC3B,MAAM,EAAE,WAAW;QACnB,IAAI,EAAE,SAAS;QACf,KAAK,EAAE,SAAS;QAChB,SAAS,EAAE,QAAQ;QACnB,SAAS,EAAE,QAAQ;KACpB,CAAC,CAAC,EAAE,CAAC,MAAM,CAAC;IACb,aAAa,EAAE,IAAI,CAAC;QAClB,OAAO,EAAE,QAAQ;QACjB,SAAS,EAAE,QAAQ;QACnB,SAAS,EAAE,QAAQ;QACnB,OAAO,EAAE,0BAA0B,CAAC,EAAE,CAAC,MAAM,CAAC,CAAC,QAAQ,EAAE;KAC1D,CAAC,CAAC,EAAE,CAAC,MAAM,CAAC;IACb,QAAQ,EAAE,IAAI,CAAC;QACb,KAAK,EAAE,IAAI,CAAC;YACV,GAAG,EAAE,QAAQ;YACb,QAAQ,EAAE,SAAS;SACpB,CAAC,CAAC,KAAK,EAAE;QACV,EAAE,EAAE,gBAAgB;QACpB,OAAO,EAAE,gBAAgB;KAC1B,CAAC;SACC,EAAE,CAAC,MAAM,CAAC;SACV,QAAQ,EAAE;IACb,KAAK,EAAE,IAAI,CAAC;QACV,MAAM,EAAE,aAAa;QACrB,WAAW,EAAE,cAAc;QAC3B,KAAK,EAAE,cAAc;KACtB,CAAC,CAAC,EAAE,CAAC,MAAM,CAAC;IACb,UAAU,EAAE,IAAI,CAAC;QACf,YAAY,EAAE,SAAS;QACvB,gBAAgB,EAAE,SAAS;QAC3B,OAAO,EAAE,mCAAmC;QAC5C,WAAW,EAAE,WAAW;QACxB,SAAS,EAAE,cAAc;QACzB,aAAa,EAAE,cAAc;QAC7B,OAAO,EAAE,cAAc;KACxB,CAAC;SACC,EAAE,CAAC,MAAM,CAAC;SACV,QAAQ,EAAE;CACd,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,kCAAkC,GAAG,IAAI,CAAC;IACrD,UAAU,EAAE,IAAI,CAAC;QACf,YAAY,EAAE,SAAS;QACvB,gBAAgB,EAAE,SAAS;QAC3B,OAAO,EAAE,kCAAkC;QAC3C,WAAW,EAAE,UAAU;QACvB,SAAS,EAAE,cAAc;QACzB,aAAa,EAAE,cAAc;QAC7B,OAAO,EAAE,cAAc;QACvB,YAAY,EAAE,cAAc;QAC5B,QAAQ,EAAE,IAAI,CAAC;YACb,IAAI,EAAE,QAAQ;YACd,QAAQ,EAAE,0BAA0B;YACpC,IAAI,EAAE,QAAQ;YACd,IAAI,EAAE,QAAQ;YACd,OAAO,EAAE,QAAQ;YACjB,QAAQ,EAAE,QAAQ;SACnB,CAAC,CAAC,KAAK,EAAE;KACX,CAAC,CAAC,EAAE,CAAC,MAAM,CAAC;CACd,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,+BAA+B,GAAG,IAAI,CAAC;IAClD,KAAK,EAAE,WAAW;IAClB,MAAM,EAAE,QAAQ;IAChB,WAAW,EAAE,SAAS;CACvB,CAAC,CAAC;AAGH,MAAM,CAAC,MAAM,qCAAqC,GAAG,IAAI,CAAC;IACxD,EAAE,EAAE,MAAM;IACV,IAAI,EAAE,QAAQ;IACd,OAAO,EAAE,QAAQ;IACjB,OAAO,EAAE,QAAQ;IACjB,SAAS,EAAE,QAAQ;IACnB,cAAc,EAAE,SAAS;IACzB,kBAAkB,EAAE,aAAa;IACjC,WAAW,EAAE,SAAS;CACvB,CAAC,CAAC;AAIH,MAAM,CAAC,MAAM,uBAAuB,GAAG,IAAI,CAAC,gCAAgC,CAAC,CAAC;AAE9E,MAAM,CAAC,MAAM,4BAA4B,GAAG,IAAI,CAAC,eAAe,CAAC,CAAC;AAGlE,MAAM,CAAC,MAAM,2BAA2B,GAAG,uBAAuB,CAAC,EAAE,CAAC,OAAO,CAAC,CAAC;AAG/E,MAAM,CAAC,MAAM,uBAAuB,GAAG,IAAI,CAAC,8BAA8B,CAAC,CAAC;AAE5E,MAAM,CAAC,MAAM,4BAA4B,GAAG,IAAI,CAAC,kBAAkB,CAAC,CAAC;AAGrE,MAAM,CAAC,MAAM,2BAA2B,GAAG,uBAAuB,CAAC,EAAE,CAAC,OAAO,CAAC,CAAC;AAG/E,MAAM,CAAC,MAAM,wBAAwB,GAAG,IAAI,CAAC;IAC3C,OAAO,EAAE,SAAS;IAClB,OAAO,EAAE,QAAQ;CAClB,CAAC,CAAC;AAGH,MAAM,CAAC,MAAM,8BAA8B,GAAG,IAAI,CAAC;IACjD,EAAE,EAAE,MAAM;IACV,QAAQ,EAAE,SAAS;IACnB,eAAe,EAAE,SAAS;IAC1B,QAAQ,EAAE,QAAQ;IAClB,OAAO,EAAE,QAAQ;IACjB,WAAW,EAAE,QAAQ;CACtB,CAAC,CAAC;AAGH,MAAM,CAAC,MAAM,8BAA8B,GAAG,IAAI,CAAC;IACjD,EAAE,EAAE,MAAM;IACV,SAAS,EAAE,SAAS;IACpB,WAAW,EAAE,SAAS;IACtB,QAAQ,EAAE,QAAQ;IAClB,OAAO,EAAE,QAAQ;IACjB,MAAM,EAAE,uBAAuB;CAChC,CAAC,CAAC;AAGH,MAAM,CAAC,MAAM,8BAA8B,GAAG,IAAI,CAAC;IACjD,MAAM,EAAE,uBAAuB;IAC/B,IAAI,EAAE,SAAS;IACf,WAAW,EAAE,4BAA4B,CAAC,QAAQ,EAAE;CACrD,CAAC,CAAC;AAGH,MAAM,CAAC,MAAM,+BAA+B,GAAG,IAAI,CAAC;IAClD,MAAM,EAAE,uBAAuB;IAC/B,IAAI,EAAE,SAAS;IACf,WAAW,EAAE,4BAA4B,CAAC,QAAQ,EAAE;CACrD,CAAC,CAAC;AAGH,MAAM,CAAC,MAAM,kCAAkC,GAAG,IAAI,CAAC;IACrD,KAAK,EAAE,IAAI,CAAC;QACV,QAAQ,EAAE,QAAQ;QAClB,OAAO,EAAE,QAAQ;QACjB,cAAc,EAAE,cAAc;QAC9B,IAAI,EAAE,QAAQ;QACd,WAAW,EAAE,QAAQ;QACrB,OAAO,EAAE,cAAc;QACvB,MAAM,EAAE,cAAc;QACtB,MAAM,EAAE,uBAAuB;QAC/B,SAAS,EAAE,QAAQ;QACnB,QAAQ,EAAE,IAAI,CAAC;YACb,MAAM,EAAE,QAAQ;YAChB,MAAM,EAAE,cAAc;YACtB,WAAW,EAAE,cAAc;SAC5B,CAAC;QACF,SAAS,EAAE,cAAc;QACzB,SAAS,EAAE,cAAc;QACzB,UAAU,EAAE,cAAc;QAC1B,WAAW,EAAE,4BAA4B,CAAC,EAAE,CAAC,MAAM,CAAC,CAAC,QAAQ,EAAE;KAChE,CAAC,CAAC,KAAK,EAAE;IACV,UAAU,EAAE,aAAa;IACzB,IAAI,EAAE,SAAS;CAChB,CAAC,CAAC;AAGH,MAAM,CAAC,MAAM,oCAAoC,GAAG,IAAI,CAAC;IACvD,EAAE,EAAE,MAAM;IACV,QAAQ,EAAE,QAAQ;IAClB,OAAO,EAAE,QAAQ;IACjB,MAAM,EAAE,uBAAuB;IAC/B,WAAW,EAAE,QAAQ;IACrB,WAAW,EAAE,4BAA4B,CAAC,QAAQ,EAAE;CACrD,CAAC,CAAC;AAIH,MAAM,CAAC,MAAM,kCAAkC,GAAG,IAAI,CAAC;IACrD,KAAK,EAAE,IAAI,CAAC;QACV,QAAQ,EAAE,QAAQ;QAClB,OAAO,EAAE,QAAQ;QACjB,cAAc,EAAE,cAAc;QAC9B,IAAI,EAAE,QAAQ;QACd,WAAW,EAAE,QAAQ;QACrB,OAAO,EAAE,cAAc;QACvB,OAAO,EAAE,QAAQ;QACjB,MAAM,EAAE,uBAAuB;QAC/B,SAAS,EAAE,QAAQ;QACnB,SAAS,EAAE,IAAI,CAAC;YACd,MAAM,EAAE,QAAQ;YAChB,MAAM,EAAE,cAAc;YACtB,WAAW,EAAE,cAAc;SAC5B,CAAC;QACF,UAAU,EAAE,cAAc;QAC1B,UAAU,EAAE,cAAc;QAC1B,cAAc,EAAE,cAAc;QAC9B,WAAW,EAAE,4BAA4B,CAAC,EAAE,CAAC,MAAM,CAAC,CAAC,QAAQ,EAAE;KAChE,CAAC,CAAC,KAAK,EAAE;IACV,UAAU,EAAE,aAAa;IACzB,IAAI,EAAE,SAAS;CAChB,CAAC,CAAC;AAGH,MAAM,CAAC,MAAM,qCAAqC,GAAG,IAAI,CAAC;IACxD,EAAE,EAAE,MAAM;IACV,QAAQ,EAAE,QAAQ;IAClB,OAAO,EAAE,QAAQ;IACjB,MAAM,EAAE,uBAAuB;IAC/B,WAAW,EAAE,4BAA4B,CAAC,QAAQ,EAAE;CACrD,CAAC,CAAC;AAIH,MAAM,CAAC,MAAM,8BAA8B,GAAG,IAAI,CAAC;IACjD,EAAE,EAAE,MAAM;IACV,IAAI,EAAE,QAAQ;IACd,OAAO,EAAE,QAAQ;IACjB,OAAO,EAAE,QAAQ;IACjB,cAAc,EAAE,QAAQ;IACxB,KAAK,EAAE,QAAQ;IACf,aAAa,EAAE,SAAS;CACzB,CAAC,CAAC,EAAE,CAAC;IACJ,EAAE,EAAE,MAAM;IACV,IAAI,EAAE,QAAQ;IACd,OAAO,EAAE,QAAQ;IACjB,OAAO,EAAE,QAAQ;IACjB,iBAAiB,EAAE,QAAQ;IAC3B,KAAK,EAAE,SAAS;IAChB,SAAS,EAAE,SAAS;IACpB,aAAa,EAAE,SAAS;CACzB,CAAC,CAAC;AAGH,MAAM,CAAC,MAAM,iCAAiC,GAAG,IAAI,CAAC;IACpD,WAAW,EAAE,QAAQ;IACrB,MAAM,EAAE,QAAQ;IAChB,MAAM,EAAE,UAAU;IAClB,iBAAiB,EAAE,SAAS;CAC7B,CAAC,CAAC;AAGH,MAAM,CAAC,MAAM,kCAAkC,GAAG,IAAI,CAAC;IACrD,EAAE,EAAE,MAAM;IACV,OAAO,EAAE,QAAQ;IACjB,IAAI,EAAE,QAAQ;IACd,WAAW,EAAE,QAAQ;IACrB,WAAW,EAAE,QAAQ;IACrB,MAAM,EAAE,SAAS;IACjB,SAAS,EAAE,SAAS;IACpB,iBAAiB,EAAE,QAAQ;CAC5B,CAAC,CAAC;AAGH,MAAM,CAAC,MAAM,0BAA0B,GAAG,IAAI,CAAC,yCAAyC,CAAC,CAAC;AAG1F,MAAM,CAAC,MAAM,0BAA0B,GAAG,IAAI,CAAC;IAC7C,IAAI,EAAE,UAAU;CACjB,CAAC,CAAC,EAAE,CAAC;IACJ,IAAI,EAAE,aAAa;IACnB,IAAI,EAAE,QAAQ;IACd,WAAW,EAAE,SAAS;IACtB,OAAO,EAAE,SAAS;CACnB,CAAC,CAAC;AAGH,MAAM,CAAC,MAAM,iCAAiC,GAAG,IAAI,CAAC;IACpD,MAAM,EAAE,0BAA0B;IAClC,MAAM,EAAE,UAAU;CACnB,CAAC,CAAC;AAGH,MAAM,CAAC,MAAM,yBAAyB,GAAG,IAAI,CAAC;IAC5C,WAAW,EAAE,QAAQ;IACrB,qBAAqB,EAAE,UAAU;IACjC,QAAQ,EAAE,aAAa;IACvB,OAAO,EAAE,QAAQ;IACjB,iBAAiB,EAAE,UAAU;IAC7B,IAAI,EAAE,QAAQ;CACf,CAAC,CAAC;AAGH,MAAM,CAAC,MAAM,kCAAkC,GAAG,IAAI,CAAC;IACrD,EAAE,EAAE,MAAM;IACV,MAAM,EAAE,QAAQ;IAChB,KAAK,EAAE,SAAS;IAChB,MAAM,EAAE,0BAA0B;IAClC,UAAU,EAAE,sBAAsB;IAClC,MAAM,EAAE,SAAS;IACjB,aAAa,EAAE,UAAU;IACzB,KAAK,EAAE,yBAAyB,CAAC,QAAQ,EAAE;CAC5C,CAAC,CAAC;AAGH,MAAM,CAAC,MAAM,kCAAkC,GAAG,IAAI,CAAC;IACrD,EAAE,EAAE,MAAM;IACV,MAAM,EAAE,QAAQ;IAChB,KAAK,EAAE,SAAS;IAChB,MAAM,EAAE,0BAA0B;IAClC,UAAU,EAAE,sBAAsB;IAClC,MAAM,EAAE,SAAS;IACjB,WAAW,EAAE,UAAU;IACvB,QAAQ,EAAE,UAAU;IACpB,MAAM,EAAE,UAAU;IAClB,KAAK,EAAE,yBAAyB,CAAC,QAAQ,EAAE;IAC3C,SAAS,EAAE,SAAS;IACpB,SAAS,EAAE,QAAQ;IACnB,SAAS,EAAE,QAAQ;IACnB,WAAW,EAAE,SAAS;CACvB,CAAC,CAAC;AAGH,MAAM,CAAC,MAAM,oCAAoC,GAAG,IAAI,CAAC;IACvD,MAAM,EAAE,QAAQ;IAChB,UAAU,EAAE,sBAAsB;IAClC,MAAM,EAAE,SAAS;IACjB,MAAM,EAAE,0BAA0B;IAClC,QAAQ,EAAE,UAAU;IACpB,SAAS,EAAE,QAAQ;IACnB,SAAS,EAAE,QAAQ;IACnB,WAAW,EAAE,SAAS;IACtB,WAAW,EAAE,UAAU;CACxB,CAAC,CAAC;AAIH,MAAM,CAAC,MAAM,sCAAsC,GAAG,IAAI,CAAC;IACzD,IAAI,EAAE,sBAAsB;IAC5B,MAAM,EAAE,cAAc;IACtB,SAAS,EAAE,SAAS;IACpB,MAAM,EAAE,UAAU;CACnB,CAAC,CAAC;AAIH,MAAM,CAAC,MAAM,uCAAuC,GAAG,IAAI,CAAC;IAC1D,EAAE,EAAE,MAAM;IACV,IAAI,EAAE,qBAAqB;IAC3B,MAAM,EAAE,QAAQ;IAChB,aAAa,EAAE,QAAQ;IACvB,OAAO,EAAE,QAAQ;IACjB,MAAM,EAAE,UAAU;IAClB,UAAU,EAAE,aAAa;IACzB,IAAI,EAAE,SAAS;IACf,WAAW,EAAE,UAAU;CACxB,CAAC,CAAC;AAIH,MAAM,CAAC,MAAM,uCAAuC,GAAG,IAAI,CAAC;IAC1D,MAAM,EAAE,UAAU;CACnB,CAAC,CAAC;AAIH,MAAM,CAAC,MAAM,wCAAwC,GAAG,IAAI,CAAC;IAC3D,EAAE,EAAE,MAAM;IACV,KAAK,EAAE,QAAQ;IACf,MAAM,EAAE,QAAQ;IAChB,OAAO,EAAE,QAAQ;IACjB,SAAS,EAAE,QAAQ;IACnB,MAAM,EAAE,QAAQ;IAChB,OAAO,EAAE,QAAQ;IACjB,QAAQ,EAAE,QAAQ;IAClB,IAAI,EAAE,SAAS;IACf,YAAY,EAAE,UAAU;CACzB,CAAC,CAAC;AAIH,MAAM,CAAC,MAAM,gCAAgC,GAAG,IAAI,CAAC;IACnD,IAAI,EAAE,sBAAsB;IAC5B,MAAM,EAAE,cAAc;IACtB,SAAS,EAAE,SAAS;IACpB,MAAM,EAAE,UAAU;CACnB,CAAC,CAAC;AAGH,MAAM,CAAC,MAAM,iCAAiC,GAAG,IAAI,CAAC;IACpD,EAAE,EAAE,MAAM;IACV,IAAI,EAAE,qBAAqB;IAC3B,MAAM,EAAE,QAAQ;IAChB,aAAa,EAAE,QAAQ;IACvB,OAAO,EAAE,QAAQ;IACjB,MAAM,EAAE,UAAU;IAClB,UAAU,EAAE,aAAa;IACzB,IAAI,EAAE,SAAS;IACf,WAAW,EAAE,UAAU;CACxB,CAAC,CAAC;AAGH,MAAM,CAAC,MAAM,sCAAsC,GAAG,IAAI,CAAC;IACzD,MAAM,EAAE,UAAU;CACnB,CAAC,CAAC;AAIH,MAAM,CAAC,MAAM,uCAAuC,GAAG,IAAI,CAAC;IAC1D,EAAE,EAAE,MAAM;IACV,KAAK,EAAE,QAAQ;IACf,MAAM,EAAE,QAAQ;IAChB,OAAO,EAAE,QAAQ;IACjB,SAAS,EAAE,QAAQ;IACnB,MAAM,EAAE,QAAQ;IAChB,OAAO,EAAE,QAAQ;IACjB,QAAQ,EAAE,QAAQ;IAClB,IAAI,EAAE,SAAS;IACf,YAAY,EAAE,UAAU;CACzB,CAAC,CAAC;AAIH,MAAM,CAAC,MAAM,sCAAsC,GAAG,IAAI,CAAC;IACzD,MAAM,EAAE,cAAc;IACtB,SAAS,EAAE,SAAS;IACpB,WAAW,EAAE,SAAS;IACtB,MAAM,EAAE,UAAU;CACnB,CAAC,CAAC;AAIH,MAAM,CAAC,MAAM,uCAAuC,GAAG,IAAI,CAAC;IAC1D,EAAE,EAAE,MAAM;IACV,MAAM,EAAE,SAAS;IACjB,KAAK,EAAE,QAAQ;IACf,WAAW,EAAE,QAAQ;IACrB,OAAO,EAAE,QAAQ;IACjB,SAAS,EAAE,QAAQ;IACnB,eAAe,EAAE,QAAQ;IACzB,MAAM,EAAE,QAAQ;IAChB,IAAI,EAAE,SAAS;IACf,MAAM,EAAE,aAAa;IACrB,YAAY,EAAE,EAAE,UAAU,EAAE,QAAQ,EAAE;IACtC,aAAa,EAAE,IAAI,CAAC;QAClB,IAAI,EAAE,QAAQ;QACd,MAAM,EAAE,QAAQ;KACjB,CAAC,CAAC,KAAK,EAAE;CACX,CAAC,CAAC;AAIH,MAAM,CAAC,MAAM,mCAAmC,GAAG,IAAI,CAAC;IACtD,KAAK,EAAE,IAAI,CAAC;QACV,OAAO,EAAE,QAAQ;QACjB,SAAS,EAAE,QAAQ;QACnB,SAAS,EAAE,QAAQ;QACnB,eAAe,EAAE,qBAAqB;KACvC,CAAC,CAAC,KAAK,EAAE;IACV,UAAU,EAAE,aAAa;CAC1B,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,oBAAoB,GAAG,IAAI,CAAC;IACvC,MAAM,EAAE,4DAA4D;IACpE,WAAW,EAAE,SAAS;IACtB,SAAS,EAAE,aAAa;IACxB,KAAK,EAAE,aAAa;CACrB,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,+BAA+B,GAAG,IAAI,CAAC;IAClD,OAAO,EAAE,IAAI,CAAC;QACZ,OAAO,EAAE,QAAQ;QACjB,SAAS,EAAE,QAAQ;QACnB,SAAS,EAAE,QAAQ;QACnB,eAAe,EAAE,qBAAqB;QACtC,OAAO,EAAE,0BAA0B,CAAC,EAAE,CAAC,MAAM,CAAC,CAAC,QAAQ,EAAE;QACzD,KAAK,EAAE,UAAU;QACjB,QAAQ,EAAE,oBAAoB,CAAC,QAAQ,EAAE;KAC1C,CAAC,CAAC,EAAE,CAAC,MAAM,CAAC;IACb,KAAK,EAAE,IAAI,CAAC;QACV,IAAI,EAAE,QAAQ;QACd,WAAW,EAAE,QAAQ;KACtB,CAAC,CAAC,EAAE,CAAC,MAAM,CAAC;CACd,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,+BAA+B,GAAG,IAAI,CAAC;IAClD,KAAK,EAAE,IAAI,CAAC,EAAE,OAAO,EAAE,QAAQ,EAAE,CAAC,CAAC,EAAE,CAAC,MAAM,CAAC;IAC7C,aAAa,EAAE,IAAI,CAAC,EAAE,OAAO,EAAE,QAAQ,EAAE,CAAC,CAAC,EAAE,CAAC,MAAM,CAAC;CACtD,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,8BAA8B,GAAG,IAAI,CAAC;IACjD,MAAM,EAAE,2BAA2B;IACnC,EAAE,EAAE,SAAS;IACb,QAAQ,EAAE,eAAe;IACzB,OAAO,EAAE,UAAU;IACnB,IAAI,EAAE,QAAQ;IACd,WAAW,EAAE,QAAQ;IACrB,OAAO,EAAE,QAAQ;IACjB,eAAe,EAAE,aAAa;IAC9B,oBAAoB,EAAE,aAAa;IACnC,mBAAmB,EAAE,aAAa;IAClC,OAAO,EAAE,QAAQ;IACjB,YAAY,EAAE,0BAA0B;IACxC,GAAG,EAAE,aAAa;IAClB,SAAS,EAAE,QAAQ;IACnB,IAAI,EAAE,SAAS;IACf,QAAQ,EAAE,SAAS;IACnB,UAAU,EAAE,SAAS;IACrB,QAAQ,EAAE,SAAS;IACnB,SAAS,EAAE,SAAS;CACrB,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,0BAA0B,GAAG,IAAI,CAAC;IAC7C,EAAE,EAAE,MAAM;IACV,OAAO,EAAE,QAAQ;IACjB,SAAS,EAAE,QAAQ;IACnB,MAAM,EAAE,wBAAwB;IAChC,IAAI,EAAE,SAAS;IACf,OAAO,EAAE,SAAS;IAClB,iBAAiB,EAAE,wBAAwB;IAC3C,SAAS,EAAE,SAAS;CACrB,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,yBAAyB,GAAG,IAAI,CAAC;IAC5C,EAAE,EAAE,MAAM;IACV,iBAAiB,EAAE,SAAS;CAC7B,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,2BAA2B,GAAG,IAAI,CAAC;IAC9C,EAAE,EAAE,MAAM;IACV,IAAI,EAAE,QAAQ;IACd,GAAG,EAAE,QAAQ;IACb,OAAO,EAAE,QAAQ;CAClB,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,8BAA8B,GAAG,IAAI,CAAC;IACjD,EAAE,EAAE,MAAM;IACV,IAAI,EAAE,QAAQ;IACd,YAAY,EAAE,QAAQ;CACvB,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,6BAA6B,GAAG,IAAI,CAAC;IAChD,EAAE,EAAE,MAAM;IACV,UAAU,EAAE,QAAQ;IACpB,UAAU,EAAE,QAAQ;CACrB,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,kCAAkC,GAAG,IAAI,CAAC;IACrD,EAAE,EAAE,MAAM;IACV,UAAU,EAAE,SAAS;IACrB,YAAY,EAAE,SAAS;IACvB,iBAAiB,EAAE,SAAS;IAC5B,SAAS,EAAE,SAAS;IACpB,SAAS,EAAE,SAAS;IACpB,WAAW,EAAE,UAAU;CACxB,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,mCAAmC,GAAG,IAAI,CAAC;IACtD,EAAE,EAAE,MAAM;IACV,SAAS,EAAE,SAAS;CACrB,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,+BAA+B,GAAG,IAAI,CAAC;IAClD,SAAS,EAAE,IAAI,CAAC;QACd,GAAG,EAAE,QAAQ;QACb,KAAK,EAAE,IAAI,CAAC;YACV,GAAG,EAAE,QAAQ;YACb,IAAI,EAAE,QAAQ;YACd,WAAW,EAAE,QAAQ;SACtB,CAAC;QACF,QAAQ,EAAE,IAAI,CAAC;YACb,GAAG,EAAE,QAAQ;YACb,MAAM,EAAE,aAAa;YACrB,WAAW,EAAE,aAAa;SAC3B,CAAC,CAAC,QAAQ,EAAE;QACb,MAAM,EAAE,IAAI,CAAC;YACX,GAAG,EAAE,QAAQ;YACb,MAAM,EAAE,aAAa;YACrB,WAAW,EAAE,aAAa;SAC3B,CAAC,CAAC,QAAQ,EAAE;QACb,OAAO,EAAE,SAAS;QAClB,WAAW,EAAE,QAAQ;QACrB,SAAS,EAAE,QAAQ;KACpB,CAAC,CAAC,KAAK,EAAE;CACX,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,0BAA0B,GAAG,IAAI,CAAC;IAC7C,EAAE,EAAE,MAAM;IACV,IAAI,EAAE,4BAA4B;CACnC,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,gCAAgC,GAAG,IAAI,CAAC;IACnD,EAAE,EAAE,MAAM;IACV,MAAM,EAAE,SAAS;IACjB,MAAM,EAAE,QAAQ;IAChB,MAAM,EAAE,aAAa;IACrB,cAAc,EAAE,aAAa;IAC7B,UAAU,EAAE,QAAQ;IACpB,OAAO,EAAE,SAAS;CACnB,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,uBAAuB,GAAG,IAAI,CAAC;IAC1C,EAAE,EAAE,MAAM;IACV,OAAO,EAAE,SAAS;IAClB,cAAc,EAAE,SAAS;CAC1B,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,yBAAyB,GAAG,IAAI,CAAC;IAC5C,EAAE,EAAE,MAAM;IACV,SAAS,EAAE,SAAS;IACpB,gBAAgB,EAAE,SAAS;CAC5B,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,sBAAsB,GAAG,IAAI,CAAC;IACzC,EAAE,EAAE,SAAS;IACb,IAAI,EAAE,yBAAyB;IAC/B,KAAK,EAAE,SAAS;IAChB,IAAI,EAAE,WAAW;IACjB,OAAO,EAAE,SAAS;IAClB,GAAG,EAAE,SAAS;IACd,OAAO,EAAE,SAAS;IAClB,MAAM,EAAE,SAAS;CAClB,CAAC,CAAC;AAGH,MAAM,CAAC,MAAM,mBAAmB,GAAG,IAAI,CAAC;IACtC,MAAM,EAAE,QAAQ;IAChB,OAAO,EAAE,WAAW;CACrB,CAAC,CAAC;AAGH,MAAM,CAAC,MAAM,wBAAwB,GAAG,IAAI,CAAC;IAC3C,WAAW,EAAE,WAAW;IACxB,SAAS,EAAE,WAAW;IACtB,OAAO,EAAE,SAAS;CACnB,CAAC,CAAC;AAGH,MAAM,CAAC,MAAM,qBAAqB,GAAG,IAAI,CAAC;IACxC,IAAI,EAAE,WAAW;IACjB,OAAO,EAAE,WAAW;IACpB,GAAG,EAAE,WAAW;IAChB,MAAM,EAAE,WAAW;CACpB,CAAC,CAAC;AAGH,MAAM,CAAC,MAAM,uBAAuB,GAAG,IAAI,CAAC;IAC1C,IAAI,EAAE,QAAQ;IACd,QAAQ,EAAE,UAAU;IACpB,WAAW,EAAE,SAAS;CACvB,CAAC,CAAC;AAGH,MAAM,CAAC,MAAM,2BAA2B,GAAG,IAAI,CAAC;IAC9C,IAAI,EAAE,QAAQ;IACd,IAAI,EAAE,+CAA+C;IACrD,OAAO,EAAE,SAAS;IAClB,GAAG,EAAE,SAAS;IACd,UAAU,EAAE,SAAS;CACtB,CAAC,CAAC;AAGH,MAAM,CAAC,MAAM,gBAAgB,GAAG,IAAI,CAAC;IACnC,QAAQ,EAAE,SAAS;IACnB,UAAU,EAAE,SAAS;IACrB,aAAa,EAAE,SAAS;IACxB,SAAS,EAAE,SAAS;CACrB,CAAC,CAAC;AAGH,MAAM,CAAC,MAAM,0BAA0B,GAAG,IAAI,CAAC;IAC7C,MAAM,EAAE,UAAU;IAClB,QAAQ,EAAE,SAAS;IACnB,UAAU,EAAE,SAAS;IACrB,KAAK,EAAE,SAAS;IAChB,QAAQ,EAAE,SAAS;IACnB,EAAE,EAAE,WAAW;IACf,OAAO,EAAE,SAAS;IAClB,QAAQ,EAAE,qBAAqB,CAAC,QAAQ,EAAE;IAC1C,OAAO,EAAE,sBAAsB,CAAC,KAAK,EAAE,CAAC,QAAQ,EAAE;IAClD,GAAG,EAAE,mBAAmB,CAAC,QAAQ,EAAE;IACnC,MAAM,EAAE,wBAAwB,CAAC,QAAQ,EAAE;IAC3C,OAAO,EAAE,uBAAuB,CAAC,KAAK,EAAE,CAAC,QAAQ,EAAE;IACnD,YAAY,EAAE,2BAA2B,CAAC,KAAK,EAAE,CAAC,QAAQ,EAAE;IAC5D,MAAM,EAAE,SAAS;IACjB,KAAK,EAAE,gBAAgB,CAAC,QAAQ,EAAE;CACnC,CAAC,CAAC;AA2BH,2FAA2F;AAC3F,MAAM,iBAAiB,GAAsB,IAAI,CAAC;AAClD,KAAK,iBAAiB,CAAC"} \ No newline at end of file diff --git a/packages/schema/src/routes.ts b/packages/schema/src/routes.ts index 7ab910ea..18995ee1 100644 --- a/packages/schema/src/routes.ts +++ b/packages/schema/src/routes.ts @@ -18,6 +18,7 @@ export const ApiRoutes = { publishTokenMint: "/api/v1/publish/token/mint", publishAttempts: "/api/v1/publish/attempts", skills: "/api/v1/skills", + skillUploadUrl: "/api/v1/skills/-/upload-url", trending: "/api/v1/trending", skillsSh: "/api/v1/skills-sh", skillScans: "/api/v1/skills/-/scan", diff --git a/packages/schema/src/schemas.ts b/packages/schema/src/schemas.ts index 96310cdd..cd4dba5b 100644 --- a/packages/schema/src/schemas.ts +++ b/packages/schema/src/schemas.ts @@ -65,12 +65,25 @@ export const ApiUploadFileResponseSchema = type({ storageId: "string", }); +export const ApiV1SkillUploadUrlRequestSchema = type({ + path: "string", + size: "number", + sha256: "string", + contentType: "string?", +}); + +export const ApiV1SkillUploadUrlResponseSchema = type({ + uploadUrl: "string", + uploadTicket: "string", +}); + export const CliPublishFileSchema = type({ path: "string", size: "number", storageId: "string", sha256: "string", contentType: "string?", + uploadTicket: "string?", }); export type CliPublishFile = (typeof CliPublishFileSchema)[inferred]; diff --git a/scripts/clawhub-cli-npm-release-check.test.mjs b/scripts/clawhub-cli-npm-release-check.test.mjs index e7a97d85..9295602d 100644 --- a/scripts/clawhub-cli-npm-release-check.test.mjs +++ b/scripts/clawhub-cli-npm-release-check.test.mjs @@ -4,7 +4,7 @@ import { spawnSync } from "node:child_process"; import { describe, expect, it } from "vitest"; describe("clawhub CLI npm release metadata check", () => { - const releaseTag = "v0.23.2"; + const releaseTag = "v0.23.3"; function runCheck(args) { const env = { ...process.env }; diff --git a/specs/security-moderation.md b/specs/security-moderation.md index 410ff951..9a5dbfcd 100644 --- a/specs/security-moderation.md +++ b/specs/security-moderation.md @@ -265,6 +265,20 @@ See also: [acceptable-usage.md](./acceptable-usage.md) for the marketplace polic - Skills directory supports an optional "Hide suspicious" filter to exclude active-but-flagged (`flagged.suspicious`) entries from browse/search results. +## Skill publish upload boundary + +- CLI skill publishing stages each file through the direct Convex HTTP surface, + keeping file bodies off the Vercel request path. Each file remains capped at + 10MB and the complete publish remains capped at 50MB. +- The server creates a short-lived, user-bound ticket before accepting a file. + The upload action enforces the declared path, byte size, content type, and + SHA-256 before attaching the resulting storage id to that ticket. +- JSON skill publishes must present the matching ticket for every staged file. + Ticket ownership and file metadata are revalidated, and ticket consumption is + committed atomically with the new skill version. +- Unconsumed staged files are deleted when their tickets expire. Failed + attachment attempts delete the just-created storage blob immediately. + ## Package publish upload boundary - Package publish is multipart-only. `POST /api/v1/packages` must reject JSON