From 2824502c7b5cbb4ddb89fc08b4401bdae14e0b86 Mon Sep 17 00:00:00 2001 From: Dotta Date: Mon, 4 May 2026 15:45:02 -0500 Subject: [PATCH] Keep managed project lookup read-only Co-Authored-By: Paperclip --- .../plugin-orchestration-apis.test.ts | 15 ++++++++ server/src/services/plugin-host-services.ts | 1 + server/src/services/projects.ts | 35 ++++++++++++++++--- 3 files changed, 47 insertions(+), 4 deletions(-) diff --git a/server/src/__tests__/plugin-orchestration-apis.test.ts b/server/src/__tests__/plugin-orchestration-apis.test.ts index eb06c3e5671..402419c68b8 100644 --- a/server/src/__tests__/plugin-orchestration-apis.test.ts +++ b/server/src/__tests__/plugin-orchestration-apis.test.ts @@ -425,6 +425,21 @@ describeEmbeddedPostgres("plugin orchestration APIs", () => { }); const services = buildHostServices(db, pluginId, "paperclip.missions", createEventBusStub()); + const missing = await services.projects.getManaged({ companyId, projectKey: "operations" }); + expect(missing.status).toBe("missing"); + expect(missing.projectId).toBeNull(); + await expect( + db + .select() + .from(pluginManagedResources) + .where(and( + eq(pluginManagedResources.companyId, companyId), + eq(pluginManagedResources.pluginId, pluginId), + eq(pluginManagedResources.resourceKind, "project"), + eq(pluginManagedResources.resourceKey, "operations"), + )), + ).resolves.toHaveLength(0); + const created = await services.projects.reconcileManaged({ companyId, projectKey: "operations" }); expect(created.status).toBe("created"); diff --git a/server/src/services/plugin-host-services.ts b/server/src/services/plugin-host-services.ts index 0c045f2faf0..e97ab8d0a8c 100644 --- a/server/src/services/plugin-host-services.ts +++ b/server/src/services/plugin-host-services.ts @@ -1159,6 +1159,7 @@ export function buildHostServices( pluginId, pluginKey, projectKey: params.projectKey, + createIfMissing: false, }); }, async reconcileManaged(params) { diff --git a/server/src/services/projects.ts b/server/src/services/projects.ts index b44307cedcd..4d568c68706 100644 --- a/server/src/services/projects.ts +++ b/server/src/services/projects.ts @@ -536,6 +536,7 @@ export function projectService(db: Db) { pluginKey: string; projectKey: string; reset?: boolean; + createIfMissing?: boolean; }): Promise => { const plugin = await db .select({ id: plugins.id, pluginKey: plugins.pluginKey, manifestJson: plugins.manifestJson }) @@ -598,10 +599,12 @@ export function projectService(db: Db) { }) .where(and(eq(projects.companyId, input.companyId), eq(projects.id, existingBinding.resourceId))); } - await db - .update(pluginManagedResources) - .set({ defaultsJson: defaults, updatedAt: new Date() }) - .where(eq(pluginManagedResources.id, existingBinding.id)); + if (input.createIfMissing !== false) { + await db + .update(pluginManagedResources) + .set({ defaultsJson: defaults, updatedAt: new Date() }) + .where(eq(pluginManagedResources.id, existingBinding.id)); + } const project = await getProjectById(existingBinding.resourceId); return { pluginKey: input.pluginKey, @@ -614,6 +617,18 @@ export function projectService(db: Db) { }; } + if (input.createIfMissing === false) { + return { + pluginKey: input.pluginKey, + resourceKind: "project", + resourceKey: input.projectKey, + companyId: input.companyId, + projectId: null, + project: null, + status: "missing", + }; + } + const project = await createProject(input.companyId, { name: declaration.displayName, description: declaration.description ?? null, @@ -636,6 +651,18 @@ export function projectService(db: Db) { }; } + if (input.createIfMissing === false) { + return { + pluginKey: input.pluginKey, + resourceKind: "project", + resourceKey: input.projectKey, + companyId: input.companyId, + projectId: null, + project: null, + status: "missing", + }; + } + const project = await createProject(input.companyId, { name: declaration.displayName, description: declaration.description ?? null,