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
16 changes: 8 additions & 8 deletions backend/__tests__/unit/models/installable.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ describe('Installable taxonomy scaffolding', () => {

it('saves an Installable with nested component + marketplace meta', async () => {
const doc = new Installable({
installableId: 'commonly/pod-welcomer',
installableId: 'pod-welcomer',
name: 'Pod Welcomer',
description: 'Greets new members when they join a pod.',
version: '1.0.0',
Expand Down Expand Up @@ -65,7 +65,7 @@ describe('Installable taxonomy scaffolding', () => {

const saved = await doc.save();
expect(saved._id).toBeDefined();
expect(saved.installableId).toBe('commonly/pod-welcomer');
expect(saved.installableId).toBe('pod-welcomer');
expect(saved.kind).toBe('app');
expect(saved.status).toBe('active'); // default
expect(saved.components).toHaveLength(1);
Expand All @@ -75,7 +75,7 @@ describe('Installable taxonomy scaffolding', () => {

it("defaults kind to 'app' when the manifest omits it", async () => {
const doc = new Installable({
installableId: 'commonly/no-kind-specified',
installableId: 'no-kind-specified',
name: 'No Kind',
description: 'Legacy-shaped manifest — no kind field.',
version: '1.0.0',
Expand All @@ -90,7 +90,7 @@ describe('Installable taxonomy scaffolding', () => {

it('saves a kind:agent Installable with an Agent + Skill components', async () => {
const doc = new Installable({
installableId: 'marketplace/sarah-legal',
installableId: '@marketplace/sarah-legal',
name: 'Sarah — Legal Researcher',
description: 'Pro agent specialized in US case law research.',
version: '1.2.0',
Expand Down Expand Up @@ -147,7 +147,7 @@ describe('Installable taxonomy scaffolding', () => {

it('saves a kind:skill standalone Installable (no runtime components)', async () => {
const doc = new Installable({
installableId: 'commonly/bluebook-citation',
installableId: 'bluebook-citation',
name: 'Bluebook Citation',
description: 'Teaches any agent to format legal citations in Bluebook style.',
version: '0.1.0',
Expand Down Expand Up @@ -237,7 +237,7 @@ describe('Installable taxonomy scaffolding', () => {
const userId = new mongoose.Types.ObjectId();

const install = new InstallableInstallation({
installableId: 'commonly/pod-welcomer',
installableId: 'pod-welcomer',
installableVersion: '1.0.0',
targetType: 'pod',
targetId: podId,
Expand Down Expand Up @@ -266,7 +266,7 @@ describe('Installable taxonomy scaffolding', () => {

it('rejects an InstallableInstallation with invalid targetType enum', async () => {
const bad = new InstallableInstallation({
installableId: 'commonly/x',
installableId: 'test-installable-x',
installableVersion: '1.0.0',
targetType: 'planet',
targetId: new mongoose.Types.ObjectId(),
Expand All @@ -279,7 +279,7 @@ describe('Installable taxonomy scaffolding', () => {

it('rejects an InstallableInstallation missing installedBy', async () => {
const bad = new InstallableInstallation({
installableId: 'commonly/x',
installableId: 'test-installable-x',
installableVersion: '1.0.0',
targetType: 'pod',
targetId: new mongoose.Types.ObjectId(),
Expand Down
2 changes: 1 addition & 1 deletion backend/models/AgentRegistry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ const ManifestSchema = new Schema(

const AgentRegistrySchema = new Schema<IAgentRegistry>(
{
agentName: { type: String, required: true, unique: true, lowercase: true, match: /^[a-z0-9-]+$/ },
agentName: { type: String, required: true, unique: true, lowercase: true, match: /^(@[a-z0-9-]+\/)?[a-z0-9-]+$/ },
displayName: { type: String, required: true },
description: { type: String, required: true },
readme: String,
Expand Down
35 changes: 33 additions & 2 deletions backend/models/Installable.ts
Original file line number Diff line number Diff line change
Expand Up @@ -234,9 +234,16 @@ export interface IVersionEntry {
export interface IInstallableStats {
totalInstalls: number;
activeInstalls: number;
forkCount: number;
lastActivity?: Date;
}

export interface IForkedFrom {
installableId: string;
version: string;
forkedAt: Date;
}

// ---------------------------------------------------------------------------
// Top-level document
// ---------------------------------------------------------------------------
Expand Down Expand Up @@ -279,6 +286,12 @@ export interface IInstallable extends Document {
// Version history (for marketplace packages with deprecation/rollback)
versions?: IVersionEntry[];

// Fork lineage
forkedFrom?: IForkedFrom;

// Long-form description for detail page
readme?: string;

// Stats
stats: IInstallableStats;

Expand Down Expand Up @@ -451,6 +464,15 @@ const VersionSubSchema = new Schema<IVersionEntry>(
{ _id: false },
);

const ForkedFromSubSchema = new Schema<IForkedFrom>(
{
installableId: { type: String, required: true },
version: { type: String, required: true },
forkedAt: { type: Date, required: true },
},
{ _id: false },
);

// ---------------------------------------------------------------------------
// Top-level schema
// ---------------------------------------------------------------------------
Expand All @@ -462,8 +484,8 @@ const InstallableSchema = new Schema<IInstallable>(
required: true,
unique: true,
lowercase: true,
// Allow either "bare-name" or "scope/name"
match: /^[a-z0-9-]+(\/[a-z0-9-]+)?$/,
// Allow "bare-name", "scope/name", or "@scope/name"
match: /^(@[a-z0-9-]+\/)?[a-z0-9-]+$/,
},
name: { type: String, required: true },
description: { type: String, required: true, default: '' },
Expand Down Expand Up @@ -503,9 +525,13 @@ const InstallableSchema = new Schema<IInstallable>(

versions: { type: [VersionSubSchema], default: undefined },

forkedFrom: { type: ForkedFromSubSchema },
readme: { type: String },

stats: {
totalInstalls: { type: Number, default: 0 },
activeInstalls: { type: Number, default: 0 },
forkCount: { type: Number, default: 0 },
lastActivity: { type: Date },
},
},
Expand All @@ -523,6 +549,11 @@ InstallableSchema.index({ source: 1, status: 1 });
InstallableSchema.index({ kind: 1, 'marketplace.published': 1 });
InstallableSchema.index({ 'marketplace.published': 1, 'marketplace.category': 1 });
InstallableSchema.index({ 'publisher.userId': 1 });
InstallableSchema.index({ 'forkedFrom.installableId': 1 });
InstallableSchema.index(
{ name: 'text', description: 'text', 'marketplace.tags': 'text' },
{ name: 'marketplace_text_search' },
);

// ---------------------------------------------------------------------------
// Model export (HMR guard)
Expand Down
Loading
Loading