diff --git a/src/apps/cli/commands/p2p.ts b/src/apps/cli/commands/p2p.ts index c504ae01..f6d9f0e6 100644 --- a/src/apps/cli/commands/p2p.ts +++ b/src/apps/cli/commands/p2p.ts @@ -1,10 +1,9 @@ import type { LiveSyncBaseCore } from "@/LiveSyncBaseCore"; import { P2P_DEFAULT_SETTINGS } from "@vrtmrz/livesync-commonlib/compat/common/types"; import type { ServiceContext } from "@vrtmrz/livesync-commonlib/context"; -import { LiveSyncTrysteroReplicator } from "@vrtmrz/livesync-commonlib/compat/replication/trystero/LiveSyncTrysteroReplicator"; import { compatGlobal } from "@vrtmrz/livesync-commonlib/compat/common/coreEnvFunctions"; import { LiveSyncError } from "@vrtmrz/livesync-commonlib/compat/common/LSError"; -import { getPeerConnectionStats } from "@vrtmrz/livesync-commonlib/compat/rpc/transports/DiagRTCPeerConnections.utils"; +import type { P2PPeerConnectionMetrics, P2PServiceViews } from "@vrtmrz/livesync-commonlib/p2p"; import { fsPromises } from "@vrtmrz/livesync-commonlib/node"; type CLIP2PPeer = { @@ -12,12 +11,7 @@ type CLIP2PPeer = { name: string; }; -type CandidateSummary = { - id: string; - candidateType: string; - protocol: string; - relayProtocol: string; -}; +type CLIP2PService = Pick; function delay(ms: number): Promise { return new Promise((resolve) => compatGlobal.setTimeout(resolve, ms)); @@ -43,35 +37,35 @@ function validateP2PSettings(core: LiveSyncBaseCore) { settings.P2P_IsHeadless = true; } -async function createReplicator(core: LiveSyncBaseCore): Promise { +function requireP2PService( + core: LiveSyncBaseCore, + service: CLIP2PService | undefined +): CLIP2PService { validateP2PSettings(core); - const replicator = await core.services.replicator.getNewReplicator(); - if (!replicator) { - throw new Error("Failed to create replicator instance. Ensure P2P is enabled in settings."); + if (!service) { + throw new Error("P2P service is not available. Ensure the P2P feature was composed for this CLI process."); } - if (!(replicator instanceof LiveSyncTrysteroReplicator)) { - throw new Error("Unexpected replicator type. Expected LiveSyncTrysteroReplicator."); - } - return replicator; + return service; } -function getSortedPeers(replicator: LiveSyncTrysteroReplicator): CLIP2PPeer[] { - return [...replicator.knownAdvertisements] +function getSortedPeers(service: Pick): CLIP2PPeer[] { + return [...service.peerDirectory.getPeers()] .map((peer) => ({ peerId: peer.peerId, name: peer.name })) .sort((a, b) => a.peerId.localeCompare(b.peerId)); } export async function collectPeers( core: LiveSyncBaseCore, + p2pService: CLIP2PService | undefined, timeoutSec: number ): Promise { - const replicator = await createReplicator(core); - await replicator.open(); + const service = requireP2PService(core, p2pService); + await service.transportLifecycle.connect(); try { await delay(timeoutSec * 1000); - return getSortedPeers(replicator); + return getSortedPeers(service); } finally { - await replicator.close(); + await service.transportLifecycle.disconnect(); } } @@ -90,32 +84,8 @@ function resolvePeer(peers: CLIP2PPeer[], peerToken: string): CLIP2PPeer | undef return undefined; } -function getReportValue( - report: Record | undefined, - key: string -): T | "unknown" { - const value = report?.[key]; - return typeof value === "string" || typeof value === "number" ? (value as T) : "unknown"; -} - -function summariseCandidate(reports: unknown[], candidateId: string): CandidateSummary | undefined { - if (candidateId === "unknown") { - return undefined; - } - const report = reports.map((r) => r as Record).find((r) => r.id === candidateId); - if (!report) { - return undefined; - } - return { - id: candidateId, - candidateType: getReportValue(report, "candidateType"), - protocol: getReportValue(report, "protocol"), - relayProtocol: getReportValue(report, "relayProtocol"), - }; -} - async function writePeerConnectionStatsIfRequested( - replicator: LiveSyncTrysteroReplicator, + service: Pick, peer: CLIP2PPeer ): Promise { const outputPath = process.env.LIVESYNC_P2P_STATS_JSONL?.trim(); @@ -123,21 +93,30 @@ async function writePeerConnectionStatsIfRequested( return; } - const peerConnection = replicator.rawHost?.room?.getPeers()[peer.peerId]; - const stats = peerConnection ? await getPeerConnectionStats(`cli-p2p-${peer.peerId}`, peerConnection) : undefined; - const localCandidate = summariseCandidate(stats?.reports ?? [], stats?.localCandidateId ?? "unknown"); - const remoteCandidate = summariseCandidate(stats?.reports ?? [], stats?.remoteCandidateId ?? "unknown"); + const stats = await service.diagnostics.getPeerConnectionMetrics(peer.peerId); + const payload = createPeerConnectionStatsPayload(peer, stats, new Date().toISOString()); + await fsPromises.appendFile(outputPath, `${JSON.stringify(payload)}\n`, "utf8"); +} + +/** Build the stable JSONL record consumed by the P2P benchmark harnesses. */ +export function createPeerConnectionStatsPayload( + peer: CLIP2PPeer, + stats: P2PPeerConnectionMetrics | undefined, + generatedAt: string +) { + const localCandidate = stats?.localCandidate; + const remoteCandidate = stats?.remoteCandidate; const selectedPath = localCandidate && remoteCandidate ? `${localCandidate.candidateType}<->${remoteCandidate.candidateType}` : "unknown"; const payload = { - generatedAt: new Date().toISOString(), + generatedAt, command: "p2p-sync", peerId: peer.peerId, peerName: peer.name, - candidatePathCollected: !!stats?.selectedPair, + candidatePathCollected: stats?.selectedPairPresent ?? false, selectedPath, selectedPair: stats ? { @@ -155,23 +134,24 @@ async function writePeerConnectionStatsIfRequested( localCandidate, remoteCandidate, }; - await fsPromises.appendFile(outputPath, `${JSON.stringify(payload)}\n`, "utf8"); + return payload; } export async function syncWithPeer( core: LiveSyncBaseCore, + p2pService: CLIP2PService | undefined, peerToken: string, timeoutSec: number ): Promise { - const replicator = await createReplicator(core); - await replicator.open(); + const service = requireP2PService(core, p2pService); + await service.transportLifecycle.connect(); try { const timeoutMs = timeoutSec * 1000; const start = Date.now(); let targetPeer: CLIP2PPeer | undefined; while (Date.now() - start <= timeoutMs) { - const peers = getSortedPeers(replicator); + const peers = getSortedPeers(service); targetPeer = resolvePeer(peers, peerToken); if (targetPeer) { break; @@ -183,11 +163,11 @@ export async function syncWithPeer( throw new Error(`Peer '${peerToken}' was not found within ${timeoutSec} seconds`); } - const pullResult = await replicator.replicateFrom(targetPeer.peerId, false); + const pullResult = await service.targetedTransfer.pullFromPeer(targetPeer.peerId, { showNotice: false }); if (pullResult && "error" in pullResult && pullResult.error) { throw pullResult.error instanceof Error ? pullResult.error : LiveSyncError.fromError(pullResult.error); } - const pushResult = await replicator.requestSynchroniseToPeer(targetPeer.peerId); + const pushResult = await service.targetedTransfer.requestPushToPeer(targetPeer.peerId); if (!pushResult || pushResult.ok !== true) { const err: unknown = pushResult && "error" in pushResult ? pushResult.error : undefined; throw err instanceof Error @@ -195,15 +175,18 @@ export async function syncWithPeer( : LiveSyncError.fromError(err ?? "P2P sync failed while requesting remote sync"); } - await writePeerConnectionStatsIfRequested(replicator, targetPeer); + await writePeerConnectionStatsIfRequested(service, targetPeer); return targetPeer; } finally { - await replicator.close(); + await service.transportLifecycle.disconnect(); } } -export async function openP2PHost(core: LiveSyncBaseCore): Promise { - const replicator = await createReplicator(core); - await replicator.open(); - return replicator; +export async function openP2PHost( + core: LiveSyncBaseCore, + p2pService: CLIP2PService | undefined +): Promise { + const service = requireP2PService(core, p2pService); + await service.transportLifecycle.connect(); + return service; } diff --git a/src/apps/cli/commands/p2p.unit.spec.ts b/src/apps/cli/commands/p2p.unit.spec.ts index d0c2342c..6cee45bc 100644 --- a/src/apps/cli/commands/p2p.unit.spec.ts +++ b/src/apps/cli/commands/p2p.unit.spec.ts @@ -1,5 +1,40 @@ -import { describe, expect, it } from "vitest"; -import { parseTimeoutSeconds } from "./p2p"; +import { describe, expect, it, vi } from "vitest"; +import { collectPeers, createPeerConnectionStatsPayload, parseTimeoutSeconds, syncWithPeer } from "./p2p"; + +function createCore() { + const settings = { P2P_Enabled: true, P2P_AppID: "app-id", P2P_IsHeadless: false }; + return { + services: { + setting: { currentSettings: () => settings }, + replicator: { getNewReplicator: vi.fn(() => Promise.reject(new Error("must not be called"))) }, + }, + } as never; +} + +function createP2PService() { + const connect = vi.fn(async () => undefined); + const disconnect = vi.fn(async () => undefined); + const pullFromPeer = vi.fn(async () => ({ ok: true })); + const requestPushToPeer = vi.fn(async () => ({ ok: true })); + return { + service: { + transportLifecycle: { isConnected: false, connect, disconnect }, + peerDirectory: { + getPeers: () => [{ peerId: "peer-a", name: "Peer A", platform: "test" }], + }, + targetedTransfer: { + pullFromPeer, + requestPushToPeer, + synchroniseWithPeer: vi.fn(), + }, + diagnostics: { requestStatus: vi.fn(), getPeerConnectionMetrics: vi.fn() }, + }, + connect, + disconnect, + pullFromPeer, + requestPushToPeer, + }; +} describe("p2p command helpers", () => { it("accepts non-negative timeout", () => { @@ -15,4 +50,88 @@ describe("p2p command helpers", () => { "p2p-sync requires a non-negative timeout in seconds" ); }); + + it("collects peers through service views without acquiring a concrete replicator", async () => { + const { service, connect, disconnect } = createP2PService(); + + await expect(collectPeers(createCore(), service as never, 0)).resolves.toEqual([ + { peerId: "peer-a", name: "Peer A" }, + ]); + expect(connect).toHaveBeenCalledOnce(); + expect(disconnect).toHaveBeenCalledOnce(); + }); + + it("synchronises through the targeted-transfer view", async () => { + const { service, pullFromPeer, requestPushToPeer } = createP2PService(); + + await expect(syncWithPeer(createCore(), service as never, "peer-a", 0)).resolves.toEqual({ + peerId: "peer-a", + name: "Peer A", + }); + expect(pullFromPeer).toHaveBeenCalledWith("peer-a", { showNotice: false }); + expect(requestPushToPeer).toHaveBeenCalledWith("peer-a"); + }); + + it("preserves the benchmark diagnostics JSONL contract", () => { + expect( + createPeerConnectionStatsPayload( + { peerId: "peer-a", name: "Peer A" }, + { + selectedPairPresent: true, + selectedPairId: "pair-1", + state: "succeeded", + currentRoundTripTime: 0.01, + totalRoundTripTime: 0.1, + requestsSent: 3, + responsesReceived: 3, + packetsDiscardedOnSend: 0, + bytesSent: 100, + bytesReceived: 200, + localCandidate: { + id: "local-1", + candidateType: "host", + protocol: "udp", + relayProtocol: "unknown", + }, + remoteCandidate: { + id: "remote-1", + candidateType: "relay", + protocol: "udp", + relayProtocol: "udp", + }, + }, + "2026-08-27T00:00:00.000Z" + ) + ).toEqual({ + generatedAt: "2026-08-27T00:00:00.000Z", + command: "p2p-sync", + peerId: "peer-a", + peerName: "Peer A", + candidatePathCollected: true, + selectedPath: "host<->relay", + selectedPair: { + id: "pair-1", + state: "succeeded", + currentRoundTripTime: 0.01, + totalRoundTripTime: 0.1, + requestsSent: 3, + responsesReceived: 3, + packetsDiscardedOnSend: 0, + bytesSent: 100, + bytesReceived: 200, + }, + localCandidate: { + id: "local-1", + candidateType: "host", + protocol: "udp", + relayProtocol: "unknown", + }, + remoteCandidate: { + id: "remote-1", + candidateType: "relay", + protocol: "udp", + relayProtocol: "udp", + }, + }); + }); }); diff --git a/src/apps/cli/commands/runCommand.ts b/src/apps/cli/commands/runCommand.ts index eeb5bc51..2d4a4a68 100644 --- a/src/apps/cli/commands/runCommand.ts +++ b/src/apps/cli/commands/runCommand.ts @@ -252,7 +252,7 @@ export async function runCommand(options: CLIOptions, context: CLICommandContext } const timeoutSec = parseTimeoutSeconds(options.commandArgs[0], "p2p-peers"); writeStderrLine(standardIo, `[Command] p2p-peers timeout=${timeoutSec}s`); - const peers = await collectPeers(core, timeoutSec); + const peers = await collectPeers(core, context.p2pReplicator, timeoutSec); if (peers.length > 0) { standardIo.writeStdout(peers.map((peer) => `[peer]\t${peer.peerId}\t${peer.name}`).join("\n") + "\n"); } @@ -269,14 +269,14 @@ export async function runCommand(options: CLIOptions, context: CLICommandContext } const timeoutSec = parseTimeoutSeconds(options.commandArgs[1], "p2p-sync"); writeStderrLine(standardIo, `[Command] p2p-sync peer=${peerToken} timeout=${timeoutSec}s`); - const peer = await syncWithPeer(core, peerToken, timeoutSec); + const peer = await syncWithPeer(core, context.p2pReplicator, peerToken, timeoutSec); writeStderrLine(standardIo, `[Done] P2P sync completed with ${peer.name} (${peer.peerId})`); return true; } if (options.command === "p2p-host") { writeStderrLine(standardIo, "[Command] p2p-host"); - await openP2PHost(core); + await openP2PHost(core, context.p2pReplicator); writeStderrLine(standardIo, "[Ready] P2P host is running. Press Ctrl+C to stop."); await new Promise(() => {}); return true; diff --git a/src/apps/cli/commands/types.ts b/src/apps/cli/commands/types.ts index 7dfa8d80..3b87a51c 100644 --- a/src/apps/cli/commands/types.ts +++ b/src/apps/cli/commands/types.ts @@ -1,7 +1,7 @@ import { LiveSyncBaseCore } from "@/LiveSyncBaseCore"; import type { ObsidianLiveSyncSettings } from "@vrtmrz/livesync-commonlib/compat/common/types"; import type { NodeServiceContext } from "@/apps/cli/services/NodeServiceContext"; -import type { UseP2PReplicatorResult } from "@vrtmrz/livesync-commonlib/compat/replication/trystero/UseP2PReplicatorResult"; +import type { UseP2PReplicatorResult } from "@vrtmrz/livesync-commonlib/p2p"; export type CLICommand = | "daemon" diff --git a/src/apps/cli/main.ts b/src/apps/cli/main.ts index 38b2b033..240d7694 100644 --- a/src/apps/cli/main.ts +++ b/src/apps/cli/main.ts @@ -23,8 +23,7 @@ import type { CLICommand, CLICommandContext, CLIOptions } from "./commands/types import { getPathFromUXFileInfo } from "@vrtmrz/livesync-commonlib/compat/common/typeUtils"; import { stripAllPrefixes } from "@vrtmrz/livesync-commonlib/compat/string_and_binary/path"; import { IgnoreRules } from "./serviceModules/IgnoreRules"; -import { useP2PReplicatorFeature } from "@vrtmrz/livesync-commonlib/compat/replication/trystero/useP2PReplicatorFeature"; -import type { UseP2PReplicatorResult } from "@vrtmrz/livesync-commonlib/compat/replication/trystero/UseP2PReplicatorResult"; +import { useP2PReplicatorFeature, type UseP2PReplicatorResult } from "@vrtmrz/livesync-commonlib/p2p"; import { createNodeStandardIo, fsPromises as fs, path } from "@vrtmrz/livesync-commonlib/node"; import type { StandardIo } from "@vrtmrz/livesync-commonlib/context"; import { writeStderrLine, writeStdoutLine } from "./cliOutput"; @@ -290,7 +289,10 @@ export async function main( ) { const options = parseArgs(standardIo); if (options.interval && options.command !== "daemon") { - writeStderrLine(standardIo, `Warning: --interval is only used in daemon mode, ignored for '${options.command}'`); + writeStderrLine( + standardIo, + `Warning: --interval is only used in daemon mode, ignored for '${options.command}'` + ); } const avoidStdoutNoise = options.command === "cat" || @@ -420,7 +422,10 @@ export async function main( // In daemon mode the default handler must run so changes are applied to the filesystem. if (options.command !== "daemon") { serviceHubInstance.replication.processSynchroniseResult.addHandler(async () => { - writeStderrLine(standardIo, `[Info] Replication result received, but not processed automatically in CLI mode.`); + writeStderrLine( + standardIo, + `[Info] Replication result received, but not processed automatically in CLI mode.` + ); return await Promise.resolve(true); }, -100); } diff --git a/src/apps/cli/test-support/p2p-replicator-replacement.ts b/src/apps/cli/test-support/p2p-replicator-replacement.ts index 18ab6bef..1f7f6447 100644 --- a/src/apps/cli/test-support/p2p-replicator-replacement.ts +++ b/src/apps/cli/test-support/p2p-replicator-replacement.ts @@ -1,6 +1,6 @@ import type { FilePathWithPrefix } from "@vrtmrz/livesync-commonlib/compat/common/types"; -import { LiveSyncTrysteroReplicator } from "@vrtmrz/livesync-commonlib/compat/replication/trystero/LiveSyncTrysteroReplicator"; import { compatGlobal } from "@vrtmrz/livesync-commonlib/compat/common/coreEnvFunctions"; +import type { P2PServiceViews } from "@vrtmrz/livesync-commonlib/p2p"; import type { CLICommandContext } from "@/apps/cli/commands/types"; import { openP2PHost } from "@/apps/cli/commands/p2p"; @@ -15,32 +15,35 @@ function describeError(value: unknown): string { return value instanceof Error ? (value.stack ?? value.message) : String(value); } -async function waitForServing(replicator: LiveSyncTrysteroReplicator, timeoutMs: number): Promise { +type ProbeP2PService = Pick; + +async function waitForServing(service: ProbeP2PService, timeoutMs: number): Promise { const started = Date.now(); while (Date.now() - started <= timeoutMs) { - if (replicator.server?.isServing) return; + if (service.transportLifecycle.isConnected) return; await delay(200); } - throw new Error("The replacement P2P replicator did not start serving within the timeout"); + throw new Error("The stable P2P service did not start serving within the timeout"); } async function waitForPeer( - replicator: LiveSyncTrysteroReplicator, + service: ProbeP2PService, targetPeer: string, timeoutMs: number ): Promise<{ peerId: string; name: string }> { const started = Date.now(); while (Date.now() - started <= timeoutMs) { - const peer = replicator.knownAdvertisements.find( - (candidate) => candidate.name === targetPeer || candidate.peerId === targetPeer - ); + const peer = service.peerDirectory + .getPeers() + .find((candidate) => candidate.name === targetPeer || candidate.peerId === targetPeer); if (peer) return peer; await delay(200); } - const knownPeers = replicator.knownAdvertisements.map((peer) => `${peer.name} (${peer.peerId})`).join(", "); - throw new Error( - `Peer '${targetPeer}' was not discovered within the timeout. Known peers: ${knownPeers || "none"}` - ); + const knownPeers = service.peerDirectory + .getPeers() + .map((peer) => `${peer.name} (${peer.peerId})`) + .join(", "); + throw new Error(`Peer '${targetPeer}' was not discovered within the timeout. Known peers: ${knownPeers || "none"}`); } function assertPullSucceeded(result: unknown): void { @@ -50,15 +53,17 @@ function assertPullSucceeded(result: unknown): void { } async function communicateWithPeer( - replicator: LiveSyncTrysteroReplicator, + service: ProbeP2PService, targetPeer: string, timeoutMs: number ): Promise<{ peerId: string; name: string }> { - await replicator.open(); - await waitForServing(replicator, timeoutMs); - const peer = await waitForPeer(replicator, targetPeer, timeoutMs); - assertPullSucceeded(await replicator.replicateFrom(peer.peerId, false)); - const pushResult = await replicator.requestSynchroniseToPeer(peer.peerId); + if (!service.transportLifecycle.isConnected) { + await service.transportLifecycle.connect(); + } + await waitForServing(service, timeoutMs); + const peer = await waitForPeer(service, targetPeer, timeoutMs); + assertPullSucceeded(await service.targetedTransfer.pullFromPeer(peer.peerId, { showNotice: false })); + const pushResult = await service.targetedTransfer.requestPushToPeer(peer.peerId); if (!pushResult || pushResult.ok !== true) { throw new Error(`P2P push failed: ${describeError(pushResult?.error)}`); } @@ -78,35 +83,39 @@ export async function runP2PReplicatorReplacementProbe( throw new Error("The CLI did not expose its P2P service-feature result to the integration probe"); } - const firstReplicator = await openP2PHost(core); - if (p2pReplicator.replicator !== firstReplicator) { - throw new Error("The P2P service feature did not expose the newly created replicator"); + const initialActiveReplicator = core.services.replicator.getActiveReplicator(); + if (!initialActiveReplicator) { + throw new Error("The CLI did not activate the initial P2P Replicator adapter"); } + const compatibilityFacade = p2pReplicator.replicator; + const p2pService = await openP2PHost(core, p2pReplicator); - const firstPeer = await communicateWithPeer(firstReplicator, targetPeer, timeoutMs); + const firstPeer = await communicateWithPeer(p2pService, targetPeer, timeoutMs); const initialised = await core.services.databaseEvents.initialiseDatabase(false, true, false); if (!initialised) { throw new Error("Database reinitialisation failed during the P2P replacement probe"); } - const replacementReplicator = p2pReplicator.replicator; - if (core.services.replicator.getActiveReplicator() !== replacementReplicator) { - throw new Error("ReplicatorService did not activate the P2P service feature's replacement replicator"); + const replacementActiveReplicator = core.services.replicator.getActiveReplicator(); + if (!replacementActiveReplicator) { + throw new Error("ReplicatorService did not activate a replacement P2P Replicator adapter"); } - if (replacementReplicator === firstReplicator) { - throw new Error("Database reinitialisation retained the previous P2P replicator instance"); + if (replacementActiveReplicator === initialActiveReplicator) { + throw new Error("Database reinitialisation retained the previous active P2P Replicator adapter"); } - if (firstReplicator.server !== undefined) { - throw new Error("The previous P2P replicator remained open after replacement"); + if (p2pReplicator.replicator !== compatibilityFacade) { + throw new Error("Database reinitialisation replaced the stable P2P service compatibility facade"); + } + if (p2pService.transportLifecycle.isConnected) { + throw new Error("Database reinitialisation left the database-bound P2P room open"); } const settings = core.services.setting.currentSettings(); settings.P2P_AutoStart = true; await core.services.control.applySettings(); - const resumedReplicator = p2pReplicator.replicator; - await waitForServing(resumedReplicator, timeoutMs); - if (firstReplicator.server !== undefined) { - throw new Error("A setting event reopened the previous P2P replicator"); + await waitForServing(p2pService, timeoutMs); + if (p2pReplicator.replicator !== compatibilityFacade) { + throw new Error("A setting event replaced the stable P2P service compatibility facade"); } const encoded = new TextEncoder().encode(noteContent); @@ -118,7 +127,7 @@ export async function runP2PReplicatorReplacementProbe( }); await core.serviceModules.fileHandler.storeFileToDB(notePath as FilePathWithPrefix, true); - const replacementPeer = await communicateWithPeer(resumedReplicator, targetPeer, timeoutMs); + const replacementPeer = await communicateWithPeer(p2pService, targetPeer, timeoutMs); if (replacementPeer.name !== firstPeer.name) { throw new Error( `The replacement replicator reached '${replacementPeer.name}' instead of the original peer '${firstPeer.name}'` @@ -126,7 +135,7 @@ export async function runP2PReplicatorReplacementProbe( } core.services.context.standardIo.writeStdout( - `[Probe] P2P replicator replaced, old transport stayed closed, and ${notePath} was sent through the replacement.\n` + `[Probe] The active P2P adapter was replaced, the stable service reopened, and ${notePath} was sent through it.\n` ); return true; } diff --git a/src/apps/cli/testdeno/test-p2p-replicator-replacement.ts b/src/apps/cli/testdeno/test-p2p-replicator-replacement.ts index a6075535..231337e5 100644 --- a/src/apps/cli/testdeno/test-p2p-replicator-replacement.ts +++ b/src/apps/cli/testdeno/test-p2p-replicator-replacement.ts @@ -39,7 +39,7 @@ async function runReplacementProbe( }; } -Deno.test("p2p lifecycle: replacement keeps real CLI communication on the current replicator", async () => { +Deno.test("p2p lifecycle: active-adapter replacement keeps real CLI communication on the stable service", async () => { const relay = Deno.env.get("RELAY") ?? "ws://localhost:4000/"; const peersTimeout = Number(Deno.env.get("PEERS_TIMEOUT") ?? "20"); const syncTimeout = Number(Deno.env.get("SYNC_TIMEOUT") ?? "60"); @@ -82,11 +82,8 @@ Deno.test("p2p lifecycle: replacement keeps real CLI communication on the curren try { await host.waitUntilContains("P2P host is running", 20000); const probe = await runReplacementProbe(probeVault, probeSettings, hostPeerName, probeTimeoutMs); - assert( - probe.code === 0, - `P2P replacement probe failed\nstdout: ${probe.stdout}\nstderr: ${probe.stderr}` - ); - assertStringIncludes(probe.stdout, "[Probe] P2P replicator replaced"); + assert(probe.code === 0, `P2P replacement probe failed\nstdout: ${probe.stdout}\nstderr: ${probe.stderr}`); + assertStringIncludes(probe.stdout, "[Probe] The active P2P adapter was replaced"); const syncResult = await runCli( verifierVault, diff --git a/src/main.ts b/src/main.ts index 4dd779c6..5a0642d0 100644 --- a/src/main.ts +++ b/src/main.ts @@ -39,8 +39,7 @@ import { useSetupProtocolFeature } from "./serviceFeatures/setupObsidian/setupPr import { useSetupQRCodeFeature } from "@/serviceFeatures/setupObsidian/qrCode"; import { useSetupURIFeature } from "@/serviceFeatures/setupObsidian/setupUri"; import { useSetupManagerHandlersFeature } from "./serviceFeatures/setupObsidian/setupManagerHandlers.ts"; -import { useP2PReplicatorFeature } from "@vrtmrz/livesync-commonlib/compat/replication/trystero/useP2PReplicatorFeature"; -import { useP2PReplicatorCommands } from "@vrtmrz/livesync-commonlib/compat/replication/trystero/useP2PReplicatorCommands"; +import { useP2PReplicatorCommands, useP2PReplicatorFeature } from "@vrtmrz/livesync-commonlib/p2p"; import { useP2PReplicatorUI } from "./serviceFeatures/useP2PReplicatorUI.ts"; import { useReviewHarness } from "./serviceFeatures/useReviewHarness.ts"; import { createOpenReplicationUI, createOpenRebuildUI } from "./features/P2PSync/P2PReplicator/P2PReplicationUI.ts";