mirror of
https://github.com/rowboatlabs/rowboat.git
synced 2026-04-26 08:56:22 +02:00
markdown agent files
This commit is contained in:
parent
8c686029cb
commit
e40c767336
7 changed files with 79 additions and 19 deletions
|
|
@ -29,7 +29,7 @@ export const Agent = z.object({
|
|||
name: z.string(),
|
||||
provider: z.string().optional(),
|
||||
model: z.string().optional(),
|
||||
description: z.string(),
|
||||
description: z.string().optional(),
|
||||
instructions: z.string(),
|
||||
tools: z.record(z.string(), ToolAttachment).optional(),
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,8 +1,12 @@
|
|||
import { WorkDir } from "../config/config.js";
|
||||
import fs from "fs/promises";
|
||||
import { glob } from "node:fs/promises";
|
||||
import path from "path";
|
||||
import z from "zod";
|
||||
import { Agent } from "./agents.js";
|
||||
import { parse, stringify } from "yaml";
|
||||
|
||||
const UpdateAgentSchema = Agent.omit({ name: true });
|
||||
|
||||
export interface IAgentsRepo {
|
||||
list(): Promise<z.infer<typeof Agent>[]>;
|
||||
|
|
@ -13,33 +17,76 @@ export interface IAgentsRepo {
|
|||
}
|
||||
|
||||
export class FSAgentsRepo implements IAgentsRepo {
|
||||
private readonly agentsDir = path.join(WorkDir, "agents");
|
||||
|
||||
async list(): Promise<z.infer<typeof Agent>[]> {
|
||||
const result: z.infer<typeof Agent>[] = [];
|
||||
// list all json files in workdir/agents/
|
||||
const agentsDir = path.join(WorkDir, "agents");
|
||||
const files = await fs.readdir(agentsDir);
|
||||
|
||||
for (const file of files) {
|
||||
const contents = await fs.readFile(path.join(agentsDir, file), "utf8");
|
||||
result.push(Agent.parse(JSON.parse(contents)));
|
||||
// list all md files in workdir/agents/
|
||||
const matches = await Array.fromAsync(glob("**/*.md", { cwd: this.agentsDir }));
|
||||
for (const file of matches) {
|
||||
try {
|
||||
const agent = await this.parseAgentMd(path.join(this.agentsDir, file));
|
||||
result.push(agent);
|
||||
} catch (error) {
|
||||
console.error(`Error parsing agent ${file}: ${error instanceof Error ? error.message : String(error)}`);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private async parseAgentMd(filePath: string): Promise<z.infer<typeof Agent>> {
|
||||
const raw = await fs.readFile(filePath, "utf8");
|
||||
|
||||
// strip the path prefix from the file name
|
||||
// and the .md extension
|
||||
const agentName = filePath
|
||||
.replace(this.agentsDir + "/", "")
|
||||
.replace(/\.md$/, "");
|
||||
let agent: z.infer<typeof Agent> = {
|
||||
name: agentName,
|
||||
instructions: raw,
|
||||
};
|
||||
let content = raw;
|
||||
|
||||
// check for frontmatter markers at start
|
||||
if (raw.startsWith("---")) {
|
||||
const end = raw.indexOf("\n---", 3);
|
||||
|
||||
if (end !== -1) {
|
||||
const fm = raw.slice(3, end).trim(); // YAML text
|
||||
content = raw.slice(end + 4).trim(); // body after frontmatter
|
||||
const yaml = parse(fm);
|
||||
const parsed = Agent
|
||||
.omit({ name: true, instructions: true })
|
||||
.parse(yaml);
|
||||
agent = {
|
||||
...agent,
|
||||
...parsed,
|
||||
instructions: content,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return agent;
|
||||
}
|
||||
|
||||
async fetch(id: string): Promise<z.infer<typeof Agent>> {
|
||||
const contents = await fs.readFile(path.join(WorkDir, "agents", `${id}.json`), "utf8");
|
||||
return Agent.parse(JSON.parse(contents));
|
||||
return this.parseAgentMd(path.join(this.agentsDir, `${id}.md`));
|
||||
}
|
||||
|
||||
async create(agent: z.infer<typeof Agent>): Promise<void> {
|
||||
await fs.writeFile(path.join(WorkDir, "agents", `${agent.name}.json`), JSON.stringify(agent, null, 2));
|
||||
await fs.writeFile(path.join(this.agentsDir, `${agent.name}.md`), agent.instructions);
|
||||
}
|
||||
|
||||
async update(id: string, agent: z.infer<typeof Agent>): Promise<void> {
|
||||
await fs.writeFile(path.join(WorkDir, "agents", `${id}.json`), JSON.stringify(agent, null, 2));
|
||||
|
||||
async update(id: string, agent: z.infer<typeof UpdateAgentSchema>): Promise<void> {
|
||||
const { instructions, ...rest } = agent;
|
||||
const contents = `---\n${stringify(rest)}\n---\n${instructions}`;
|
||||
await fs.writeFile(path.join(this.agentsDir, `${id}.md`), contents);
|
||||
}
|
||||
|
||||
async delete(id: string): Promise<void> {
|
||||
await fs.unlink(path.join(WorkDir, "agents", `${id}.json`));
|
||||
await fs.unlink(path.join(this.agentsDir, `${id}.md`));
|
||||
}
|
||||
}
|
||||
|
|
@ -13,7 +13,6 @@ export class InMemoryBus implements IBus {
|
|||
private subscribers: Map<string, ((event: z.infer<typeof RunEvent>) => Promise<void>)[]> = new Map();
|
||||
|
||||
async publish(event: z.infer<typeof RunEvent>): Promise<void> {
|
||||
console.log(this.subscribers);
|
||||
const pending: Promise<void>[] = [];
|
||||
for (const subscriber of this.subscribers.get(event.runId) || []) {
|
||||
pending.push(subscriber(event));
|
||||
|
|
@ -21,7 +20,6 @@ export class InMemoryBus implements IBus {
|
|||
for (const subscriber of this.subscribers.get('*') || []) {
|
||||
pending.push(subscriber(event));
|
||||
}
|
||||
console.log(pending.length);
|
||||
await Promise.all(pending);
|
||||
}
|
||||
|
||||
|
|
@ -30,7 +28,6 @@ export class InMemoryBus implements IBus {
|
|||
this.subscribers.set(runId, []);
|
||||
}
|
||||
this.subscribers.get(runId)!.push(handler);
|
||||
console.log(this.subscribers);
|
||||
return () => {
|
||||
this.subscribers.get(runId)!.splice(this.subscribers.get(runId)!.indexOf(handler), 1);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -621,7 +621,6 @@ const routes = new Hono()
|
|||
unsub = await bus.subscribe('*', async (event) => {
|
||||
if (aborted) return;
|
||||
|
||||
console.log('got ev', event);
|
||||
await stream.writeSSE({
|
||||
data: JSON.stringify(event),
|
||||
event: "message",
|
||||
|
|
|
|||
|
|
@ -941,7 +941,7 @@ function AgentPickerModal({
|
|||
onCancel: () => void;
|
||||
}) {
|
||||
const items = agents.map((agent) => ({
|
||||
label: `${agent.name} – ${truncate(agent.description, 40)}`,
|
||||
label: `${agent.name}${agent.description ? ` – ${truncate(agent.description, 40)}` : ""}`,
|
||||
value: agent.name,
|
||||
}));
|
||||
return (
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue