@artenx/dshb 0.1.23 → 0.1.24

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -3,7 +3,7 @@ import { n as SyncStateStore } from "../../stores-CKyEt0yk.js";
3
3
  import { n as SyncNodeEntry, s as SyncNodeService } from "../../node-sync-3Z9QLz2n.js";
4
4
  import { SyncPeerManager } from "./peer-manager.js";
5
5
  import { f as SyncSessionService } from "../../session-sync-C2j2rk9K.js";
6
- import { s as SyncSshTransport } from "../../ssh-transport-DGkQh-HA.js";
6
+ import { s as SyncSshTransport } from "../../ssh-transport-gb2PVatu.js";
7
7
  //#region src/core/sync/coordinator.d.ts
8
8
  interface NodeTransferPlan {
9
9
  toLocal: SyncNodeEntry[];
@@ -136,7 +136,21 @@ function assertSelectedSessionEntries(expectedEntries, selectedEntries, side) {
136
136
  const hasExpectedEventSummary = expectedEntry.eventCount !== void 0 || expectedEntry.eventDigest !== void 0 || expectedEntry.inheritedEventCount !== void 0;
137
137
  const expectedWorkspace = metadata.workspaceOrigin;
138
138
  const currentWorkspace = current?.workspaceOrigin;
139
- if (!current || current.localEntityId !== metadata.localEntityId || compareSyncRevision(current.revision, metadata.revision) !== 0 || current.contentDigest !== metadata.contentDigest || current.stateDigest !== metadata.stateDigest || Boolean(current.deleted) !== Boolean(metadata.deleted) || expectedWorkspace?.originInstanceId !== currentWorkspace?.originInstanceId || expectedWorkspace?.originEntityId !== currentWorkspace?.originEntityId || hasExpectedEventSummary && (!eventSummary || eventSummary.eventCount !== expectedEntry.eventCount || eventSummary.eventDigest !== expectedEntry.eventDigest || eventSummary.inheritedEventCount !== expectedEntry.inheritedEventCount)) throw new SyncCoordinatorError("session-manifest-stale", `${side}会话内容在 manifest 之后发生变化`);
139
+ const mismatches = [];
140
+ if (!current) mismatches.push("entry-missing");
141
+ else {
142
+ if (current.localEntityId !== metadata.localEntityId) mismatches.push("localEntityId");
143
+ if (compareSyncRevision(current.revision, metadata.revision) !== 0) mismatches.push("revision");
144
+ if (current.contentDigest !== metadata.contentDigest) mismatches.push("contentDigest");
145
+ if (current.stateDigest !== metadata.stateDigest) mismatches.push("stateDigest");
146
+ if (Boolean(current.deleted) !== Boolean(metadata.deleted)) mismatches.push("deleted");
147
+ if (expectedWorkspace?.originInstanceId !== currentWorkspace?.originInstanceId || expectedWorkspace?.originEntityId !== currentWorkspace?.originEntityId) mismatches.push("workspaceOrigin");
148
+ if (hasExpectedEventSummary && (!eventSummary || eventSummary.eventCount !== expectedEntry.eventCount || eventSummary.eventDigest !== expectedEntry.eventDigest || eventSummary.inheritedEventCount !== expectedEntry.inheritedEventCount)) mismatches.push("eventSummary");
149
+ }
150
+ if (mismatches.length > 0) {
151
+ const detail = current && hasExpectedEventSummary ? ` expected(eventCount=${expectedEntry.eventCount}, inherited=${expectedEntry.inheritedEventCount}, digest=${expectedEntry.eventDigest}) actual(${eventSummary ? `eventCount=${eventSummary.eventCount}, inherited=${eventSummary.inheritedEventCount}, digest=${eventSummary.eventDigest}` : "no-events"})` : "";
152
+ throw new SyncCoordinatorError("session-manifest-stale", `${side}会话内容在 manifest 之后发生变化(key=${key},字段=${mismatches.join(",")})${detail}`);
153
+ }
140
154
  }
141
155
  }
