@artenx/dshb 0.1.30 → 0.1.32
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/lib/core/index.js +6 -1
- package/lib/core/index.js.map +1 -1
- package/lib/core/sync/coordinator.d.ts +2 -2
- package/lib/core/sync/coordinator.js +108 -46
- 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 +81 -5
- 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 +5 -2
- package/lib/core/sync/protocol.js.map +1 -1
- package/lib/core/sync/remote-helper.js +107 -76
- 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 +1 -1
- package/lib/core/sync/ssh-transport.js +104 -6
- package/lib/core/sync/ssh-transport.js.map +1 -1
- package/lib/{protocol-KaWZArY8.d.ts → protocol-DJ0zyVlB.d.ts} +5 -2
- package/lib/{session-sync-C2j2rk9K.d.ts → session-sync-BuOJvFxK.d.ts} +64 -2
- package/lib/{session-sync-C0uoizyB.js → session-sync-CIqjuWkh.js} +548 -74
- package/lib/session-sync-CIqjuWkh.js.map +1 -0
- package/lib/{ssh-transport-uK9Ep0yp.d.ts → ssh-transport-B2oNDs0D.d.ts} +14 -4
- package/package.json +1 -1
- package/lib/session-sync-C0uoizyB.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"internal-routes.js","names":[],"sources":["../../../src/core/sync/internal-routes.ts"],"sourcesContent":["import type { IncomingMessage, ServerResponse } from 'node:http'\nimport type { Context } from '@deepseek-ai/cordis'\nimport type {} from '@deepseek-ai/dsh-host-webserver'\nimport { isLoopbackRequest } from '../../auth/loopback.js'\nimport { SyncNodeManifestError, type SyncNodeService } from './node-sync.js'\nimport { SyncSessionError, type SyncSessionEventPrefix, type SyncSessionService } from './session-sync.js'\nimport {\n MAX_SYNC_TRANSFER_BYTES,\n SYNC_PROTOCOL_VERSION,\n SYNC_SESSION_BUNDLE_OBJECTS_CAPABILITY,\n SYNC_SESSION_EVENT_TAIL_CAPABILITY,\n SYNC_SESSION_MANIFEST_CAPABILITY,\n} from './protocol.js'\nimport type { SyncStateStore } from './stores.js'\n\nconst MAX_APPLY_BODY_BYTES = MAX_SYNC_TRANSFER_BYTES\n\nfunction sendJson(res: ServerResponse, status: number, body: unknown): void {\n res.writeHead(status, { 'content-type': 'application/json', 'cache-control': 'no-store' })\n res.end(JSON.stringify(body))\n}\n\nfunction pathSegments(req: IncomingMessage): string[] {\n return new URL(req.url ?? '/', 'http://x').pathname.split('/').filter(Boolean)\n}\n\nfunction readJsonBody(req: IncomingMessage): Promise<Record<string, unknown> | null> {\n return new Promise((resolve) => {\n if (req.headers['content-type']?.split(';', 1)[0]?.trim().toLowerCase() !== 'application/json') {\n resolve(null)\n return\n }\n const chunks: Buffer[] = []\n let size = 0\n let settled = false\n const fail = (): void => {\n if (settled) return\n settled = true\n resolve(null)\n }\n req.on('data', (chunk: Buffer) => {\n if (settled) return\n size += chunk.length\n if (size > MAX_APPLY_BODY_BYTES) {\n fail()\n req.destroy()\n return\n }\n chunks.push(chunk)\n })\n req.on('end', () => {\n if (settled) return\n try {\n const value: unknown = JSON.parse(Buffer.concat(chunks).toString('utf8'))\n if (value === null || typeof value !== 'object' || Array.isArray(value)) {\n fail()\n return\n }\n settled = true\n resolve(value as Record<string, unknown>)\n } catch {\n fail()\n }\n })\n req.on('error', fail)\n })\n}\n\nexport function registerSyncInternalRoutes(\n ctx: Context,\n state: SyncStateStore,\n nodeSync?: SyncNodeService,\n sessionSync?: SyncSessionService,\n): void {\n const capabilities = [\n 'peer-handshake',\n ...(nodeSync ? ['node-sync'] : []),\n ...(sessionSync ? ['session-sync', SYNC_SESSION_MANIFEST_CAPABILITY, SYNC_SESSION_EVENT_TAIL_CAPABILITY, SYNC_SESSION_BUNDLE_OBJECTS_CAPABILITY] : []),\n ]\n ctx.effect(() =>\n ctx.webServer.register({\n kind: 'exact',\n path: '/api/dshb/sync/internal/handshake',\n handler: (req: IncomingMessage, res: ServerResponse) => {\n if (!isLoopbackRequest(req.socket.remoteAddress, req.headers.host)) {\n sendJson(res, 403, { ok: false, error: 'loopback access required' })\n return\n }\n if (req.method !== 'GET') {\n sendJson(res, 405, { ok: false, error: 'method not allowed' })\n return\n }\n try {\n sendJson(res, 200, {\n ok: true,\n protocolVersion: SYNC_PROTOCOL_VERSION,\n instanceId: state.instanceId(),\n capabilities,\n })\n } catch {\n sendJson(res, 503, { ok: false, error: 'sync identity unavailable' })\n }\n },\n }),\n )\n\n if (nodeSync) ctx.effect(() =>\n ctx.webServer.register({\n kind: 'prefix',\n path: '/api/dshb/sync/internal/nodes',\n handler: async (req, res) => {\n if (!isLoopbackRequest(req.socket.remoteAddress, req.headers.host)) {\n sendJson(res, 403, { ok: false, error: 'loopback access required' })\n return\n }\n const segments = pathSegments(req)\n const action = segments[5]\n if (segments.length !== 6) {\n sendJson(res, 404, { ok: false, error: 'not found' })\n return\n }\n if (req.method === 'GET' && action === 'manifest') {\n try {\n sendJson(res, 200, { ok: true, manifest: await nodeSync.exportManifest(), capabilities })\n } catch {\n sendJson(res, 500, { ok: false, error: 'node manifest export failed' })\n }\n return\n }\n if (req.method === 'POST' && action === 'apply') {\n const body = await readJsonBody(req)\n if (!body || !Array.isArray(body.records) || typeof body.sourceName !== 'string') {\n sendJson(res, 400, { ok: false, error: 'records and sourceName are required' })\n return\n }\n try {\n sendJson(res, 200, { ok: true, result: await nodeSync.applyManifest(body.records, body.sourceName) })\n } catch (error) {\n const status = error instanceof SyncNodeManifestError ? 400 : 500\n sendJson(res, status, { ok: false, error: status === 400 ? 'node manifest rejected' : 'node manifest apply failed' })\n }\n return\n }\n sendJson(res, 404, { ok: false, error: 'not found' })\n },\n }),\n )\n\n if (!sessionSync) return\n ctx.effect(() =>\n ctx.webServer.register({\n kind: 'prefix',\n path: '/api/dshb/sync/internal/sessions',\n handler: async (req, res) => {\n if (!isLoopbackRequest(req.socket.remoteAddress, req.headers.host)) {\n sendJson(res, 403, { ok: false, error: 'loopback access required' })\n return\n }\n const segments = pathSegments(req)\n const action = segments[5]\n if (segments.length !== 6) {\n sendJson(res, 404, { ok: false, error: 'not found' })\n return\n }\n if (req.method === 'GET' && action === 'bundle') {\n try {\n sendJson(res, 200, { ok: true, bundle: await sessionSync.exportBundle() })\n } catch (error) {\n const code = error instanceof SyncSessionError ? error.code : 'session-export-failed'\n const status = code === 'bundle-too-large' ? 413 : 500\n sendJson(res, status, { ok: false, code, error: error instanceof SyncSessionError ? error.message : 'session bundle export failed' })\n }\n return\n }\n if (req.method === 'GET' && action === 'manifest') {\n try {\n sendJson(res, 200, { ok: true, manifest: await sessionSync.exportManifest() })\n } catch (error) {\n const code = error instanceof SyncSessionError ? error.code : 'session-export-failed'\n const status = code === 'bundle-too-large' ? 413 : 500\n sendJson(res, status, { ok: false, code, error: error instanceof SyncSessionError ? error.message : 'session manifest export failed' })\n }\n return\n }\n if (req.method === 'POST' && action === 'entries') {\n const body = await readJsonBody(req)\n const prefixesValid = body?.prefixes === undefined || (Array.isArray(body.prefixes) &&\n body.prefixes.every((prefix) => prefix !== null && typeof prefix === 'object' && !Array.isArray(prefix)))\n if (!body || !Array.isArray(body.keys) || !body.keys.every((key) => typeof key === 'string') ||\n typeof body.manifestDigest !== 'string' || !prefixesValid) {\n sendJson(res, 400, { ok: false, code: 'session-request-invalid', error: 'keys, manifestDigest, and valid prefixes are required' })\n return\n }\n try {\n sendJson(res, 200, {\n ok: true,\n bundle: await sessionSync.exportEntries(\n body.keys as string[],\n body.manifestDigest,\n (body.prefixes ?? []) as SyncSessionEventPrefix[],\n ),\n })\n } catch (error) {\n const code = error instanceof SyncSessionError ? error.code : 'session-export-failed'\n const status = code === 'bundle-too-large' ? 413 : 500\n sendJson(res, status, { ok: false, code, error: error instanceof SyncSessionError ? error.message : 'session entries export failed' })\n }\n return\n }\n if (req.method === 'POST' && action === 'apply') {\n const body = await readJsonBody(req)\n if (!body || body.bundle === null || typeof body.bundle !== 'object' ||\n typeof body.sourceName !== 'string' || typeof body.peerId !== 'string' || !/^p-[a-f0-9]{24}$/.test(body.peerId)) {\n sendJson(res, 400, { ok: false, code: 'session-request-invalid', error: 'bundle, sourceName, and peerId are required' })\n return\n }\n try {\n sendJson(res, 200, {\n ok: true,\n result: await sessionSync.applyBundle(body.bundle, body.sourceName, body.peerId),\n })\n } catch (error) {\n const code = error instanceof SyncSessionError ? error.code : 'session-apply-failed'\n const status = code === 'bundle-too-large' ? 413\n : ['manifest-invalid', 'manifest-digest', 'object-invalid', 'session-format'].includes(code) ? 400\n : code === 'workspace-unmapped' || code === 'session-delta-prefix' ? 409 : 500\n sendJson(res, status, { ok: false, code, error: error instanceof SyncSessionError ? error.message : 'session bundle apply failed' })\n }\n return\n }\n sendJson(res, 404, { ok: false, error: 'not found' })\n },\n }),\n )\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"internal-routes.js","names":[],"sources":["../../../src/core/sync/internal-routes.ts"],"sourcesContent":["import type { IncomingMessage, ServerResponse } from 'node:http'\nimport type { Context } from '@deepseek-ai/cordis'\nimport type {} from '@deepseek-ai/dsh-host-webserver'\nimport { isLoopbackRequest } from '../../auth/loopback.js'\nimport { SyncNodeManifestError, type SyncNodeService } from './node-sync.js'\nimport {\n MAX_SESSION_OBJECT_STREAM_BYTES,\n SYNC_PROTOCOL_VERSION,\n SYNC_SESSION_BUNDLE_OBJECTS_CAPABILITY,\n SYNC_SESSION_EVENT_TAIL_CAPABILITY,\n SYNC_SESSION_MANIFEST_CAPABILITY,\n SYNC_SESSION_OBJECT_STREAM_CAPABILITY,\n} from './protocol.js'\nimport { SyncSessionError, type SyncSessionEventPrefix, type SyncSessionService, validateObjectPayload } from './session-sync.js'\nimport type { SyncStateStore } from './stores.js'\n\n// 与 MAX_SYNC_TRANSFER_BYTES 对齐的请求体硬上限:超限直接断开(resolve null → 400),\n// 避免对端(或恶意请求)把宿主堆吃满。协调器分批后正常请求远小于该值。\nconst MAX_APPLY_BODY_BYTES = 128 * 1024 * 1024\n\nfunction sendJson(res: ServerResponse, status: number, body: unknown): void {\n res.writeHead(status, { 'content-type': 'application/json', 'cache-control': 'no-store' })\n res.end(JSON.stringify(body))\n}\n\nfunction pathSegments(req: IncomingMessage): string[] {\n return new URL(req.url ?? '/', 'http://x').pathname.split('/').filter(Boolean)\n}\n\nfunction readJsonBody(req: IncomingMessage): Promise<Record<string, unknown> | null> {\n return new Promise((resolve) => {\n if (req.headers['content-type']?.split(';', 1)[0]?.trim().toLowerCase() !== 'application/json') {\n resolve(null)\n return\n }\n const chunks: Buffer[] = []\n let size = 0\n let settled = false\n const fail = (): void => {\n if (settled) return\n settled = true\n resolve(null)\n }\n req.on('data', (chunk: Buffer) => {\n if (settled) return\n size += chunk.length\n if (size > MAX_APPLY_BODY_BYTES) {\n fail()\n req.destroy()\n return\n }\n chunks.push(chunk)\n })\n req.on('end', () => {\n if (settled) return\n try {\n const value: unknown = JSON.parse(Buffer.concat(chunks).toString('utf8'))\n if (value === null || typeof value !== 'object' || Array.isArray(value)) {\n fail()\n return\n }\n settled = true\n resolve(value as Record<string, unknown>)\n } catch {\n fail()\n }\n })\n req.on('error', fail)\n })\n}\n\n/**\n * 会话对象带外流端点(POST /api/dshb/sync/internal/sessions/objects):\n * - 拉取模式(application/json 请求体 {objects: 描述符[]}):把本地对象编码为\n * \"JSON 头行 + 原始字节\" 帧流响应,磁盘到网络流式读出,内存 O(帧)。\n * - 推送模式(application/x-dshb-object-stream 请求体):请求帧流直接落盘\n * 收件箱(边收边校验摘要),返回 JSON 摘要。\n * 两种模式全程流式,单对象体积上限 MAX_SESSION_OBJECT_STREAM_BYTES(64GB),\n * 不受 JSON bundle 传输上限约束。\n */\nasync function handleSessionObjects(\n sessionSync: SyncSessionService,\n req: IncomingMessage,\n res: ServerResponse,\n): Promise<void> {\n if (!isLoopbackRequest(req.socket.remoteAddress, req.headers.host)) {\n sendJson(res, 403, { ok: false, error: 'loopback access required' })\n return\n }\n const contentType = req.headers['content-type']?.split(';', 1)[0]?.trim().toLowerCase()\n if (contentType === 'application/x-dshb-object-stream') {\n try {\n const received = await sessionSync.ingestObjectStream(req)\n sendJson(res, 200, { ok: true, received })\n } catch (error) {\n const code = error instanceof SyncSessionError ? error.code : 'object-stream-failed'\n sendJson(res, 422, { ok: false, code, error: error instanceof SyncSessionError ? error.message : 'object stream ingest failed' })\n }\n return\n }\n if (contentType !== 'application/json') {\n sendJson(res, 400, { ok: false, code: 'object-request-invalid', error: 'json or object-stream content type is required' })\n return\n }\n const body = await readJsonBody(req)\n if (!body || !Array.isArray(body.objects) || body.objects.length > 10_000 ||\n body.objects.some((object) => {\n try {\n return validateObjectPayload(object).deferred !== true\n } catch {\n return true\n }\n })) {\n sendJson(res, 400, { ok: false, code: 'object-request-invalid', error: 'deferred session object descriptors are required' })\n return\n }\n const objects = (body.objects as unknown[]).map((object) => validateObjectPayload(object))\n res.writeHead(200, { 'content-type': 'application/x-dshb-object-stream', 'cache-control': 'no-store' })\n try {\n for await (const chunk of sessionSync.streamObjects(objects)) {\n if (!res.write(chunk)) {\n await new Promise<void>((resolve, reject) => {\n res.once('drain', resolve)\n res.once('error', reject)\n })\n }\n }\n res.end()\n } catch {\n res.destroy()\n }\n}\n\nexport function registerSyncInternalRoutes(\n ctx: Context,\n state: SyncStateStore,\n nodeSync?: SyncNodeService,\n sessionSync?: SyncSessionService,\n): void {\n const capabilities = [\n 'peer-handshake',\n ...(nodeSync ? ['node-sync'] : []),\n ...(sessionSync ? ['session-sync', SYNC_SESSION_MANIFEST_CAPABILITY, SYNC_SESSION_EVENT_TAIL_CAPABILITY, SYNC_SESSION_BUNDLE_OBJECTS_CAPABILITY, SYNC_SESSION_OBJECT_STREAM_CAPABILITY] : []),\n ]\n ctx.effect(() =>\n ctx.webServer.register({\n kind: 'exact',\n path: '/api/dshb/sync/internal/handshake',\n handler: (req: IncomingMessage, res: ServerResponse) => {\n if (!isLoopbackRequest(req.socket.remoteAddress, req.headers.host)) {\n sendJson(res, 403, { ok: false, error: 'loopback access required' })\n return\n }\n if (req.method !== 'GET') {\n sendJson(res, 405, { ok: false, error: 'method not allowed' })\n return\n }\n try {\n sendJson(res, 200, {\n ok: true,\n protocolVersion: SYNC_PROTOCOL_VERSION,\n instanceId: state.instanceId(),\n capabilities,\n })\n } catch {\n sendJson(res, 503, { ok: false, error: 'sync identity unavailable' })\n }\n },\n }),\n )\n\n if (nodeSync) ctx.effect(() =>\n ctx.webServer.register({\n kind: 'prefix',\n path: '/api/dshb/sync/internal/nodes',\n handler: async (req, res) => {\n if (!isLoopbackRequest(req.socket.remoteAddress, req.headers.host)) {\n sendJson(res, 403, { ok: false, error: 'loopback access required' })\n return\n }\n const segments = pathSegments(req)\n const action = segments[5]\n if (segments.length !== 6) {\n sendJson(res, 404, { ok: false, error: 'not found' })\n return\n }\n if (req.method === 'GET' && action === 'manifest') {\n try {\n sendJson(res, 200, { ok: true, manifest: await nodeSync.exportManifest(), capabilities })\n } catch {\n sendJson(res, 500, { ok: false, error: 'node manifest export failed' })\n }\n return\n }\n if (req.method === 'POST' && action === 'apply') {\n const body = await readJsonBody(req)\n if (!body || !Array.isArray(body.records) || typeof body.sourceName !== 'string') {\n sendJson(res, 400, { ok: false, error: 'records and sourceName are required' })\n return\n }\n try {\n sendJson(res, 200, { ok: true, result: await nodeSync.applyManifest(body.records, body.sourceName) })\n } catch (error) {\n const status = error instanceof SyncNodeManifestError ? 400 : 500\n sendJson(res, status, { ok: false, error: status === 400 ? 'node manifest rejected' : 'node manifest apply failed' })\n }\n return\n }\n sendJson(res, 404, { ok: false, error: 'not found' })\n },\n }),\n )\n\n if (!sessionSync) return\n ctx.effect(() =>\n ctx.webServer.register({\n kind: 'prefix',\n path: '/api/dshb/sync/internal/sessions',\n handler: async (req, res) => {\n if (!isLoopbackRequest(req.socket.remoteAddress, req.headers.host)) {\n sendJson(res, 403, { ok: false, error: 'loopback access required' })\n return\n }\n const segments = pathSegments(req)\n const action = segments[5]\n if (segments.length !== 6) {\n sendJson(res, 404, { ok: false, error: 'not found' })\n return\n }\n if (req.method === 'GET' && action === 'bundle') {\n try {\n sendJson(res, 200, { ok: true, bundle: await sessionSync.exportBundle() })\n } catch (error) {\n const code = error instanceof SyncSessionError ? error.code : 'session-export-failed'\n const status = code === 'bundle-too-large' ? 413 : 500\n sendJson(res, status, { ok: false, code, error: error instanceof SyncSessionError ? error.message : 'session bundle export failed' })\n }\n return\n }\n if (req.method === 'GET' && action === 'manifest') {\n try {\n sendJson(res, 200, { ok: true, manifest: await sessionSync.exportManifest() })\n } catch (error) {\n const code = error instanceof SyncSessionError ? error.code : 'session-export-failed'\n const status = code === 'bundle-too-large' ? 413 : 500\n sendJson(res, status, { ok: false, code, error: error instanceof SyncSessionError ? error.message : 'session manifest export failed' })\n }\n return\n }\n if (req.method === 'POST' && action === 'entries') {\n const body = await readJsonBody(req)\n const prefixesValid = body?.prefixes === undefined || (Array.isArray(body.prefixes) &&\n body.prefixes.every((prefix) => prefix !== null && typeof prefix === 'object' && !Array.isArray(prefix)))\n if (!body || !Array.isArray(body.keys) || !body.keys.every((key) => typeof key === 'string') ||\n typeof body.manifestDigest !== 'string' || !prefixesValid) {\n sendJson(res, 400, { ok: false, code: 'session-request-invalid', error: 'keys, manifestDigest, and valid prefixes are required' })\n return\n }\n try {\n sendJson(res, 200, {\n ok: true,\n bundle: await sessionSync.exportEntries(\n body.keys as string[],\n body.manifestDigest,\n (body.prefixes ?? []) as SyncSessionEventPrefix[],\n { deferredObjects: body.deferredObjects === true },\n ),\n })\n } catch (error) {\n const code = error instanceof SyncSessionError ? error.code : 'session-export-failed'\n const status = code === 'bundle-too-large' ? 413 : 500\n sendJson(res, status, { ok: false, code, error: error instanceof SyncSessionError ? error.message : 'session entries export failed' })\n }\n return\n }\n if (req.method === 'POST' && action === 'objects') {\n await handleSessionObjects(sessionSync, req, res)\n return\n }\n if (req.method === 'POST' && action === 'apply') {\n const body = await readJsonBody(req)\n if (!body || body.bundle === null || typeof body.bundle !== 'object' ||\n typeof body.sourceName !== 'string' || typeof body.peerId !== 'string' || !/^p-[a-f0-9]{24}$/.test(body.peerId)) {\n sendJson(res, 400, { ok: false, code: 'session-request-invalid', error: 'bundle, sourceName, and peerId are required' })\n return\n }\n try {\n sendJson(res, 200, {\n ok: true,\n result: await sessionSync.applyBundle(body.bundle, body.sourceName, body.peerId),\n })\n } catch (error) {\n const code = error instanceof SyncSessionError ? error.code : 'session-apply-failed'\n const status = code === 'bundle-too-large' ? 413\n : ['manifest-invalid', 'manifest-digest', 'object-invalid', 'session-format'].includes(code) ? 400\n : code === 'workspace-unmapped' || code === 'session-delta-prefix' ? 409 : 500\n sendJson(res, status, { ok: false, code, error: error instanceof SyncSessionError ? error.message : 'session bundle apply failed' })\n }\n return\n }\n sendJson(res, 404, { ok: false, error: 'not found' })\n },\n }),\n )\n}\n"],"mappings":"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"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { n as SyncStateStore } from "../../stores-CKyEt0yk.js";
|
|
2
2
|
import { s as SyncNodeService } from "../../node-sync-3Z9QLz2n.js";
|
|
3
|
-
import {
|
|
3
|
+
import { h as SyncSessionService } from "../../session-sync-BuOJvFxK.js";
|
|
4
4
|
//#region src/core/sync/offline-archive.d.ts
|
|
5
5
|
declare const MAX_OFFLINE_ARCHIVE_BYTES: number;
|
|
6
6
|
declare class SyncOfflineArchiveError extends Error {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { f as validateSyncSessionBundle, n as SyncSessionError } from "../../session-sync-CIqjuWkh.js";
|
|
2
2
|
import { SyncOfflineArchiveJournalStore } from "./offline-journal.js";
|
|
3
3
|
import { homedir } from "node:os";
|
|
4
4
|
import { appendFileSync, chmodSync, closeSync, createReadStream, createWriteStream, mkdirSync, mkdtempSync, openSync, readSync, readdirSync, rmSync, statSync, statfsSync, writeFileSync } from "node:fs";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { n as SyncSessionError } from "../../session-sync-CIqjuWkh.js";
|
|
2
2
|
import { SyncOfflineArchiveError } from "./offline-archive.js";
|
|
3
3
|
import { createReadStream } from "node:fs";
|
|
4
4
|
import { randomBytes } from "node:crypto";
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as
|
|
2
|
-
export { MAX_SYNC_CONTROL_FRAME_BYTES, MAX_SYNC_DATA_CHUNK_BYTES, MAX_SYNC_TRANSFER_BYTES, SYNC_PROTOCOL_VERSION, SYNC_SESSION_BUNDLE_OBJECTS_CAPABILITY, SYNC_SESSION_EVENT_TAIL_CAPABILITY, SYNC_SESSION_MANIFEST_CAPABILITY, SyncFrame, SyncFrameDecoder, SyncProtocolError, encodeSyncControlFrame, encodeSyncDataFrame };
|
|
1
|
+
import { a as MAX_SYNC_TRANSFER_BYTES, c as SYNC_SESSION_EVENT_TAIL_CAPABILITY, d as SyncFrame, f as SyncFrameDecoder, h as encodeSyncDataFrame, i as MAX_SYNC_DATA_CHUNK_BYTES, l as SYNC_SESSION_MANIFEST_CAPABILITY, m as encodeSyncControlFrame, n as MAX_SESSION_OBJECT_STREAM_BYTES, o as SYNC_PROTOCOL_VERSION, p as SyncProtocolError, r as MAX_SYNC_CONTROL_FRAME_BYTES, s as SYNC_SESSION_BUNDLE_OBJECTS_CAPABILITY, t as MAX_SESSION_OBJECT_INLINE_BYTES, u as SYNC_SESSION_OBJECT_STREAM_CAPABILITY } from "../../protocol-DJ0zyVlB.js";
|
|
2
|
+
export { MAX_SESSION_OBJECT_INLINE_BYTES, MAX_SESSION_OBJECT_STREAM_BYTES, MAX_SYNC_CONTROL_FRAME_BYTES, MAX_SYNC_DATA_CHUNK_BYTES, MAX_SYNC_TRANSFER_BYTES, SYNC_PROTOCOL_VERSION, SYNC_SESSION_BUNDLE_OBJECTS_CAPABILITY, SYNC_SESSION_EVENT_TAIL_CAPABILITY, SYNC_SESSION_MANIFEST_CAPABILITY, SYNC_SESSION_OBJECT_STREAM_CAPABILITY, SyncFrame, SyncFrameDecoder, SyncProtocolError, encodeSyncControlFrame, encodeSyncDataFrame };
|
|
@@ -4,9 +4,12 @@ const SYNC_PROTOCOL_VERSION = 1;
|
|
|
4
4
|
const SYNC_SESSION_MANIFEST_CAPABILITY = "session-manifest-v1";
|
|
5
5
|
const SYNC_SESSION_EVENT_TAIL_CAPABILITY = "session-event-tail-delta-v1";
|
|
6
6
|
const SYNC_SESSION_BUNDLE_OBJECTS_CAPABILITY = "session-bundle-objects-v1";
|
|
7
|
+
const SYNC_SESSION_OBJECT_STREAM_CAPABILITY = "session-object-stream-v1";
|
|
7
8
|
const MAX_SYNC_CONTROL_FRAME_BYTES = 65536;
|
|
8
9
|
const MAX_SYNC_DATA_CHUNK_BYTES = 1048576;
|
|
9
|
-
const MAX_SYNC_TRANSFER_BYTES =
|
|
10
|
+
const MAX_SYNC_TRANSFER_BYTES = 134217728;
|
|
11
|
+
const MAX_SESSION_OBJECT_INLINE_BYTES = 262144;
|
|
12
|
+
const MAX_SESSION_OBJECT_STREAM_BYTES = 68719476736;
|
|
10
13
|
const FRAME_HEADER_BYTES = 5;
|
|
11
14
|
const CONTROL_FRAME = 0;
|
|
12
15
|
const DATA_FRAME = 1;
|
|
@@ -122,6 +125,6 @@ var SyncFrameDecoder = class {
|
|
|
122
125
|
}
|
|
123
126
|
};
|
|
124
127
|
//#endregion
|
|
125
|
-
export { MAX_SYNC_CONTROL_FRAME_BYTES, MAX_SYNC_DATA_CHUNK_BYTES, MAX_SYNC_TRANSFER_BYTES, SYNC_PROTOCOL_VERSION, SYNC_SESSION_BUNDLE_OBJECTS_CAPABILITY, SYNC_SESSION_EVENT_TAIL_CAPABILITY, SYNC_SESSION_MANIFEST_CAPABILITY, SyncFrameDecoder, SyncProtocolError, encodeSyncControlFrame, encodeSyncDataFrame };
|
|
128
|
+
export { MAX_SESSION_OBJECT_INLINE_BYTES, MAX_SESSION_OBJECT_STREAM_BYTES, MAX_SYNC_CONTROL_FRAME_BYTES, MAX_SYNC_DATA_CHUNK_BYTES, MAX_SYNC_TRANSFER_BYTES, SYNC_PROTOCOL_VERSION, SYNC_SESSION_BUNDLE_OBJECTS_CAPABILITY, SYNC_SESSION_EVENT_TAIL_CAPABILITY, SYNC_SESSION_MANIFEST_CAPABILITY, SYNC_SESSION_OBJECT_STREAM_CAPABILITY, SyncFrameDecoder, SyncProtocolError, encodeSyncControlFrame, encodeSyncDataFrame };
|
|
126
129
|
|
|
127
130
|
//# sourceMappingURL=protocol.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"protocol.js","names":[],"sources":["../../../src/core/sync/protocol.ts"],"sourcesContent":["import { createHash, timingSafeEqual } from 'node:crypto'\n\nexport const SYNC_PROTOCOL_VERSION = 1\nexport const SYNC_SESSION_MANIFEST_CAPABILITY = 'session-manifest-v1'\nexport const SYNC_SESSION_EVENT_TAIL_CAPABILITY = 'session-event-tail-delta-v1'\nexport const SYNC_SESSION_BUNDLE_OBJECTS_CAPABILITY = 'session-bundle-objects-v1'\nexport const MAX_SYNC_CONTROL_FRAME_BYTES = 64 * 1024\nexport const MAX_SYNC_DATA_CHUNK_BYTES = 1024 * 1024\nexport const MAX_SYNC_TRANSFER_BYTES =
|
|
1
|
+
{"version":3,"file":"protocol.js","names":[],"sources":["../../../src/core/sync/protocol.ts"],"sourcesContent":["import { createHash, timingSafeEqual } from 'node:crypto'\n\nexport const SYNC_PROTOCOL_VERSION = 1\nexport const SYNC_SESSION_MANIFEST_CAPABILITY = 'session-manifest-v1'\nexport const SYNC_SESSION_EVENT_TAIL_CAPABILITY = 'session-event-tail-delta-v1'\nexport const SYNC_SESSION_BUNDLE_OBJECTS_CAPABILITY = 'session-bundle-objects-v1'\n// 对象带外流:附件/spill 负载不再内联进 bundle JSON,而是通过专用 RPC 以\n// 磁盘到磁盘的帧流传输(内存占用 O(单帧)),单对象与会话引用总量不再受\n// JSON 传输上限约束。两端都声明该能力时才启用,旧版本对端回退内联路径。\nexport const SYNC_SESSION_OBJECT_STREAM_CAPABILITY = 'session-object-stream-v1'\nexport const MAX_SYNC_CONTROL_FRAME_BYTES = 64 * 1024\nexport const MAX_SYNC_DATA_CHUNK_BYTES = 1024 * 1024\n// 单次传输(bundle 请求/响应、原始附件/溢出流)的硬上限。必须远小于宿主进程\n// JS 堆(桌面端约 1.4GB):JSON.parse 的对象图膨胀约为 JSON 文本的 5-10 倍,\n// 100GB 级上限等于无内存保护。协调器按更小的目标分批传输(见 coordinator.ts)。\n// 注意:该上限只约束 JSON bundle;对象带外流是磁盘流式传输,走独立上限。\nexport const MAX_SYNC_TRANSFER_BYTES = 128 * 1024 * 1024\n// 对象内联进 bundle JSON 的字节阈值:小对象内联(减少往返),大对象走带外流。\nexport const MAX_SESSION_OBJECT_INLINE_BYTES = 256 * 1024\n// 单个带外对象流的字节上限:磁盘到磁盘传输,内存不随体积增长,上限只为\n// 防失控对端无限发送。64GB 覆盖一切真实会话负载。\nexport const MAX_SESSION_OBJECT_STREAM_BYTES = 64 * 1024 * 1024 * 1024\n\nconst FRAME_HEADER_BYTES = 5\nconst CONTROL_FRAME = 0\nconst DATA_FRAME = 1\nconst DATA_METADATA_BYTES = 36\n\nexport type SyncFrame =\n | { kind: 'control'; value: Record<string, unknown> }\n | { kind: 'data'; sequence: number; digest: string; data: Buffer }\n\nexport class SyncProtocolError extends Error {\n constructor(message: string) {\n super(message)\n this.name = 'SyncProtocolError'\n }\n}\n\nfunction makeHeader(type: number, payloadLength: number): Buffer {\n const header = Buffer.allocUnsafe(FRAME_HEADER_BYTES)\n header[0] = type\n header.writeUInt32BE(payloadLength, 1)\n return header\n}\n\nexport function encodeSyncControlFrame(value: Record<string, unknown>): Buffer {\n const payload = Buffer.from(`${JSON.stringify(value)}\\n`, 'utf8')\n if (payload.length > MAX_SYNC_CONTROL_FRAME_BYTES) throw new SyncProtocolError('control frame exceeds size limit')\n return Buffer.concat([makeHeader(CONTROL_FRAME, payload.length), payload])\n}\n\nexport function encodeSyncDataFrame(sequence: number, data: Buffer): Buffer {\n if (!Number.isSafeInteger(sequence) || sequence < 0 || sequence > 0xffffffff) {\n throw new SyncProtocolError('data sequence is invalid')\n }\n if (data.length > MAX_SYNC_DATA_CHUNK_BYTES) throw new SyncProtocolError('data chunk exceeds size limit')\n const metadata = Buffer.allocUnsafe(DATA_METADATA_BYTES)\n metadata.writeUInt32BE(sequence, 0)\n createHash('sha256').update(data).digest().copy(metadata, 4)\n return Buffer.concat([makeHeader(DATA_FRAME, metadata.length + data.length), metadata, data])\n}\n\nexport class SyncFrameDecoder {\n private readonly header = Buffer.allocUnsafe(FRAME_HEADER_BYTES)\n private headerOffset = 0\n private frameType = -1\n private payload: Buffer | undefined\n private payloadOffset = 0\n private nextDataSequence = 0\n private ended = false\n\n push(chunk: Buffer): SyncFrame[] {\n if (this.ended) throw new SyncProtocolError('frame decoder is already finished')\n const frames: SyncFrame[] = []\n let inputOffset = 0\n\n while (inputOffset < chunk.length) {\n if (!this.payload) {\n const copied = Math.min(FRAME_HEADER_BYTES - this.headerOffset, chunk.length - inputOffset)\n chunk.copy(this.header, this.headerOffset, inputOffset, inputOffset + copied)\n this.headerOffset += copied\n inputOffset += copied\n if (this.headerOffset < FRAME_HEADER_BYTES) continue\n\n this.frameType = this.header[0]\n const payloadLength = this.header.readUInt32BE(1)\n if (this.frameType === CONTROL_FRAME) {\n if (payloadLength < 2 || payloadLength > MAX_SYNC_CONTROL_FRAME_BYTES) {\n throw new SyncProtocolError('control frame length is invalid')\n }\n } else if (this.frameType === DATA_FRAME) {\n if (payloadLength < DATA_METADATA_BYTES || payloadLength > DATA_METADATA_BYTES + MAX_SYNC_DATA_CHUNK_BYTES) {\n throw new SyncProtocolError('data frame length is invalid')\n }\n } else {\n throw new SyncProtocolError('unknown frame type')\n }\n this.payload = Buffer.allocUnsafe(payloadLength)\n this.payloadOffset = 0\n }\n\n const payload = this.payload\n const copied = Math.min(payload.length - this.payloadOffset, chunk.length - inputOffset)\n chunk.copy(payload, this.payloadOffset, inputOffset, inputOffset + copied)\n this.payloadOffset += copied\n inputOffset += copied\n\n if (this.payloadOffset === payload.length) {\n frames.push(this.decodeFrame(this.frameType, payload))\n this.resetFrame()\n }\n }\n return frames\n }\n\n finish(): void {\n if (this.headerOffset !== 0 || this.payload !== undefined) throw new SyncProtocolError('truncated frame stream')\n this.ended = true\n }\n\n private decodeFrame(type: number, payload: Buffer): SyncFrame {\n if (type === CONTROL_FRAME) {\n if (payload[payload.length - 1] !== 0x0a) throw new SyncProtocolError('control frame must end with a newline')\n let value: unknown\n try {\n value = JSON.parse(payload.subarray(0, -1).toString('utf8'))\n } catch {\n throw new SyncProtocolError('control frame contains invalid JSON')\n }\n if (value === null || typeof value !== 'object' || Array.isArray(value)) {\n throw new SyncProtocolError('control frame must contain an object')\n }\n return { kind: 'control', value: value as Record<string, unknown> }\n }\n\n const sequence = payload.readUInt32BE(0)\n if (sequence !== this.nextDataSequence) throw new SyncProtocolError('data sequence is out of order')\n const expectedDigest = payload.subarray(4, DATA_METADATA_BYTES)\n const data = payload.subarray(DATA_METADATA_BYTES)\n const actualDigest = createHash('sha256').update(data).digest()\n if (!timingSafeEqual(expectedDigest, actualDigest)) throw new SyncProtocolError('data chunk digest mismatch')\n this.nextDataSequence += 1\n return { kind: 'data', sequence, digest: actualDigest.toString('hex'), data: Buffer.from(data) }\n }\n\n private resetFrame(): void {\n this.headerOffset = 0\n this.frameType = -1\n this.payload = undefined\n this.payloadOffset = 0\n }\n}\n"],"mappings":"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"}
|
|
@@ -5,6 +5,7 @@ const { createHash, timingSafeEqual } = require('node:crypto')
|
|
|
5
5
|
const MAX_CONTROL = 65536
|
|
6
6
|
const MAX_CHUNK = 1048576
|
|
7
7
|
const MAX_TRANSFER = 107374182400
|
|
8
|
+
const OBJECT_STREAM_LIMIT = 68719476736
|
|
8
9
|
const PATHS = {
|
|
9
10
|
handshake: { path: '/api/dshb/sync/internal/handshake', method: 'GET' },
|
|
10
11
|
'node-manifest': { path: '/api/dshb/sync/internal/nodes/manifest', method: 'GET' },
|
|
@@ -13,9 +14,11 @@ const PATHS = {
|
|
|
13
14
|
'session-manifest': { path: '/api/dshb/sync/internal/sessions/manifest', method: 'GET' },
|
|
14
15
|
'session-entries': { path: '/api/dshb/sync/internal/sessions/entries', method: 'POST' },
|
|
15
16
|
'session-apply': { path: '/api/dshb/sync/internal/sessions/apply', method: 'POST' },
|
|
17
|
+
'session-objects': { path: '/api/dshb/sync/internal/sessions/objects', method: 'POST' },
|
|
18
|
+
'session-objects-push': { path: '/api/dshb/sync/internal/sessions/objects', method: 'POST' },
|
|
16
19
|
}
|
|
17
|
-
const
|
|
18
|
-
|
|
20
|
+
const BUFFERED_BODY_OPERATIONS = ['node-apply', 'session-entries', 'session-apply', 'session-objects']
|
|
21
|
+
const STREAMING_RESPONSE_OPERATIONS = ['node-manifest', 'session-bundle', 'session-manifest', 'session-entries', 'session-objects']
|
|
19
22
|
|
|
20
23
|
function encodeControl(value) {
|
|
21
24
|
const body = Buffer.from(JSON.stringify(value) + '\n', 'utf8')
|
|
@@ -38,69 +41,116 @@ function encodeData(sequence, data) {
|
|
|
38
41
|
return Buffer.concat([header, payload])
|
|
39
42
|
}
|
|
40
43
|
|
|
41
|
-
function
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
const
|
|
63
|
-
const
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
frames.push({ kind: 'data', data })
|
|
44
|
+
function decodeOneFrame(kind, payload, state) {
|
|
45
|
+
if (kind === 0) {
|
|
46
|
+
if (payload.length < 2 || payload[payload.length - 1] !== 10) throw new Error('invalid control frame')
|
|
47
|
+
return { kind: 'control', value: JSON.parse(payload.subarray(0, -1).toString('utf8')) }
|
|
48
|
+
}
|
|
49
|
+
if (payload.length < 36) throw new Error('invalid data frame')
|
|
50
|
+
const currentSequence = payload.readUInt32BE(0)
|
|
51
|
+
const expected = payload.subarray(4, 36)
|
|
52
|
+
const data = payload.subarray(36)
|
|
53
|
+
const actual = createHash('sha256').update(data).digest()
|
|
54
|
+
if (currentSequence !== state.sequence || !timingSafeEqual(expected, actual)) throw new Error('invalid data chunk')
|
|
55
|
+
state.sequence += 1
|
|
56
|
+
state.totalData += data.length
|
|
57
|
+
if (state.totalData > MAX_TRANSFER && state.totalData > OBJECT_STREAM_LIMIT) throw new Error('transfer exceeds size limit')
|
|
58
|
+
return { kind: 'data', data }
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
async function readFrame(iterator, state) {
|
|
62
|
+
for (;;) {
|
|
63
|
+
if (state.buffer.length >= 5) {
|
|
64
|
+
const kind = state.buffer[0]
|
|
65
|
+
const length = state.buffer.readUInt32BE(1)
|
|
66
|
+
const maxLength = kind === 0 ? MAX_CONTROL : 36 + MAX_CHUNK
|
|
67
|
+
if ((kind !== 0 && kind !== 1) || length > maxLength) throw new Error('invalid frame length')
|
|
68
|
+
if (state.buffer.length >= 5 + length) {
|
|
69
|
+
const payload = state.buffer.subarray(5, 5 + length)
|
|
70
|
+
state.buffer = state.buffer.subarray(5 + length)
|
|
71
|
+
return decodeOneFrame(kind, payload, state)
|
|
72
|
+
}
|
|
71
73
|
}
|
|
74
|
+
const next = await iterator.next()
|
|
75
|
+
if (next.done) throw new Error('helper input ended inside a frame')
|
|
76
|
+
state.buffer = Buffer.concat([state.buffer, Buffer.from(next.value)])
|
|
72
77
|
}
|
|
73
|
-
return frames
|
|
74
78
|
}
|
|
75
79
|
|
|
76
|
-
function
|
|
77
|
-
const frames = decodeFrames(buffer)
|
|
78
|
-
const first = frames[0]
|
|
79
|
-
if (!first || first.kind !== 'control') throw new Error('request control frame required')
|
|
80
|
-
const request = first.value
|
|
80
|
+
function validateRequest(request) {
|
|
81
81
|
if (!request || request.kind !== 'request' || request.protocolVersion !== 1 || !Object.prototype.hasOwnProperty.call(PATHS, request.operation)) {
|
|
82
82
|
throw new Error('unsupported helper request')
|
|
83
83
|
}
|
|
84
|
-
if (Object.keys(request).some((key)
|
|
84
|
+
if (Object.keys(request).some(function (key) { return ['kind', 'protocolVersion', 'operation', 'port'].indexOf(key) < 0 })) {
|
|
85
85
|
throw new Error('unsupported helper request field')
|
|
86
86
|
}
|
|
87
87
|
if (!Number.isInteger(request.port) || request.port < 1 || request.port > 65535) throw new Error('invalid loopback port')
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
}
|
|
96
|
-
if (frames.length !== 1) throw new Error('unexpected helper request frames')
|
|
97
|
-
return { request, operation }
|
|
88
|
+
return PATHS[request.operation]
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function writeStdout(data) {
|
|
92
|
+
return new Promise(function (resolve, reject) {
|
|
93
|
+
if (process.stdout.write(data)) resolve()
|
|
94
|
+
else process.stdout.once('drain', resolve)
|
|
95
|
+
})
|
|
98
96
|
}
|
|
99
97
|
|
|
100
98
|
async function run() {
|
|
101
|
-
const
|
|
102
|
-
const {
|
|
99
|
+
const iterator = process.stdin[Symbol.asyncIterator]()
|
|
100
|
+
const state = { buffer: Buffer.alloc(0), sequence: 0, totalData: 0 }
|
|
101
|
+
const first = await readFrame(iterator, state)
|
|
102
|
+
if (first.kind !== 'control' || first.value.kind !== 'request') throw new Error('request control frame required')
|
|
103
|
+
const request = first.value
|
|
104
|
+
const operation = validateRequest(request)
|
|
103
105
|
const url = 'http://127.0.0.1:' + request.port + operation.path
|
|
106
|
+
|
|
107
|
+
if (request.operation === 'session-objects-push') {
|
|
108
|
+
// 对象推送:请求体(对象帧流)直接转发为 HTTP 流式请求体,内存 O(帧)。
|
|
109
|
+
const { Readable } = require('node:stream')
|
|
110
|
+
const bodyStream = new Readable({ read: function () {} })
|
|
111
|
+
const pump = (async function () {
|
|
112
|
+
try {
|
|
113
|
+
for (;;) {
|
|
114
|
+
const frame = await readFrame(iterator, state)
|
|
115
|
+
if (frame.kind === 'control' && frame.value.kind === 'request-end') break
|
|
116
|
+
if (frame.kind !== 'data') throw new Error('invalid object push frame')
|
|
117
|
+
if (!bodyStream.push(Buffer.from(frame.data))) {
|
|
118
|
+
await new Promise(function (resolve) { bodyStream.once('drain', resolve) })
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
bodyStream.push(null)
|
|
122
|
+
} catch (error) {
|
|
123
|
+
bodyStream.destroy(error)
|
|
124
|
+
}
|
|
125
|
+
})()
|
|
126
|
+
const response = await fetch(url, {
|
|
127
|
+
method: 'POST',
|
|
128
|
+
redirect: 'error',
|
|
129
|
+
headers: { 'content-type': 'application/x-dshb-object-stream' },
|
|
130
|
+
body: bodyStream,
|
|
131
|
+
duplex: 'half',
|
|
132
|
+
})
|
|
133
|
+
const text = await response.text()
|
|
134
|
+
if (Buffer.byteLength(text, 'utf8') > MAX_CONTROL) throw new Error('response control frame too large')
|
|
135
|
+
await writeStdout(encodeControl({ kind: 'response', status: response.status, body: JSON.parse(text) }))
|
|
136
|
+
if (!response.ok) process.exitCode = 1
|
|
137
|
+
await pump
|
|
138
|
+
return
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
let body
|
|
142
|
+
if (BUFFERED_BODY_OPERATIONS.indexOf(request.operation) >= 0) {
|
|
143
|
+
// 帧序:control(request) → data… → control(request-end)。
|
|
144
|
+
const chunks = []
|
|
145
|
+
for (;;) {
|
|
146
|
+
const frame = await readFrame(iterator, state)
|
|
147
|
+
if (frame.kind === 'control' && frame.value.kind === 'request-end') break
|
|
148
|
+
if (frame.kind !== 'data') throw new Error('invalid apply request body')
|
|
149
|
+
chunks.push(frame.data)
|
|
150
|
+
}
|
|
151
|
+
body = Buffer.concat(chunks)
|
|
152
|
+
}
|
|
153
|
+
|
|
104
154
|
const response = await fetch(url, {
|
|
105
155
|
method: operation.method,
|
|
106
156
|
redirect: 'error',
|
|
@@ -108,23 +158,15 @@ async function run() {
|
|
|
108
158
|
...(body ? { body } : {}),
|
|
109
159
|
})
|
|
110
160
|
|
|
111
|
-
|
|
112
|
-
return new Promise(function(resolve, reject) {
|
|
113
|
-
if (process.stdout.write(data)) resolve()
|
|
114
|
-
else process.stdout.once('drain', resolve)
|
|
115
|
-
})
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
if (request.operation === 'node-manifest' || request.operation === 'session-bundle' ||
|
|
119
|
-
request.operation === 'session-manifest' || request.operation === 'session-entries') {
|
|
161
|
+
if (STREAMING_RESPONSE_OPERATIONS.indexOf(request.operation) >= 0) {
|
|
120
162
|
await writeStdout(encodeControl({ kind: 'response', status: response.status, bodyMode: 'data' }))
|
|
121
|
-
if (!response.body) throw new Error('
|
|
163
|
+
if (!response.body) throw new Error('streaming response body missing')
|
|
122
164
|
var sequence = 0
|
|
123
165
|
var total = 0
|
|
124
166
|
for await (var chunk of response.body) {
|
|
125
167
|
var data = Buffer.from(chunk)
|
|
126
168
|
total += data.length
|
|
127
|
-
if (total > MAX_TRANSFER) throw new Error('
|
|
169
|
+
if (total > MAX_TRANSFER && total > OBJECT_STREAM_LIMIT) throw new Error('response exceeds size limit')
|
|
128
170
|
for (var offset = 0; offset < data.length; offset += MAX_CHUNK) {
|
|
129
171
|
await writeStdout(encodeData(sequence++, data.subarray(offset, Math.min(offset + MAX_CHUNK, data.length))))
|
|
130
172
|
}
|
|
@@ -143,20 +185,9 @@ async function run() {
|
|
|
143
185
|
if (!response.ok && !recoverableDeltaMismatch) process.exitCode = 1
|
|
144
186
|
}
|
|
145
187
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
process.exitCode = 1
|
|
150
|
-
process.stdin.destroy()
|
|
151
|
-
return
|
|
152
|
-
}
|
|
153
|
-
input.push(Buffer.from(chunk))
|
|
154
|
-
})
|
|
155
|
-
process.stdin.on('end', () => {
|
|
156
|
-
run().catch(() => {
|
|
157
|
-
try { process.stdout.write(encodeControl({ kind: 'error', code: 'helper-failed' })) } catch {}
|
|
158
|
-
process.exitCode = 1
|
|
159
|
-
})
|
|
188
|
+
run().catch(function () {
|
|
189
|
+
try { process.stdout.write(encodeControl({ kind: 'error', code: 'helper-failed' })) } catch (err) {}
|
|
190
|
+
process.exitCode = 1
|
|
160
191
|
})
|
|
161
192
|
`;
|
|
162
193
|
const REMOTE_SYNC_HELPER_COMMAND = `node -e 'eval(Buffer.from("${Buffer.from(REMOTE_HELPER_SOURCE, "utf8").toString("base64")}","base64").toString("utf8"))'`;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"remote-helper.js","names":[],"sources":["../../../src/core/sync/remote-helper.ts"],"sourcesContent":["import { Buffer } from 'node:buffer'\n\nconst REMOTE_HELPER_SOURCE = String.raw`'use strict'\nconst { createHash, timingSafeEqual } = require('node:crypto')\nconst MAX_CONTROL = 65536\nconst MAX_CHUNK = 1048576\nconst MAX_TRANSFER = 107374182400\nconst PATHS = {\n handshake: { path: '/api/dshb/sync/internal/handshake', method: 'GET' },\n 'node-manifest': { path: '/api/dshb/sync/internal/nodes/manifest', method: 'GET' },\n 'node-apply': { path: '/api/dshb/sync/internal/nodes/apply', method: 'POST' },\n 'session-bundle': { path: '/api/dshb/sync/internal/sessions/bundle', method: 'GET' },\n 'session-manifest': { path: '/api/dshb/sync/internal/sessions/manifest', method: 'GET' },\n 'session-entries': { path: '/api/dshb/sync/internal/sessions/entries', method: 'POST' },\n 'session-apply': { path: '/api/dshb/sync/internal/sessions/apply', method: 'POST' },\n}\nconst
|
|
1
|
+
{"version":3,"file":"remote-helper.js","names":[],"sources":["../../../src/core/sync/remote-helper.ts"],"sourcesContent":["import { Buffer } from 'node:buffer'\n\nconst REMOTE_HELPER_SOURCE = String.raw`'use strict'\nconst { createHash, timingSafeEqual } = require('node:crypto')\nconst MAX_CONTROL = 65536\nconst MAX_CHUNK = 1048576\nconst MAX_TRANSFER = 107374182400\nconst OBJECT_STREAM_LIMIT = 68719476736\nconst PATHS = {\n handshake: { path: '/api/dshb/sync/internal/handshake', method: 'GET' },\n 'node-manifest': { path: '/api/dshb/sync/internal/nodes/manifest', method: 'GET' },\n 'node-apply': { path: '/api/dshb/sync/internal/nodes/apply', method: 'POST' },\n 'session-bundle': { path: '/api/dshb/sync/internal/sessions/bundle', method: 'GET' },\n 'session-manifest': { path: '/api/dshb/sync/internal/sessions/manifest', method: 'GET' },\n 'session-entries': { path: '/api/dshb/sync/internal/sessions/entries', method: 'POST' },\n 'session-apply': { path: '/api/dshb/sync/internal/sessions/apply', method: 'POST' },\n 'session-objects': { path: '/api/dshb/sync/internal/sessions/objects', method: 'POST' },\n 'session-objects-push': { path: '/api/dshb/sync/internal/sessions/objects', method: 'POST' },\n}\nconst BUFFERED_BODY_OPERATIONS = ['node-apply', 'session-entries', 'session-apply', 'session-objects']\nconst STREAMING_RESPONSE_OPERATIONS = ['node-manifest', 'session-bundle', 'session-manifest', 'session-entries', 'session-objects']\n\nfunction encodeControl(value) {\n const body = Buffer.from(JSON.stringify(value) + '\\n', 'utf8')\n if (body.length > MAX_CONTROL) throw new Error('control frame too large')\n const header = Buffer.alloc(5)\n header[0] = 0\n header.writeUInt32BE(body.length, 1)\n return Buffer.concat([header, body])\n}\n\nfunction encodeData(sequence, data) {\n const digest = createHash('sha256').update(data).digest()\n const metadata = Buffer.alloc(36)\n metadata.writeUInt32BE(sequence, 0)\n digest.copy(metadata, 4)\n const payload = Buffer.concat([metadata, data])\n const header = Buffer.alloc(5)\n header[0] = 1\n header.writeUInt32BE(payload.length, 1)\n return Buffer.concat([header, payload])\n}\n\nfunction decodeOneFrame(kind, payload, state) {\n if (kind === 0) {\n if (payload.length < 2 || payload[payload.length - 1] !== 10) throw new Error('invalid control frame')\n return { kind: 'control', value: JSON.parse(payload.subarray(0, -1).toString('utf8')) }\n }\n if (payload.length < 36) throw new Error('invalid data frame')\n const currentSequence = payload.readUInt32BE(0)\n const expected = payload.subarray(4, 36)\n const data = payload.subarray(36)\n const actual = createHash('sha256').update(data).digest()\n if (currentSequence !== state.sequence || !timingSafeEqual(expected, actual)) throw new Error('invalid data chunk')\n state.sequence += 1\n state.totalData += data.length\n if (state.totalData > MAX_TRANSFER && state.totalData > OBJECT_STREAM_LIMIT) throw new Error('transfer exceeds size limit')\n return { kind: 'data', data }\n}\n\nasync function readFrame(iterator, state) {\n for (;;) {\n if (state.buffer.length >= 5) {\n const kind = state.buffer[0]\n const length = state.buffer.readUInt32BE(1)\n const maxLength = kind === 0 ? MAX_CONTROL : 36 + MAX_CHUNK\n if ((kind !== 0 && kind !== 1) || length > maxLength) throw new Error('invalid frame length')\n if (state.buffer.length >= 5 + length) {\n const payload = state.buffer.subarray(5, 5 + length)\n state.buffer = state.buffer.subarray(5 + length)\n return decodeOneFrame(kind, payload, state)\n }\n }\n const next = await iterator.next()\n if (next.done) throw new Error('helper input ended inside a frame')\n state.buffer = Buffer.concat([state.buffer, Buffer.from(next.value)])\n }\n}\n\nfunction validateRequest(request) {\n if (!request || request.kind !== 'request' || request.protocolVersion !== 1 || !Object.prototype.hasOwnProperty.call(PATHS, request.operation)) {\n throw new Error('unsupported helper request')\n }\n if (Object.keys(request).some(function (key) { return ['kind', 'protocolVersion', 'operation', 'port'].indexOf(key) < 0 })) {\n throw new Error('unsupported helper request field')\n }\n if (!Number.isInteger(request.port) || request.port < 1 || request.port > 65535) throw new Error('invalid loopback port')\n return PATHS[request.operation]\n}\n\nfunction writeStdout(data) {\n return new Promise(function (resolve, reject) {\n if (process.stdout.write(data)) resolve()\n else process.stdout.once('drain', resolve)\n })\n}\n\nasync function run() {\n const iterator = process.stdin[Symbol.asyncIterator]()\n const state = { buffer: Buffer.alloc(0), sequence: 0, totalData: 0 }\n const first = await readFrame(iterator, state)\n if (first.kind !== 'control' || first.value.kind !== 'request') throw new Error('request control frame required')\n const request = first.value\n const operation = validateRequest(request)\n const url = 'http://127.0.0.1:' + request.port + operation.path\n\n if (request.operation === 'session-objects-push') {\n // 对象推送:请求体(对象帧流)直接转发为 HTTP 流式请求体,内存 O(帧)。\n const { Readable } = require('node:stream')\n const bodyStream = new Readable({ read: function () {} })\n const pump = (async function () {\n try {\n for (;;) {\n const frame = await readFrame(iterator, state)\n if (frame.kind === 'control' && frame.value.kind === 'request-end') break\n if (frame.kind !== 'data') throw new Error('invalid object push frame')\n if (!bodyStream.push(Buffer.from(frame.data))) {\n await new Promise(function (resolve) { bodyStream.once('drain', resolve) })\n }\n }\n bodyStream.push(null)\n } catch (error) {\n bodyStream.destroy(error)\n }\n })()\n const response = await fetch(url, {\n method: 'POST',\n redirect: 'error',\n headers: { 'content-type': 'application/x-dshb-object-stream' },\n body: bodyStream,\n duplex: 'half',\n })\n const text = await response.text()\n if (Buffer.byteLength(text, 'utf8') > MAX_CONTROL) throw new Error('response control frame too large')\n await writeStdout(encodeControl({ kind: 'response', status: response.status, body: JSON.parse(text) }))\n if (!response.ok) process.exitCode = 1\n await pump\n return\n }\n\n let body\n if (BUFFERED_BODY_OPERATIONS.indexOf(request.operation) >= 0) {\n // 帧序:control(request) → data… → control(request-end)。\n const chunks = []\n for (;;) {\n const frame = await readFrame(iterator, state)\n if (frame.kind === 'control' && frame.value.kind === 'request-end') break\n if (frame.kind !== 'data') throw new Error('invalid apply request body')\n chunks.push(frame.data)\n }\n body = Buffer.concat(chunks)\n }\n\n const response = await fetch(url, {\n method: operation.method,\n redirect: 'error',\n headers: { accept: 'application/json', ...(body ? { 'content-type': 'application/json' } : {}) },\n ...(body ? { body } : {}),\n })\n\n if (STREAMING_RESPONSE_OPERATIONS.indexOf(request.operation) >= 0) {\n await writeStdout(encodeControl({ kind: 'response', status: response.status, bodyMode: 'data' }))\n if (!response.body) throw new Error('streaming response body missing')\n var sequence = 0\n var total = 0\n for await (var chunk of response.body) {\n var data = Buffer.from(chunk)\n total += data.length\n if (total > MAX_TRANSFER && total > OBJECT_STREAM_LIMIT) throw new Error('response exceeds size limit')\n for (var offset = 0; offset < data.length; offset += MAX_CHUNK) {\n await writeStdout(encodeData(sequence++, data.subarray(offset, Math.min(offset + MAX_CHUNK, data.length))))\n }\n }\n await writeStdout(encodeControl({ kind: 'response-end' }))\n if (!response.ok) process.exitCode = 1\n return\n }\n\n var text = await response.text()\n if (Buffer.byteLength(text, 'utf8') > MAX_CONTROL) throw new Error('response control frame too large')\n var responseBody = JSON.parse(text)\n await writeStdout(encodeControl({ kind: 'response', status: response.status, body: responseBody }))\n var recoverableDeltaMismatch = request.operation === 'session-apply' && response.status === 409 &&\n responseBody && responseBody.code === 'session-delta-prefix'\n if (!response.ok && !recoverableDeltaMismatch) process.exitCode = 1\n}\n\nrun().catch(function () {\n try { process.stdout.write(encodeControl({ kind: 'error', code: 'helper-failed' })) } catch (err) {}\n process.exitCode = 1\n})\n`\n\nconst REMOTE_HELPER_BASE64 = Buffer.from(REMOTE_HELPER_SOURCE, 'utf8').toString('base64')\n\nexport const REMOTE_SYNC_HELPER_COMMAND =\n `node -e 'eval(Buffer.from(\"${REMOTE_HELPER_BASE64}\",\"base64\").toString(\"utf8\"))'`\n"],"mappings":";;AAEA,MAAM,uBAAuB,OAAO,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAiMvC,MAAa,6BACX,8BAH2B,OAAO,KAAK,sBAAsB,MAAM,CAAC,CAAC,SAAS,QAGhD,EAAqB"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { SyncPeerManager } from "./peer-manager.js";
|
|
2
|
-
import { s as SyncSshTransport } from "../../ssh-transport-
|
|
2
|
+
import { s as SyncSshTransport } from "../../ssh-transport-B2oNDs0D.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,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { h as SyncSessionService } from "../../session-sync-BuOJvFxK.js";
|
|
2
2
|
import { Context } from "@deepseek-ai/cordis";
|
|
3
3
|
//#region src/core/sync/session-routes.d.ts
|
|
4
4
|
declare function registerSyncSessionRoutes(ctx: Context, sessions: SyncSessionService): void;
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import {
|
|
2
|
-
export { SyncSessionApplyOptions, SyncSessionApplyResult, SyncSessionBundle, SyncSessionEntry, SyncSessionError, SyncSessionEventDelta, SyncSessionEventPrefix, SyncSessionInstructionSnapshot, SyncSessionManifest, SyncSessionManifestEntry, SyncSessionObject, SyncSessionService, SyncSessionTransferPlan, SyncSessionWorkspace, WorkspaceRegistryLike, makeSyncSessionBundle, makeSyncSessionManifest, planSessionTransfers, sessionEventListDigest, syncSessionEntryKey, validateSyncSessionBundle, validateSyncSessionManifest };
|
|
1
|
+
import { C as sessionEventListDigest, D as validateSyncSessionManifest, E as validateSyncSessionBundle, S as planSessionTransfers, T as validateObjectPayload, _ as SyncSessionWorkspace, a as SyncSessionBundle, b as makeSyncSessionBundle, c as SyncSessionEventDelta, d as SyncSessionManifest, f as SyncSessionManifestEntry, g as SyncSessionTransferPlan, h as SyncSessionService, i as SyncSessionApplyResult, l as SyncSessionEventPrefix, m as SyncSessionObjectInbox, n as SyncObjectInboxWriter, o as SyncSessionEntry, p as SyncSessionObject, r as SyncSessionApplyOptions, s as SyncSessionError, t as ExportObjectCache, u as SyncSessionInstructionSnapshot, v as WorkspaceRegistryLike, w as syncSessionEntryKey, x as makeSyncSessionManifest, y as estimateJsonBytes } from "../../session-sync-BuOJvFxK.js";
|
|
2
|
+
export { ExportObjectCache, SyncObjectInboxWriter, SyncSessionApplyOptions, SyncSessionApplyResult, SyncSessionBundle, SyncSessionEntry, SyncSessionError, SyncSessionEventDelta, SyncSessionEventPrefix, SyncSessionInstructionSnapshot, SyncSessionManifest, SyncSessionManifestEntry, SyncSessionObject, SyncSessionObjectInbox, SyncSessionService, SyncSessionTransferPlan, SyncSessionWorkspace, WorkspaceRegistryLike, estimateJsonBytes, makeSyncSessionBundle, makeSyncSessionManifest, planSessionTransfers, sessionEventListDigest, syncSessionEntryKey, validateObjectPayload, validateSyncSessionBundle, validateSyncSessionManifest };
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as
|
|
2
|
-
export { SyncSessionError, SyncSessionService, makeSyncSessionBundle, makeSyncSessionManifest, planSessionTransfers, sessionEventListDigest, syncSessionEntryKey, validateSyncSessionBundle, validateSyncSessionManifest };
|
|
1
|
+
import { a as estimateJsonBytes, c as planSessionTransfers, d as validateObjectPayload, f as validateSyncSessionBundle, i as SyncSessionService, l as sessionEventListDigest, n as SyncSessionError, o as makeSyncSessionBundle, p as validateSyncSessionManifest, r as SyncSessionObjectInbox, s as makeSyncSessionManifest, t as ExportObjectCache, u as syncSessionEntryKey } from "../../session-sync-CIqjuWkh.js";
|
|
2
|
+
export { ExportObjectCache, SyncSessionError, SyncSessionObjectInbox, SyncSessionService, estimateJsonBytes, makeSyncSessionBundle, makeSyncSessionManifest, planSessionTransfers, sessionEventListDigest, syncSessionEntryKey, validateObjectPayload, validateSyncSessionBundle, validateSyncSessionManifest };
|
|
@@ -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-
|
|
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-B2oNDs0D.js";
|
|
2
2
|
export { SyncRemoteHandshake, SyncRemoteNodeManifest, SyncSessionDeltaPrefixError, SyncSshErrorCategory, SyncSshProbeError, SyncSshSession, SyncSshTransport, buildSyncSshConnectConfig };
|