mirror of
https://github.com/esengine/DeepSeek-Reasonix.git
synced 2026-11-07 10:45:59 -10:00
feat(crash-dashboard): fold skill/MCP registry into the admin backend
Give the owner one admin console instead of a standalone, undeployed registry worker. The registry source moves under the crash worker as a second data plane (REGISTRY_DB); its public + publish API is served by delegating to the folded Hono app, and a server-rendered Community tab moderates user-published skills/MCP (approve/reject/hide/verify) gated on the unified dashboard admin role, writing the shared audit log. Requires a reasonix-registry D1 (schema in registry-schema.sql) before the auto-deploy runs; the id in wrangler.toml is a placeholder.
This commit is contained in:
1 parent
ac5f37f40b
commit
606d782379
31 files changed
+197
-1716
No files matched your search
@@ -1,4 +1,2 @@
|
||||
node_modules/
|
||||
.wrangler/
|
||||
.dev.vars
|
||||
dist/
|
||||
Generated
+10
-48
@@ -6,6 +6,7 @@
|
||||
"": {
|
||||
"name": "reasonix-crash-report",
|
||||
"dependencies": {
|
||||
"hono": "^4.6.14",
|
||||
"zod": "^3.25.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
@@ -696,9 +697,6 @@
|
||||
"arm"
|
||||
],
|
||||
"dev": true,
|
||||
"libc": [
|
||||
"glibc"
|
||||
],
|
||||
"license": "LGPL-3.0-or-later",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -716,9 +714,6 @@
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"libc": [
|
||||
"glibc"
|
||||
],
|
||||
"license": "LGPL-3.0-or-later",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -736,9 +731,6 @@
|
||||
"ppc64"
|
||||
],
|
||||
"dev": true,
|
||||
"libc": [
|
||||
"glibc"
|
||||
],
|
||||
"license": "LGPL-3.0-or-later",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -756,9 +748,6 @@
|
||||
"riscv64"
|
||||
],
|
||||
"dev": true,
|
||||
"libc": [
|
||||
"glibc"
|
||||
],
|
||||
"license": "LGPL-3.0-or-later",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -776,9 +765,6 @@
|
||||
"s390x"
|
||||
],
|
||||
"dev": true,
|
||||
"libc": [
|
||||
"glibc"
|
||||
],
|
||||
"license": "LGPL-3.0-or-later",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -796,9 +782,6 @@
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"libc": [
|
||||
"glibc"
|
||||
],
|
||||
"license": "LGPL-3.0-or-later",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -816,9 +799,6 @@
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"libc": [
|
||||
"musl"
|
||||
],
|
||||
"license": "LGPL-3.0-or-later",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -836,9 +816,6 @@
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"libc": [
|
||||
"musl"
|
||||
],
|
||||
"license": "LGPL-3.0-or-later",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -856,9 +833,6 @@
|
||||
"arm"
|
||||
],
|
||||
"dev": true,
|
||||
"libc": [
|
||||
"glibc"
|
||||
],
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -882,9 +856,6 @@
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"libc": [
|
||||
"glibc"
|
||||
],
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -908,9 +879,6 @@
|
||||
"ppc64"
|
||||
],
|
||||
"dev": true,
|
||||
"libc": [
|
||||
"glibc"
|
||||
],
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -934,9 +902,6 @@
|
||||
"riscv64"
|
||||
],
|
||||
"dev": true,
|
||||
"libc": [
|
||||
"glibc"
|
||||
],
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -960,9 +925,6 @@
|
||||
"s390x"
|
||||
],
|
||||
"dev": true,
|
||||
"libc": [
|
||||
"glibc"
|
||||
],
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -986,9 +948,6 @@
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"libc": [
|
||||
"glibc"
|
||||
],
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -1012,9 +971,6 @@
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"libc": [
|
||||
"musl"
|
||||
],
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -1038,9 +994,6 @@
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"libc": [
|
||||
"musl"
|
||||
],
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -1311,6 +1264,15 @@
|
||||
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/hono": {
|
||||
"version": "4.12.27",
|
||||
"resolved": "https://registry.npmjs.org/hono/-/hono-4.12.27.tgz",
|
||||
"integrity": "sha512-1yrb/+w6HWQJrUCLkJ2IF5jNIPvvFkblV5RNOYl6bV+OA6p9GLcMpHFFGTosSvHvcAUibuUukRqhlYI4z32C7Q==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=16.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/kleur": {
|
||||
"version": "4.1.5",
|
||||
"resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz",
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
"typecheck": "tsc --noEmit"
|
||||
},
|
||||
"dependencies": {
|
||||
"hono": "^4.6.14",
|
||||
"zod": "^3.25.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
File renamed without changes.
@@ -161,6 +161,9 @@ export async function logAction(env: Env, actor: User, action: string, target =
|
||||
}
|
||||
|
||||
export function userNav(user: User): string {
|
||||
const admin = user.role === "admin" ? `<a class="navlink" href="/admin">Admin</a>` : "";
|
||||
const admin =
|
||||
user.role === "admin"
|
||||
? `<a class="navlink" href="/community">Community</a><a class="navlink" href="/admin">Admin</a>`
|
||||
: "";
|
||||
return `<span class="chip"><span class="badge ${user.role}">${user.role}</span>${esc(user.email)}</span><a class="navlink" href="/account">Account</a>${admin}<form method="post" action="/logout" class="inline"><button class="btn ghost sm">Sign out</button></form>`;
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
// Moderation console for user-published skills/MCP servers. Gated on the unified
|
||||
// dashboard admin role; reads and writes the registry database.
|
||||
import { esc, page } from "./shell";
|
||||
import { type User, userNav } from "./auth";
|
||||
import type { PackageRow } from "./registry/types";
|
||||
|
||||
const STATUS_TABS = [
|
||||
{ key: "pending", label: "Pending" },
|
||||
{ key: "active", label: "Active" },
|
||||
{ key: "hidden", label: "Hidden" },
|
||||
{ key: "rejected", label: "Rejected" },
|
||||
];
|
||||
|
||||
function actionForm(pkg: PackageRow, action: string, label: string, cls: string, backStatus: string, confirm?: string): string {
|
||||
const onsubmit = confirm ? ` onsubmit="return confirm('${esc(confirm)}')"` : "";
|
||||
return `<form method="post" action="/community/${esc(pkg.scope_handle)}/${esc(pkg.name)}/${action}" class="inline"${onsubmit}>
|
||||
<input type="hidden" name="status" value="${esc(backStatus)}"><button class="btn ${cls} sm" type="submit">${esc(label)}</button></form>`;
|
||||
}
|
||||
|
||||
function rowActions(pkg: PackageRow, backStatus: string): string {
|
||||
if (pkg.status === "active") {
|
||||
const verify = pkg.verified
|
||||
? actionForm(pkg, "unverify", "Unverify", "ghost", backStatus)
|
||||
: actionForm(pkg, "verify", "Verify", "ghost", backStatus);
|
||||
return `${verify}${actionForm(pkg, "hide", "Hide", "danger", backStatus, `Hide ${pkg.slug}?`)}`;
|
||||
}
|
||||
const approve = actionForm(pkg, "approve", "Approve", "", backStatus);
|
||||
const reject = pkg.status === "rejected" ? "" : actionForm(pkg, "reject", "Reject", "danger", backStatus, `Reject ${pkg.slug}?`);
|
||||
return `${approve}${reject}`;
|
||||
}
|
||||
|
||||
function sourceLinks(pkg: PackageRow): string {
|
||||
const links: string[] = [];
|
||||
if (pkg.repo_url) links.push(`<a class="navlink" href="${esc(pkg.repo_url)}" target="_blank" rel="noopener">repo</a>`);
|
||||
if (pkg.homepage) links.push(`<a class="navlink" href="${esc(pkg.homepage)}" target="_blank" rel="noopener">home</a>`);
|
||||
return links.join(" · ");
|
||||
}
|
||||
|
||||
export function renderCommunity(viewer: User, packages: PackageRow[], status: string): string {
|
||||
const tabs = STATUS_TABS.map(
|
||||
(t) => `<a class="filter-tab${t.key === status ? " active" : ""}" href="/community?status=${t.key}">${t.label}</a>`,
|
||||
).join("");
|
||||
const rows = packages.length
|
||||
? packages
|
||||
.map((p) => {
|
||||
const verified = p.verified ? ` <span class="badge admin">verified</span>` : "";
|
||||
const links = sourceLinks(p);
|
||||
return `<tr>
|
||||
<td><div class="crash-summary"><span>${esc(p.slug)}${verified}</span><small>${esc(p.summary || "—")}</small></div></td>
|
||||
<td><span class="pill">${esc(p.kind)}</span></td>
|
||||
<td>@${esc(p.scope_handle)}</td>
|
||||
<td class="n">${esc(p.latest_version || "—")} · ${p.install_count} inst · ${p.star_count}★</td>
|
||||
<td class="n">${esc(p.created_at.slice(0, 10))}</td>
|
||||
<td><div class="actions">${rowActions(p, status)}</div>${links ? `<div class="muted">${links}</div>` : ""}</td>
|
||||
</tr>`;
|
||||
})
|
||||
.join("")
|
||||
: `<tr><td colspan="6"><div class="empty">No ${esc(status)} packages</div></td></tr>`;
|
||||
return page(
|
||||
"Reasonix · Community",
|
||||
"community",
|
||||
`<h1>Community</h1><p class="sub">Review user-published skills and MCP servers — approve to publish, verify to badge, hide to take down</p>
|
||||
<div class="filter-tabs">${tabs}</div>
|
||||
<div class="card full"><table><thead><tr><th>package</th><th>kind</th><th>publisher</th><th>version · installs · stars</th><th>submitted</th><th></th></tr></thead>
|
||||
<tbody>${rows}</tbody></table></div>`,
|
||||
userNav(viewer),
|
||||
);
|
||||
}
|
||||
@@ -4,12 +4,18 @@ export interface RateLimiter {
|
||||
|
||||
export interface Env {
|
||||
DB: D1Database;
|
||||
// Skill/MCP registry database — the folded registry API + moderation console
|
||||
// read and write it; the crash tables stay in DB.
|
||||
REGISTRY_DB: D1Database;
|
||||
RATE_LIMITER: RateLimiter;
|
||||
PING_LIMITER: RateLimiter;
|
||||
METRICS_LIMITER: RateLimiter;
|
||||
WRITE_LIMITER?: RateLimiter;
|
||||
ADMIN_EMAILS?: string;
|
||||
// Shared identity service (id.reasonix.io) and the site that hosts its login
|
||||
// page (reasonix.io). Overridable for local dev.
|
||||
ID_ORIGIN?: string;
|
||||
APP_ORIGIN?: string;
|
||||
// Browsers allowed to call the registry API with credentials (comma-separated).
|
||||
ALLOWED_ORIGINS?: string;
|
||||
}
|
||||
@@ -17,6 +17,11 @@ import {
|
||||
type Role,
|
||||
type User,
|
||||
} from "./auth";
|
||||
import registryApp from "./registry/app";
|
||||
import type { Bindings as RegistryBindings } from "./registry/env";
|
||||
import { PackageRepo } from "./registry/db/packages";
|
||||
import { EventRepo } from "./registry/db/events";
|
||||
import { renderCommunity } from "./community";
|
||||
|
||||
const MAX_BODY_BYTES = 96 * 1024;
|
||||
const LATEST_SAMPLES_PER_GROUP = 5;
|
||||
@@ -902,6 +907,71 @@ function requireViewer(user: User | null, login: string): Response | null {
|
||||
return null;
|
||||
}
|
||||
|
||||
// The folded registry API runs against its own database and resolves identity
|
||||
// itself; hand it the second binding plus the account/site origins it expects.
|
||||
function registryBindings(env: Env): RegistryBindings {
|
||||
return {
|
||||
DB: env.REGISTRY_DB,
|
||||
WRITE_LIMITER: env.WRITE_LIMITER,
|
||||
ACCOUNTS_ORIGIN: env.ID_ORIGIN ?? "https://id.reasonix.io",
|
||||
APP_ORIGIN: env.APP_ORIGIN ?? "https://reasonix.io",
|
||||
ALLOWED_ORIGINS: env.ALLOWED_ORIGINS ?? "https://reasonix.io,https://www.reasonix.io",
|
||||
};
|
||||
}
|
||||
|
||||
function communityStatus(url: URL): string {
|
||||
const s = url.searchParams.get("status") ?? "pending";
|
||||
return ["pending", "active", "hidden", "rejected"].includes(s) ? s : "pending";
|
||||
}
|
||||
|
||||
async function handleCommunityList(env: Env, admin: User, status: string): Promise<Response> {
|
||||
const rows = await new PackageRepo(env.REGISTRY_DB).listByStatus(status, 200);
|
||||
return html(renderCommunity(admin, rows, status));
|
||||
}
|
||||
|
||||
async function handleCommunityAction(
|
||||
request: Request,
|
||||
env: Env,
|
||||
admin: User,
|
||||
handle: string,
|
||||
name: string,
|
||||
action: string,
|
||||
): Promise<Response> {
|
||||
if (!sameOrigin(request)) return new Response("forbidden", { status: 403 });
|
||||
const form = await formObject(request);
|
||||
const backStatus = ["pending", "active", "hidden", "rejected"].includes(form.status) ? form.status : "pending";
|
||||
const back = redirect(`/community?status=${backStatus}`);
|
||||
const slug = `${handle}/${name}`;
|
||||
const repo = new PackageRepo(env.REGISTRY_DB);
|
||||
const now = new Date().toISOString();
|
||||
|
||||
if (action === "verify" || action === "unverify") {
|
||||
await repo.setVerified(slug, action === "verify", now);
|
||||
await logAction(env, admin, `pkg_${action}`, slug);
|
||||
return back;
|
||||
}
|
||||
if (action === "approve") {
|
||||
const before = await repo.bySlug(slug);
|
||||
const row = await repo.setStatus(slug, "active", now);
|
||||
// Emit the publish event on first approval so the feed only announces
|
||||
// packages that actually went public.
|
||||
if (row && before && before.status !== "active") {
|
||||
await new EventRepo(env.REGISTRY_DB).log({
|
||||
type: "publish",
|
||||
packageId: row.id,
|
||||
actorHandle: row.scope_handle,
|
||||
summary: `published ${row.slug}@${row.latest_version}`,
|
||||
now,
|
||||
});
|
||||
}
|
||||
await logAction(env, admin, "pkg_approve", slug);
|
||||
return back;
|
||||
}
|
||||
await repo.setStatus(slug, action === "reject" ? "rejected" : "hidden", now);
|
||||
await logAction(env, admin, `pkg_${action}`, slug);
|
||||
return back;
|
||||
}
|
||||
|
||||
export default {
|
||||
async fetch(request: Request, env: Env): Promise<Response> {
|
||||
const url = new URL(request.url);
|
||||
@@ -912,6 +982,13 @@ export default {
|
||||
if (path === "/v1/ping" && method === "POST") return handlePing(request, env);
|
||||
if (path === "/v1/metrics" && method === "POST") return handleMetrics(request, env);
|
||||
|
||||
// Skill/MCP registry API — the folded Hono app handles its own auth, CORS
|
||||
// and rate limiting against the registry database (public reads + publish,
|
||||
// plus the JSON /v1/admin the site's moderation panel calls).
|
||||
if (path.startsWith("/v1/packages") || path === "/v1/activity" || path.startsWith("/v1/admin")) {
|
||||
return registryApp.fetch(request, registryBindings(env));
|
||||
}
|
||||
|
||||
const login = loginUrl(env, request);
|
||||
|
||||
// Authentication moved to id.reasonix.io; these paths just bounce there.
|
||||
@@ -947,6 +1024,16 @@ export default {
|
||||
return handleAdminUsers(request, env, user);
|
||||
}
|
||||
|
||||
if (path === "/community" && method === "GET") {
|
||||
if (!user) return redirect(login);
|
||||
return user.role === "admin" ? handleCommunityList(env, user, communityStatus(url)) : redirect("/account");
|
||||
}
|
||||
const pkgActionMatch = path.match(/^\/community\/([^/]+)\/([^/]+)\/(approve|reject|hide|verify|unverify)$/);
|
||||
if (pkgActionMatch && method === "POST") {
|
||||
if (user?.role !== "admin") return new Response("forbidden", { status: 403 });
|
||||
return handleCommunityAction(request, env, user, pkgActionMatch[1], pkgActionMatch[2], pkgActionMatch[3]);
|
||||
}
|
||||
|
||||
if (
|
||||
path === "/v1/report" ||
|
||||
path === "/v1/ping" ||
|
||||
@@ -956,7 +1043,8 @@ export default {
|
||||
path === "/logout" ||
|
||||
path === "/account" ||
|
||||
path.startsWith("/stats") ||
|
||||
path.startsWith("/admin")
|
||||
path.startsWith("/admin") ||
|
||||
path.startsWith("/community")
|
||||
) {
|
||||
return new Response("method not allowed", { status: 405 });
|
||||
}
|
||||
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -6,6 +6,7 @@
|
||||
"lib": ["ES2022"],
|
||||
"types": ["@cloudflare/workers-types"],
|
||||
"strict": true,
|
||||
"skipLibCheck": true,
|
||||
"noEmit": true
|
||||
},
|
||||
"include": ["src"]
|
||||
|
||||
@@ -13,12 +13,23 @@ ADMIN_EMAILS = "[email protected]"
|
||||
# page. Override for local dev.
|
||||
ID_ORIGIN = "https://id.reasonix.io"
|
||||
APP_ORIGIN = "https://reasonix.io"
|
||||
# Browsers allowed to call the folded registry API with credentials.
|
||||
ALLOWED_ORIGINS = "https://reasonix.io,https://www.reasonix.io"
|
||||
|
||||
[[d1_databases]]
|
||||
binding = "DB"
|
||||
database_name = "reasonix-crash"
|
||||
database_id = "54825184-366f-4877-80fc-924efab0239b"
|
||||
|
||||
# Skill/MCP registry data plane, folded in from the standalone worker. Before
|
||||
# merging (the crash worker auto-deploys on push to main-v2):
|
||||
# wrangler d1 create reasonix-registry # paste the id below
|
||||
# wrangler d1 execute reasonix-registry --remote --file=registry-schema.sql
|
||||
[[d1_databases]]
|
||||
binding = "REGISTRY_DB"
|
||||
database_name = "reasonix-registry"
|
||||
database_id = "PLACEHOLDER-run-wrangler-d1-create-reasonix-registry"
|
||||
|
||||
[[unsafe.bindings]]
|
||||
name = "RATE_LIMITER"
|
||||
type = "ratelimit"
|
||||
@@ -39,3 +50,10 @@ name = "METRICS_LIMITER"
|
||||
type = "ratelimit"
|
||||
namespace_id = "1003"
|
||||
simple = { limit = 30, period = 60 }
|
||||
|
||||
# Per-IP throttle for registry writes (publish / install-ping / star).
|
||||
[[unsafe.bindings]]
|
||||
name = "WRITE_LIMITER"
|
||||
type = "ratelimit"
|
||||
namespace_id = "3001"
|
||||
simple = { limit = 30, period = 60 }
|
||||
Generated
-1548
File diff suppressed because it is too large.
Load diff
@@ -1,24 +0,0 @@
|
||||
{
|
||||
"name": "reasonix-registry",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "wrangler dev",
|
||||
"deploy": "wrangler deploy",
|
||||
"typecheck": "tsc --noEmit",
|
||||
"db:apply:local": "wrangler d1 migrations apply reasonix-registry --local",
|
||||
"db:apply:remote": "wrangler d1 migrations apply reasonix-registry --remote"
|
||||
},
|
||||
"dependencies": {
|
||||
"hono": "^4.6.14",
|
||||
"zod": "^3.25.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@cloudflare/workers-types": "^4.20260601.0",
|
||||
"typescript": "^5.8.0",
|
||||
"wrangler": "^4.102.0"
|
||||
},
|
||||
"overrides": {
|
||||
"esbuild": "0.28.1"
|
||||
}
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
-- Local demo data for the registry. Apply to the LOCAL D1 only:
|
||||
-- wrangler d1 execute reasonix-registry --local --file=seed.sql
|
||||
DELETE FROM events;
|
||||
DELETE FROM stars;
|
||||
DELETE FROM package_versions;
|
||||
DELETE FROM packages;
|
||||
|
||||
INSERT INTO packages
|
||||
(kind, scope_handle, name, slug, summary, source, install_kind, repo_url, tags, latest_version, install_count, star_count, verified, publisher_id, created_at, updated_at)
|
||||
VALUES
|
||||
('skill','esengine','pr-review','esengine/pr-review','Adversarial multi-pass PR review with inline findings','https://github.com/esengine/reasonix-skills/pr-review','skill','https://github.com/esengine/reasonix-skills','review,git,quality','1.2.0',428,96,1,1,datetime('now','-40 days'),datetime('now','-3 days')),
|
||||
('skill','esengine','go-test-gen','esengine/go-test-gen','Generate table-driven Go tests for the current package','https://github.com/esengine/reasonix-skills/go-test-gen','skill','https://github.com/esengine/reasonix-skills','go,testing','0.4.1',213,44,1,1,datetime('now','-22 days'),datetime('now','-6 days')),
|
||||
('mcp','sivancola','feishu-notify','sivancola/feishu-notify','Send Feishu/Lark interactive cards from your agent','https://github.com/SivanCola/feishu-mcp/.mcp.json','mcp','https://github.com/SivanCola/feishu-mcp','feishu,notify,im','0.3.0',156,31,0,2,datetime('now','-14 days'),datetime('now','-2 days')),
|
||||
('skill','huqiantao','commit-msg','huqiantao/commit-msg','Write conventional commit messages from the staged diff','https://raw.githubusercontent.com/HUQIANTAO/rx/main/commit-msg/SKILL.md','skill','https://github.com/HUQIANTAO/rx','git,commits','1.0.0',502,73,0,3,datetime('now','-9 days'),datetime('now','-1 day')),
|
||||
('mcp','community','postgres','community/postgres','Query and inspect a Postgres database over MCP','npx -y @modelcontextprotocol/server-postgres','mcp','https://github.com/modelcontextprotocol/servers','database,sql,postgres','0.6.2',89,18,0,4,datetime('now','-5 days'),datetime('now','-5 days')),
|
||||
('skill','gtc2080','changelog','gtc2080/changelog','Draft a CHANGELOG entry from merged PRs since the last tag','https://github.com/GTC2080/rx-skills/changelog','skill','https://github.com/GTC2080/rx-skills','release,docs','0.2.0',37,9,0,5,datetime('now','-2 days'),datetime('now','-2 days'));
|
||||
|
||||
INSERT INTO package_versions (package_id, version, source, content_hash, risk_level, created_at)
|
||||
SELECT id, latest_version, source, '', 'low', updated_at FROM packages;
|
||||
|
||||
INSERT INTO events (type, package_id, actor_handle, summary, created_at)
|
||||
SELECT 'publish', id, scope_handle, 'published ' || slug || '@' || latest_version, created_at FROM packages;
|
||||
|
||||
INSERT INTO events (type, package_id, actor_handle, summary, created_at) VALUES
|
||||
((SELECT id FROM packages WHERE slug='huqiantao/commit-msg'),'milestone','huqiantao','huqiantao/commit-msg reached 500 installs',datetime('now','-6 hours')),
|
||||
((SELECT id FROM packages WHERE slug='esengine/pr-review'),'star','sivancola','starred esengine/pr-review',datetime('now','-3 hours')),
|
||||
((SELECT id FROM packages WHERE slug='gtc2080/changelog'),'publish','gtc2080','published gtc2080/[email protected]',datetime('now','-90 minutes'));
|
||||
|
||||
-- install pings over the last week to give the trending rank something to sort on
|
||||
INSERT INTO events (type, package_id, actor_handle, summary, created_at)
|
||||
SELECT 'install', p.id, '', 'installed ' || p.slug, datetime('now','-' || (abs(random()) % 6) || ' days')
|
||||
FROM packages p, (SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5);
|
||||
|
||||
-- a pending submission (moderation): must stay out of the public list/detail until approved
|
||||
INSERT INTO packages
|
||||
(kind, scope_handle, name, slug, summary, source, install_kind, tags, latest_version, status, publisher_id, created_at, updated_at)
|
||||
VALUES
|
||||
('skill','newbie','untested-skill','newbie/untested-skill','A freshly submitted skill awaiting review','https://example.com/untested/SKILL.md','skill','experimental','0.1.0','pending',9,datetime('now','-20 minutes'),datetime('now','-20 minutes'));
|
||||
@@ -1,5 +0,0 @@
|
||||
// Reasonix skill + MCP registry — publish, discover, and track installs for
|
||||
// reasonix.io. The Hono app is itself the Workers fetch handler.
|
||||
import app from "./app";
|
||||
|
||||
export default app;
|
||||
@@ -1,16 +0,0 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES2022",
|
||||
"module": "ES2022",
|
||||
"moduleResolution": "bundler",
|
||||
"lib": ["ES2022"],
|
||||
"types": ["@cloudflare/workers-types"],
|
||||
"strict": true,
|
||||
"noUncheckedIndexedAccess": true,
|
||||
"noImplicitOverride": true,
|
||||
"verbatimModuleSyntax": true,
|
||||
"skipLibCheck": true,
|
||||
"noEmit": true
|
||||
},
|
||||
"include": ["src"]
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
# Skill + MCP registry for reasonix.io — publish, discover, and track installs.
|
||||
# Identity is delegated to the account service (id.reasonix.io); this worker
|
||||
# never holds SESSION_PEPPER. First deploy:
|
||||
# wrangler d1 create reasonix-registry # paste database_id below
|
||||
# wrangler d1 migrations apply reasonix-registry --remote
|
||||
# wrangler deploy
|
||||
name = "reasonix-registry"
|
||||
main = "src/index.ts"
|
||||
compatibility_date = "2026-06-01"
|
||||
|
||||
routes = [{ pattern = "registry.reasonix.io", custom_domain = true }]
|
||||
|
||||
[vars]
|
||||
# Account service that resolves the session cookie / CLI bearer to an account.
|
||||
ACCOUNTS_ORIGIN = "https://id.reasonix.io"
|
||||
# Where the web frontend lives (canonical links back to package pages).
|
||||
APP_ORIGIN = "https://reasonix.io"
|
||||
# Browsers that may call this API with credentials (comma-separated).
|
||||
ALLOWED_ORIGINS = "https://reasonix.io,https://www.reasonix.io"
|
||||
|
||||
[[d1_databases]]
|
||||
binding = "DB"
|
||||
database_name = "reasonix-registry"
|
||||
# Fill in after `wrangler d1 create reasonix-registry`.
|
||||
database_id = "PLACEHOLDER-run-wrangler-d1-create-reasonix-registry"
|
||||
migrations_dir = "migrations"
|
||||
|
||||
# Per-IP throttle for writes (publish / install-ping / star).
|
||||
[[unsafe.bindings]]
|
||||
name = "WRITE_LIMITER"
|
||||
type = "ratelimit"
|
||||
namespace_id = "3001"
|
||||
simple = { limit = 30, period = 60 }
|
||||
Reference in new issue
Block a user