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
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ export const agentVersionsCollectionSchema = z.object({
agentId: z.string(),
version: z.string().regex(/^\d+\.\d+\.\d+$/),
content: z.string().max(100000),
contentHash: z.string().length(64), // SHA256 hex = 64 chars
changelog: z.string().max(1000).optional(),
isLatest: z.boolean(),
downloads: z.number().int().min(0),
Expand Down
1 change: 1 addition & 0 deletions packages/backend/src/collections/agents.collection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ export const agentsCollectionSchema = z.object({
readme: z.string().max(50000).optional(),
latestVersion: z.string().regex(/^\d+\.\d+\.\d+$/),
latestContent: z.string().max(100000),
latestContentHash: z.string().length(64), // SHA256 hex = 64 chars
totalDownloads: z.number().int().min(0),
stars: z.number().int().min(0),
forks: z.number().int().min(0),
Expand Down
1 change: 1 addition & 0 deletions packages/backend/src/routes/agents/get-agent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ export const getAgentHandler = (serviceProvider: ServiceProvider<AppServiceMap>)
mappedAgent.versions = versions.map((v) => ({
version: v.version,
agentVersion: v.agentVersion,
contentHash: v.contentHash,
publishedAt: v.publishedAt.toISOString(),
downloads: v.downloads,
isLatest: v.isLatest,
Expand Down
1 change: 1 addition & 0 deletions packages/backend/src/routes/agents/publish.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ export const publishAgentHandler = (serviceProvider: ServiceProvider<AppServiceM
name: agent.name,
owner: agent.ownerUsername,
version: agent.latestVersion,
contentHash: agent.latestContentHash,
visibility: agent.visibility,
publishedAt: agent.updatedAt.toISOString(),
},
Expand Down
16 changes: 15 additions & 1 deletion packages/backend/src/services/agent.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,18 @@ import type {
UpdateAgentMetadataRequest,
} from '@agentage/shared';
import { isMarkdownWithFrontmatter, parseAgentMarkdown } from '@agentage/shared';
import { randomUUID } from 'crypto';
import { createHash, randomUUID } from 'crypto';
import * as semver from 'semver';
import type { LoggerService, Service } from './app.services';
import type { TypedDb } from './typed-db';

/**
* Compute SHA256 hash of content
*/
function computeContentHash(content: string): string {
return createHash('sha256').update(content, 'utf8').digest('hex');
}

export interface ListAgentsFilter {
page: number;
limit: number;
Expand Down Expand Up @@ -140,6 +147,9 @@ export function createAgentService(db: TypedDb, logger: LoggerService): AgentSer
owner: user._id,
});

// Compute content hash
const contentHash = computeContentHash(data.content);

if (existingAgent) {
// Publishing new version - verify version is greater
if (!semver.gt(data.version, existingAgent.latestVersion)) {
Expand Down Expand Up @@ -170,6 +180,7 @@ export function createAgentService(db: TypedDb, logger: LoggerService): AgentSer
agentId: existingAgent._id,
version: data.version,
content: data.content,
contentHash,
contentType,
agentVersion,
tools,
Expand All @@ -188,6 +199,7 @@ export function createAgentService(db: TypedDb, logger: LoggerService): AgentSer
$set: {
latestVersion: data.version,
latestContent: data.content,
latestContentHash: contentHash,
contentType,
agentVersion,
tools,
Expand Down Expand Up @@ -228,6 +240,7 @@ export function createAgentService(db: TypedDb, logger: LoggerService): AgentSer
readme: data.readme,
latestVersion: data.version,
latestContent: data.content,
latestContentHash: contentHash,
totalDownloads: 0,
stars: 0,
forks: 0,
Expand All @@ -243,6 +256,7 @@ export function createAgentService(db: TypedDb, logger: LoggerService): AgentSer
agentId: agentId,
version: data.version,
content: data.content,
contentHash,
contentType,
agentVersion,
tools,
Expand Down
2 changes: 2 additions & 0 deletions packages/backend/src/utils/agent-response.mapper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ export function mapAgentToDetailResponse(agent: AgentDocument): AgentDetailUiRes
...mapAgentToListResponse(agent),
readme: agent.readme,
latestContent: agent.latestContent,
latestContentHash: agent.latestContentHash,
mcpServers: agent.mcpServers,
sections: agent.sections,
versions: [], // Populated separately
Expand All @@ -43,6 +44,7 @@ export function mapAgentVersionToResponse(version: AgentVersionDocument): AgentV
agentVersion: version.agentVersion,
contentType: version.contentType,
content: version.content,
contentHash: version.contentHash,
tools: version.tools,
mcpServers: version.mcpServers,
sections: version.sections,
Expand Down
5 changes: 5 additions & 0 deletions packages/shared/src/types/agent.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ export interface AgentDocument {
// Latest version (denormalized for performance)
latestVersion: string;
latestContent: string;
latestContentHash: string; // SHA256 hash of latestContent

// Statistics
totalDownloads: number;
Expand All @@ -94,6 +95,7 @@ export interface AgentVersionDocument {
version: string;
contentType: 'markdown' | 'plain';
content: string;
contentHash: string; // SHA256 hash of content
changelog?: string;

// Frontmatter data
Expand Down Expand Up @@ -232,11 +234,13 @@ export interface AgentUiResponse {
export interface AgentDetailUiResponse extends AgentUiResponse {
readme?: string;
latestContent: string;
latestContentHash: string;
mcpServers?: AgentMcpServers;
sections?: AgentSection[];
versions: {
version: string;
agentVersion?: string;
contentHash: string;
publishedAt: string;
downloads: number;
isLatest: boolean;
Expand All @@ -248,6 +252,7 @@ export interface AgentVersionUiResponse {
agentVersion?: string;
contentType: 'markdown' | 'plain';
content: string;
contentHash: string;
tools?: string[];
mcpServers?: AgentMcpServers;
sections?: AgentSection[];
Expand Down
Loading