Diffinery

codician/diffinerypublic

Open-source, agent-first GitHub and GitHub Actions alternative that runs on your own Cloudflare account: git hosting, pull requests, code review and drop-in GitHub Actions YAML on Workers, Durable Objects, R2 and Containers.

32 lines · 1.7 KBRaw History
1import { unzip, zip } from "../lib/archive.js";
2
3/** The URL the plugins' "Diffinery URL" setting (userConfig `url`) defaults to in their sources. */
4export const PLUGIN_DEFAULT_URL = "https://diffinery.com";
5const MANIFEST = ".claude-plugin/plugin.json";
6// The fixed timestamp scripts/pack-plugins.py gives every entry, so a rewrite of the same zip is the same bytes.
7const STAMP = Date.UTC(2026, 0, 1) / 1000;
8
9const hex = (b: ArrayBuffer) => [...new Uint8Array(b)].map((x) => x.toString(16).padStart(2, "0")).join("");
10
11/**
12 * A Claude Code plugin zip as `origin` serves it. A self-hosted instance's copy defaults the plugin's
13 * Diffinery URL setting to that instance, so a plugin uploaded to claude.ai or set up by `diffinery setup`
14 * from there reaches it with nothing to configure; diffinery.com serves the zip as packed.
15 */
16export async function pluginZipFor(origin: string, packed: Uint8Array, sha256: string): Promise<{ bytes: Uint8Array; sha256: string }> {
17 if (origin === PLUGIN_DEFAULT_URL) return { bytes: packed, sha256 };
18 const entries = await unzip(packed);
19 let changed = false;
20 const out = entries.map((e) => {
21 if ("dir" in e || e.path !== MANIFEST) return e;
22 const manifest = JSON.parse(new TextDecoder().decode(e.content)) as { userConfig?: { url?: { default?: unknown } } };
23 const url = manifest.userConfig?.url;
24 if (!url || url.default !== PLUGIN_DEFAULT_URL) return e;
25 url.default = origin;
26 changed = true;
27 return { ...e, content: new TextEncoder().encode(`${JSON.stringify(manifest, null, 2)}\n`) };
28 });
29 if (!changed) return { bytes: packed, sha256 };
30 const bytes = await zip(out, STAMP);
31 return { bytes, sha256: hex(await crypto.subtle.digest("SHA-256", bytes as Uint8Array<ArrayBuffer>)) };
32}