From 8969a713a1f7d73b58e7b60a2e968ffdbd4da7e1 Mon Sep 17 00:00:00 2001 From: Paperclip Bot Date: Fri, 5 Jun 2026 08:13:45 +0000 Subject: [PATCH] Scope plugin config bridge reads to invocation company --- .../plugins/sdk/src/host-client-factory.ts | 12 +++++- .../__tests__/plugin-config-bridge.test.ts | 42 +++++++++++++++++++ 2 files changed, 52 insertions(+), 2 deletions(-) create mode 100644 server/src/__tests__/plugin-config-bridge.test.ts diff --git a/packages/plugins/sdk/src/host-client-factory.ts b/packages/plugins/sdk/src/host-client-factory.ts index ec58bf4e..94d0e1fa 100644 --- a/packages/plugins/sdk/src/host-client-factory.ts +++ b/packages/plugins/sdk/src/host-client-factory.ts @@ -627,8 +627,16 @@ export function createHostClientHandlers( return { // Config - "config.get": gated("config.get", async (params) => { - return services.config.get(params); + "config.get": gated("config.get", async (params, context) => { + const scopedCompanyId = readNonEmptyString(context?.invocationScope?.companyId); + const explicitCompanyId = Object.prototype.hasOwnProperty.call(params ?? {}, "companyId") + ? params.companyId ?? null + : undefined; + return services.config.get( + explicitCompanyId === undefined + ? (scopedCompanyId ? { companyId: scopedCompanyId } : {}) + : { companyId: explicitCompanyId }, + ); }), "localFolders.declarations": gated("localFolders.declarations", async (params) => { diff --git a/server/src/__tests__/plugin-config-bridge.test.ts b/server/src/__tests__/plugin-config-bridge.test.ts new file mode 100644 index 00000000..57d0a1e1 --- /dev/null +++ b/server/src/__tests__/plugin-config-bridge.test.ts @@ -0,0 +1,42 @@ +import { describe, expect, it, vi } from "vitest"; +import { createHostClientHandlers } from "../../../packages/plugins/sdk/src/host-client-factory.js"; + +describe("plugin config bridge scoping", () => { + it("falls back to the invocation company scope for config.get when the worker omits companyId", async () => { + const getConfig = vi.fn(async (params: { companyId?: string | null }) => ({ + scope: params.companyId ?? null, + })); + + const handlers = createHostClientHandlers({ + pluginId: "test.plugin", + capabilities: [], + services: { + config: { get: getConfig }, + } as never, + }); + + await expect( + handlers["config.get"]({}, { invocationScope: { companyId: "company-a" } }), + ).resolves.toEqual({ scope: "company-a" }); + expect(getConfig).toHaveBeenCalledWith({ companyId: "company-a" }); + }); + + it("preserves an explicit global config.get request even inside a company-scoped invocation", async () => { + const getConfig = vi.fn(async (params: { companyId?: string | null }) => ({ + scope: params.companyId ?? null, + })); + + const handlers = createHostClientHandlers({ + pluginId: "test.plugin", + capabilities: [], + services: { + config: { get: getConfig }, + } as never, + }); + + await expect( + handlers["config.get"]({ companyId: null }, { invocationScope: { companyId: "company-a" } }), + ).resolves.toEqual({ scope: null }); + expect(getConfig).toHaveBeenCalledWith({ companyId: null }); + }); +});