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
102 changes: 102 additions & 0 deletions data/developer-resources.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
[
{
"id": "res-001",
"title": "OpenClaw TypeScript SDK",
"url": "https://github.com/openclaw-ai/openclaw-ts",
"type": "sdk",
"description": "Official TypeScript SDK for building, testing, and deploying OpenClaw-powered agents.",
"language": "TypeScript",
"stars": 1820,
"updatedAt": "2026-02-02T15:40:00.000Z"
},
{
"id": "res-002",
"title": "OpenClaw Python SDK",
"url": "https://github.com/openclaw-ai/openclaw-py",
"type": "sdk",
"description": "Python client with async support, retries, and streaming primitives for agent workflows.",
"language": "Python",
"stars": 1460,
"updatedAt": "2026-01-30T11:22:00.000Z"
},
{
"id": "res-003",
"title": "Agent Memory Toolkit",
"url": "https://github.com/foragents-dev/agent-memory-toolkit",
"type": "library",
"description": "Drop-in persistence, embeddings, and retrieval helpers for long-running autonomous agents.",
"language": "TypeScript",
"stars": 930,
"updatedAt": "2026-01-26T18:10:00.000Z"
},
{
"id": "res-004",
"title": "Structured Prompting Utilities",
"url": "https://github.com/foragents-dev/structured-prompts",
"type": "library",
"description": "Composable prompt templates, validators, and schema-backed generation utilities.",
"language": "Python",
"stars": 680,
"updatedAt": "2026-01-18T09:45:00.000Z"
},
{
"id": "res-005",
"title": "Slack Triage Bot Example",
"url": "https://github.com/foragents-dev/examples-slack-triage",
"type": "example",
"description": "End-to-end sample that triages support tickets from Slack into action queues.",
"language": "TypeScript",
"stars": 540,
"updatedAt": "2026-02-01T20:04:00.000Z"
},
{
"id": "res-006",
"title": "GitHub PR Review Agent Example",
"url": "https://github.com/foragents-dev/examples-pr-review",
"type": "example",
"description": "Reference implementation for automated pull request reviews with policy checks.",
"language": "Go",
"stars": 790,
"updatedAt": "2026-02-04T07:12:00.000Z"
},
{
"id": "res-007",
"title": "Building Reliable Agent Loops",
"url": "https://foragents.dev/guides/reliable-agent-loops",
"type": "tutorial",
"description": "Hands-on tutorial for retries, state checkpoints, and safe execution patterns.",
"language": "Markdown",
"stars": 420,
"updatedAt": "2026-01-29T13:32:00.000Z"
},
{
"id": "res-008",
"title": "Observability for Multi-Agent Systems",
"url": "https://foragents.dev/guides/agent-observability",
"type": "tutorial",
"description": "Guide to tracing, metrics, and debugging workflows across distributed agent runtimes.",
"language": "Markdown",
"stars": 388,
"updatedAt": "2026-01-31T16:55:00.000Z"
},
{
"id": "res-009",
"title": "Prompt Diff Inspector",
"url": "https://github.com/foragents-dev/prompt-diff-inspector",
"type": "tool",
"description": "CLI tool for comparing prompt revisions, outputs, and evaluation changes side-by-side.",
"language": "Rust",
"stars": 1015,
"updatedAt": "2026-02-03T22:18:00.000Z"
},
{
"id": "res-010",
"title": "Agent Sandbox Runner",
"url": "https://github.com/foragents-dev/agent-sandbox-runner",
"type": "tool",
"description": "Secure local sandbox for replaying agent tasks with deterministic fixtures and logs.",
"language": "TypeScript",
"stars": 1240,
"updatedAt": "2026-02-05T10:06:00.000Z"
}
]
119 changes: 119 additions & 0 deletions src/app/api/developer/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
import { promises as fs } from "fs";
import path from "path";
import { NextResponse } from "next/server";

type ResourceType = "sdk" | "library" | "example" | "tutorial" | "tool";

interface DeveloperResource {
id: string;
title: string;
url: string;
type: ResourceType;
description: string;
language: string;
stars: number;
updatedAt: string;
}

const VALID_TYPES: ResourceType[] = ["sdk", "library", "example", "tutorial", "tool"];
const DATA_FILE = path.join(process.cwd(), "data", "developer-resources.json");

const readResources = async (): Promise<DeveloperResource[]> => {
const file = await fs.readFile(DATA_FILE, "utf8");
const parsed = JSON.parse(file) as DeveloperResource[];
return Array.isArray(parsed) ? parsed : [];
};

const writeResources = async (resources: DeveloperResource[]) => {
await fs.writeFile(DATA_FILE, JSON.stringify(resources, null, 2), "utf8");
};

export async function GET(request: Request) {
try {
const { searchParams } = new URL(request.url);
const typeParam = searchParams.get("type")?.toLowerCase();
const searchParam = searchParams.get("search")?.toLowerCase().trim();

const resources = await readResources();

const filtered = resources.filter((resource) => {
const matchesType = !typeParam || typeParam === "all" || resource.type === typeParam;

const matchesSearch =
!searchParam ||
resource.title.toLowerCase().includes(searchParam) ||
resource.description.toLowerCase().includes(searchParam) ||
resource.language.toLowerCase().includes(searchParam);

return matchesType && matchesSearch;
});

return NextResponse.json({ resources: filtered });
} catch (error) {
console.error("Failed to load developer resources", error);
return NextResponse.json(
{ error: "Failed to load developer resources" },
{ status: 500 }
);
}
}

export async function POST(request: Request) {
try {
const body = (await request.json()) as Partial<DeveloperResource>;

const title = body.title?.trim();
const url = body.url?.trim();
const type = body.type;
const description = body.description?.trim();

if (!title || !url || !type || !description) {
return NextResponse.json(
{ error: "title, url, type, and description are required" },
{ status: 400 }
);
}

if (!VALID_TYPES.includes(type)) {
return NextResponse.json(
{ error: `type must be one of: ${VALID_TYPES.join(", ")}` },
{ status: 400 }
);
}

let parsedUrl: URL;
try {
parsedUrl = new URL(url);
} catch {
return NextResponse.json({ error: "url must be a valid URL" }, { status: 400 });
}

if (!["http:", "https:"].includes(parsedUrl.protocol)) {
return NextResponse.json({ error: "url must start with http:// or https://" }, { status: 400 });
}

const resources = await readResources();

const newResource: DeveloperResource = {
id: crypto.randomUUID(),
title,
url,
type,
description,
language: "Unknown",
stars: 0,
updatedAt: new Date().toISOString(),
};

resources.unshift(newResource);
await writeResources(resources);

return NextResponse.json({ resource: newResource }, { status: 201 });
} catch (error) {
console.error("Failed to create developer resource", error);
return NextResponse.json(
{ error: "Failed to create developer resource" },
{ status: 500 }
);
}
}
Loading
Loading