Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 16 additions & 16 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ members = [
]

[workspace.package]
version = "0.2.66"
version = "0.2.67"
edition = "2024"
license = "MIT"
repository = "https://github.com/profullstack/c0mpute"
Expand Down
2 changes: 1 addition & 1 deletion apps/tui/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@c0mpute/tui",
"version": "0.2.66",
"version": "0.2.67",
"private": true,
"type": "module",
"bin": {
Expand Down
2 changes: 1 addition & 1 deletion apps/web/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "c0mpute",
"version": "0.2.66",
"version": "0.2.67",
"private": true,
"type": "module",
"scripts": {
Expand Down
16 changes: 14 additions & 2 deletions apps/web/src/app/api/plugins/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
loadAllPlugins,
tagline,
installCommand,
installedByDefault,
modelOptions,
} from "@/lib/plugins";

Expand All @@ -20,19 +21,30 @@ export function GET() {
// Omitted rather than sent as [] for the plugins that have no model to
// choose, which is most of them.
const models = modelOptions(p).map(({ id, model }) => ({ id, ...model }));
const install = installCommand(p);

return {
id: p.id,
name: p.name,
version: p.version,
kind: p.kind,
// available | partial | coming-soon. A coming-soon plugin is a spec
// with no implementation behind it yet.
status: p.status,
status_note: p.statusNote,
builtin: p.builtin,
installed_by_default: installedByDefault(p),
tagline: tagline(p),
description: p.description,
keywords: p.keywords ?? [],
homepage: p.homepage,
source: p.source,
install_command: installCommand(p),
install_url: `https://c0mpute.com/plugins/${p.id}/install.sh`,
// null when there is nothing to install: built in, coming soon, or no
// install.sh in the repo.
install_command: install,
install_url: install
? `https://c0mpute.com/plugins/${p.id}/install.sh`
: null,
web: `https://c0mpute.com/plugins/${p.id}`,
models: models.length > 0 ? models : undefined,
};
Expand Down
49 changes: 42 additions & 7 deletions apps/web/src/app/plugins/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
byCategory,
tagline,
installCommand,
installedByDefault,
modelOptions,
CATEGORY_LABELS,
CATEGORY_BLURBS,
Expand All @@ -16,7 +17,7 @@ import {
export const metadata = {
title: "plugins — c0mpute",
description:
"c0mpute workload plugins, network services and applications: AI inference, embeddings, image and video generation, speech and OCR; transcoding, rendering and streaming; crawling, storage and hosting. MIT-licensed, manifest-driven.",
"c0mpute workload plugins, network services and applications: AI inference, image and video generation, speech, transcoding, web fetching, workflow steps, webhooks and OpenDisk storage, with more marked coming soon. MIT-licensed, manifest-driven.",
alternates: { canonical: "https://c0mpute.com/plugins" },
};

Expand Down Expand Up @@ -64,6 +65,12 @@ export default function PluginsPage() {
<Badge label="application" /> is a product built <em>on</em>{" "}
c0mpute — useful, and not something the protocol depends on.
</p>
<p>
<Badge label="partial" /> runs today with a caveat noted on the
card.{" "}
<Badge label="coming soon" /> is a published spec with no
implementation yet — nothing to install or run.
</p>
</section>

{groups.map(({ category, plugins }) => (
Expand All @@ -74,6 +81,8 @@ export default function PluginsPage() {
<span className="text-[var(--color-dim)] font-normal">
{" "}
· {plugins.length}
{comingSoonCount(plugins) > 0 &&
` (${comingSoonCount(plugins)} coming soon)`}
</span>
</h2>
<p className="text-xs text-[var(--color-dim)]">
Expand Down Expand Up @@ -119,6 +128,10 @@ function kindLabel(p: PluginManifest): string {
return p.category === "applications" ? "application" : "service";
}

function comingSoonCount(plugins: PluginManifest[]): number {
return plugins.filter((p) => p.status === "coming-soon").length;
}

function Badge({ label }: { label: string }) {
return (
<span className="border border-[var(--color-rule)] rounded px-1.5 py-0.5 accent whitespace-nowrap">
Expand All @@ -134,15 +147,27 @@ function PluginCard({ p }: { p: PluginManifest }) {
: p.dispatch?.mode === "container"
? "container"
: "subprocess";
const comingSoon = p.status === "coming-soon";
const install = installCommand(p);

return (
<article className="border border-[var(--color-rule)] bg-[var(--color-card)] rounded p-5 space-y-3">
<article
className={`border border-[var(--color-rule)] bg-[var(--color-card)] rounded p-5 space-y-3${comingSoon ? " opacity-70" : ""}`}
>
<header className="flex items-baseline justify-between gap-3">
<h3 className="text-lg accent">
<span className="text-[var(--color-dim)]">[</span>
{p.id}
<span className="text-[var(--color-dim)]">]</span>{" "}
<span className="text-[var(--color-fg)]">{p.name}</span>
{p.status !== "available" && (
<>
{" "}
<span className="text-xs align-middle">
<Badge label={comingSoon ? "coming soon" : "partial"} />
</span>
</>
)}
</h3>
<span className="text-xs text-[var(--color-dim)] whitespace-nowrap">
v{p.version} · {kindLabel(p)} · {dispatchLabel}
Expand All @@ -161,14 +186,24 @@ function PluginCard({ p }: { p: PluginManifest }) {

<ModelList p={p} />

{p.dispatch?.mode === "in-process" ? (
<p className="text-xs accent">ships with c0mpute · installed by default</p>
) : (
<CodeBlock>{`$ ${installCommand(p)}`}</CodeBlock>
{p.status === "partial" && p.statusNote && (
<p className="text-xs text-[var(--color-dim)]">
<span className="accent">partial:</span> {p.statusNote}
</p>
)}

{comingSoon ? (
<p className="text-xs text-[var(--color-dim)]">
coming soon · spec only, not yet implemented
</p>
) : installedByDefault(p) ? (
<p className="text-xs accent">ships with c0mpute · installed by default</p>
) : install ? (
<CodeBlock>{`$ ${install}`}</CodeBlock>
) : null}

<footer className="flex flex-wrap gap-x-4 gap-y-1 text-xs text-[var(--color-dim)]">
{p.surfaces?.cli && (
{p.surfaces?.cli && !comingSoon && (
<span>
cli: <code className="text-[var(--color-fg)]">{p.surfaces.cli}</code>
</span>
Expand Down
2 changes: 1 addition & 1 deletion apps/web/src/app/releases/latest.json/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ export const dynamic = "force-dynamic";
* this in sync with the workspace version in `Cargo.toml` on each release so a
* freshly built CLI reports "already latest" rather than a phantom upgrade.
*/
const FALLBACK_VERSION = "0.2.66";
const FALLBACK_VERSION = "0.2.67";
const FALLBACK_MIN_REQUIRED = "0.0.1";

type Channel = "stable" | "beta" | "nightly";
Expand Down
35 changes: 29 additions & 6 deletions apps/web/src/components/webmcp-provider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -95,15 +95,38 @@ const TOOLS: WebMcpToolDescriptor[] = [
},
additionalProperties: false,
},
execute: (args) => {
execute: async (args) => {
const plugin =
typeof args.plugin === "string" ? args.plugin.trim() : "";
if (plugin) {
return json({
plugin,
command: `c0mpute plugin install ${plugin}`,
install_url: `https://c0mpute.com/plugins/${plugin}/install.sh`,
});
// Answer from the live listing, so a plugin that is built in or not
// built yet never gets an install command that would 404.
try {
const list = (await fetchJson("/api/plugins")) as {
plugins?: Array<{
id: string;
status?: string;
builtin?: boolean;
install_command?: string | null;
install_url?: string | null;
}>;
};
const found = list.plugins?.find((p) => p.id === plugin);
if (!found) return errorResult(`No plugin named "${plugin}".`);
if (found.status === "coming-soon") {
return json({ plugin, status: "coming-soon", note: "Not implemented yet; nothing to install." });
}
if (found.builtin) {
return json({ plugin, builtin: true, note: "Ships inside the c0mpute binary; no install needed." });
}
return json({
plugin,
command: found.install_command ?? null,
install_url: found.install_url ?? null,
});
} catch (e) {
return errorResult(`Failed to look up plugin: ${(e as Error).message}`);
}
}
return json({
command: "curl -fsSL https://c0mpute.com/install.sh | sh",
Expand Down
Loading
Loading