142
156
  function sessionEventPrefixesFor(selectedEntries, receiverEntries) {
@@ -1 +1 @@
1
- {"version":3,"file":"coordinator.js","names":[],"sources":["../../../src/core/sync/coordinator.ts"],"sourcesContent":["import { createHash } from 'node:crypto'\nimport { SyncNodeManifestError, type SyncNodeApplyResult, type SyncNodeEntry, type SyncNodeService } from './node-sync.js'\nimport { SyncHostKeyChallengeError } from './host-key.js'\nimport { compareSyncRevision } from './revision.js'\nimport { syncRecordKey, type SyncPeer } from './model.js'\nimport { SyncPeerManager, SyncPeerNotFoundError } from './peer-manager.js'\nimport type { SyncStateStore } from './stores.js'\nimport { SyncSessionDeltaPrefixError, SyncSshProbeError, SyncSshTransport } from './ssh-transport.js'\nimport {\n makeSyncSessionBundle,\n planSessionTransfers,\n SyncSessionError,\n SyncSessionService,\n sessionEventListDigest,\n syncSessionEntryKey,\n validateSyncSessionBundle,\n validateSyncSessionManifest,\n} from './session-sync.js'\nimport type {\n SyncSessionApplyResult,\n SyncSessionBundle,\n SyncSessionEntry,\n SyncSessionEventPrefix,\n SyncSessionManifest,\n SyncSessionManifestEntry,\n} from './session-sync.js'\nimport { SYNC_SESSION_BUNDLE_OBJECTS_CAPABILITY, SYNC_SESSION_EVENT_TAIL_CAPABILITY, SYNC_SESSION_MANIFEST_CAPABILITY } from './protocol.js'\n\nconst EMPTY_APPLY_RESULT: SyncNodeApplyResult = { created: 0, updated: 0, deleted: 0, pendingRemoval: 0, skipped: 0 }\nconst TRANSPORT_RETRY_DELAY_MS = 250\n\nexport interface NodeTransferPlan {\n toLocal: SyncNodeEntry[]\n toRemote: SyncNodeEntry[]\n skipped: number\n}\n\nexport interface SyncRunSummary {\n peerId: string\n direction: SyncPeer['direction']\n startedAt: string\n finishedAt: string\n remoteInstanceId: string\n imported: number\n exported: number\n skipped: number\n pendingRemoval: number\n importedSessions: number\n exportedSessions: number\n skippedSessions: number\n}\n\nexport class SyncCoordinatorError extends Error {\n constructor(readonly code: string, message: string) {\n super(message)\n this.name = 'SyncCoordinatorError'\n }\n}\n\nfunction addCount(result: SyncNodeApplyResult): number {\n return result.created + result.updated + result.deleted\n}\n\nfunction manifestDigest(entries: readonly SyncNodeEntry[]): string {\n const versions = entries\n .map((entry) => ({\n key: syncRecordKey('node', entry.metadata),\n revision: entry.metadata.revision,\n contentDigest: entry.metadata.contentDigest,\n deleted: Boolean(entry.metadata.deleted),\n }))\n .sort((a, b) => a.key < b.key ? -1 : a.key > b.key ? 1 : 0)\n return `sha256:${createHash('sha256').update(JSON.stringify(versions)).digest('hex')}`\n}\n\nexport function planNodeTransfers(\n direction: SyncPeer['direction'],\n localEntries: readonly SyncNodeEntry[],\n remoteEntries: readonly SyncNodeEntry[],\n): NodeTransferPlan {\n const toLocal: SyncNodeEntry[] = []\n const toRemote: SyncNodeEntry[] = []\n if (direction === 'pull') return { toLocal: [...remoteEntries], toRemote, skipped: 0 }\n\n const localByKey = new Map(localEntries.map((entry) => [syncRecordKey('node', entry.metadata), entry]))\n const remoteByKey = new Map(remoteEntries.map((entry) => [syncRecordKey('node', entry.metadata), entry]))\n const keys = new Set([...localByKey.keys(), ...remoteByKey.keys()])\n let skipped = 0\n\n for (const key of keys) {\n const local = localByKey.get(key)\n const remote = remoteByKey.get(key)\n if (!local) {\n toLocal.push(remote!)\n continue\n }\n if (!remote) {\n toRemote.push(local)\n continue\n }\n\n const comparison = compareSyncRevision(local.metadata.revision, remote.metadata.revision)\n if (comparison > 0) {\n toRemote.push(local)\n } else if (comparison < 0) {\n toLocal.push(remote)\n } else if (\n local.metadata.contentDigest === remote.metadata.contentDigest &&\n Boolean(local.metadata.deleted) === Boolean(remote.metadata.deleted)\n ) {\n skipped += 1\n } else {\n throw new SyncCoordinatorError('revision-conflict', 'Equal node revisions contain different content')\n }\n }\n return { toLocal, toRemote, skipped }\n}\n\nfunction mapFailure(error: unknown): SyncCoordinatorError {\n if (error instanceof SyncCoordinatorError) return error\n if (error instanceof SyncPeerNotFoundError) return new SyncCoordinatorError('peer-not-found', '同步节点已不存在')\n if (error instanceof SyncHostKeyChallengeError) {\n const code = error.decision === 'changed' ? 'host-key-changed' : 'host-key-untrusted'\n return new SyncCoordinatorError(code, error.message)\n }\n if (error instanceof SyncSshProbeError) {\n const messages: Record<string, string> = {\n auth: 'SSH 认证失败',\n network: 'SSH 节点不可达',\n timeout: 'SSH 连接超时',\n hostkey: 'SSH 主机密钥校验失败',\n helper: '远端同步 helper 不可用',\n protocol: '同步协议不兼容',\n }\n const baseMsg = messages[error.category] ?? 'SSH 同步失败'\n const detail = (error as Error & { detail?: string }).detail\n return new SyncCoordinatorError(`ssh-${error.category}`, detail ? `${baseMsg}:${detail}` : baseMsg)\n }\n if (error instanceof SyncNodeManifestError) return new SyncCoordinatorError('manifest-invalid', '节点 manifest 校验失败')\n if (error instanceof SyncSessionError) {\n return new SyncCoordinatorError(error.code.startsWith('session-') ? error.code : `session-${error.code}`, error.message)\n }\n return new SyncCoordinatorError('sync-failed', '同步作业失败')\n}\n\nfunction resultCount(result: SyncNodeApplyResult): { applied: number; skipped: number; pendingRemoval: number } {\n return {\n applied: addCount(result),\n skipped: result.skipped,\n pendingRemoval: result.pendingRemoval,\n }\n}\n\nfunction emptySessionResult(): SyncSessionApplyResult {\n return { created: 0, updated: 0, archived: 0, deleted: 0, skipped: 0, unmapped: 0 }\n}\n\nfunction sessionResultCount(result: SyncSessionApplyResult): number {\n return result.created + result.updated + result.deleted\n}\n\nfunction mergeSessionSkipped(...reports: Array<readonly { sessionId: string; reason: string }[]>): Array<{ sessionId: string; reason: string }> {\n const merged = new Map<string, { sessionId: string; reason: string }>()\n for (const report of reports) {\n for (const entry of report) merged.set(JSON.stringify([entry.sessionId, entry.reason]), entry)\n }\n return [...merged.values()]\n}\n\nfunction assertSelectedSessionEntries(\n expectedEntries: readonly SyncSessionManifestEntry[],\n selectedEntries: readonly SyncSessionEntry[],\n side: string,\n): void {\n const expected = new Map(expectedEntries.map((entry) => [syncSessionEntryKey(entry), entry]))\n const selected = new Map(selectedEntries.map((entry) => [syncSessionEntryKey(entry), entry]))\n if (selected.size !== selectedEntries.length || selected.size !== expected.size) {\n throw new SyncCoordinatorError('session-manifest-stale', `${side}会话内容与 manifest 清单不一致`)\n }\n for (const [key, expectedEntry] of expected) {\n const metadata = expectedEntry.metadata\n const currentEntry = selected.get(key)\n const current = currentEntry?.metadata\n const eventSummary = currentEntry?.eventDelta\n ? {\n eventCount: currentEntry.eventDelta.fullEventCount,\n eventDigest: currentEntry.eventDelta.fullEventDigest,\n inheritedEventCount: currentEntry.inheritedEventCount,\n }\n : currentEntry?.events\n ? {\n eventCount: currentEntry.events.length,\n eventDigest: sessionEventListDigest(currentEntry.events),\n inheritedEventCount: currentEntry.inheritedEventCount,\n }\n : undefined\n const hasExpectedEventSummary = expectedEntry.eventCount !== undefined || expectedEntry.eventDigest !== undefined ||\n expectedEntry.inheritedEventCount !== undefined\n const expectedWorkspace = metadata.workspaceOrigin\n const currentWorkspace = current?.workspaceOrigin\n if (!current || current.localEntityId !== metadata.localEntityId ||\n compareSyncRevision(current.revision, metadata.revision) !== 0 ||\n current.contentDigest !== metadata.contentDigest || current.stateDigest !== metadata.stateDigest ||\n Boolean(current.deleted) !== Boolean(metadata.deleted) ||\n expectedWorkspace?.originInstanceId !== currentWorkspace?.originInstanceId ||\n expectedWorkspace?.originEntityId !== currentWorkspace?.originEntityId ||\n (hasExpectedEventSummary && (!eventSummary || eventSummary.eventCount !== expectedEntry.eventCount ||\n eventSummary.eventDigest !== expectedEntry.eventDigest || eventSummary.inheritedEventCount !== expectedEntry.inheritedEventCount))) {\n throw new SyncCoordinatorError('session-manifest-stale', `${side}会话内容在 manifest 之后发生变化`)\n }\n }\n}\n\nfunction sessionEventPrefixesFor(\n selectedEntries: readonly SyncSessionManifestEntry[],\n receiverEntries: readonly SyncSessionManifestEntry[],\n): SyncSessionEventPrefix[] {\n const receiverByKey = new Map(receiverEntries.map((entry) => [syncSessionEntryKey(entry), entry]))\n const prefixes: SyncSessionEventPrefix[] = []\n for (const selected of selectedEntries) {\n if (selected.branchId || selected.metadata.deleted) continue\n const receiver = receiverByKey.get(syncSessionEntryKey(selected))\n if (!receiver || receiver.branchId || receiver.metadata.deleted || receiver.eventCount === undefined ||\n receiver.eventDigest === undefined || receiver.inheritedEventCount === undefined) continue\n prefixes.push({\n key: syncSessionEntryKey(selected),\n eventCount: receiver.eventCount,\n eventDigest: receiver.eventDigest,\n inheritedEventCount: receiver.inheritedEventCount,\n })\n }\n return prefixes\n}\n\nexport class SyncCoordinator {\n private readonly inFlight = new Map<string, Promise<SyncRunSummary>>()\n\n constructor(\n private readonly peers: SyncPeerManager,\n private readonly state: SyncStateStore,\n private readonly nodes: SyncNodeService,\n private readonly transport: SyncSshTransport,\n private readonly now: () => number = Date.now,\n private readonly sessions?: SyncSessionService,\n ) {}\n\n run(peerId: string): Promise<SyncRunSummary> {\n const active = this.inFlight.get(peerId)\n if (active) return active\n const run = this.execute(peerId)\n this.inFlight.set(peerId, run)\n void run.then(() => this.clear(peerId, run), () => this.clear(peerId, run))\n return run\n }\n\n private async execute(peerId: string): Promise<SyncRunSummary> {\n const peer = await this.peers.get(peerId)\n if (!peer) throw new SyncCoordinatorError('peer-not-found', '同步节点已不存在')\n if (!peer.enabled) throw new SyncCoordinatorError('peer-disabled', '此同步节点已停用')\n\n const startedAt = new Date(this.now()).toISOString()\n this.peers.updateProgress(peer.id, {\n lastRunAt: startedAt,\n lastRunStatus: 'running',\n lastErrorCode: undefined,\n lastErrorMessage: undefined,\n })\n\n try {\n if (peer.syncSessions && !this.sessions) throw new SyncCoordinatorError('sessions-unavailable', '会话同步服务不可用')\n const remoteNodeManifest = await this.retryTransport(() => this.transport.fetchNodeManifest(peer.id))\n if (remoteNodeManifest.protocolVersion !== 1 || remoteNodeManifest.instanceId === this.state.instanceId()) {\n throw new SyncCoordinatorError('protocol-invalid', '同步 peer 协议版本或实例身份无效')\n }\n const remoteEntries = this.nodes.validateManifest(remoteNodeManifest.records)\n const localManifest = await this.nodes.exportManifest()\n const localEntries = this.nodes.validateManifest(localManifest.records)\n const plan = planNodeTransfers(peer.direction, localEntries, remoteEntries)\n\n const localResult = plan.toLocal.length > 0\n ? await this.nodes.applyManifest(plan.toLocal, peer.name, 'peer')\n : EMPTY_APPLY_RESULT\n const remoteResult = plan.toRemote.length > 0\n ? await this.retryTransport(() => this.transport.applyNodeManifest(peer.id, plan.toRemote, localManifest.instanceId))\n : EMPTY_APPLY_RESULT\n const localCounts = resultCount(localResult)\n const remoteCounts = resultCount(remoteResult)\n let importedSessions = 0\n let exportedSessions = 0\n let skippedSessions = 0\n let digest = manifestDigest(remoteEntries)\n if (peer.syncSessions) {\n const sessionService = this.sessions!\n let plan: ReturnType<typeof planSessionTransfers>\n let localBundleToApply: ReturnType<typeof makeSyncSessionBundle>\n let remoteBundleToApply: ReturnType<typeof makeSyncSessionBundle>\n let remoteSkipped = 0\n let remoteSessionDigest = ''\n let localSessionDigest = ''\n let localInstanceId = ''\n let modernManifests: { remote: SyncSessionManifest; local: SyncSessionManifest } | undefined\n if (remoteNodeManifest.capabilities?.includes(SYNC_SESSION_MANIFEST_CAPABILITY)) {\n const remoteManifest = validateSyncSessionManifest(\n await this.retryTransport(() => this.transport.fetchSessionManifest(peer.id)),\n )\n if (remoteManifest.instanceId !== remoteNodeManifest.instanceId) {\n throw new SyncCoordinatorError('protocol-invalid', '节点与会话 manifest 的实例身份不一致')\n }\n const localManifest = await sessionService.exportManifest()\n modernManifests = { remote: remoteManifest, local: localManifest }\n plan = planSessionTransfers(peer.direction, localManifest.entries, remoteManifest.entries)\n remoteSkipped = remoteManifest.skipped.length\n remoteSessionDigest = remoteManifest.digest\n localSessionDigest = localManifest.digest\n localInstanceId = localManifest.instanceId\n const useEventDeltas = remoteNodeManifest.capabilities.includes(SYNC_SESSION_EVENT_TAIL_CAPABILITY)\n const useSharedObjects = remoteNodeManifest.capabilities.includes(SYNC_SESSION_BUNDLE_OBJECTS_CAPABILITY)\n const toWireBundle = (wired: SyncSessionBundle, skippedReport: Array<{ sessionId: string; reason: string }>): SyncSessionBundle =>\n makeSyncSessionBundle(wired.instanceId, wired.entries, skippedReport, wired.objects)\n\n const remoteEntriesToApply = plan.toLocal.length > 0\n ? validateSyncSessionBundle(await this.retryTransport(() =>\n this.transport.fetchSessionEntries(\n peer.id,\n plan.toLocal.map(syncSessionEntryKey),\n remoteManifest.digest,\n useEventDeltas ? sessionEventPrefixesFor(plan.toLocal, localManifest.entries) : [],\n ),\n ))\n : makeSyncSessionBundle(remoteManifest.instanceId, [])\n if (remoteEntriesToApply.instanceId !== remoteManifest.instanceId) {\n throw new SyncCoordinatorError('protocol-invalid', '远端会话内容与 manifest 实例不一致')\n }\n assertSelectedSessionEntries(plan.toLocal, remoteEntriesToApply.entries, '远端')\n const localEntriesToApply = plan.toRemote.length > 0\n ? await sessionService.exportEntries(\n plan.toRemote.map(syncSessionEntryKey),\n localManifest.digest,\n useEventDeltas ? sessionEventPrefixesFor(plan.toRemote, remoteManifest.entries) : [],\n { sharedObjects: useSharedObjects },\n )\n : makeSyncSessionBundle(localManifest.instanceId, [])\n assertSelectedSessionEntries(plan.toRemote, localEntriesToApply.entries, '本地')\n const remoteSkippedReport = mergeSessionSkipped(remoteManifest.skipped, remoteEntriesToApply.skipped)\n const localSkippedReport = mergeSessionSkipped(localManifest.skipped, localEntriesToApply.skipped)\n remoteSkipped = remoteSkippedReport.length\n localBundleToApply = makeSyncSessionBundle(remoteManifest.instanceId, remoteEntriesToApply.entries, remoteSkippedReport)\n remoteBundleToApply = toWireBundle(localEntriesToApply, localSkippedReport)\n } else {\n const remoteBundle = validateSyncSessionBundle(await this.retryTransport(() => this.transport.fetchSessionBundle(peer.id)))\n if (remoteBundle.instanceId !== remoteNodeManifest.instanceId) {\n throw new SyncCoordinatorError('protocol-invalid', '节点与会话 manifest 的实例身份不一致')\n }\n const localBundle = await sessionService.exportBundle()\n plan = planSessionTransfers(peer.direction, localBundle.entries, remoteBundle.entries)\n remoteSkipped = remoteBundle.skipped.length\n remoteSessionDigest = remoteBundle.digest\n localSessionDigest = localBundle.digest\n localInstanceId = localBundle.instanceId\n localBundleToApply = makeSyncSessionBundle(remoteBundle.instanceId, plan.toLocal, remoteBundle.skipped)\n remoteBundleToApply = makeSyncSessionBundle(localBundle.instanceId, plan.toRemote, localBundle.skipped)\n }\n let sessionLocalResult = emptySessionResult()\n if (plan.toLocal.length > 0 || remoteSkipped > 0) {\n try {\n sessionLocalResult = await sessionService.applyBundle(localBundleToApply, peer.name, peer.id)\n } catch (error) {\n if (!(error instanceof SyncSessionError && error.code === 'session-delta-prefix') || !modernManifests) throw error\n const fullRemoteEntries = validateSyncSessionBundle(await this.retryTransport(() =>\n this.transport.fetchSessionEntries(\n peer.id,\n plan.toLocal.map(syncSessionEntryKey),\n modernManifests!.remote.digest,\n ),\n ))\n assertSelectedSessionEntries(plan.toLocal, fullRemoteEntries.entries, '远端')\n const skippedReport = mergeSessionSkipped(modernManifests.remote.skipped, fullRemoteEntries.skipped)\n remoteSkipped = skippedReport.length\n localBundleToApply = makeSyncSessionBundle(modernManifests.remote.instanceId, fullRemoteEntries.entries, skippedReport)\n sessionLocalResult = await sessionService.applyBundle(localBundleToApply, peer.name, peer.id)\n }\n }\n\n let sessionRemoteResult = emptySessionResult()\n if (plan.toRemote.length > 0) {\n try {\n sessionRemoteResult = await this.retryTransport(() =>\n this.transport.applySessionBundle(peer.id, remoteBundleToApply, localInstanceId),\n )\n } catch (error) {\n if (!(error instanceof SyncSessionDeltaPrefixError) || !modernManifests) throw error\n const fullLocalEntries = await sessionService.exportEntries(\n plan.toRemote.map(syncSessionEntryKey),\n modernManifests.local.digest,\n [],\n { sharedObjects: remoteBundleToApply?.objects !== undefined },\n )\n assertSelectedSessionEntries(plan.toRemote, fullLocalEntries.entries, '本地')\n const skippedReport = mergeSessionSkipped(modernManifests.local.skipped, fullLocalEntries.skipped)\n remoteBundleToApply = fullLocalEntries.objects !== undefined\n ? makeSyncSessionBundle(fullLocalEntries.instanceId, fullLocalEntries.entries, skippedReport, fullLocalEntries.objects)\n : makeSyncSessionBundle(fullLocalEntries.instanceId, fullLocalEntries.entries, skippedReport)\n sessionRemoteResult = await this.retryTransport(() =>\n this.transport.applySessionBundle(peer.id, remoteBundleToApply, localInstanceId),\n )\n }\n }\n importedSessions = sessionResultCount(sessionLocalResult)\n exportedSessions = sessionResultCount(sessionRemoteResult)\n skippedSessions = plan.skipped + sessionLocalResult.skipped + sessionRemoteResult.skipped\n digest = `sha256:${createHash('sha256').update(`${digest}\\0${remoteSessionDigest}\\0${localSessionDigest}`).digest('hex')}`\n }\n const finishedAt = new Date(this.now()).toISOString()\n const summary: SyncRunSummary = {\n peerId,\n direction: peer.direction,\n startedAt,\n finishedAt,\n remoteInstanceId: remoteNodeManifest.instanceId,\n imported: localCounts.applied,\n exported: remoteCounts.applied,\n skipped: plan.skipped + localCounts.skipped + remoteCounts.skipped + skippedSessions,\n pendingRemoval: localCounts.pendingRemoval + remoteCounts.pendingRemoval,\n importedSessions,\n exportedSessions,\n skippedSessions,\n }\n\n this.peers.updateProgress(peer.id, {\n lastRunAt: finishedAt,\n lastRunStatus: 'succeeded',\n lastSuccessAt: finishedAt,\n lastManifestDigest: digest,\n watermark: digest,\n lastErrorCode: undefined,\n lastErrorMessage: undefined,\n lastResult: {\n imported: summary.imported,\n exported: summary.exported,\n skipped: summary.skipped,\n pendingRemoval: summary.pendingRemoval,\n importedSessions,\n exportedSessions,\n skippedSessions,\n },\n })\n return summary\n } catch (error) {\n const failure = mapFailure(error)\n if (!(error instanceof SyncCoordinatorError) && !(error instanceof SyncSshProbeError) && !(error instanceof SyncSessionError) && !(error instanceof SyncNodeManifestError) && !(error instanceof SyncHostKeyChallengeError) && !(error instanceof SyncPeerNotFoundError)) {\n failure.message = `${failure.message}:${error instanceof Error ? error.message : String(error)}`\n }\n try {\n this.peers.updateProgress(peer.id, {\n lastRunAt: new Date(this.now()).toISOString(),\n lastRunStatus: 'failed',\n lastErrorCode: failure.code,\n lastErrorMessage: failure.message,\n })\n } catch {}\n throw failure\n }\n }\n\n private clear(peerId: string, run: Promise<SyncRunSummary>): void {\n if (this.inFlight.get(peerId) === run) this.inFlight.delete(peerId)\n }\n\n private async retryTransport<T>(operation: () => Promise<T>): Promise<T> {\n try {\n return await operation()\n } catch (error) {\n if (!(error instanceof SyncSshProbeError) || !['network', 'timeout', 'helper'].includes(error.category)) throw error\n await new Promise((resolve) => setTimeout(resolve, TRANSPORT_RETRY_DELAY_MS))\n return operation()\n }\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"coordinator.js","names":[],"sources":["../../../src/core/sync/coordinator.ts"],"sourcesContent":["import { createHash } from 'node:crypto'\nimport { SyncNodeManifestError, type SyncNodeApplyResult, type SyncNodeEntry, type SyncNodeService } from './node-sync.js'\nimport { SyncHostKeyChallengeError } from './host-key.js'\nimport { compareSyncRevision } from './revision.js'\nimport { syncRecordKey, type SyncPeer } from './model.js'\nimport { SyncPeerManager, SyncPeerNotFoundError } from './peer-manager.js'\nimport type { SyncStateStore } from './stores.js'\nimport { SyncSessionDeltaPrefixError, SyncSshProbeError, SyncSshTransport } from './ssh-transport.js'\nimport {\n makeSyncSessionBundle,\n planSessionTransfers,\n SyncSessionError,\n SyncSessionService,\n sessionEventListDigest,\n syncSessionEntryKey,\n validateSyncSessionBundle,\n validateSyncSessionManifest,\n} from './session-sync.js'\nimport type {\n SyncSessionApplyResult,\n SyncSessionBundle,\n SyncSessionEntry,\n SyncSessionEventPrefix,\n SyncSessionManifest,\n SyncSessionManifestEntry,\n} from './session-sync.js'\nimport { SYNC_SESSION_BUNDLE_OBJECTS_CAPABILITY, SYNC_SESSION_EVENT_TAIL_CAPABILITY, SYNC_SESSION_MANIFEST_CAPABILITY } from './protocol.js'\n\nconst EMPTY_APPLY_RESULT: SyncNodeApplyResult = { created: 0, updated: 0, deleted: 0, pendingRemoval: 0, skipped: 0 }\nconst TRANSPORT_RETRY_DELAY_MS = 250\n\nexport interface NodeTransferPlan {\n toLocal: SyncNodeEntry[]\n toRemote: SyncNodeEntry[]\n skipped: number\n}\n\nexport interface SyncRunSummary {\n peerId: string\n direction: SyncPeer['direction']\n startedAt: string\n finishedAt: string\n remoteInstanceId: string\n imported: number\n exported: number\n skipped: number\n pendingRemoval: number\n importedSessions: number\n exportedSessions: number\n skippedSessions: number\n}\n\nexport class SyncCoordinatorError extends Error {\n constructor(readonly code: string, message: string) {\n super(message)\n this.name = 'SyncCoordinatorError'\n }\n}\n\nfunction addCount(result: SyncNodeApplyResult): number {\n return result.created + result.updated + result.deleted\n}\n\nfunction manifestDigest(entries: readonly SyncNodeEntry[]): string {\n const versions = entries\n .map((entry) => ({\n key: syncRecordKey('node', entry.metadata),\n revision: entry.metadata.revision,\n contentDigest: entry.metadata.contentDigest,\n deleted: Boolean(entry.metadata.deleted),\n }))\n .sort((a, b) => a.key < b.key ? -1 : a.key > b.key ? 1 : 0)\n return `sha256:${createHash('sha256').update(JSON.stringify(versions)).digest('hex')}`\n}\n\nexport function planNodeTransfers(\n direction: SyncPeer['direction'],\n localEntries: readonly SyncNodeEntry[],\n remoteEntries: readonly SyncNodeEntry[],\n): NodeTransferPlan {\n const toLocal: SyncNodeEntry[] = []\n const toRemote: SyncNodeEntry[] = []\n if (direction === 'pull') return { toLocal: [...remoteEntries], toRemote, skipped: 0 }\n\n const localByKey = new Map(localEntries.map((entry) => [syncRecordKey('node', entry.metadata), entry]))\n const remoteByKey = new Map(remoteEntries.map((entry) => [syncRecordKey('node', entry.metadata), entry]))\n const keys = new Set([...localByKey.keys(), ...remoteByKey.keys()])\n let skipped = 0\n\n for (const key of keys) {\n const local = localByKey.get(key)\n const remote = remoteByKey.get(key)\n if (!local) {\n toLocal.push(remote!)\n continue\n }\n if (!remote) {\n toRemote.push(local)\n continue\n }\n\n const comparison = compareSyncRevision(local.metadata.revision, remote.metadata.revision)\n if (comparison > 0) {\n toRemote.push(local)\n } else if (comparison < 0) {\n toLocal.push(remote)\n } else if (\n local.metadata.contentDigest === remote.metadata.contentDigest &&\n Boolean(local.metadata.deleted) === Boolean(remote.metadata.deleted)\n ) {\n skipped += 1\n } else {\n throw new SyncCoordinatorError('revision-conflict', 'Equal node revisions contain different content')\n }\n }\n return { toLocal, toRemote, skipped }\n}\n\nfunction mapFailure(error: unknown): SyncCoordinatorError {\n if (error instanceof SyncCoordinatorError) return error\n if (error instanceof SyncPeerNotFoundError) return new SyncCoordinatorError('peer-not-found', '同步节点已不存在')\n if (error instanceof SyncHostKeyChallengeError) {\n const code = error.decision === 'changed' ? 'host-key-changed' : 'host-key-untrusted'\n return new SyncCoordinatorError(code, error.message)\n }\n if (error instanceof SyncSshProbeError) {\n const messages: Record<string, string> = {\n auth: 'SSH 认证失败',\n network: 'SSH 节点不可达',\n timeout: 'SSH 连接超时',\n hostkey: 'SSH 主机密钥校验失败',\n helper: '远端同步 helper 不可用',\n protocol: '同步协议不兼容',\n }\n const baseMsg = messages[error.category] ?? 'SSH 同步失败'\n const detail = (error as Error & { detail?: string }).detail\n return new SyncCoordinatorError(`ssh-${error.category}`, detail ? `${baseMsg}:${detail}` : baseMsg)\n }\n if (error instanceof SyncNodeManifestError) return new SyncCoordinatorError('manifest-invalid', '节点 manifest 校验失败')\n if (error instanceof SyncSessionError) {\n return new SyncCoordinatorError(error.code.startsWith('session-') ? error.code : `session-${error.code}`, error.message)\n }\n return new SyncCoordinatorError('sync-failed', '同步作业失败')\n}\n\nfunction resultCount(result: SyncNodeApplyResult): { applied: number; skipped: number; pendingRemoval: number } {\n return {\n applied: addCount(result),\n skipped: result.skipped,\n pendingRemoval: result.pendingRemoval,\n }\n}\n\nfunction emptySessionResult(): SyncSessionApplyResult {\n return { created: 0, updated: 0, archived: 0, deleted: 0, skipped: 0, unmapped: 0 }\n}\n\nfunction sessionResultCount(result: SyncSessionApplyResult): number {\n return result.created + result.updated + result.deleted\n}\n\nfunction mergeSessionSkipped(...reports: Array<readonly { sessionId: string; reason: string }[]>): Array<{ sessionId: string; reason: string }> {\n const merged = new Map<string, { sessionId: string; reason: string }>()\n for (const report of reports) {\n for (const entry of report) merged.set(JSON.stringify([entry.sessionId, entry.reason]), entry)\n }\n return [...merged.values()]\n}\n\nfunction assertSelectedSessionEntries(\n expectedEntries: readonly SyncSessionManifestEntry[],\n selectedEntries: readonly SyncSessionEntry[],\n side: string,\n): void {\n const expected = new Map(expectedEntries.map((entry) => [syncSessionEntryKey(entry), entry]))\n const selected = new Map(selectedEntries.map((entry) => [syncSessionEntryKey(entry), entry]))\n if (selected.size !== selectedEntries.length || selected.size !== expected.size) {\n throw new SyncCoordinatorError('session-manifest-stale', `${side}会话内容与 manifest 清单不一致`)\n }\n for (const [key, expectedEntry] of expected) {\n const metadata = expectedEntry.metadata\n const currentEntry = selected.get(key)\n const current = currentEntry?.metadata\n const eventSummary = currentEntry?.eventDelta\n ? {\n eventCount: currentEntry.eventDelta.fullEventCount,\n eventDigest: currentEntry.eventDelta.fullEventDigest,\n inheritedEventCount: currentEntry.inheritedEventCount,\n }\n : currentEntry?.events\n ? {\n eventCount: currentEntry.events.length,\n eventDigest: sessionEventListDigest(currentEntry.events),\n inheritedEventCount: currentEntry.inheritedEventCount,\n }\n : undefined\n const hasExpectedEventSummary = expectedEntry.eventCount !== undefined || expectedEntry.eventDigest !== undefined ||\n expectedEntry.inheritedEventCount !== undefined\n const expectedWorkspace = metadata.workspaceOrigin\n const currentWorkspace = current?.workspaceOrigin\n const mismatches: string[] = []\n if (!current) mismatches.push('entry-missing')\n else {\n if (current.localEntityId !== metadata.localEntityId) mismatches.push('localEntityId')\n if (compareSyncRevision(current.revision, metadata.revision) !== 0) mismatches.push('revision')\n if (current.contentDigest !== metadata.contentDigest) mismatches.push('contentDigest')\n if (current.stateDigest !== metadata.stateDigest) mismatches.push('stateDigest')\n if (Boolean(current.deleted) !== Boolean(metadata.deleted)) mismatches.push('deleted')\n if (expectedWorkspace?.originInstanceId !== currentWorkspace?.originInstanceId ||\n expectedWorkspace?.originEntityId !== currentWorkspace?.originEntityId) mismatches.push('workspaceOrigin')\n if (hasExpectedEventSummary && (!eventSummary || eventSummary.eventCount !== expectedEntry.eventCount ||\n eventSummary.eventDigest !== expectedEntry.eventDigest || eventSummary.inheritedEventCount !== expectedEntry.inheritedEventCount)) {\n mismatches.push('eventSummary')\n }\n }\n if (mismatches.length > 0) {\n const detail = current && hasExpectedEventSummary\n ? ` expected(eventCount=${expectedEntry.eventCount}, inherited=${expectedEntry.inheritedEventCount}, digest=${expectedEntry.eventDigest})` +\n ` actual(${eventSummary ? `eventCount=${eventSummary.eventCount}, inherited=${eventSummary.inheritedEventCount}, digest=${eventSummary.eventDigest}` : 'no-events'})`\n : ''\n throw new SyncCoordinatorError('session-manifest-stale', `${side}会话内容在 manifest 之后发生变化(key=${key},字段=${mismatches.join(',')})${detail}`)\n }\n }\n}\n\nfunction sessionEventPrefixesFor(\n selectedEntries: readonly SyncSessionManifestEntry[],\n receiverEntries: readonly SyncSessionManifestEntry[],\n): SyncSessionEventPrefix[] {\n const receiverByKey = new Map(receiverEntries.map((entry) => [syncSessionEntryKey(entry), entry]))\n const prefixes: SyncSessionEventPrefix[] = []\n for (const selected of selectedEntries) {\n if (selected.branchId || selected.metadata.deleted) continue\n const receiver = receiverByKey.get(syncSessionEntryKey(selected))\n if (!receiver || receiver.branchId || receiver.metadata.deleted || receiver.eventCount === undefined ||\n receiver.eventDigest === undefined || receiver.inheritedEventCount === undefined) continue\n prefixes.push({\n key: syncSessionEntryKey(selected),\n eventCount: receiver.eventCount,\n eventDigest: receiver.eventDigest,\n inheritedEventCount: receiver.inheritedEventCount,\n })\n }\n return prefixes\n}\n\nexport class SyncCoordinator {\n private readonly inFlight = new Map<string, Promise<SyncRunSummary>>()\n\n constructor(\n private readonly peers: SyncPeerManager,\n private readonly state: SyncStateStore,\n private readonly nodes: SyncNodeService,\n private readonly transport: SyncSshTransport,\n private readonly now: () => number = Date.now,\n private readonly sessions?: SyncSessionService,\n ) {}\n\n run(peerId: string): Promise<SyncRunSummary> {\n const active = this.inFlight.get(peerId)\n if (active) return active\n const run = this.execute(peerId)\n this.inFlight.set(peerId, run)\n void run.then(() => this.clear(peerId, run), () => this.clear(peerId, run))\n return run\n }\n\n private async execute(peerId: string): Promise<SyncRunSummary> {\n const peer = await this.peers.get(peerId)\n if (!peer) throw new SyncCoordinatorError('peer-not-found', '同步节点已不存在')\n if (!peer.enabled) throw new SyncCoordinatorError('peer-disabled', '此同步节点已停用')\n\n const startedAt = new Date(this.now()).toISOString()\n this.peers.updateProgress(peer.id, {\n lastRunAt: startedAt,\n lastRunStatus: 'running',\n lastErrorCode: undefined,\n lastErrorMessage: undefined,\n })\n\n try {\n if (peer.syncSessions && !this.sessions) throw new SyncCoordinatorError('sessions-unavailable', '会话同步服务不可用')\n const remoteNodeManifest = await this.retryTransport(() => this.transport.fetchNodeManifest(peer.id))\n if (remoteNodeManifest.protocolVersion !== 1 || remoteNodeManifest.instanceId === this.state.instanceId()) {\n throw new SyncCoordinatorError('protocol-invalid', '同步 peer 协议版本或实例身份无效')\n }\n const remoteEntries = this.nodes.validateManifest(remoteNodeManifest.records)\n const localManifest = await this.nodes.exportManifest()\n const localEntries = this.nodes.validateManifest(localManifest.records)\n const plan = planNodeTransfers(peer.direction, localEntries, remoteEntries)\n\n const localResult = plan.toLocal.length > 0\n ? await this.nodes.applyManifest(plan.toLocal, peer.name, 'peer')\n : EMPTY_APPLY_RESULT\n const remoteResult = plan.toRemote.length > 0\n ? await this.retryTransport(() => this.transport.applyNodeManifest(peer.id, plan.toRemote, localManifest.instanceId))\n : EMPTY_APPLY_RESULT\n const localCounts = resultCount(localResult)\n const remoteCounts = resultCount(remoteResult)\n let importedSessions = 0\n let exportedSessions = 0\n let skippedSessions = 0\n let digest = manifestDigest(remoteEntries)\n if (peer.syncSessions) {\n const sessionService = this.sessions!\n let plan: ReturnType<typeof planSessionTransfers>\n let localBundleToApply: ReturnType<typeof makeSyncSessionBundle>\n let remoteBundleToApply: ReturnType<typeof makeSyncSessionBundle>\n let remoteSkipped = 0\n let remoteSessionDigest = ''\n let localSessionDigest = ''\n let localInstanceId = ''\n let modernManifests: { remote: SyncSessionManifest; local: SyncSessionManifest } | undefined\n if (remoteNodeManifest.capabilities?.includes(SYNC_SESSION_MANIFEST_CAPABILITY)) {\n const remoteManifest = validateSyncSessionManifest(\n await this.retryTransport(() => this.transport.fetchSessionManifest(peer.id)),\n )\n if (remoteManifest.instanceId !== remoteNodeManifest.instanceId) {\n throw new SyncCoordinatorError('protocol-invalid', '节点与会话 manifest 的实例身份不一致')\n }\n const localManifest = await sessionService.exportManifest()\n modernManifests = { remote: remoteManifest, local: localManifest }\n plan = planSessionTransfers(peer.direction, localManifest.entries, remoteManifest.entries)\n remoteSkipped = remoteManifest.skipped.length\n remoteSessionDigest = remoteManifest.digest\n localSessionDigest = localManifest.digest\n localInstanceId = localManifest.instanceId\n const useEventDeltas = remoteNodeManifest.capabilities.includes(SYNC_SESSION_EVENT_TAIL_CAPABILITY)\n const useSharedObjects = remoteNodeManifest.capabilities.includes(SYNC_SESSION_BUNDLE_OBJECTS_CAPABILITY)\n const toWireBundle = (wired: SyncSessionBundle, skippedReport: Array<{ sessionId: string; reason: string }>): SyncSessionBundle =>\n makeSyncSessionBundle(wired.instanceId, wired.entries, skippedReport, wired.objects)\n\n const remoteEntriesToApply = plan.toLocal.length > 0\n ? validateSyncSessionBundle(await this.retryTransport(() =>\n this.transport.fetchSessionEntries(\n peer.id,\n plan.toLocal.map(syncSessionEntryKey),\n remoteManifest.digest,\n useEventDeltas ? sessionEventPrefixesFor(plan.toLocal, localManifest.entries) : [],\n ),\n ))\n : makeSyncSessionBundle(remoteManifest.instanceId, [])\n if (remoteEntriesToApply.instanceId !== remoteManifest.instanceId) {\n throw new SyncCoordinatorError('protocol-invalid', '远端会话内容与 manifest 实例不一致')\n }\n assertSelectedSessionEntries(plan.toLocal, remoteEntriesToApply.entries, '远端')\n const localEntriesToApply = plan.toRemote.length > 0\n ? await sessionService.exportEntries(\n plan.toRemote.map(syncSessionEntryKey),\n localManifest.digest,\n useEventDeltas ? sessionEventPrefixesFor(plan.toRemote, remoteManifest.entries) : [],\n { sharedObjects: useSharedObjects },\n )\n : makeSyncSessionBundle(localManifest.instanceId, [])\n assertSelectedSessionEntries(plan.toRemote, localEntriesToApply.entries, '本地')\n const remoteSkippedReport = mergeSessionSkipped(remoteManifest.skipped, remoteEntriesToApply.skipped)\n const localSkippedReport = mergeSessionSkipped(localManifest.skipped, localEntriesToApply.skipped)\n remoteSkipped = remoteSkippedReport.length\n localBundleToApply = makeSyncSessionBundle(remoteManifest.instanceId, remoteEntriesToApply.entries, remoteSkippedReport)\n remoteBundleToApply = toWireBundle(localEntriesToApply, localSkippedReport)\n } else {\n const remoteBundle = validateSyncSessionBundle(await this.retryTransport(() => this.transport.fetchSessionBundle(peer.id)))\n if (remoteBundle.instanceId !== remoteNodeManifest.instanceId) {\n throw new SyncCoordinatorError('protocol-invalid', '节点与会话 manifest 的实例身份不一致')\n }\n const localBundle = await sessionService.exportBundle()\n plan = planSessionTransfers(peer.direction, localBundle.entries, remoteBundle.entries)\n remoteSkipped = remoteBundle.skipped.length\n remoteSessionDigest = remoteBundle.digest\n localSessionDigest = localBundle.digest\n localInstanceId = localBundle.instanceId\n localBundleToApply = makeSyncSessionBundle(remoteBundle.instanceId, plan.toLocal, remoteBundle.skipped)\n remoteBundleToApply = makeSyncSessionBundle(localBundle.instanceId, plan.toRemote, localBundle.skipped)\n }\n let sessionLocalResult = emptySessionResult()\n if (plan.toLocal.length > 0 || remoteSkipped > 0) {\n try {\n sessionLocalResult = await sessionService.applyBundle(localBundleToApply, peer.name, peer.id)\n } catch (error) {\n if (!(error instanceof SyncSessionError && error.code === 'session-delta-prefix') || !modernManifests) throw error\n const fullRemoteEntries = validateSyncSessionBundle(await this.retryTransport(() =>\n this.transport.fetchSessionEntries(\n peer.id,\n plan.toLocal.map(syncSessionEntryKey),\n modernManifests!.remote.digest,\n ),\n ))\n assertSelectedSessionEntries(plan.toLocal, fullRemoteEntries.entries, '远端')\n const skippedReport = mergeSessionSkipped(modernManifests.remote.skipped, fullRemoteEntries.skipped)\n remoteSkipped = skippedReport.length\n localBundleToApply = makeSyncSessionBundle(modernManifests.remote.instanceId, fullRemoteEntries.entries, skippedReport)\n sessionLocalResult = await sessionService.applyBundle(localBundleToApply, peer.name, peer.id)\n }\n }\n\n let sessionRemoteResult = emptySessionResult()\n if (plan.toRemote.length > 0) {\n try {\n sessionRemoteResult = await this.retryTransport(() =>\n this.transport.applySessionBundle(peer.id, remoteBundleToApply, localInstanceId),\n )\n } catch (error) {\n if (!(error instanceof SyncSessionDeltaPrefixError) || !modernManifests) throw error\n const fullLocalEntries = await sessionService.exportEntries(\n plan.toRemote.map(syncSessionEntryKey),\n modernManifests.local.digest,\n [],\n { sharedObjects: remoteBundleToApply?.objects !== undefined },\n )\n assertSelectedSessionEntries(plan.toRemote, fullLocalEntries.entries, '本地')\n const skippedReport = mergeSessionSkipped(modernManifests.local.skipped, fullLocalEntries.skipped)\n remoteBundleToApply = fullLocalEntries.objects !== undefined\n ? makeSyncSessionBundle(fullLocalEntries.instanceId, fullLocalEntries.entries, skippedReport, fullLocalEntries.objects)\n : makeSyncSessionBundle(fullLocalEntries.instanceId, fullLocalEntries.entries, skippedReport)\n sessionRemoteResult = await this.retryTransport(() =>\n this.transport.applySessionBundle(peer.id, remoteBundleToApply, localInstanceId),\n )\n }\n }\n importedSessions = sessionResultCount(sessionLocalResult)\n exportedSessions = sessionResultCount(sessionRemoteResult)\n skippedSessions = plan.skipped + sessionLocalResult.skipped + sessionRemoteResult.skipped\n digest = `sha256:${createHash('sha256').update(`${digest}\\0${remoteSessionDigest}\\0${localSessionDigest}`).digest('hex')}`\n }\n const finishedAt = new Date(this.now()).toISOString()\n const summary: SyncRunSummary = {\n peerId,\n direction: peer.direction,\n startedAt,\n finishedAt,\n remoteInstanceId: remoteNodeManifest.instanceId,\n imported: localCounts.applied,\n exported: remoteCounts.applied,\n skipped: plan.skipped + localCounts.skipped + remoteCounts.skipped + skippedSessions,\n pendingRemoval: localCounts.pendingRemoval + remoteCounts.pendingRemoval,\n importedSessions,\n exportedSessions,\n skippedSessions,\n }\n\n this.peers.updateProgress(peer.id, {\n lastRunAt: finishedAt,\n lastRunStatus: 'succeeded',\n lastSuccessAt: finishedAt,\n lastManifestDigest: digest,\n watermark: digest,\n lastErrorCode: undefined,\n lastErrorMessage: undefined,\n lastResult: {\n imported: summary.imported,\n exported: summary.exported,\n skipped: summary.skipped,\n pendingRemoval: summary.pendingRemoval,\n importedSessions,\n exportedSessions,\n skippedSessions,\n },\n })\n return summary\n } catch (error) {\n const failure = mapFailure(error)\n if (!(error instanceof SyncCoordinatorError) && !(error instanceof SyncSshProbeError) && !(error instanceof SyncSessionError) && !(error instanceof SyncNodeManifestError) && !(error instanceof SyncHostKeyChallengeError) && !(error instanceof SyncPeerNotFoundError)) {\n failure.message = `${failure.message}:${error instanceof Error ? error.message : String(error)}`\n }\n try {\n this.peers.updateProgress(peer.id, {\n lastRunAt: new Date(this.now()).toISOString(),\n lastRunStatus: 'failed',\n lastErrorCode: failure.code,\n lastErrorMessage: failure.message,\n })\n } catch {}\n throw failure\n }\n }\n\n private clear(peerId: string, run: Promise<SyncRunSummary>): void {\n if (this.inFlight.get(peerId) === run) this.inFlight.delete(peerId)\n }\n\n private async retryTransport<T>(operation: () => Promise<T>): Promise<T> {\n try {\n return await operation()\n } catch (error) {\n if (!(error instanceof SyncSshProbeError) || !['network', 'timeout', 'helper'].includes(error.category)) throw error\n await new Promise((resolve) => setTimeout(resolve, TRANSPORT_RETRY_DELAY_MS))\n return operation()\n }\n }\n}\n"],"mappings":"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"}
@@ -1,5 +1,5 @@
1
1
  import { SyncPeerManager } from "./peer-manager.js";
2
- import { s as SyncSshTransport } from "../../ssh-transport-DGkQh-HA.js";
2
+ import { s as SyncSshTransport } from "../../ssh-transport-gb2PVatu.js";
3
3
  import { SyncCoordinator } from "./coordinator.js";
4
4
  import { Context } from "@deepseek-ai/cordis";
5
5
  //#region src/core/sync/routes.d.ts
@@ -1,2 +1,2 @@
1
- import { a as SyncSshProbeError, c as buildSyncSshConnectConfig, i as SyncSshErrorCategory, n as SyncRemoteNodeManifest, o as SyncSshSession, r as SyncSessionDeltaPrefixError, s as SyncSshTransport, t as SyncRemoteHandshake } from "../../ssh-transport-DGkQh-HA.js";
1
+ import { a as SyncSshProbeError, c as buildSyncSshConnectConfig, i as SyncSshErrorCategory, n as SyncRemoteNodeManifest, o as SyncSshSession, r as SyncSessionDeltaPrefixError, s as SyncSshTransport, t as SyncRemoteHandshake } from "../../ssh-transport-gb2PVatu.js";
2
2
  export { SyncRemoteHandshake, SyncRemoteNodeManifest, SyncSessionDeltaPrefixError, SyncSshErrorCategory, SyncSshProbeError, SyncSshSession, SyncSshTransport, buildSyncSshConnectConfig };
@@ -8,6 +8,7 @@ import { Client } from "ssh2";
8
8
  import { once } from "node:events";
9
9
  //#region src/core/sync/ssh-transport.ts
10
10
  const SSH_READY_TIMEOUT_MS = 3e4;
11
+ const SSH_READ_IDLE_TIMEOUT_MS = 6e4;
11
12
  const DEFAULT_REMOTE_WEB_PORT = 3080;
12
13
  var SyncSshProbeError = class extends Error {
13
14
  category;
@@ -55,19 +56,33 @@ var SyncSshSession = class {
55
56
  this.inputEnded = true;
56
57
  this.channel.end();
57
58
  }
58
- async *readFrames() {
59
+ async *readFrames(idleTimeoutMs = SSH_READ_IDLE_TIMEOUT_MS) {
59
60
  if (this.reading) throw new SyncProtocolError("SSH sync channel can only be read once");
60
61
  this.reading = true;
61
62
  const channel = this.channel;
62
63
  const decoder = this.decoder;
64
+ let idleDeadline;
65
+ const armIdleDeadline = () => {
66
+ clearTimeout(idleDeadline);
67
+ idleDeadline = setTimeout(() => {
68
+ this.pendingError = /* @__PURE__ */ new Error(`SSH channel idle for more than ${Math.round(idleTimeoutMs / 1e3)}s`);
69
+ channel.destroy();
70
+ }, idleTimeoutMs);
71
+ };
63
72
  try {
64
- for await (const chunk of channel.stdout) for (const frame of decoder.push(Buffer.from(chunk))) yield frame;
73
+ armIdleDeadline();
74
+ for await (const chunk of channel.stdout) {
75
+ armIdleDeadline();
76
+ for (const frame of decoder.push(Buffer.from(chunk))) yield frame;
77
+ }
65
78
  if (this.pendingError) throw new SyncProtocolError(`SSH channel error: ${this.pendingError.message}`);
66
79
  decoder.finish();
67
80
  } catch (error) {
68
81
  if (error instanceof SyncProtocolError) throw error;
69
82
  const detail = this.pendingError ? this.pendingError.message : error instanceof Error ? error.message : "unknown error";
70
83
  throw new SyncProtocolError(`SSH channel read failed: ${detail}`);
84
+ } finally {
85
+ clearTimeout(idleDeadline);
71
86
  }
72
87
  }
73
88
  close() {
@@ -88,7 +103,7 @@ function buildSyncSshConnectConfig(peer, secrets, challenges, onChallenge) {
88
103
  username: peer.username,
89
104
  hostHash: "sha256",
90
105
  readyTimeout: SSH_READY_TIMEOUT_MS,
91
- keepaliveInterval: 0,
106
+ keepaliveInterval: 15e3,
92
107
  hostVerifier: (sha256Hex) => {
93
108
  try {
94
109
  const decision = inspectSyncHostKey(peer, sha256Hex, challenges);
@@ -138,9 +153,16 @@ var SyncSshTransport = class {
138
153
  const finish = (error) => {
139
154
  if (settled) return;
140
155
  settled = true;
156
+ clearTimeout(readyDeadline);
141
157
  if (error) reject(error);
142
158
  else resolve();
143
159
  };
160
+ const readyDeadline = setTimeout(() => {
161
+ const timeout = /* @__PURE__ */ new Error("Timed out while waiting for SSH handshake");
162
+ timeout.level = "client-timeout";
163
+ finish(timeout);
164
+ client.end();
165
+ }, SSH_READY_TIMEOUT_MS * 2);
144
166
  client.once("ready", () => finish());
145
167
  client.on("error", (error) => finish(challenge ?? error));
146
168
  const config = buildSyncSshConnectConfig(peer, secrets, this.hostKeyChallenges, (error) => {
@@ -153,7 +175,12 @@ var SyncSshTransport = class {
153
175
  }
154
176
  });
155
177
  const session = new SyncSshSession(client, await new Promise((resolve, reject) => {
178
+ const execDeadline = setTimeout(() => {
179
+ reject(/* @__PURE__ */ new Error("Timed out while waiting for the sync helper channel"));
180
+ client.end();
181
+ }, SSH_READY_TIMEOUT_MS);
156
182
  client.exec(REMOTE_SYNC_HELPER_COMMAND, (error, opened) => {
183
+ clearTimeout(execDeadline);
157
184
  if (error) reject(error);
158
185
  else resolve(opened);
159
186
  });
@@ -1 +1 @@
1
- {"version":3,"file":"ssh-transport.js","names":[],"sources":["../../../src/core/sync/ssh-transport.ts"],"sourcesContent":["import { Client, type ConnectConfig } from 'ssh2'\nimport type { ClientChannel } from 'ssh2'\nimport { once } from 'node:events'\nimport { classifySshError } from '../../exec-ssh/connection.js'\nimport { SyncPeerManager, SyncPeerNotFoundError, type SyncPeerSecrets } from './peer-manager.js'\nimport { isRecord, type SyncPeer } from './model.js'\nimport { SyncHostKeyChallengeError, SyncHostKeyChallenges, inspectSyncHostKey } from './host-key.js'\nimport type { SyncNodeApplyResult, SyncNodeManifest } from './node-sync.js'\nimport {\n MAX_SYNC_DATA_CHUNK_BYTES,\n MAX_SYNC_TRANSFER_BYTES,\n SYNC_PROTOCOL_VERSION,\n SyncFrameDecoder,\n SyncProtocolError,\n encodeSyncControlFrame,\n encodeSyncDataFrame,\n type SyncFrame,\n} from './protocol.js'\nimport { REMOTE_SYNC_HELPER_COMMAND } from './remote-helper.js'\nimport type { SyncSessionApplyResult, SyncSessionBundle, SyncSessionEventPrefix, SyncSessionManifest } from './session-sync.js'\n\nconst SSH_READY_TIMEOUT_MS = 30_000\nconst DEFAULT_REMOTE_WEB_PORT = 3080\n\nexport type SyncSshErrorCategory = 'auth' | 'network' | 'hostkey' | 'timeout' | 'helper' | 'protocol'\n\nexport class SyncSshProbeError extends Error {\n constructor(readonly category: SyncSshErrorCategory) {\n super(`SSH sync probe failed: ${category}`)\n this.name = 'SyncSshProbeError'\n }\n}\n\nexport class SyncSessionDeltaPrefixError extends Error {\n constructor() {\n super('remote session event prefix changed during transfer')\n this.name = 'SyncSessionDeltaPrefixError'\n }\n}\n\nexport interface SyncRemoteHandshake {\n protocolVersion: number\n instanceId: string\n capabilities: string[]\n}\n\nexport interface SyncRemoteNodeManifest extends SyncNodeManifest {\n capabilities?: string[]\n}\n\nexport class SyncSshSession {\n private readonly decoder = new SyncFrameDecoder()\n private reading = false\n private closed = false\n private inputEnded = false\n private nextDataSequence = 0\n private pendingError: Error | undefined\n\n constructor(\n private readonly client: Client,\n private readonly channel: ClientChannel,\n ) {\n channel.on('error', (error: Error) => { this.pendingError = error })\n channel.on('exit', (code: number | null) => {\n if (code !== null && code !== 0) this.pendingError = new Error(`remote helper exited with code ${code}`)\n })\n }\n\n async sendControl(value: Record<string, unknown>): Promise<void> {\n await this.write(encodeSyncControlFrame(value))\n }\n\n async sendData(sequence: number, data: Buffer): Promise<void> {\n if (sequence !== this.nextDataSequence) throw new SyncProtocolError('outgoing data sequence is out of order')\n await this.write(encodeSyncDataFrame(sequence, data))\n this.nextDataSequence += 1\n }\n\n endInput(): void {\n if (this.closed || this.inputEnded) return\n this.inputEnded = true\n this.channel.end()\n }\n\n async *readFrames(): AsyncGenerator<SyncFrame> {\n if (this.reading) throw new SyncProtocolError('SSH sync channel can only be read once')\n this.reading = true\n const channel = this.channel\n const decoder = this.decoder\n try {\n for await (const chunk of channel.stdout as AsyncIterable<Buffer>) {\n for (const frame of decoder.push(Buffer.from(chunk))) yield frame\n }\n if (this.pendingError) throw new SyncProtocolError(`SSH channel error: ${this.pendingError.message}`)\n decoder.finish()\n } catch (error) {\n if (error instanceof SyncProtocolError) throw error\n const detail = this.pendingError ? this.pendingError.message : error instanceof Error ? error.message : 'unknown error'\n throw new SyncProtocolError(`SSH channel read failed: ${detail}`)\n }\n }\n\n close(): void {\n if (this.closed) return\n this.closed = true\n this.channel.destroy()\n this.client.end()\n }\n\n private async write(frame: Buffer): Promise<void> {\n if (this.closed || this.inputEnded) throw new SyncProtocolError('SSH sync channel input is closed')\n if (!this.channel.write(frame)) await once(this.channel, 'drain')\n }\n}\n\nexport function buildSyncSshConnectConfig(\n peer: SyncPeer,\n secrets: SyncPeerSecrets,\n challenges: SyncHostKeyChallenges,\n onChallenge: (error: SyncHostKeyChallengeError) => void,\n): ConnectConfig {\n return {\n host: peer.host,\n port: peer.port,\n username: peer.username,\n hostHash: 'sha256',\n readyTimeout: SSH_READY_TIMEOUT_MS,\n keepaliveInterval: 0,\n hostVerifier: (sha256Hex: string): boolean => {\n try {\n const decision = inspectSyncHostKey(peer, sha256Hex, challenges)\n if (!decision.trusted) onChallenge(decision.challenge)\n return decision.trusted\n } catch (error) {\n if (error instanceof SyncHostKeyChallengeError) onChallenge(error)\n return false\n }\n },\n ...(peer.authKind === 'password'\n ? { password: secrets.password }\n : { privateKey: secrets.privateKey, ...(secrets.passphrase ? { passphrase: secrets.passphrase } : {}) }),\n }\n}\n\nexport class SyncSshTransport {\n private readonly hostKeyChallenges = new SyncHostKeyChallenges()\n\n constructor(private readonly peerManager: SyncPeerManager) {}\n\n async probe(peerId: string): Promise<SyncRemoteHandshake> {\n const { peer, session } = await this.openSession(peerId)\n try {\n const handshake = await this.requestHandshake(session, peer.remoteWebPort ?? DEFAULT_REMOTE_WEB_PORT)\n if (handshake.protocolVersion !== SYNC_PROTOCOL_VERSION) throw new SyncSshProbeError('protocol')\n return handshake\n } catch (error) {\n if (\n error instanceof SyncHostKeyChallengeError ||\n error instanceof SyncPeerNotFoundError ||\n error instanceof SyncSshProbeError\n ) {\n throw error\n }\n if (error instanceof SyncProtocolError) throw new SyncSshProbeError('protocol')\n throw new SyncSshProbeError('helper')\n } finally {\n session.close()\n }\n }\n\n async openSession(peerId: string): Promise<{ peer: SyncPeer; session: SyncSshSession }> {\n const { peer, secrets } = await this.peerManager.connectionData(peerId)\n if (peer.authKind === 'password' && !secrets.password) throw new SyncSshProbeError('auth')\n if (peer.authKind === 'key' && !secrets.privateKey) throw new SyncSshProbeError('auth')\n\n const client = new Client()\n let challenge: SyncHostKeyChallengeError | undefined\n let sessionOpened = false\n try {\n await new Promise<void>((resolve, reject) => {\n let settled = false\n const finish = (error?: unknown): void => {\n if (settled) return\n settled = true\n if (error) reject(error)\n else resolve()\n }\n\n client.once('ready', () => finish())\n client.on('error', (error: Error) => finish(challenge ?? error))\n const config = buildSyncSshConnectConfig(peer, secrets, this.hostKeyChallenges, (error) => {\n challenge = error\n })\n\n try {\n client.connect(config)\n } catch (error) {\n finish(error)\n }\n })\n\n const channel = await new Promise<ClientChannel>((resolve, reject) => {\n client.exec(REMOTE_SYNC_HELPER_COMMAND, (error, opened) => {\n if (error) reject(error)\n else resolve(opened)\n })\n })\n const session = new SyncSshSession(client, channel)\n sessionOpened = true\n return { peer, session }\n } catch (error) {\n if (error instanceof SyncHostKeyChallengeError || error instanceof SyncPeerNotFoundError || error instanceof SyncSshProbeError) {\n throw error\n }\n if (challenge) throw challenge\n const category = classifySshError(error)\n if (category === 'auth' || category === 'network' || category === 'timeout') {\n throw new SyncSshProbeError(category)\n }\n if (category === 'hostkey') throw new SyncSshProbeError('hostkey')\n if (error instanceof SyncProtocolError) throw new SyncSshProbeError('protocol')\n throw new SyncSshProbeError('helper')\n } finally {\n if (!sessionOpened) {\n try {\n client.end()\n } catch {}\n }\n }\n }\n\n async fetchNodeManifest(peerId: string): Promise<SyncRemoteNodeManifest> {\n const value = await this.fetchDataOperation(peerId, 'node-manifest')\n if (!isRecord(value) || value.ok !== true || !isRecord(value.manifest)) throw new SyncSshProbeError('helper')\n const manifest = value.manifest\n if (manifest.protocolVersion !== SYNC_PROTOCOL_VERSION || typeof manifest.instanceId !== 'string' || !Array.isArray(manifest.records)) {\n throw new SyncSshProbeError('protocol')\n }\n const capabilities = value.capabilities === undefined ? undefined : value.capabilities\n if (capabilities !== undefined && (!Array.isArray(capabilities) || !capabilities.every((item) => typeof item === 'string'))) {\n throw new SyncSshProbeError('protocol')\n }\n return { ...manifest, ...(capabilities ? { capabilities } : {}) } as unknown as SyncRemoteNodeManifest\n }\n\n async applyNodeManifest(\n peerId: string,\n records: SyncNodeManifest['records'],\n sourceName: string,\n ): Promise<SyncNodeApplyResult> {\n const requestBody = Buffer.from(JSON.stringify({ records, sourceName }), 'utf8')\n if (requestBody.length > MAX_SYNC_TRANSFER_BYTES) throw new SyncSshProbeError('protocol')\n const { peer, session } = await this.openSession(peerId)\n try {\n await session.sendControl({\n kind: 'request',\n protocolVersion: SYNC_PROTOCOL_VERSION,\n operation: 'node-apply',\n port: peer.remoteWebPort ?? DEFAULT_REMOTE_WEB_PORT,\n })\n for (let offset = 0, sequence = 0; offset < requestBody.length; offset += MAX_SYNC_DATA_CHUNK_BYTES, sequence += 1) {\n await session.sendData(sequence, requestBody.subarray(offset, Math.min(offset + MAX_SYNC_DATA_CHUNK_BYTES, requestBody.length)))\n }\n await session.sendControl({ kind: 'request-end' })\n session.endInput()\n\n let response: Record<string, unknown> | undefined\n for await (const frame of session.readFrames()) {\n if (frame.kind !== 'control' || response) throw new SyncProtocolError('unexpected node apply response frame')\n response = frame.value\n }\n if (!response || response.kind === 'error') throw new SyncSshProbeError('helper')\n if (response.kind !== 'response' || response.status !== 200 || response.body === null || typeof response.body !== 'object') {\n throw new SyncSshProbeError('helper')\n }\n const body = response.body as Record<string, unknown>\n if (body.ok !== true || body.result === null || typeof body.result !== 'object') throw new SyncSshProbeError('helper')\n const result = body.result as Record<string, unknown>\n for (const key of ['created', 'updated', 'deleted', 'pendingRemoval', 'skipped'] as const) {\n if (!Number.isSafeInteger(result[key]) || (result[key] as number) < 0) throw new SyncSshProbeError('protocol')\n }\n return result as unknown as SyncNodeApplyResult\n } catch (error) {\n if (error instanceof SyncSshProbeError) throw error\n if (error instanceof SyncProtocolError) throw new SyncSshProbeError('protocol')\n throw new SyncSshProbeError('helper')\n } finally {\n session.close()\n }\n }\n\n async fetchSessionBundle(peerId: string): Promise<SyncSessionBundle> {\n const value = await this.fetchDataOperation(peerId, 'session-bundle')\n if (!isRecord(value) || value.ok !== true || !isRecord(value.bundle)) throw new SyncSshProbeError('helper')\n const bundle = value.bundle\n if (bundle.protocolVersion !== 1 || typeof bundle.instanceId !== 'string' ||\n !Array.isArray(bundle.entries) || !Array.isArray(bundle.skipped) || typeof bundle.digest !== 'string') {\n throw new SyncSshProbeError('protocol')\n }\n return bundle as unknown as SyncSessionBundle\n }\n\n async fetchSessionManifest(peerId: string): Promise<SyncSessionManifest> {\n const value = await this.fetchDataOperation(peerId, 'session-manifest')\n if (!isRecord(value) || value.ok !== true || !isRecord(value.manifest)) throw new SyncSshProbeError('helper')\n const manifest = value.manifest\n if (manifest.protocolVersion !== 1 || typeof manifest.instanceId !== 'string' ||\n !Array.isArray(manifest.entries) || !Array.isArray(manifest.skipped) || typeof manifest.digest !== 'string') {\n throw new SyncSshProbeError('protocol')\n }\n return manifest as unknown as SyncSessionManifest\n }\n\n async fetchSessionEntries(\n peerId: string,\n keys: readonly string[],\n manifestDigest: string,\n prefixes: readonly SyncSessionEventPrefix[] = [],\n ): Promise<SyncSessionBundle> {\n const value = await this.fetchDataOperation(peerId, 'session-entries', { keys, manifestDigest, prefixes })\n if (!isRecord(value) || value.ok !== true || !isRecord(value.bundle)) throw new SyncSshProbeError('helper')\n const bundle = value.bundle\n if (bundle.protocolVersion !== 1 || typeof bundle.instanceId !== 'string' ||\n !Array.isArray(bundle.entries) || !Array.isArray(bundle.skipped) || typeof bundle.digest !== 'string') {\n throw new SyncSshProbeError('protocol')\n }\n return bundle as unknown as SyncSessionBundle\n }\n\n async applySessionBundle(peerId: string, bundle: SyncSessionBundle, sourceName: string): Promise<SyncSessionApplyResult> {\n const requestBody = Buffer.from(JSON.stringify({ bundle, sourceName, peerId }), 'utf8')\n if (requestBody.length > MAX_SYNC_TRANSFER_BYTES) throw new SyncSshProbeError('protocol')\n const { peer, session } = await this.openSession(peerId)\n try {\n await session.sendControl({\n kind: 'request',\n protocolVersion: SYNC_PROTOCOL_VERSION,\n operation: 'session-apply',\n port: peer.remoteWebPort ?? DEFAULT_REMOTE_WEB_PORT,\n })\n for (let offset = 0, sequence = 0; offset < requestBody.length; offset += MAX_SYNC_DATA_CHUNK_BYTES, sequence += 1) {\n await session.sendData(sequence, requestBody.subarray(offset, Math.min(offset + MAX_SYNC_DATA_CHUNK_BYTES, requestBody.length)))\n }\n await session.sendControl({ kind: 'request-end' })\n session.endInput()\n\n let response: Record<string, unknown> | undefined\n for await (const frame of session.readFrames()) {\n if (frame.kind !== 'control' || response) throw new SyncProtocolError('unexpected session apply response frame')\n response = frame.value\n }\n if (!response || response.kind === 'error') throw new SyncSshProbeError('helper')\n if (response.kind === 'response' && response.status === 409 && isRecord(response.body) &&\n response.body.code === 'session-delta-prefix') throw new SyncSessionDeltaPrefixError()\n if (response.kind !== 'response' || response.status !== 200 || response.body === null || typeof response.body !== 'object') {\n throw new SyncSshProbeError('helper')\n }\n const body = response.body as Record<string, unknown>\n if (body.ok !== true || body.result === null || typeof body.result !== 'object') throw new SyncSshProbeError('helper')\n const result = body.result as Record<string, unknown>\n for (const key of ['created', 'updated', 'archived', 'deleted', 'skipped', 'unmapped'] as const) {\n if (!Number.isSafeInteger(result[key]) || (result[key] as number) < 0) throw new SyncSshProbeError('protocol')\n }\n return result as unknown as SyncSessionApplyResult\n } catch (error) {\n if (error instanceof SyncSessionDeltaPrefixError) throw error\n if (error instanceof SyncSshProbeError) throw error\n if (error instanceof SyncProtocolError) throw new SyncSshProbeError('protocol')\n throw new SyncSshProbeError('helper')\n } finally {\n session.close()\n }\n }\n\n async trustObservedHostKey(peerId: string): Promise<void> {\n const { peer } = await this.peerManager.connectionData(peerId)\n const fingerprint = this.hostKeyChallenges.acceptObserved(peer)\n await this.peerManager.pinHostKey(peerId, fingerprint)\n }\n\n private async fetchDataOperation(\n peerId: string,\n operation: 'node-manifest' | 'session-bundle' | 'session-manifest' | 'session-entries',\n requestBody?: Record<string, unknown>,\n ): Promise<unknown> {\n const body = requestBody === undefined ? undefined : Buffer.from(JSON.stringify(requestBody), 'utf8')\n if (body && body.length > MAX_SYNC_TRANSFER_BYTES) throw new SyncSshProbeError('protocol')\n const { peer, session } = await this.openSession(peerId)\n try {\n await session.sendControl({\n kind: 'request',\n protocolVersion: SYNC_PROTOCOL_VERSION,\n operation,\n port: peer.remoteWebPort ?? DEFAULT_REMOTE_WEB_PORT,\n })\n if (body) {\n for (let offset = 0, sequence = 0; offset < body.length; offset += MAX_SYNC_DATA_CHUNK_BYTES, sequence += 1) {\n await session.sendData(sequence, body.subarray(offset, Math.min(offset + MAX_SYNC_DATA_CHUNK_BYTES, body.length)))\n }\n await session.sendControl({ kind: 'request-end' })\n }\n session.endInput()\n\n let responseStatus: number | undefined\n let bodyMode: string | undefined\n let responseEnded = false\n let totalBytes = 0\n const chunks: Buffer[] = []\n for await (const frame of session.readFrames()) {\n if (frame.kind === 'control') {\n if (frame.value.kind === 'error') throw new SyncSshProbeError('helper')\n if (frame.value.kind === 'response' && responseStatus === undefined) {\n responseStatus = typeof frame.value.status === 'number' ? frame.value.status : undefined\n bodyMode = typeof frame.value.bodyMode === 'string' ? frame.value.bodyMode : undefined\n continue\n }\n if (frame.value.kind === 'response-end' && responseStatus !== undefined && !responseEnded) {\n responseEnded = true\n continue\n }\n throw new SyncProtocolError(`unexpected ${operation} control frame`)\n }\n if (responseStatus === undefined || bodyMode !== 'data' || responseEnded) {\n throw new SyncProtocolError(`unexpected ${operation} data frame`)\n }\n totalBytes += frame.data.length\n if (totalBytes > MAX_SYNC_TRANSFER_BYTES) throw new SyncProtocolError(`${operation} exceeds transfer limit`)\n chunks.push(frame.data)\n }\n if (responseStatus !== 200 || !responseEnded) throw new SyncSshProbeError('helper')\n try {\n return JSON.parse(Buffer.concat(chunks, totalBytes).toString('utf8')) as unknown\n } catch {\n throw new SyncProtocolError(`${operation} response is invalid JSON`)\n }\n } catch (error) {\n if (error instanceof SyncSshProbeError) throw error\n if (error instanceof SyncProtocolError) {\n const probe = new SyncSshProbeError('protocol')\n ;(probe as Error & { detail?: string }).detail = error.message\n throw probe\n }\n throw new SyncSshProbeError('helper')\n } finally {\n session.close()\n }\n }\n\n private async requestHandshake(session: SyncSshSession, port: number): Promise<SyncRemoteHandshake> {\n await session.sendControl({\n kind: 'request',\n protocolVersion: SYNC_PROTOCOL_VERSION,\n operation: 'handshake',\n port,\n })\n session.endInput()\n\n let response: Record<string, unknown> | undefined\n for await (const frame of session.readFrames()) {\n if (frame.kind !== 'control' || response) throw new SyncProtocolError('unexpected helper frame')\n response = frame.value\n }\n if (!response) throw new SyncProtocolError('helper returned no handshake response')\n return validateHandshakeResponse(response)\n }\n}\n\nfunction validateHandshakeResponse(value: Record<string, unknown>): SyncRemoteHandshake {\n if (value.kind === 'error') throw new SyncSshProbeError('helper')\n if (value.kind !== 'response' || value.status !== 200 || value.body === null || typeof value.body !== 'object') {\n throw new SyncSshProbeError('helper')\n }\n const body = value.body as Record<string, unknown>\n if (\n typeof body.protocolVersion !== 'number' ||\n typeof body.instanceId !== 'string' ||\n !Array.isArray(body.capabilities) ||\n !body.capabilities.every((capability) => typeof capability === 'string')\n ) {\n throw new SyncSshProbeError('protocol')\n }\n return {\n protocolVersion: body.protocolVersion,\n instanceId: body.instanceId,\n capabilities: body.capabilities,\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"ssh-transport.js","names":[],"sources":["../../../src/core/sync/ssh-transport.ts"],"sourcesContent":["import { Client, type ConnectConfig } from 'ssh2'\nimport type { ClientChannel } from 'ssh2'\nimport { once } from 'node:events'\nimport { classifySshError } from '../../exec-ssh/connection.js'\nimport { SyncPeerManager, SyncPeerNotFoundError, type SyncPeerSecrets } from './peer-manager.js'\nimport { isRecord, type SyncPeer } from './model.js'\nimport { SyncHostKeyChallengeError, SyncHostKeyChallenges, inspectSyncHostKey } from './host-key.js'\nimport type { SyncNodeApplyResult, SyncNodeManifest } from './node-sync.js'\nimport {\n MAX_SYNC_DATA_CHUNK_BYTES,\n MAX_SYNC_TRANSFER_BYTES,\n SYNC_PROTOCOL_VERSION,\n SyncFrameDecoder,\n SyncProtocolError,\n encodeSyncControlFrame,\n encodeSyncDataFrame,\n type SyncFrame,\n} from './protocol.js'\nimport { REMOTE_SYNC_HELPER_COMMAND } from './remote-helper.js'\nimport type { SyncSessionApplyResult, SyncSessionBundle, SyncSessionEventPrefix, SyncSessionManifest } from './session-sync.js'\n\nconst SSH_READY_TIMEOUT_MS = 30_000\nconst SSH_READ_IDLE_TIMEOUT_MS = 60_000\nconst DEFAULT_REMOTE_WEB_PORT = 3080\n\nexport type SyncSshErrorCategory = 'auth' | 'network' | 'hostkey' | 'timeout' | 'helper' | 'protocol'\n\nexport class SyncSshProbeError extends Error {\n constructor(readonly category: SyncSshErrorCategory) {\n super(`SSH sync probe failed: ${category}`)\n this.name = 'SyncSshProbeError'\n }\n}\n\nexport class SyncSessionDeltaPrefixError extends Error {\n constructor() {\n super('remote session event prefix changed during transfer')\n this.name = 'SyncSessionDeltaPrefixError'\n }\n}\n\nexport interface SyncRemoteHandshake {\n protocolVersion: number\n instanceId: string\n capabilities: string[]\n}\n\nexport interface SyncRemoteNodeManifest extends SyncNodeManifest {\n capabilities?: string[]\n}\n\nexport class SyncSshSession {\n private readonly decoder = new SyncFrameDecoder()\n private reading = false\n private closed = false\n private inputEnded = false\n private nextDataSequence = 0\n private pendingError: Error | undefined\n\n constructor(\n private readonly client: Client,\n private readonly channel: ClientChannel,\n ) {\n channel.on('error', (error: Error) => { this.pendingError = error })\n channel.on('exit', (code: number | null) => {\n if (code !== null && code !== 0) this.pendingError = new Error(`remote helper exited with code ${code}`)\n })\n }\n\n async sendControl(value: Record<string, unknown>): Promise<void> {\n await this.write(encodeSyncControlFrame(value))\n }\n\n async sendData(sequence: number, data: Buffer): Promise<void> {\n if (sequence !== this.nextDataSequence) throw new SyncProtocolError('outgoing data sequence is out of order')\n await this.write(encodeSyncDataFrame(sequence, data))\n this.nextDataSequence += 1\n }\n\n endInput(): void {\n if (this.closed || this.inputEnded) return\n this.inputEnded = true\n this.channel.end()\n }\n\n async *readFrames(idleTimeoutMs = SSH_READ_IDLE_TIMEOUT_MS): AsyncGenerator<SyncFrame> {\n if (this.reading) throw new SyncProtocolError('SSH sync channel can only be read once')\n this.reading = true\n const channel = this.channel\n const decoder = this.decoder\n let idleDeadline: NodeJS.Timeout | undefined\n const armIdleDeadline = (): void => {\n clearTimeout(idleDeadline)\n idleDeadline = setTimeout(() => {\n this.pendingError = new Error(`SSH channel idle for more than ${Math.round(idleTimeoutMs / 1000)}s`)\n channel.destroy()\n }, idleTimeoutMs)\n }\n try {\n armIdleDeadline()\n for await (const chunk of channel.stdout as AsyncIterable<Buffer>) {\n armIdleDeadline()\n for (const frame of decoder.push(Buffer.from(chunk))) yield frame\n }\n if (this.pendingError) throw new SyncProtocolError(`SSH channel error: ${this.pendingError.message}`)\n decoder.finish()\n } catch (error) {\n if (error instanceof SyncProtocolError) throw error\n const detail = this.pendingError ? this.pendingError.message : error instanceof Error ? error.message : 'unknown error'\n throw new SyncProtocolError(`SSH channel read failed: ${detail}`)\n } finally {\n clearTimeout(idleDeadline)\n }\n }\n\n close(): void {\n if (this.closed) return\n this.closed = true\n this.channel.destroy()\n this.client.end()\n }\n\n private async write(frame: Buffer): Promise<void> {\n if (this.closed || this.inputEnded) throw new SyncProtocolError('SSH sync channel input is closed')\n if (!this.channel.write(frame)) await once(this.channel, 'drain')\n }\n}\n\nexport function buildSyncSshConnectConfig(\n peer: SyncPeer,\n secrets: SyncPeerSecrets,\n challenges: SyncHostKeyChallenges,\n onChallenge: (error: SyncHostKeyChallengeError) => void,\n): ConnectConfig {\n return {\n host: peer.host,\n port: peer.port,\n username: peer.username,\n hostHash: 'sha256',\n readyTimeout: SSH_READY_TIMEOUT_MS,\n keepaliveInterval: 15_000,\n hostVerifier: (sha256Hex: string): boolean => {\n try {\n const decision = inspectSyncHostKey(peer, sha256Hex, challenges)\n if (!decision.trusted) onChallenge(decision.challenge)\n return decision.trusted\n } catch (error) {\n if (error instanceof SyncHostKeyChallengeError) onChallenge(error)\n return false\n }\n },\n ...(peer.authKind === 'password'\n ? { password: secrets.password }\n : { privateKey: secrets.privateKey, ...(secrets.passphrase ? { passphrase: secrets.passphrase } : {}) }),\n }\n}\n\nexport class SyncSshTransport {\n private readonly hostKeyChallenges = new SyncHostKeyChallenges()\n\n constructor(private readonly peerManager: SyncPeerManager) {}\n\n async probe(peerId: string): Promise<SyncRemoteHandshake> {\n const { peer, session } = await this.openSession(peerId)\n try {\n const handshake = await this.requestHandshake(session, peer.remoteWebPort ?? DEFAULT_REMOTE_WEB_PORT)\n if (handshake.protocolVersion !== SYNC_PROTOCOL_VERSION) throw new SyncSshProbeError('protocol')\n return handshake\n } catch (error) {\n if (\n error instanceof SyncHostKeyChallengeError ||\n error instanceof SyncPeerNotFoundError ||\n error instanceof SyncSshProbeError\n ) {\n throw error\n }\n if (error instanceof SyncProtocolError) throw new SyncSshProbeError('protocol')\n throw new SyncSshProbeError('helper')\n } finally {\n session.close()\n }\n }\n\n async openSession(peerId: string): Promise<{ peer: SyncPeer; session: SyncSshSession }> {\n const { peer, secrets } = await this.peerManager.connectionData(peerId)\n if (peer.authKind === 'password' && !secrets.password) throw new SyncSshProbeError('auth')\n if (peer.authKind === 'key' && !secrets.privateKey) throw new SyncSshProbeError('auth')\n\n const client = new Client()\n let challenge: SyncHostKeyChallengeError | undefined\n let sessionOpened = false\n try {\n await new Promise<void>((resolve, reject) => {\n let settled = false\n const finish = (error?: unknown): void => {\n if (settled) return\n settled = true\n clearTimeout(readyDeadline)\n if (error) reject(error)\n else resolve()\n }\n\n // ssh2's readyTimeout can be defeated by stalls behind transparent\n // proxies; this deadline guarantees the connect promise settles.\n const readyDeadline = setTimeout(() => {\n const timeout = new Error('Timed out while waiting for SSH handshake')\n ;(timeout as Error & { level?: string }).level = 'client-timeout'\n finish(timeout)\n client.end()\n }, SSH_READY_TIMEOUT_MS * 2)\n client.once('ready', () => finish())\n client.on('error', (error: Error) => finish(challenge ?? error))\n const config = buildSyncSshConnectConfig(peer, secrets, this.hostKeyChallenges, (error) => {\n challenge = error\n })\n\n try {\n client.connect(config)\n } catch (error) {\n finish(error)\n }\n })\n\n const channel = await new Promise<ClientChannel>((resolve, reject) => {\n const execDeadline = setTimeout(() => {\n reject(new Error('Timed out while waiting for the sync helper channel'))\n client.end()\n }, SSH_READY_TIMEOUT_MS)\n client.exec(REMOTE_SYNC_HELPER_COMMAND, (error, opened) => {\n clearTimeout(execDeadline)\n if (error) reject(error)\n else resolve(opened)\n })\n })\n const session = new SyncSshSession(client, channel)\n sessionOpened = true\n return { peer, session }\n } catch (error) {\n if (error instanceof SyncHostKeyChallengeError || error instanceof SyncPeerNotFoundError || error instanceof SyncSshProbeError) {\n throw error\n }\n if (challenge) throw challenge\n const category = classifySshError(error)\n if (category === 'auth' || category === 'network' || category === 'timeout') {\n throw new SyncSshProbeError(category)\n }\n if (category === 'hostkey') throw new SyncSshProbeError('hostkey')\n if (error instanceof SyncProtocolError) throw new SyncSshProbeError('protocol')\n throw new SyncSshProbeError('helper')\n } finally {\n if (!sessionOpened) {\n try {\n client.end()\n } catch {}\n }\n }\n }\n\n async fetchNodeManifest(peerId: string): Promise<SyncRemoteNodeManifest> {\n const value = await this.fetchDataOperation(peerId, 'node-manifest')\n if (!isRecord(value) || value.ok !== true || !isRecord(value.manifest)) throw new SyncSshProbeError('helper')\n const manifest = value.manifest\n if (manifest.protocolVersion !== SYNC_PROTOCOL_VERSION || typeof manifest.instanceId !== 'string' || !Array.isArray(manifest.records)) {\n throw new SyncSshProbeError('protocol')\n }\n const capabilities = value.capabilities === undefined ? undefined : value.capabilities\n if (capabilities !== undefined && (!Array.isArray(capabilities) || !capabilities.every((item) => typeof item === 'string'))) {\n throw new SyncSshProbeError('protocol')\n }\n return { ...manifest, ...(capabilities ? { capabilities } : {}) } as unknown as SyncRemoteNodeManifest\n }\n\n async applyNodeManifest(\n peerId: string,\n records: SyncNodeManifest['records'],\n sourceName: string,\n ): Promise<SyncNodeApplyResult> {\n const requestBody = Buffer.from(JSON.stringify({ records, sourceName }), 'utf8')\n if (requestBody.length > MAX_SYNC_TRANSFER_BYTES) throw new SyncSshProbeError('protocol')\n const { peer, session } = await this.openSession(peerId)\n try {\n await session.sendControl({\n kind: 'request',\n protocolVersion: SYNC_PROTOCOL_VERSION,\n operation: 'node-apply',\n port: peer.remoteWebPort ?? DEFAULT_REMOTE_WEB_PORT,\n })\n for (let offset = 0, sequence = 0; offset < requestBody.length; offset += MAX_SYNC_DATA_CHUNK_BYTES, sequence += 1) {\n await session.sendData(sequence, requestBody.subarray(offset, Math.min(offset + MAX_SYNC_DATA_CHUNK_BYTES, requestBody.length)))\n }\n await session.sendControl({ kind: 'request-end' })\n session.endInput()\n\n let response: Record<string, unknown> | undefined\n for await (const frame of session.readFrames()) {\n if (frame.kind !== 'control' || response) throw new SyncProtocolError('unexpected node apply response frame')\n response = frame.value\n }\n if (!response || response.kind === 'error') throw new SyncSshProbeError('helper')\n if (response.kind !== 'response' || response.status !== 200 || response.body === null || typeof response.body !== 'object') {\n throw new SyncSshProbeError('helper')\n }\n const body = response.body as Record<string, unknown>\n if (body.ok !== true || body.result === null || typeof body.result !== 'object') throw new SyncSshProbeError('helper')\n const result = body.result as Record<string, unknown>\n for (const key of ['created', 'updated', 'deleted', 'pendingRemoval', 'skipped'] as const) {\n if (!Number.isSafeInteger(result[key]) || (result[key] as number) < 0) throw new SyncSshProbeError('protocol')\n }\n return result as unknown as SyncNodeApplyResult\n } catch (error) {\n if (error instanceof SyncSshProbeError) throw error\n if (error instanceof SyncProtocolError) throw new SyncSshProbeError('protocol')\n throw new SyncSshProbeError('helper')\n } finally {\n session.close()\n }\n }\n\n async fetchSessionBundle(peerId: string): Promise<SyncSessionBundle> {\n const value = await this.fetchDataOperation(peerId, 'session-bundle')\n if (!isRecord(value) || value.ok !== true || !isRecord(value.bundle)) throw new SyncSshProbeError('helper')\n const bundle = value.bundle\n if (bundle.protocolVersion !== 1 || typeof bundle.instanceId !== 'string' ||\n !Array.isArray(bundle.entries) || !Array.isArray(bundle.skipped) || typeof bundle.digest !== 'string') {\n throw new SyncSshProbeError('protocol')\n }\n return bundle as unknown as SyncSessionBundle\n }\n\n async fetchSessionManifest(peerId: string): Promise<SyncSessionManifest> {\n const value = await this.fetchDataOperation(peerId, 'session-manifest')\n if (!isRecord(value) || value.ok !== true || !isRecord(value.manifest)) throw new SyncSshProbeError('helper')\n const manifest = value.manifest\n if (manifest.protocolVersion !== 1 || typeof manifest.instanceId !== 'string' ||\n !Array.isArray(manifest.entries) || !Array.isArray(manifest.skipped) || typeof manifest.digest !== 'string') {\n throw new SyncSshProbeError('protocol')\n }\n return manifest as unknown as SyncSessionManifest\n }\n\n async fetchSessionEntries(\n peerId: string,\n keys: readonly string[],\n manifestDigest: string,\n prefixes: readonly SyncSessionEventPrefix[] = [],\n ): Promise<SyncSessionBundle> {\n const value = await this.fetchDataOperation(peerId, 'session-entries', { keys, manifestDigest, prefixes })\n if (!isRecord(value) || value.ok !== true || !isRecord(value.bundle)) throw new SyncSshProbeError('helper')\n const bundle = value.bundle\n if (bundle.protocolVersion !== 1 || typeof bundle.instanceId !== 'string' ||\n !Array.isArray(bundle.entries) || !Array.isArray(bundle.skipped) || typeof bundle.digest !== 'string') {\n throw new SyncSshProbeError('protocol')\n }\n return bundle as unknown as SyncSessionBundle\n }\n\n async applySessionBundle(peerId: string, bundle: SyncSessionBundle, sourceName: string): Promise<SyncSessionApplyResult> {\n const requestBody = Buffer.from(JSON.stringify({ bundle, sourceName, peerId }), 'utf8')\n if (requestBody.length > MAX_SYNC_TRANSFER_BYTES) throw new SyncSshProbeError('protocol')\n const { peer, session } = await this.openSession(peerId)\n try {\n await session.sendControl({\n kind: 'request',\n protocolVersion: SYNC_PROTOCOL_VERSION,\n operation: 'session-apply',\n port: peer.remoteWebPort ?? DEFAULT_REMOTE_WEB_PORT,\n })\n for (let offset = 0, sequence = 0; offset < requestBody.length; offset += MAX_SYNC_DATA_CHUNK_BYTES, sequence += 1) {\n await session.sendData(sequence, requestBody.subarray(offset, Math.min(offset + MAX_SYNC_DATA_CHUNK_BYTES, requestBody.length)))\n }\n await session.sendControl({ kind: 'request-end' })\n session.endInput()\n\n let response: Record<string, unknown> | undefined\n for await (const frame of session.readFrames()) {\n if (frame.kind !== 'control' || response) throw new SyncProtocolError('unexpected session apply response frame')\n response = frame.value\n }\n if (!response || response.kind === 'error') throw new SyncSshProbeError('helper')\n if (response.kind === 'response' && response.status === 409 && isRecord(response.body) &&\n response.body.code === 'session-delta-prefix') throw new SyncSessionDeltaPrefixError()\n if (response.kind !== 'response' || response.status !== 200 || response.body === null || typeof response.body !== 'object') {\n throw new SyncSshProbeError('helper')\n }\n const body = response.body as Record<string, unknown>\n if (body.ok !== true || body.result === null || typeof body.result !== 'object') throw new SyncSshProbeError('helper')\n const result = body.result as Record<string, unknown>\n for (const key of ['created', 'updated', 'archived', 'deleted', 'skipped', 'unmapped'] as const) {\n if (!Number.isSafeInteger(result[key]) || (result[key] as number) < 0) throw new SyncSshProbeError('protocol')\n }\n return result as unknown as SyncSessionApplyResult\n } catch (error) {\n if (error instanceof SyncSessionDeltaPrefixError) throw error\n if (error instanceof SyncSshProbeError) throw error\n if (error instanceof SyncProtocolError) throw new SyncSshProbeError('protocol')\n throw new SyncSshProbeError('helper')\n } finally {\n session.close()\n }\n }\n\n async trustObservedHostKey(peerId: string): Promise<void> {\n const { peer } = await this.peerManager.connectionData(peerId)\n const fingerprint = this.hostKeyChallenges.acceptObserved(peer)\n await this.peerManager.pinHostKey(peerId, fingerprint)\n }\n\n private async fetchDataOperation(\n peerId: string,\n operation: 'node-manifest' | 'session-bundle' | 'session-manifest' | 'session-entries',\n requestBody?: Record<string, unknown>,\n ): Promise<unknown> {\n const body = requestBody === undefined ? undefined : Buffer.from(JSON.stringify(requestBody), 'utf8')\n if (body && body.length > MAX_SYNC_TRANSFER_BYTES) throw new SyncSshProbeError('protocol')\n const { peer, session } = await this.openSession(peerId)\n try {\n await session.sendControl({\n kind: 'request',\n protocolVersion: SYNC_PROTOCOL_VERSION,\n operation,\n port: peer.remoteWebPort ?? DEFAULT_REMOTE_WEB_PORT,\n })\n if (body) {\n for (let offset = 0, sequence = 0; offset < body.length; offset += MAX_SYNC_DATA_CHUNK_BYTES, sequence += 1) {\n await session.sendData(sequence, body.subarray(offset, Math.min(offset + MAX_SYNC_DATA_CHUNK_BYTES, body.length)))\n }\n await session.sendControl({ kind: 'request-end' })\n }\n session.endInput()\n\n let responseStatus: number | undefined\n let bodyMode: string | undefined\n let responseEnded = false\n let totalBytes = 0\n const chunks: Buffer[] = []\n for await (const frame of session.readFrames()) {\n if (frame.kind === 'control') {\n if (frame.value.kind === 'error') throw new SyncSshProbeError('helper')\n if (frame.value.kind === 'response' && responseStatus === undefined) {\n responseStatus = typeof frame.value.status === 'number' ? frame.value.status : undefined\n bodyMode = typeof frame.value.bodyMode === 'string' ? frame.value.bodyMode : undefined\n continue\n }\n if (frame.value.kind === 'response-end' && responseStatus !== undefined && !responseEnded) {\n responseEnded = true\n continue\n }\n throw new SyncProtocolError(`unexpected ${operation} control frame`)\n }\n if (responseStatus === undefined || bodyMode !== 'data' || responseEnded) {\n throw new SyncProtocolError(`unexpected ${operation} data frame`)\n }\n totalBytes += frame.data.length\n if (totalBytes > MAX_SYNC_TRANSFER_BYTES) throw new SyncProtocolError(`${operation} exceeds transfer limit`)\n chunks.push(frame.data)\n }\n if (responseStatus !== 200 || !responseEnded) throw new SyncSshProbeError('helper')\n try {\n return JSON.parse(Buffer.concat(chunks, totalBytes).toString('utf8')) as unknown\n } catch {\n throw new SyncProtocolError(`${operation} response is invalid JSON`)\n }\n } catch (error) {\n if (error instanceof SyncSshProbeError) throw error\n if (error instanceof SyncProtocolError) {\n const probe = new SyncSshProbeError('protocol')\n ;(probe as Error & { detail?: string }).detail = error.message\n throw probe\n }\n throw new SyncSshProbeError('helper')\n } finally {\n session.close()\n }\n }\n\n private async requestHandshake(session: SyncSshSession, port: number): Promise<SyncRemoteHandshake> {\n await session.sendControl({\n kind: 'request',\n protocolVersion: SYNC_PROTOCOL_VERSION,\n operation: 'handshake',\n port,\n })\n session.endInput()\n\n let response: Record<string, unknown> | undefined\n for await (const frame of session.readFrames()) {\n if (frame.kind !== 'control' || response) throw new SyncProtocolError('unexpected helper frame')\n response = frame.value\n }\n if (!response) throw new SyncProtocolError('helper returned no handshake response')\n return validateHandshakeResponse(response)\n }\n}\n\nfunction validateHandshakeResponse(value: Record<string, unknown>): SyncRemoteHandshake {\n if (value.kind === 'error') throw new SyncSshProbeError('helper')\n if (value.kind !== 'response' || value.status !== 200 || value.body === null || typeof value.body !== 'object') {\n throw new SyncSshProbeError('helper')\n }\n const body = value.body as Record<string, unknown>\n if (\n typeof body.protocolVersion !== 'number' ||\n typeof body.instanceId !== 'string' ||\n !Array.isArray(body.capabilities) ||\n !body.capabilities.every((capability) => typeof capability === 'string')\n ) {\n throw new SyncSshProbeError('protocol')\n }\n return {\n protocolVersion: body.protocolVersion,\n instanceId: body.instanceId,\n capabilities: body.capabilities,\n }\n}\n"],"mappings":"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"}
@@ -35,7 +35,7 @@ declare class SyncSshSession {
35
35
  sendControl(value: Record<string, unknown>): Promise<void>;
36
36
  sendData(sequence: number, data: Buffer): Promise<void>;
37
37
  endInput(): void;
38
- readFrames(): AsyncGenerator<SyncFrame>;
38
+ readFrames(idleTimeoutMs?: number): AsyncGenerator<SyncFrame>;
39
39
  close(): void;
40
40
  private write;
41
41
  }
@@ -61,4 +61,4 @@ declare class SyncSshTransport {
61
61
  }
62
62
  //#endregion
63
63
  export { SyncSshProbeError as a, buildSyncSshConnectConfig as c, SyncSshErrorCategory as i, SyncRemoteNodeManifest as n, SyncSshSession as o, SyncSessionDeltaPrefixError as r, SyncSshTransport as s, SyncRemoteHandshake as t };
64
- //# sourceMappingURL=ssh-transport-DGkQh-HA.d.ts.map
64
+ //# sourceMappingURL=ssh-transport-gb2PVatu.d.ts.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@artenx/dshb",
3
- "version": "0.1.23",
3
+ "version": "0.1.24",
4
4
  "description": "Routes DSH filesystem, shell, subprocess, and PTY operations to local, SSH, and Docker execution nodes, with a node registry, an authentication gate, a mobile-aware management UI, and per-workspace custom project instructions.",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",