Official

market

The plugin marketplace: browse, install through an agent's scan, update and publish plugins, and the marketplace tools of agents in the Plugins workspace.

The app opens the listing; nothing installs until an agent in your Plugins workspace has read the files and you enable the plugin. In a terminal: cvg install convergence/market@0.1.0

Permissions in 0.1.0

  • Marketplace marketOfficial onlyUses the marketplace APIs. Only official plugins can have it.Search, read, install, update and publish marketplace plugins, and sign in to the marketplace
  • Manage plugins plugins.manageOfficial onlyReloads and enables plugins. Only official plugins can have it.Ask for the enable card of a plugin an agent installed
  • Control chats chats.controlHighCreates chats, sends prompts and cancels runs.Open the chat in the Plugins workspace where an agent scans, installs or updates a plugin
  • Provide agent tools tools.provideMediumGives tools to agents.Give agents in the Plugins workspace the marketplace tools
  • Read chats chats.readMediumReads your transcripts and chat lists.Find the agent, model and effort last used in the Plugins workspace
  • Show panels ui.slotsLowShows views in the listed parts of the window.Show the marketplace button, with its update count, and the marketplace pageSlots: railcenter

Files

remote.ts6.6 KB
// The shapes the marketplace host answers take, parsed at the host
// boundary. The registry is free-form JSON, so every schema stays loose:
// only the fields the page reads are named, everything else passes through
// untyped. A parse failure surfaces as the section's error, never a cast.
import * as z from "zod";

const versionSchema = z.looseObject({
  version: z.string(),
  permissions: z.unknown().optional(),
  yanked: z.boolean().optional(),
  malicious: z.boolean().optional(),
  publishedAt: z.string().optional(),
});
export type ListingVersion = z.infer<typeof versionSchema>;

const installedSchema = z.looseObject({
  version: z.string(),
  enabled: z.boolean(),
  folder: z.string(),
});
export type MarketInstalled = z.infer<typeof installedSchema>;

export const listingSchema = z.looseObject({
  id: z.string(),
  name: z.string().optional(),
  description: z.string().optional(),
  publisher: z
    .looseObject({ name: z.string().optional(), displayName: z.string().optional(), verified: z.boolean().optional() })
    .optional(),
  visibility: z.string().optional(),
  latestVersion: z.string().optional(),
  downloads: z.unknown().optional(),
  rating: z.looseObject({ average: z.number().optional(), count: z.number().optional() }).optional(),
  createdAt: z.string().optional(),
  basedOn: z.looseObject({ id: z.string(), version: z.string() }).nullable().optional(),
  mods: z.array(z.looseObject({ id: z.string(), name: z.string().optional() })).optional(),
  permissions: z.unknown().optional(),
  versions: z.array(versionSchema).optional(),
  installed: installedSchema.nullable().optional(),
});
export type Listing = z.infer<typeof listingSchema>;

export const searchAnswerSchema = z.looseObject({
  results: z.array(listingSchema).optional(),
  total: z.number().optional(),
});
export type SearchAnswer = z.infer<typeof searchAnswerSchema>;

export const releaseSchema = z.looseObject({
  id: z.string().optional(),
  version: z.string().optional(),
  files: z
    .array(
      z.looseObject({
        path: z.string(),
        size: z.number().optional(),
        binary: z.boolean().optional(),
        executable: z.boolean().optional(),
      }),
    )
    .optional(),
});
export type MarketRelease = z.infer<typeof releaseSchema>;

export const releaseFileSchema = z.looseObject({
  path: z.string().optional(),
  size: z.number().optional(),
  text: z.string().optional(),
  truncated: z.boolean().optional(),
  binary: z.boolean().optional(),
});
export type MarketReleaseFile = z.infer<typeof releaseFileSchema>;

export interface MarketComment {
  readonly id?: string;
  readonly user?: { readonly name?: string };
  readonly body?: string;
  readonly stars?: number;
  readonly createdAt?: string;
  readonly pluginVersion?: string;
  readonly replies?: readonly MarketComment[];
}
const commentSchema: z.ZodType<MarketComment> = z.looseObject({
  id: z.string().optional(),
  user: z.looseObject({ name: z.string().optional() }).optional(),
  body: z.string().optional(),
  stars: z.number().optional(),
  createdAt: z.string().optional(),
  pluginVersion: z.string().optional(),
  replies: z.array(z.lazy(() => commentSchema)).optional(),
});
export const commentsSchema = z.looseObject({ threads: z.array(commentSchema).optional() });
export type MarketComments = z.infer<typeof commentsSchema>;

