* feat: AI translations at release, 7 new languages and regional fallback

* fix: proper plural forms for count strings and full translations

* fix: plural forms for selected, hidden and active counts

* fix: synced plugin updates failing on desktop and server-only plugins shown as stopped

* fix: restore rounded corners on app icons and favicon

* feat: desktop app auto updates with automatic, ask first or never modes

* feat: community and custom plugin registries

* feat: plugin guide for AI tools in the SDK with a lint check

* fix: keep the terminal WebSocket open past nginx's 60s default (#1588)

* chore: update changelog

* feat: show plugin download counts only and stop CI builds inflating them

* feat: hide installed plugins in the browse tab by default

* chore: pin bundled plugins to latest releases

* chore: update translations

* fix: focusing a saved password field and saving no longer erases it

* fix: tell desktop users when their 2.8 data needs 2.9 first

* fix: point windows update info at the x64 installer

* fix: keep desktop client tunnel sockets alive behind proxies

* chore: pin bundled plugins to latest releases

* fix: cover client tunnel keepalive in the c2s runtime tests

* fix: typing after opening a host no longer goes to the host search

* fix: accept tab and = separators in SSH config import (#1598)

* chore: update changelog

* chore: drop plugin versions from core changelog

* chore: update translations

* chore: lint, format, and bump version to 26.10.1

---------

Co-authored-by: Darnel Hunter <68697450+innotelinc@users.noreply.github.com>
Co-authored-by: Iain <Iain1970@protonmail.com>
This commit is contained in:
Luke Gustafson
2026-10-12 11:35:48 -05:00
committed by GitHub
co-authored by Darnel Hunter Iain
parent 6873bc92bb
commit 62ac931468
174 changed files with 94334 additions and 39995 deletions
@@ -10,6 +10,7 @@ const state = vi.hoisted(() => ({
install: vi.fn(),
fetch: vi.fn(),
loadBundled: vi.fn(),
syncedKeys: vi.fn(async (_keys: unknown[]) => {}),
}));
vi.mock("../../../utils/logger.js", () => ({
@@ -29,6 +30,9 @@ vi.mock("../../../database/repositories/factory.js", () => ({
}),
createCurrentPluginPermissionGrantRepository: () => ({}),
}));
vi.mock("../../../plugins/registries.js", () => ({
setSyncedRegistryKeys: state.syncedKeys,
}));
vi.mock("../../../sync/client/http.js", () => ({
remoteFetch: state.fetch,
remoteJson: vi.fn(),
@@ -100,6 +104,19 @@ afterEach(() => {
});
describe("mirrorPlugins", () => {
it("trusts the server's custom registry keys before downloading", async () => {
vi.mocked(remoteJson).mockResolvedValue({
plugins: [],
registryKeys: ["a-key"],
});
await mirrorPlugins({} as never);
expect(state.syncedKeys).toHaveBeenCalledWith(["a-key"]);
vi.mocked(remoteJson).mockResolvedValue({ plugins: [] });
await mirrorPlugins({} as never);
expect(state.syncedKeys).toHaveBeenLastCalledWith([]);
});
it("keeps the installed copy when the download fails", async () => {
serverHas("1.1.0");
state.fetch.mockResolvedValue({ ok: false, status: 502 });