feat(FN-2519): add remote tunnel manager for settings sync

- Add remote-access contracts, provider adapters, and a tunnel process manager with lifecycle handling
- Wire tunnel manager into ProjectEngine startup/shutdown flow and export new remote-access modules
- Update settings modal UX for remote auth URLs, including wrapping and related UI test coverage
- Document tunnel manager behavior and remote settings sync details in architecture, CLI, and settings docs
This commit is contained in:
Fusion
2026-04-26 02:20:28 -07:00
committed by gsxdsm
parent 7527a7ddaf
commit cb1da0c659
16 changed files with 1234 additions and 28 deletions

View File

@@ -2,6 +2,7 @@ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import { ProjectEngine } from "../project-engine.js";
import { runtimeLog } from "../logger.js";
import { aiMergeTask } from "../merger.js";
import { TunnelProcessManager } from "../remote-access/tunnel-process-manager.js";
const mocks = vi.hoisted(() => ({
syncInsightExtractionAutomation: vi.fn(),
@@ -234,6 +235,55 @@ describe("ProjectEngine auto-summarize wiring", () => {
});
});
describe("ProjectEngine remote tunnel manager wiring", () => {
beforeEach(() => {
vi.clearAllMocks();
const mockStore = createMockStore(baseSettings);
mocks.currentStore = mockStore.store;
});
it("is unavailable before start and available after start", async () => {
const engine = createEngine();
expect(engine.getRemoteTunnelManager()).toBeUndefined();
await engine.start();
expect(engine.getRemoteTunnelManager()).toBeInstanceOf(TunnelProcessManager);
await engine.stop();
expect(engine.getRemoteTunnelManager()).toBeUndefined();
});
it("calls tunnel manager stop once during shutdown", async () => {
const stopSpy = vi.spyOn(TunnelProcessManager.prototype, "stop").mockResolvedValueOnce(undefined);
const engine = createEngine();
await engine.start();
await engine.stop();
expect(stopSpy).toHaveBeenCalledTimes(1);
stopSpy.mockRestore();
});
it("warns when tunnel manager shutdown fails and clears manager reference", async () => {
const warnSpy = vi.spyOn(runtimeLog, "warn").mockImplementation(() => {});
const stopSpy = vi.spyOn(TunnelProcessManager.prototype, "stop").mockRejectedValueOnce(new Error("tunnel stop failed"));
const engine = createEngine();
await engine.start();
await engine.stop();
expect(warnSpy).toHaveBeenCalledWith(
expect.stringContaining("Tunnel process manager stop failed"),
);
expect(engine.getRemoteTunnelManager()).toBeUndefined();
stopSpy.mockRestore();
warnSpy.mockRestore();
});
});
describe("ProjectEngine shutdown merge handling", () => {
beforeEach(() => {
vi.clearAllMocks();