@artenx/dshb 0.1.21 → 0.1.23
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.
- package/README.md +1 -1
- package/lib/auth/index.js +22 -11
- package/lib/auth/index.js.map +1 -1
- package/lib/client.js +1 -1
- package/lib/client.js.map +1 -1
- package/lib/core/index.js +2 -2
- package/lib/core/index.js.map +1 -1
- package/lib/core/sync/coordinator.d.ts +2 -2
- package/lib/core/sync/coordinator.js +121 -15
- package/lib/core/sync/coordinator.js.map +1 -1
- package/lib/core/sync/internal-routes.d.ts +1 -1
- package/lib/core/sync/internal-routes.js +58 -9
- package/lib/core/sync/internal-routes.js.map +1 -1
- package/lib/core/sync/offline-archive.d.ts +1 -1
- package/lib/core/sync/offline-archive.js +1 -1
- package/lib/core/sync/offline-routes.js +1 -1
- package/lib/core/sync/protocol.d.ts +2 -2
- package/lib/core/sync/protocol.js +4 -1
- package/lib/core/sync/protocol.js.map +1 -1
- package/lib/core/sync/remote-helper.js +10 -4
- package/lib/core/sync/remote-helper.js.map +1 -1
- package/lib/core/sync/routes.d.ts +1 -1
- package/lib/core/sync/session-routes.d.ts +1 -1
- package/lib/core/sync/session-routes.js +1 -1
- package/lib/core/sync/session-sync.d.ts +2 -2
- package/lib/core/sync/session-sync.js +2 -2
- package/lib/core/sync/ssh-transport.d.ts +2 -2
- package/lib/core/sync/ssh-transport.js +97 -104
- package/lib/core/sync/ssh-transport.js.map +1 -1
- package/lib/{protocol-CV7LGE44.d.ts → protocol-KaWZArY8.d.ts} +5 -2
- package/lib/{session-sync-EdfwUON5.js → session-sync-C0uoizyB.js} +834 -98
- package/lib/session-sync-C0uoizyB.js.map +1 -0
- package/lib/{session-sync-g6qKZzjV.d.ts → session-sync-C2j2rk9K.d.ts} +56 -4
- package/lib/{ssh-transport-BcIiLxsX.d.ts → ssh-transport-DGkQh-HA.d.ts} +14 -5
- package/package.json +1 -1
- package/lib/session-sync-EdfwUON5.js.map +0 -1
|
@@ -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 } 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 interface SyncRemoteHandshake {\n protocolVersion: number\n instanceId: string\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<SyncNodeManifest> {\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-manifest',\n port: peer.remoteWebPort ?? DEFAULT_REMOTE_WEB_PORT,\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 node manifest control frame')\n }\n if (responseStatus === undefined || bodyMode !== 'data' || responseEnded) {\n throw new SyncProtocolError('unexpected node manifest data frame')\n }\n totalBytes += frame.data.length\n if (totalBytes > MAX_SYNC_TRANSFER_BYTES) throw new SyncProtocolError('node manifest exceeds transfer limit')\n chunks.push(frame.data)\n }\n if (responseStatus !== 200 || !responseEnded) throw new SyncSshProbeError('helper')\n\n let value: unknown\n try {\n value = JSON.parse(Buffer.concat(chunks, totalBytes).toString('utf8'))\n } catch {\n throw new SyncProtocolError('node manifest response is invalid JSON')\n }\n if (value === null || typeof value !== 'object' || Array.isArray(value) || (value as Record<string, unknown>).ok !== true) {\n throw new SyncSshProbeError('helper')\n }\n const manifest = (value as { manifest?: unknown }).manifest\n if (\n manifest === null || typeof manifest !== 'object' || Array.isArray(manifest) ||\n (manifest as Record<string, unknown>).protocolVersion !== SYNC_PROTOCOL_VERSION ||\n typeof (manifest as Record<string, unknown>).instanceId !== 'string' ||\n !Array.isArray((manifest as Record<string, unknown>).records)\n ) {\n throw new SyncSshProbeError('protocol')\n }\n return manifest as SyncNodeManifest\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 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 { peer, session } = await this.openSession(peerId)\n try {\n await session.sendControl({\n kind: 'request',\n protocolVersion: SYNC_PROTOCOL_VERSION,\n operation: 'session-bundle',\n port: peer.remoteWebPort ?? DEFAULT_REMOTE_WEB_PORT,\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 session bundle control frame')\n }\n if (responseStatus === undefined || bodyMode !== 'data' || responseEnded) {\n throw new SyncProtocolError('unexpected session bundle data frame')\n }\n totalBytes += frame.data.length\n if (totalBytes > MAX_SYNC_TRANSFER_BYTES) throw new SyncProtocolError('session bundle exceeds transfer limit')\n chunks.push(frame.data)\n }\n if (responseStatus !== 200 || !responseEnded) throw new SyncSshProbeError('helper')\n let value: unknown\n try {\n value = JSON.parse(Buffer.concat(chunks, totalBytes).toString('utf8'))\n } catch {\n throw new SyncProtocolError('session bundle response is invalid JSON')\n }\n if (!isRecord(value) || value.ok !== true || !isRecord(value.bundle)) throw new SyncSshProbeError('helper')\n const bundle = value.bundle\n if (\n 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 return bundle as unknown as SyncSessionBundle\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 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 !== 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 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 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 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,5 +1,8 @@
|
|
|
1
1
|
//#region src/core/sync/protocol.d.ts
|
|
2
2
|
declare const SYNC_PROTOCOL_VERSION = 1;
|
|
3
|
+
declare const SYNC_SESSION_MANIFEST_CAPABILITY = "session-manifest-v1";
|
|
4
|
+
declare const SYNC_SESSION_EVENT_TAIL_CAPABILITY = "session-event-tail-delta-v1";
|
|
5
|
+
declare const SYNC_SESSION_BUNDLE_OBJECTS_CAPABILITY = "session-bundle-objects-v1";
|
|
3
6
|
declare const MAX_SYNC_CONTROL_FRAME_BYTES: number;
|
|
4
7
|
declare const MAX_SYNC_DATA_CHUNK_BYTES: number;
|
|
5
8
|
declare const MAX_SYNC_TRANSFER_BYTES: number;
|
|
@@ -31,5 +34,5 @@ declare class SyncFrameDecoder {
|
|
|
31
34
|
private resetFrame;
|
|
32
35
|
}
|
|
33
36
|
//#endregion
|
|
34
|
-
export {
|
|
35
|
-
//# sourceMappingURL=protocol-
|
|
37
|
+
export { SYNC_SESSION_BUNDLE_OBJECTS_CAPABILITY as a, SyncFrame as c, encodeSyncControlFrame as d, encodeSyncDataFrame as f, SYNC_PROTOCOL_VERSION as i, SyncFrameDecoder as l, MAX_SYNC_DATA_CHUNK_BYTES as n, SYNC_SESSION_EVENT_TAIL_CAPABILITY as o, MAX_SYNC_TRANSFER_BYTES as r, SYNC_SESSION_MANIFEST_CAPABILITY as s, MAX_SYNC_CONTROL_FRAME_BYTES as t, SyncProtocolError as u };
|
|
38
|
+
//# sourceMappingURL=protocol-KaWZArY8.d.ts.map
|