export const categoriesSchema = z.looseObject({
  categories: z.array(z.looseObject({ id: z.string(), title: z.string() })),
});
export type MarketCategories = z.infer<typeof categoriesSchema>;

export const accountSchema = z.union([
  z.looseObject({
    signedIn: z.literal(false),
    registry: z.string(),
    error: z.string().optional(),
    expired: z.boolean().optional(),
  }),
  z.looseObject({
    signedIn: z.literal(true),
    registry: z.string(),
    account: z
      .looseObject({
        publisher: z.looseObject({ name: z.string().optional() }).optional(),
        user: z.looseObject({ email: z.string().optional() }).optional(),
      })
      .optional(),
  }),
]);
export type MarketAccount = z.infer<typeof accountSchema>;

export const updateItemSchema = z.object({
  id: z.string(),
  folder: z.string(),
  installed: z.string().optional(),
  latest: z.string().optional(),
  official: z.boolean().optional(),
  direct: z.boolean().optional(),
  malicious: z.boolean().optional(),
  yanked: z.boolean().optional(),
  reason: z.string().optional(),
});
export type MarketUpdateItem = z.infer<typeof updateItemSchema>;
export const updatesSchema = z.looseObject({ updates: z.array(updateItemSchema).optional() });

export const loginSchema = z.looseObject({
  userCode: z.string(),
  verificationUri: z.string(),
  verificationUriComplete: z.string().nullable().optional(),
});
export type MarketLogin = z.infer<typeof loginSchema>;

export const draftPluginSchema = z.looseObject({
  name: z.string(),
  id: z.string(),
  version: z.string().optional(),
  official: z.boolean().optional(),
  changed: z.boolean().optional(),
  suggested: z.boolean().optional(),
  reasons: z.array(z.string()).optional(),
});
export type DraftPlugin = z.infer<typeof draftPluginSchema>;
export const bugDraftSchema = z.looseObject({
  plugins: z.array(draftPluginSchema).optional(),
  note: z.string().optional(),
  registry: z.string().optional(),
  signedIn: z.boolean().optional(),
  appVersion: z.string().optional(),
});
export type BugDraft = z.infer<typeof bugDraftSchema>;

export const attachmentSchema = z.looseObject({
  id: z.string(),
  kind: z.string(),
  title: z.string(),
  name: z.string(),
  size: z.number(),
  defaultOn: z.boolean().optional(),
  removed: z.array(z.looseObject({ line: z.number(), kind: z.string() })).optional(),
  text: z.string().optional(),
  path: z.string().optional(),
});
export type BugAttachment = z.infer<typeof attachmentSchema>;
export const bugPreviewSchema = z.looseObject({
  plugins: z.array(z.looseObject({ folder: z.string(), id: z.string() })).optional(),
  attachments: z.array(attachmentSchema).optional(),
  note: z.string().optional(),
});
export type BugPreview = z.infer<typeof bugPreviewSchema>;

export const sentReportSchema = z.looseObject({
  id: z.string(),
  number: z.number(),
  text: z.string(),
  plugins: z.array(z.looseObject({ id: z.string() })).optional(),
});
export type SentReport = z.infer<typeof sentReportSchema>;
export const bugReportsSchema = z.looseObject({ reports: z.array(sentReportSchema).optional() });

// The settings a release declares (`plugin.json` `contributes.settings`),
// for scopes a setting fills (MARKETPLACE.md §15.3 rule 6).
export const settingsContributesSchema = z.looseObject({
  contributes: z.looseObject({ settings: z.unknown() }).optional(),
});

Versions

VersionPublishedPlugin APISizePermissionsStatus
0.1.0latestOct 5, 2026>=2 <344.9 KB6 permissionsListed

Reviews and comments

0 threads · 0 reviews

No comments yet.