multi-agent-protocol 0.0.3 → 0.0.6

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.
@@ -0,0 +1,459 @@
1
+ # Implementation Guide: Agentic-Mesh Transport for MAP SDK
2
+
3
+ This guide describes how to implement agentic-mesh as a transport option for the Multi-Agent Protocol (MAP) TypeScript SDK.
4
+
5
+ ## Overview
6
+
7
+ **Goal:** Allow MAP SDK clients to connect over agentic-mesh encrypted tunnels (Nebula/Tailscale/Headscale).
8
+
9
+ **Architecture:**
10
+
11
+ ```
12
+ ┌─────────────────────────────────────────────────────────────────┐
13
+ │ multi-agent-protocol/ts-sdk │
14
+ ├─────────────────────────────────────────────────────────────────┤
15
+ │ │
16
+ │ ┌─────────────┐ │
17
+ │ │ MAPClient │ Standard MAP SDK client │
18
+ │ └──────┬──────┘ │
19
+ │ │ │
20
+ │ ▼ │
21
+ │ ┌─────────────────────────────────────────────────────────┐ │
22
+ │ │ Transport Layer (pluggable) │ │
23
+ │ ├─────────────────────────────────────────────────────────┤ │
24
+ │ │ WebSocketTransport │ StdioTransport │ AgenticMeshTransport │
25
+ │ │ │ │ (NEW) │
26
+ │ └─────────────────────────────────────────────────────────┘ │
27
+ │ │ │
28
+ └──────────────────────────────────────────────┼──────────────────┘
29
+
30
+
31
+ ┌────────────────────────────┐
32
+ │ agentic-mesh TransportAdapter │
33
+ │ (Nebula/Tailscale tunnels) │
34
+ └────────────────────────────┘
35
+ ```
36
+
37
+ ---
38
+
39
+ ## Step 1: Create Transport Interface
40
+
41
+ **File:** `multi-agent-protocol/ts-sdk/src/transports/types.ts`
42
+
43
+ Define the base transport interface that all transports implement:
44
+
45
+ ```typescript
46
+ import type { MAPFrame } from '../types'
47
+
48
+ /**
49
+ * Transport interface for MAP protocol communication.
50
+ */
51
+ export interface Transport {
52
+ /** Connect to the remote endpoint */
53
+ connect(): Promise<void>
54
+
55
+ /** Disconnect from the remote endpoint */
56
+ disconnect(): Promise<void>
57
+
58
+ /** Send a frame to the remote endpoint */
59
+ send(frame: MAPFrame): Promise<void>
60
+
61
+ /** Async iterator for receiving frames */
62
+ receive(): AsyncIterable<MAPFrame>
63
+
64
+ /** Whether currently connected */
65
+ readonly isConnected: boolean
66
+ }
67
+
68
+ /**
69
+ * Base transport configuration.
70
+ */
71
+ export interface TransportConfig {
72
+ /** Connection timeout in milliseconds */
73
+ timeout?: number
74
+ }
75
+ ```
76
+
77
+ ---
78
+
79
+ ## Step 2: Create Agentic-Mesh Transport
80
+
81
+ **File:** `multi-agent-protocol/ts-sdk/src/transports/agentic-mesh.ts`
82
+
83
+ ### Reusable Components from agentic-mesh
84
+
85
+ | Component | Location | Purpose |
86
+ |-----------|----------|---------|
87
+ | `TunnelStream` | [`src/map/stream/tunnel-stream.ts:90`](../src/map/stream/tunnel-stream.ts#L90) | NDJSON framing over transport |
88
+ | `createNdjsonFramer` | [`src/map/stream/tunnel-stream.ts:16`](../src/map/stream/tunnel-stream.ts#L16) | JSON encode/decode utilities |
89
+ | `TransportAdapter` | [`src/transports/types.ts`](../src/transports/types.ts) | Nebula/Tailscale abstraction |
90
+ | `MapFrame` | [`src/map/types.ts:685`](../src/map/types.ts#L685) | Request/Response/Notification types |
91
+
92
+ ### Implementation
93
+
94
+ ```typescript
95
+ import type { Transport, TransportConfig } from './types'
96
+ import type { MAPFrame } from '../types'
97
+
98
+ // Import from agentic-mesh
99
+ import { TunnelStream } from 'agentic-mesh/map/stream'
100
+ import type { TransportAdapter, PeerEndpoint } from 'agentic-mesh/transports'
101
+
102
+ /**
103
+ * Configuration for agentic-mesh transport.
104
+ */
105
+ export interface AgenticMeshTransportConfig extends TransportConfig {
106
+ /** The agentic-mesh transport adapter (Nebula, Tailscale, Headscale) */
107
+ transport: TransportAdapter
108
+
109
+ /** Remote peer to connect to */
110
+ peer: PeerEndpoint
111
+
112
+ /** Local peer ID for identification */
113
+ localPeerId: string
114
+ }
115
+
116
+ /**
117
+ * MAP transport over agentic-mesh encrypted tunnels.
118
+ */
119
+ export class AgenticMeshTransport implements Transport {
120
+ private stream: TunnelStream | null = null
121
+ private readonly config: AgenticMeshTransportConfig
122
+
123
+ constructor(config: AgenticMeshTransportConfig) {
124
+ this.config = config
125
+ }
126
+
127
+ get isConnected(): boolean {
128
+ return this.stream?.isOpen ?? false
129
+ }
130
+
131
+ async connect(): Promise<void> {
132
+ // Start underlying transport if needed
133
+ if (!this.config.transport.isRunning) {
134
+ await this.config.transport.start()
135
+ }
136
+
137
+ // Create tunnel stream over the mesh transport
138
+ this.stream = new TunnelStream({
139
+ transport: this.config.transport,
140
+ peerId: this.config.peer.id,
141
+ streamId: `map-${this.config.localPeerId}-${Date.now()}`,
142
+ })
143
+
144
+ await this.stream.open()
145
+ }
146
+
147
+ async disconnect(): Promise<void> {
148
+ if (this.stream) {
149
+ await this.stream.close()
150
+ this.stream = null
151
+ }
152
+ }
153
+
154
+ async send(frame: MAPFrame): Promise<void> {
155
+ if (!this.stream?.isOpen) {
156
+ throw new Error('Transport not connected')
157
+ }
158
+ await this.stream.write(frame)
159
+ }
160
+
161
+ async *receive(): AsyncIterable<MAPFrame> {
162
+ if (!this.stream) return
163
+
164
+ for await (const frame of this.stream) {
165
+ yield frame
166
+ }
167
+ }
168
+ }
169
+
170
+ /**
171
+ * Create an agentic-mesh transport.
172
+ */
173
+ export function createAgenticMeshTransport(
174
+ config: AgenticMeshTransportConfig
175
+ ): AgenticMeshTransport {
176
+ return new AgenticMeshTransport(config)
177
+ }
178
+ ```
179
+
180
+ ---
181
+
182
+ ## Step 3: Export Transport Module
183
+
184
+ **File:** `multi-agent-protocol/ts-sdk/src/transports/index.ts`
185
+
186
+ ```typescript
187
+ export * from './types'
188
+ export * from './agentic-mesh'
189
+
190
+ // Re-export other transports
191
+ export * from './websocket'
192
+ export * from './stdio'
193
+ ```
194
+
195
+ ---
196
+
197
+ ## Step 4: Wire into MAP Client
198
+
199
+ **File:** `multi-agent-protocol/ts-sdk/src/client.ts`
200
+
201
+ Modify the MAP client to accept a transport:
202
+
203
+ ```typescript
204
+ import type { Transport } from './transports'
205
+ import type {
206
+ ConnectParams,
207
+ ConnectResult,
208
+ MAPFrame,
209
+ MAPRequestFrame,
210
+ MAPResponseFrame,
211
+ } from './types'
212
+
213
+ export class MAPClient {
214
+ private readonly transport: Transport
215
+ private readonly pendingRequests = new Map<
216
+ string | number,
217
+ { resolve: (result: unknown) => void; reject: (error: Error) => void }
218
+ >()
219
+ private nextRequestId = 1
220
+
221
+ constructor(transport: Transport) {
222
+ this.transport = transport
223
+ }
224
+
225
+ async connect(params: ConnectParams): Promise<ConnectResult> {
226
+ // Connect transport
227
+ await this.transport.connect()
228
+
229
+ // Start receiving frames in background
230
+ this.startReceiving()
231
+
232
+ // Send MAP connect request
233
+ return this.request('map/connect', params)
234
+ }
235
+
236
+ async disconnect(): Promise<void> {
237
+ await this.request('map/disconnect', {})
238
+ await this.transport.disconnect()
239
+ }
240
+
241
+ /**
242
+ * Send a JSON-RPC request and wait for response.
243
+ */
244
+ async request<T>(method: string, params: unknown): Promise<T> {
245
+ const id = this.nextRequestId++
246
+
247
+ const frame: MAPRequestFrame = {
248
+ jsonrpc: '2.0',
249
+ id,
250
+ method,
251
+ params,
252
+ }
253
+
254
+ return new Promise((resolve, reject) => {
255
+ this.pendingRequests.set(id, { resolve: resolve as any, reject })
256
+ this.transport.send(frame).catch(reject)
257
+ })
258
+ }
259
+
260
+ /**
261
+ * Start receiving frames from transport.
262
+ */
263
+ private async startReceiving(): Promise<void> {
264
+ try {
265
+ for await (const frame of this.transport.receive()) {
266
+ this.handleFrame(frame)
267
+ }
268
+ } catch (error) {
269
+ // Handle disconnect
270
+ console.error('Transport error:', error)
271
+ }
272
+ }
273
+
274
+ /**
275
+ * Handle incoming frame.
276
+ */
277
+ private handleFrame(frame: MAPFrame): void {
278
+ // Response frame
279
+ if ('id' in frame && ('result' in frame || 'error' in frame)) {
280
+ const pending = this.pendingRequests.get(frame.id)
281
+ if (pending) {
282
+ this.pendingRequests.delete(frame.id)
283
+ if ('error' in frame) {
284
+ pending.reject(new Error(frame.error.message))
285
+ } else {
286
+ pending.resolve(frame.result)
287
+ }
288
+ }
289
+ return
290
+ }
291
+
292
+ // Notification frame
293
+ if ('method' in frame && !('id' in frame)) {
294
+ this.handleNotification(frame)
295
+ }
296
+ }
297
+
298
+ private handleNotification(frame: MAPNotificationFrame): void {
299
+ // Handle events, messages, etc.
300
+ // Emit to subscribers
301
+ }
302
+ }
303
+ ```
304
+
305
+ ---
306
+
307
+ ## Step 5: Usage Example
308
+
309
+ ```typescript
310
+ import { MAPClient } from '@anthropic/multi-agent-protocol'
311
+ import { AgenticMeshTransport } from '@anthropic/multi-agent-protocol/transports/agentic-mesh'
312
+ import { createNebulaTransport } from 'agentic-mesh'
313
+
314
+ async function main() {
315
+ // 1. Create the encrypted mesh transport
316
+ const nebulaTransport = createNebulaTransport({
317
+ configPath: '/etc/nebula/config.yml',
318
+ certPath: '/etc/nebula/host.crt',
319
+ keyPath: '/etc/nebula/host.key',
320
+ })
321
+
322
+ // 2. Create MAP transport wrapper
323
+ const transport = new AgenticMeshTransport({
324
+ transport: nebulaTransport,
325
+ peer: {
326
+ id: 'map-server',
327
+ nebulaIp: '10.0.0.1',
328
+ port: 4242
329
+ },
330
+ localPeerId: 'my-client',
331
+ })
332
+
333
+ // 3. Create MAP client with transport
334
+ const client = new MAPClient(transport)
335
+
336
+ // 4. Connect using standard MAP protocol
337
+ const session = await client.connect({
338
+ participantType: 'client',
339
+ name: 'My Dashboard',
340
+ capabilities: {
341
+ observation: { canObserve: true, canQuery: true },
342
+ },
343
+ })
344
+
345
+ console.log('Connected:', session.sessionId)
346
+
347
+ // 5. Use MAP SDK normally
348
+ const agents = await client.request('map/agents.list', {})
349
+ console.log('Agents:', agents)
350
+
351
+ // 6. Subscribe to events
352
+ const subscription = await client.request('map/subscribe', {
353
+ filter: { eventTypes: ['agent_registered', 'agent_state_changed'] },
354
+ })
355
+
356
+ // 7. Disconnect when done
357
+ await client.disconnect()
358
+ }
359
+
360
+ main().catch(console.error)
361
+ ```
362
+
363
+ ---
364
+
365
+ ## Key Code Pointers
366
+
367
+ ### agentic-mesh Source Files
368
+
369
+ | What | File | Line |
370
+ |------|------|------|
371
+ | NDJSON framing | [`src/map/stream/tunnel-stream.ts`](../src/map/stream/tunnel-stream.ts) | 16-67 |
372
+ | TunnelStream class | [`src/map/stream/tunnel-stream.ts`](../src/map/stream/tunnel-stream.ts) | 90-200 |
373
+ | MapFrame types | [`src/map/types.ts`](../src/map/types.ts) | 682-702 |
374
+ | JSON-RPC handling | [`src/map/connection/base.ts`](../src/map/connection/base.ts) | 1-150 |
375
+ | TransportAdapter interface | [`src/transports/types.ts`](../src/transports/types.ts) | 1-50 |
376
+ | Nebula transport | [`src/transports/nebula-transport.ts`](../src/transports/nebula-transport.ts) | - |
377
+ | Tailscale transport | [`src/transports/tailscale-transport.ts`](../src/transports/tailscale-transport.ts) | - |
378
+ | PeerConnection (reference) | [`src/map/connection/peer.ts`](../src/map/connection/peer.ts) | - |
379
+
380
+ ### Existing Exports
381
+
382
+ agentic-mesh already exports what's needed:
383
+
384
+ ```typescript
385
+ // src/map/index.ts
386
+ export * from './stream' // TunnelStream, createNdjsonFramer
387
+ export * from './types' // MapFrame, all MAP types
388
+ export * from './connection' // BaseConnection
389
+
390
+ // src/index.ts
391
+ export * from './map' // All MAP exports
392
+ ```
393
+
394
+ ---
395
+
396
+ ## Files to Create
397
+
398
+ | File | Purpose |
399
+ |------|---------|
400
+ | `ts-sdk/src/transports/types.ts` | Transport interface definition |
401
+ | `ts-sdk/src/transports/agentic-mesh.ts` | Agentic-mesh transport implementation |
402
+ | `ts-sdk/src/transports/index.ts` | Export all transports |
403
+
404
+ ## Files to Modify
405
+
406
+ | File | Change |
407
+ |------|--------|
408
+ | `ts-sdk/src/client.ts` | Accept transport in constructor |
409
+ | `ts-sdk/package.json` | Add `agentic-mesh` as optional peer dependency |
410
+
411
+ ---
412
+
413
+ ## Testing
414
+
415
+ Create tests for the transport:
416
+
417
+ ```typescript
418
+ // ts-sdk/tests/transports/agentic-mesh.test.ts
419
+
420
+ import { describe, it, expect, vi } from 'vitest'
421
+ import { AgenticMeshTransport } from '../../src/transports/agentic-mesh'
422
+
423
+ describe('AgenticMeshTransport', () => {
424
+ it('should connect via tunnel stream', async () => {
425
+ const mockTransport = {
426
+ isRunning: false,
427
+ start: vi.fn(),
428
+ connect: vi.fn(),
429
+ }
430
+
431
+ const transport = new AgenticMeshTransport({
432
+ transport: mockTransport as any,
433
+ peer: { id: 'test-peer', nebulaIp: '10.0.0.1', port: 4242 },
434
+ localPeerId: 'local',
435
+ })
436
+
437
+ await transport.connect()
438
+ expect(mockTransport.start).toHaveBeenCalled()
439
+ })
440
+
441
+ it('should send frames via stream', async () => {
442
+ // Test frame sending
443
+ })
444
+
445
+ it('should receive frames via async iterator', async () => {
446
+ // Test frame receiving
447
+ })
448
+ })
449
+ ```
450
+
451
+ ---
452
+
453
+ ## Summary
454
+
455
+ 1. **Define transport interface** - Standard interface for all MAP transports
456
+ 2. **Implement AgenticMeshTransport** - Wraps TunnelStream for MAP frames
457
+ 3. **Export from ts-sdk** - Make transport available to SDK users
458
+ 4. **Wire into MAPClient** - Accept transport in constructor
459
+ 5. **Use normally** - Standard MAP SDK API over encrypted mesh tunnels
@@ -0,0 +1,251 @@
1
+ # Git Transport Integration Guide
2
+
3
+ This guide explains how git transport is integrated with agentic-mesh and how to use it from the MAP side.
4
+
5
+ ## Architecture Overview
6
+
7
+ ```
8
+ ┌──────────────────────────────────────────────────────────────────┐
9
+ │ MAP Client/Agent │
10
+ │ │
11
+ │ Option A: CLI Option B: Programmatic │
12
+ │ ───────────────── ─────────────────────── │
13
+ │ git fetch mesh://peer-b/ client.sync('peer-b') │
14
+ │ │ │ │
15
+ │ ▼ ▼ │
16
+ │ git-remote-mesh helper GitSyncClient │
17
+ │ │ │ │
18
+ │ └────────────┬───────────────────┘ │
19
+ │ ▼ │
20
+ │ GitTransportService (HTTP :3456) │
21
+ │ │ │
22
+ │ ▼ │
23
+ │ MeshPeer.sendGitMessage() │
24
+ │ │ │
25
+ │ ▼ │
26
+ │ PeerConnection.sendGitMessage() │
27
+ └──────────────────────┬───────────────────────────────────────────┘
28
+ │ MAP Protocol (NDJSON over transport)
29
+
30
+ ┌──────────────────────────────────────────────────────────────────┐
31
+ │ Remote Peer │
32
+ │ │
33
+ │ PeerConnection receives 'git/message' │
34
+ │ │ │
35
+ │ ▼ │
36
+ │ MeshPeer.git:message event │
37
+ │ │ │
38
+ │ ▼ │
39
+ │ GitTransportService.handleRemoteMessage() │
40
+ │ │ │
41
+ │ ▼ │
42
+ │ GitProtocolHandler (spawns native git) │
43
+ └──────────────────────────────────────────────────────────────────┘
44
+ ```
45
+
46
+ ## Key Integration Points
47
+
48
+ ### 1. Enable Git in MeshPeer Config
49
+
50
+ **File:** `src/map/types.ts` (lines 45-52)
51
+
52
+ ```typescript
53
+ const peer = createMeshPeer({
54
+ peerId: 'my-agent',
55
+ git: {
56
+ enabled: true,
57
+ httpPort: 3456, // Port for git-remote-mesh helper
58
+ repoPath: '/path/to/repo', // Default repo path
59
+ },
60
+ })
61
+ ```
62
+
63
+ ### 2. Git Service Lifecycle
64
+
65
+ **File:** `src/map/mesh-peer.ts`
66
+
67
+ The git service is initialized in the constructor (lines 74-84) and started/stopped with the peer:
68
+
69
+ ```typescript
70
+ // Start (line 170-173)
71
+ if (this.gitService) {
72
+ this.gitService.setPeerSender(this.createGitPeerSender())
73
+ await this.gitService.start()
74
+ }
75
+
76
+ // Stop (line 195-197)
77
+ if (this.gitService) {
78
+ await this.gitService.stop()
79
+ }
80
+ ```
81
+
82
+ ### 3. Message Routing
83
+
84
+ **File:** `src/map/mesh-peer.ts` (lines 225-241)
85
+
86
+ Git messages are sent via `createGitPeerSender()`:
87
+
88
+ ```typescript
89
+ private createGitPeerSender(): PeerMessageSender {
90
+ return {
91
+ sendToPeer: async (peerId: string, message: AnyGitMessage) => {
92
+ const conn = this.peerConnections.get(peerId)
93
+ if (!conn) throw new Error(`No connection to peer ${peerId}`)
94
+ await conn.sendGitMessage(message)
95
+ },
96
+ isConnected: (peerId: string) => this.peerConnections.has(peerId),
97
+ }
98
+ }
99
+ ```
100
+
101
+ ### 4. Receiving Git Messages
102
+
103
+ **File:** `src/map/mesh-peer.ts` (lines 354-359)
104
+
105
+ When a peer connection receives a git message, it's forwarded to the git service:
106
+
107
+ ```typescript
108
+ conn.on('git:message', (gitMessage) => {
109
+ if (this.gitService) {
110
+ this.gitService.handleRemoteMessage(peerId, gitMessage)
111
+ }
112
+ })
113
+ ```
114
+
115
+ ### 5. PeerConnection Git Support
116
+
117
+ **File:** `src/map/connection/peer.ts`
118
+
119
+ Git messages use a dedicated method type:
120
+
121
+ ```typescript
122
+ const GIT_MESSAGE_METHOD = 'git/message' as const // line 32
123
+
124
+ // Send (lines 224-232)
125
+ async sendGitMessage(message: AnyGitMessage): Promise<void> {
126
+ await this.stream.notify(GIT_MESSAGE_METHOD, message)
127
+ }
128
+
129
+ // Receive (line 296)
130
+ this.emit('git:message', message)
131
+ ```
132
+
133
+ ## Usage from MAP Side
134
+
135
+ ### Option A: Using GitSyncClient (Recommended)
136
+
137
+ ```typescript
138
+ import { createMeshPeer } from 'agentic-mesh/map'
139
+
140
+ const peer = createMeshPeer({
141
+ peerId: 'agent-a',
142
+ git: { enabled: true, repoPath: '/my/repo' },
143
+ })
144
+
145
+ await peer.start()
146
+ await peer.connectToPeer('agent-b', endpoint)
147
+
148
+ // Create sync client
149
+ const client = peer.git!.createSyncClient('/my/repo')
150
+
151
+ // Sync operations
152
+ await client.sync('agent-b', { branch: 'main', bidirectional: true })
153
+ await client.pull('agent-b', 'main')
154
+ await client.push('agent-b', 'feature-branch')
155
+ await client.clone('agent-b', '/new/repo')
156
+ ```
157
+
158
+ ### Option B: Using Standard Git Commands
159
+
160
+ Requires `git-remote-mesh` in PATH:
161
+
162
+ ```bash
163
+ # Install globally
164
+ npm install -g agentic-mesh
165
+
166
+ # Or add to PATH
167
+ export PATH="$PATH:./node_modules/.bin"
168
+
169
+ # Use mesh:// URLs
170
+ git remote add agent-b mesh://agent-b-id/
171
+ git fetch agent-b
172
+ git push agent-b main
173
+ ```
174
+
175
+ ### Option C: Direct Protocol Access
176
+
177
+ For low-level control:
178
+
179
+ ```typescript
180
+ // List refs from remote peer
181
+ const refs = await peer.git!.protocolHandler.listRefs({
182
+ refPrefix: 'refs/heads/',
183
+ })
184
+
185
+ // Fetch pack data
186
+ const pack = await peer.git!.protocolHandler.uploadPack({
187
+ wants: ['abc123...'],
188
+ haves: ['def456...'],
189
+ })
190
+ ```
191
+
192
+ ## Wire Protocol Messages
193
+
194
+ **File:** `src/git/types.ts`
195
+
196
+ | Message Type | Direction | Purpose |
197
+ |--------------|-----------|---------|
198
+ | `git/list-refs` | Request/Response | List remote refs |
199
+ | `git/upload-pack` | Request/Response | Fetch pack data |
200
+ | `git/receive-pack` | Request/Response | Push pack data |
201
+ | `git/pack-stream` | Notification | Start binary stream |
202
+ | `git/pack-chunk` | Notification | Stream chunk |
203
+ | `git/pack-complete` | Notification | End stream |
204
+ | `git/error` | Response | Error response |
205
+
206
+ ## Binary Streaming
207
+
208
+ **File:** `src/git/transport-service.ts` (lines 451-465, 501-545)
209
+
210
+ Large packs (>1MB by default) are automatically streamed:
211
+
212
+ ```typescript
213
+ // Config (lines 67-71)
214
+ streaming: {
215
+ enabled: true,
216
+ threshold: 1024 * 1024, // 1MB
217
+ chunkSize: 64 * 1024, // 64KB chunks
218
+ }
219
+ ```
220
+
221
+ Flow:
222
+ 1. Response sent with `streaming: true`, no `packData`
223
+ 2. `git/pack-stream` message initiates transfer
224
+ 3. `git/pack-chunk` messages send data (base64 encoded)
225
+ 4. `git/pack-complete` finalizes with checksum
226
+
227
+ ## File Reference
228
+
229
+ | File | Purpose |
230
+ |------|---------|
231
+ | `src/git/types.ts` | Type definitions, message types |
232
+ | `src/git/protocol-handler.ts` | Native git operations |
233
+ | `src/git/transport-service.ts` | HTTP server + peer routing |
234
+ | `src/git/sync-client.ts` | High-level sync API |
235
+ | `src/git/pack-streamer.ts` | Binary streaming |
236
+ | `src/git/git-remote-mesh.ts` | CLI remote helper |
237
+ | `src/map/mesh-peer.ts` | MeshPeer integration |
238
+ | `src/map/connection/peer.ts` | Git message handling |
239
+
240
+ ## Testing
241
+
242
+ ```bash
243
+ # Unit tests
244
+ npm test -- git-transport
245
+
246
+ # Integration tests (requires git)
247
+ npm test -- tests/integration/git-transport
248
+
249
+ # All tests
250
+ npm test
251
+ ```