feat(FN-3590): prioritize workspace source entry for bundled plugins and ad
This merge introduces eval score categorization with a new `eval-scoring.ts` module (FN-3601, FN-3390), hardened bundled plugin entry resolution to prioritize workspace source over installed copies (FN-3590), and documented reply-link threading behavior in the mailbox (FN-3598). It also adds mobile Fusion-Task-Id: FN-3590
This commit is contained in:
@@ -328,7 +328,7 @@ describe("GET /plugins/:id/settings", () => {
|
||||
expect(res.body).toEqual({ apiKey: "secret", enabled: true });
|
||||
});
|
||||
|
||||
it("returns 404 for non-existent plugin", async () => {
|
||||
it("returns 404 for non-existent non-bundled plugin", async () => {
|
||||
(pluginStore.getPlugin as ReturnType<typeof vi.fn>).mockRejectedValueOnce(
|
||||
Object.assign(new Error('Plugin "nonexistent" not found'), { code: "ENOENT" }),
|
||||
);
|
||||
@@ -339,6 +339,17 @@ describe("GET /plugins/:id/settings", () => {
|
||||
expect(res.body).toHaveProperty("error");
|
||||
});
|
||||
|
||||
it("returns empty settings for bundled runtime plugin before first install", async () => {
|
||||
(pluginStore.getPlugin as ReturnType<typeof vi.fn>).mockRejectedValueOnce(
|
||||
Object.assign(new Error('Plugin "fusion-plugin-hermes-runtime" not found'), { code: "ENOENT" }),
|
||||
);
|
||||
|
||||
const res = await GET(buildApp(), "/api/plugins/fusion-plugin-hermes-runtime/settings");
|
||||
|
||||
expect(res.status).toBe(200);
|
||||
expect(res.body).toEqual({});
|
||||
});
|
||||
|
||||
it("supports projectId query param scoping", async () => {
|
||||
const scopedPluginStore = createMockPluginStore();
|
||||
(scopedPluginStore.getPlugin as ReturnType<typeof vi.fn>).mockResolvedValueOnce({
|
||||
@@ -1054,6 +1065,23 @@ describe("PUT /plugins/:id/settings auto-install for bundled runtime plugins", (
|
||||
expect(ensure).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("returns 500 with intentional message when bundled auto-install reports missing bundle", async () => {
|
||||
(pluginStore.getPlugin as ReturnType<typeof vi.fn>).mockRejectedValueOnce(
|
||||
new Error("Plugin not found"),
|
||||
);
|
||||
const ensure = vi.fn().mockResolvedValue(false);
|
||||
|
||||
const res = await REQUEST(
|
||||
buildAppWithBundleHook(ensure),
|
||||
"PUT",
|
||||
"/api/plugins/fusion-plugin-hermes-runtime/settings",
|
||||
{ settings: { apiKey: "k" } },
|
||||
);
|
||||
|
||||
expect(res.status).toBe(500);
|
||||
expect(res.body.error).toContain("is unavailable in this build");
|
||||
});
|
||||
|
||||
it("returns 500 when auto-install throws", async () => {
|
||||
(pluginStore.getPlugin as ReturnType<typeof vi.fn>).mockRejectedValueOnce(
|
||||
new Error("Plugin not found"),
|
||||
|
||||
@@ -3247,7 +3247,15 @@ export function createApiRoutes(store: TaskStore, options?: ServerOptions): Rout
|
||||
const plugin = await pluginStore.getPlugin(id);
|
||||
res.json(plugin.settings);
|
||||
} catch (err: unknown) {
|
||||
if (err instanceof Error && (err instanceof Error ? err.message : String(err)).includes("not found")) {
|
||||
const isNotFoundError = err instanceof Error && (err instanceof Error ? err.message : String(err)).includes("not found");
|
||||
const isBundledFallback = BUNDLED_PLUGIN_RUNTIMES.some((r) => r.pluginId === id);
|
||||
if (isNotFoundError && isBundledFallback) {
|
||||
// Bundled runtime plugins can be surfaced in settings before they've
|
||||
// been lazily installed. Return empty defaults so cards can open.
|
||||
res.json({});
|
||||
return;
|
||||
}
|
||||
if (isNotFoundError) {
|
||||
throw notFound(`Plugin "${id}" not found`);
|
||||
}
|
||||
throw internalError(err instanceof Error ? err.message : "Unknown error");
|
||||
@@ -3606,8 +3614,16 @@ export function createApiRoutes(store: TaskStore, options?: ServerOptions): Rout
|
||||
}
|
||||
if (!alreadyRegistered) {
|
||||
try {
|
||||
await options.ensureBundledPluginInstalled(id);
|
||||
const installOk = await options.ensureBundledPluginInstalled(id);
|
||||
if (!installOk) {
|
||||
throw internalError(
|
||||
`Bundled plugin "${id}" is unavailable in this build and could not be auto-installed`,
|
||||
);
|
||||
}
|
||||
} catch (installErr) {
|
||||
if (installErr instanceof ApiError) {
|
||||
throw installErr;
|
||||
}
|
||||
throw internalError(
|
||||
`Failed to auto-install bundled plugin "${id}": ${installErr instanceof Error ? installErr.message : String(installErr)}`,
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user