@willjackson/claude-code-bridge 0.1.0

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/dist/index.js ADDED
@@ -0,0 +1,976 @@
1
+ import {
2
+ ArtifactSchema,
3
+ Bridge,
4
+ BridgeMessageSchema,
5
+ ConnectionState,
6
+ ContextSchema,
7
+ DEFAULT_CONFIG,
8
+ DirectoryTreeSchema,
9
+ FileChunkSchema,
10
+ MessageType,
11
+ TaskRequestSchema,
12
+ TaskResultSchema,
13
+ WebSocketTransport,
14
+ createChildLogger,
15
+ createContextRequestMessage,
16
+ createContextSyncMessage,
17
+ createLogger,
18
+ createMessage,
19
+ createNotificationMessage,
20
+ createTaskDelegateMessage,
21
+ createTaskResponseMessage,
22
+ deserializeMessage,
23
+ detectEnvironment,
24
+ discoverAllPeers,
25
+ discoverDdevProjects,
26
+ discoverDockerPeers,
27
+ discoverDocksalProjects,
28
+ discoverLandoProjects,
29
+ getDefaultConfig,
30
+ getHostGateway,
31
+ isDockerAvailable,
32
+ loadConfig,
33
+ loadConfigSync,
34
+ mergeConfig,
35
+ parseDockerLabels,
36
+ safeDeserializeMessage,
37
+ safeValidateMessage,
38
+ serializeMessage,
39
+ validateMessage
40
+ } from "./chunk-BRH476VK.js";
41
+
42
+ // src/bridge/context.ts
43
+ import { readdirSync, readFileSync, statSync, realpathSync } from "fs";
44
+ import { join, basename, relative, extname } from "path";
45
+ import { v4 as uuidv4 } from "uuid";
46
+ import { minimatch } from "minimatch";
47
+
48
+ // src/utils/tokens.ts
49
+ var TOKENS_PER_WORD = 1.3;
50
+ function estimateTokens(text) {
51
+ if (!text || text.length === 0) {
52
+ return 0;
53
+ }
54
+ const words = text.split(/\s+/).filter((word) => word.length > 0);
55
+ return Math.ceil(words.length * TOKENS_PER_WORD);
56
+ }
57
+ function truncateToTokenLimit(text, maxTokens) {
58
+ if (!text || text.length === 0) {
59
+ return "";
60
+ }
61
+ if (maxTokens <= 0) {
62
+ return "";
63
+ }
64
+ if (estimateTokens(text) <= maxTokens) {
65
+ return text;
66
+ }
67
+ const words = text.split(/\s+/).filter((word) => word.length > 0);
68
+ const maxWords = Math.floor(maxTokens / TOKENS_PER_WORD);
69
+ if (maxWords <= 0) {
70
+ return "";
71
+ }
72
+ const truncatedWords = words.slice(0, maxWords);
73
+ return truncatedWords.join(" ");
74
+ }
75
+
76
+ // src/bridge/context.ts
77
+ function buildDirectoryTree(rootPath, options = {}) {
78
+ const includePatterns = options.includePatterns ?? [];
79
+ const excludePatterns = options.excludePatterns ?? [];
80
+ const maxDepth = options.maxDepth ?? 10;
81
+ const visitedPaths = /* @__PURE__ */ new Set();
82
+ function isIncluded(relativePath) {
83
+ if (includePatterns.length === 0) {
84
+ return true;
85
+ }
86
+ return includePatterns.some((pattern) => minimatch(relativePath, pattern));
87
+ }
88
+ function isExcluded(relativePath) {
89
+ return excludePatterns.some((pattern) => minimatch(relativePath, pattern));
90
+ }
91
+ function shouldIncludeDirectory(relativePath) {
92
+ if (includePatterns.length === 0) {
93
+ return true;
94
+ }
95
+ return includePatterns.some((pattern) => {
96
+ if (pattern.startsWith("**")) {
97
+ return true;
98
+ }
99
+ const patternParts = pattern.split("/");
100
+ const pathParts = relativePath.split("/");
101
+ for (let i = 0; i < pathParts.length && i < patternParts.length; i++) {
102
+ if (patternParts[i] === "**") {
103
+ return true;
104
+ }
105
+ if (!minimatch(pathParts[i], patternParts[i])) {
106
+ return false;
107
+ }
108
+ }
109
+ return true;
110
+ });
111
+ }
112
+ function buildTree(dirPath, depth) {
113
+ const name = basename(dirPath);
114
+ const tree = {
115
+ name,
116
+ type: "directory",
117
+ children: []
118
+ };
119
+ if (depth >= maxDepth) {
120
+ return tree;
121
+ }
122
+ try {
123
+ const realPath = realpathSync(dirPath);
124
+ if (visitedPaths.has(realPath)) {
125
+ return tree;
126
+ }
127
+ visitedPaths.add(realPath);
128
+ } catch {
129
+ return tree;
130
+ }
131
+ try {
132
+ const entries = readdirSync(dirPath, { withFileTypes: true });
133
+ for (const entry of entries) {
134
+ const fullPath = join(dirPath, entry.name);
135
+ const relativePath = relative(rootPath, fullPath);
136
+ if (isExcluded(relativePath)) {
137
+ continue;
138
+ }
139
+ if (entry.isSymbolicLink()) {
140
+ try {
141
+ const stats = statSync(fullPath);
142
+ if (stats.isDirectory()) {
143
+ if (shouldIncludeDirectory(relativePath)) {
144
+ tree.children.push(buildTree(fullPath, depth + 1));
145
+ }
146
+ } else if (stats.isFile()) {
147
+ if (isIncluded(relativePath)) {
148
+ tree.children.push({
149
+ name: entry.name,
150
+ type: "file"
151
+ });
152
+ }
153
+ }
154
+ } catch {
155
+ continue;
156
+ }
157
+ } else if (entry.isDirectory()) {
158
+ if (shouldIncludeDirectory(relativePath)) {
159
+ tree.children.push(buildTree(fullPath, depth + 1));
160
+ }
161
+ } else if (entry.isFile()) {
162
+ if (isIncluded(relativePath)) {
163
+ tree.children.push({
164
+ name: entry.name,
165
+ type: "file"
166
+ });
167
+ }
168
+ }
169
+ }
170
+ tree.children.sort((a, b) => {
171
+ if (a.type !== b.type) {
172
+ return a.type === "directory" ? -1 : 1;
173
+ }
174
+ return a.name.localeCompare(b.name);
175
+ });
176
+ } catch {
177
+ }
178
+ return tree;
179
+ }
180
+ return buildTree(rootPath, 0);
181
+ }
182
+ var ContextManager = class {
183
+ rootPath;
184
+ includePatterns;
185
+ excludePatterns;
186
+ maxDepth;
187
+ /** Stored snapshots for delta comparison */
188
+ snapshotStates = /* @__PURE__ */ new Map();
189
+ /**
190
+ * Create a new ContextManager
191
+ * @param options Configuration options
192
+ */
193
+ constructor(options) {
194
+ this.rootPath = options.rootPath;
195
+ this.includePatterns = options.includePatterns;
196
+ this.excludePatterns = options.excludePatterns;
197
+ this.maxDepth = options.maxDepth ?? 10;
198
+ }
199
+ /**
200
+ * Generate a complete snapshot of the current project state
201
+ * @returns ProjectSnapshot with tree structure, summary, and key files
202
+ */
203
+ async generateSnapshot() {
204
+ const tree = buildDirectoryTree(this.rootPath, {
205
+ includePatterns: this.includePatterns,
206
+ excludePatterns: this.excludePatterns,
207
+ maxDepth: this.maxDepth
208
+ });
209
+ const files = this.collectMatchingFiles(this.rootPath);
210
+ const keyFiles = this.identifyKeyFiles(files);
211
+ const summary = this.generateSummary(files);
212
+ const snapshotId = uuidv4();
213
+ const fileStates = /* @__PURE__ */ new Map();
214
+ for (const file of files) {
215
+ try {
216
+ const stats = statSync(file);
217
+ const relativePath = relative(this.rootPath, file);
218
+ fileStates.set(relativePath, {
219
+ path: relativePath,
220
+ mtime: stats.mtimeMs,
221
+ size: stats.size
222
+ });
223
+ } catch {
224
+ }
225
+ }
226
+ this.snapshotStates.set(snapshotId, {
227
+ id: snapshotId,
228
+ files: fileStates
229
+ });
230
+ return {
231
+ id: snapshotId,
232
+ timestamp: Date.now(),
233
+ tree,
234
+ summary,
235
+ keyFiles
236
+ };
237
+ }
238
+ /**
239
+ * Get changes since the last snapshot
240
+ *
241
+ * Compares the current file system state with a previously stored snapshot
242
+ * to detect added, modified, and deleted files.
243
+ *
244
+ * @param lastSyncId ID of the last snapshot to compare against
245
+ * @returns ContextDelta with changes since the last sync
246
+ * @throws Error if lastSyncId is not found
247
+ */
248
+ async getDelta(lastSyncId) {
249
+ const lastState = this.snapshotStates.get(lastSyncId);
250
+ if (!lastState) {
251
+ throw new Error(`Snapshot ${lastSyncId} not found`);
252
+ }
253
+ const currentSnapshot = await this.generateSnapshot();
254
+ const currentState = this.snapshotStates.get(currentSnapshot.id);
255
+ const changes = [];
256
+ for (const [path, currentFile] of currentState.files) {
257
+ const lastFile = lastState.files.get(path);
258
+ if (!lastFile) {
259
+ changes.push({
260
+ path,
261
+ action: "added"
262
+ });
263
+ } else if (currentFile.mtime !== lastFile.mtime || currentFile.size !== lastFile.size) {
264
+ const diff = await this.generateFileDiff(path);
265
+ changes.push({
266
+ path,
267
+ action: "modified",
268
+ diff
269
+ });
270
+ }
271
+ }
272
+ for (const path of lastState.files.keys()) {
273
+ if (!currentState.files.has(path)) {
274
+ changes.push({
275
+ path,
276
+ action: "deleted"
277
+ });
278
+ }
279
+ }
280
+ return {
281
+ fromSyncId: lastSyncId,
282
+ toSyncId: currentSnapshot.id,
283
+ changes
284
+ };
285
+ }
286
+ /**
287
+ * Generate a simple diff for a modified file
288
+ * Returns the new content (simplified diff for now)
289
+ */
290
+ async generateFileDiff(relativePath) {
291
+ try {
292
+ const fullPath = join(this.rootPath, relativePath);
293
+ const content = readFileSync(fullPath, "utf-8");
294
+ return content.length > 1e3 ? content.slice(0, 1e3) + "..." : content;
295
+ } catch {
296
+ return "";
297
+ }
298
+ }
299
+ /**
300
+ * Get relevant file chunks based on a task description, respecting token limits
301
+ * @param task Description of the task to get context for
302
+ * @param maxTokens Maximum tokens for the returned context
303
+ * @returns Array of FileChunk objects within the token limit
304
+ */
305
+ async getRelevantContext(task, maxTokens) {
306
+ const files = this.collectMatchingFiles(this.rootPath);
307
+ const rankedFiles = this.rankFilesForTask(files, task);
308
+ const chunks = [];
309
+ let currentTokens = 0;
310
+ for (const filePath of rankedFiles) {
311
+ try {
312
+ const content = readFileSync(filePath, "utf-8");
313
+ const fileTokens = estimateTokens(content);
314
+ if (currentTokens + fileTokens <= maxTokens) {
315
+ chunks.push({
316
+ path: relative(this.rootPath, filePath),
317
+ content,
318
+ language: this.getLanguage(filePath)
319
+ });
320
+ currentTokens += fileTokens;
321
+ } else if (chunks.length === 0) {
322
+ const remainingTokens = maxTokens - currentTokens;
323
+ const truncatedContent = this.truncateContent(content, remainingTokens);
324
+ if (truncatedContent) {
325
+ chunks.push({
326
+ path: relative(this.rootPath, filePath),
327
+ content: truncatedContent,
328
+ language: this.getLanguage(filePath)
329
+ });
330
+ break;
331
+ }
332
+ }
333
+ if (currentTokens >= maxTokens) {
334
+ break;
335
+ }
336
+ } catch {
337
+ continue;
338
+ }
339
+ }
340
+ return chunks;
341
+ }
342
+ /**
343
+ * Collect all files matching include patterns and not matching exclude patterns
344
+ */
345
+ collectMatchingFiles(dirPath) {
346
+ const files = [];
347
+ this.collectFilesRecursive(dirPath, files, 0);
348
+ return files;
349
+ }
350
+ collectFilesRecursive(dirPath, files, depth) {
351
+ if (depth >= this.maxDepth) {
352
+ return;
353
+ }
354
+ try {
355
+ const entries = readdirSync(dirPath, { withFileTypes: true });
356
+ for (const entry of entries) {
357
+ const fullPath = join(dirPath, entry.name);
358
+ const relativePath = relative(this.rootPath, fullPath);
359
+ if (this.isExcluded(relativePath)) {
360
+ continue;
361
+ }
362
+ if (entry.isDirectory()) {
363
+ this.collectFilesRecursive(fullPath, files, depth + 1);
364
+ } else if (entry.isFile() && this.isIncluded(relativePath)) {
365
+ files.push(fullPath);
366
+ }
367
+ }
368
+ } catch {
369
+ }
370
+ }
371
+ /**
372
+ * Check if a path matches any include pattern
373
+ */
374
+ isIncluded(relativePath) {
375
+ if (this.includePatterns.length === 0) {
376
+ return true;
377
+ }
378
+ return this.includePatterns.some((pattern) => minimatch(relativePath, pattern));
379
+ }
380
+ /**
381
+ * Check if a path matches any exclude pattern
382
+ */
383
+ isExcluded(relativePath) {
384
+ return this.excludePatterns.some((pattern) => minimatch(relativePath, pattern));
385
+ }
386
+ /**
387
+ * Check if a directory should be traversed based on include patterns
388
+ */
389
+ shouldIncludeDirectory(relativePath) {
390
+ if (this.includePatterns.length === 0) {
391
+ return true;
392
+ }
393
+ return this.includePatterns.some((pattern) => {
394
+ const patternParts = pattern.split("/");
395
+ const pathParts = relativePath.split("/");
396
+ if (pattern.startsWith("**")) {
397
+ return true;
398
+ }
399
+ for (let i = 0; i < pathParts.length && i < patternParts.length; i++) {
400
+ if (patternParts[i] === "**") {
401
+ return true;
402
+ }
403
+ if (!minimatch(pathParts[i], patternParts[i])) {
404
+ return false;
405
+ }
406
+ }
407
+ return true;
408
+ });
409
+ }
410
+ /**
411
+ * Identify key files in the project (config files, entry points, etc.)
412
+ */
413
+ identifyKeyFiles(files) {
414
+ const keyFileNames = [
415
+ "package.json",
416
+ "tsconfig.json",
417
+ "index.ts",
418
+ "index.js",
419
+ "main.ts",
420
+ "main.js",
421
+ "app.ts",
422
+ "app.js",
423
+ "README.md",
424
+ "CLAUDE.md"
425
+ ];
426
+ const keyFiles = [];
427
+ for (const file of files) {
428
+ const fileName = basename(file);
429
+ if (keyFileNames.includes(fileName)) {
430
+ keyFiles.push(relative(this.rootPath, file));
431
+ }
432
+ }
433
+ return keyFiles;
434
+ }
435
+ /**
436
+ * Generate a summary string describing the project
437
+ */
438
+ generateSummary(files) {
439
+ const extensions = /* @__PURE__ */ new Map();
440
+ for (const file of files) {
441
+ const ext = extname(file) || "(no extension)";
442
+ extensions.set(ext, (extensions.get(ext) || 0) + 1);
443
+ }
444
+ const extSummary = Array.from(extensions.entries()).sort((a, b) => b[1] - a[1]).slice(0, 5).map(([ext, count]) => `${ext}: ${count}`).join(", ");
445
+ return `Project contains ${files.length} files. Top extensions: ${extSummary}`;
446
+ }
447
+ /**
448
+ * Rank files by relevance to a task description
449
+ * Files with matching keywords are ranked higher
450
+ */
451
+ rankFilesForTask(files, task) {
452
+ const taskLower = task.toLowerCase();
453
+ const keywords = taskLower.split(/\s+/).filter((w) => w.length > 2);
454
+ const scored = files.map((file) => {
455
+ const relativePath = relative(this.rootPath, file).toLowerCase();
456
+ let score = 0;
457
+ for (const keyword of keywords) {
458
+ if (relativePath.includes(keyword)) {
459
+ score += 10;
460
+ }
461
+ }
462
+ const fileName = basename(file);
463
+ if (["index.ts", "index.js", "main.ts", "main.js"].includes(fileName)) {
464
+ score += 5;
465
+ }
466
+ if (fileName === "package.json") {
467
+ score += 3;
468
+ }
469
+ return { file, score };
470
+ });
471
+ scored.sort((a, b) => {
472
+ if (a.score !== b.score) {
473
+ return b.score - a.score;
474
+ }
475
+ return a.file.localeCompare(b.file);
476
+ });
477
+ return scored.map((s) => s.file);
478
+ }
479
+ /**
480
+ * Get programming language from file extension
481
+ */
482
+ getLanguage(filePath) {
483
+ const ext = extname(filePath).toLowerCase();
484
+ const languageMap = {
485
+ ".ts": "typescript",
486
+ ".tsx": "typescript",
487
+ ".js": "javascript",
488
+ ".jsx": "javascript",
489
+ ".py": "python",
490
+ ".rb": "ruby",
491
+ ".go": "go",
492
+ ".rs": "rust",
493
+ ".java": "java",
494
+ ".c": "c",
495
+ ".cpp": "cpp",
496
+ ".h": "c",
497
+ ".hpp": "cpp",
498
+ ".cs": "csharp",
499
+ ".php": "php",
500
+ ".swift": "swift",
501
+ ".kt": "kotlin",
502
+ ".scala": "scala",
503
+ ".md": "markdown",
504
+ ".json": "json",
505
+ ".yaml": "yaml",
506
+ ".yml": "yaml",
507
+ ".xml": "xml",
508
+ ".html": "html",
509
+ ".css": "css",
510
+ ".scss": "scss",
511
+ ".less": "less",
512
+ ".sql": "sql",
513
+ ".sh": "bash",
514
+ ".bash": "bash",
515
+ ".zsh": "zsh"
516
+ };
517
+ return languageMap[ext] || "text";
518
+ }
519
+ /**
520
+ * Truncate file content to fit within a token limit
521
+ */
522
+ truncateContent(content, maxTokens) {
523
+ const lines = content.split("\n");
524
+ const result = [];
525
+ let currentTokens = 0;
526
+ for (const line of lines) {
527
+ const lineTokens = estimateTokens(line);
528
+ if (currentTokens + lineTokens <= maxTokens) {
529
+ result.push(line);
530
+ currentTokens += lineTokens;
531
+ } else {
532
+ break;
533
+ }
534
+ }
535
+ return result.join("\n");
536
+ }
537
+ };
538
+
539
+ // src/transport/index.ts
540
+ function createTransport(type, options) {
541
+ switch (type) {
542
+ case "websocket":
543
+ return new WebSocketTransport();
544
+ default:
545
+ throw new Error(`Unknown transport type: ${type}`);
546
+ }
547
+ }
548
+
549
+ // src/environment/docksal.ts
550
+ function isDocksalEnvironment() {
551
+ return !!process.env.DOCKSAL_STACK;
552
+ }
553
+ function getDocksalProjectInfo() {
554
+ if (!isDocksalEnvironment()) {
555
+ return null;
556
+ }
557
+ const projectName = process.env.DOCKSAL_PROJECT;
558
+ const projectRoot = process.env.PROJECT_ROOT;
559
+ const stack = process.env.DOCKSAL_STACK;
560
+ if (!projectName || !projectRoot || !stack) {
561
+ return null;
562
+ }
563
+ return {
564
+ projectName,
565
+ projectRoot,
566
+ stack
567
+ };
568
+ }
569
+
570
+ // src/environment/ddev.ts
571
+ function isDdevEnvironment() {
572
+ return process.env.IS_DDEV_PROJECT === "true";
573
+ }
574
+ function getDdevProjectInfo() {
575
+ if (!isDdevEnvironment()) {
576
+ return null;
577
+ }
578
+ const projectName = process.env.DDEV_PROJECT;
579
+ const projectRoot = process.env.DDEV_DOCROOT;
580
+ if (!projectName || !projectRoot) {
581
+ return null;
582
+ }
583
+ return {
584
+ projectName,
585
+ projectRoot
586
+ };
587
+ }
588
+
589
+ // src/environment/lando.ts
590
+ function isLandoEnvironment() {
591
+ return process.env.LANDO === "ON";
592
+ }
593
+ function getLandoProjectInfo() {
594
+ if (!isLandoEnvironment()) {
595
+ return null;
596
+ }
597
+ const projectName = process.env.LANDO_APP_NAME;
598
+ if (!projectName) {
599
+ return null;
600
+ }
601
+ return {
602
+ projectName
603
+ };
604
+ }
605
+
606
+ // src/utils/errors.ts
607
+ var BridgeError = class extends Error {
608
+ code;
609
+ context;
610
+ constructor(message, code, context) {
611
+ super(message);
612
+ this.name = "BridgeError";
613
+ this.code = code;
614
+ this.context = context;
615
+ if (Error.captureStackTrace) {
616
+ Error.captureStackTrace(this, this.constructor);
617
+ }
618
+ }
619
+ /**
620
+ * Get formatted error message with context
621
+ */
622
+ toDetailedString() {
623
+ const parts = [`${this.name}[${this.code}]: ${this.message}`];
624
+ if (this.context) {
625
+ parts.push(`Context: ${JSON.stringify(this.context)}`);
626
+ }
627
+ return parts.join("\n");
628
+ }
629
+ };
630
+ var ErrorCodes = {
631
+ // Configuration errors
632
+ CONFIG_INVALID: "CONFIG_INVALID",
633
+ CONFIG_MISSING: "CONFIG_MISSING",
634
+ CONFIG_PARSE_ERROR: "CONFIG_PARSE_ERROR",
635
+ // Connection errors
636
+ CONNECTION_FAILED: "CONNECTION_FAILED",
637
+ CONNECTION_REFUSED: "CONNECTION_REFUSED",
638
+ CONNECTION_TIMEOUT: "CONNECTION_TIMEOUT",
639
+ CONNECTION_CLOSED: "CONNECTION_CLOSED",
640
+ NOT_CONNECTED: "NOT_CONNECTED",
641
+ ALREADY_CONNECTED: "ALREADY_CONNECTED",
642
+ // Peer errors
643
+ PEER_NOT_FOUND: "PEER_NOT_FOUND",
644
+ NO_PEERS_CONNECTED: "NO_PEERS_CONNECTED",
645
+ PEER_DISCONNECTED: "PEER_DISCONNECTED",
646
+ // Task errors
647
+ TASK_TIMEOUT: "TASK_TIMEOUT",
648
+ TASK_FAILED: "TASK_FAILED",
649
+ NO_TASK_HANDLER: "NO_TASK_HANDLER",
650
+ // Context errors
651
+ CONTEXT_TIMEOUT: "CONTEXT_TIMEOUT",
652
+ CONTEXT_SYNC_FAILED: "CONTEXT_SYNC_FAILED",
653
+ SNAPSHOT_NOT_FOUND: "SNAPSHOT_NOT_FOUND",
654
+ // Protocol errors
655
+ INVALID_MESSAGE: "INVALID_MESSAGE",
656
+ SERIALIZATION_ERROR: "SERIALIZATION_ERROR",
657
+ // Bridge lifecycle errors
658
+ BRIDGE_ALREADY_STARTED: "BRIDGE_ALREADY_STARTED",
659
+ BRIDGE_NOT_STARTED: "BRIDGE_NOT_STARTED",
660
+ BRIDGE_SHUTDOWN: "BRIDGE_SHUTDOWN",
661
+ // General errors
662
+ UNKNOWN: "UNKNOWN"
663
+ };
664
+ var ConfigurationError = class _ConfigurationError extends BridgeError {
665
+ setting;
666
+ constructor(message, code, setting, context) {
667
+ super(message, code, {
668
+ ...context,
669
+ setting
670
+ });
671
+ this.name = "ConfigurationError";
672
+ this.setting = setting;
673
+ }
674
+ static missing(setting) {
675
+ return new _ConfigurationError(
676
+ `Missing required configuration: '${setting}'`,
677
+ ErrorCodes.CONFIG_MISSING,
678
+ setting
679
+ );
680
+ }
681
+ static invalid(setting, reason, value) {
682
+ return new _ConfigurationError(
683
+ `Invalid configuration for '${setting}': ${reason}`,
684
+ ErrorCodes.CONFIG_INVALID,
685
+ setting,
686
+ { value }
687
+ );
688
+ }
689
+ static parseError(filePath, error) {
690
+ return new _ConfigurationError(
691
+ `Failed to parse configuration file '${filePath}': ${error.message}`,
692
+ ErrorCodes.CONFIG_PARSE_ERROR,
693
+ void 0,
694
+ { filePath, originalError: error.message }
695
+ );
696
+ }
697
+ };
698
+ var ConnectionError = class _ConnectionError extends BridgeError {
699
+ url;
700
+ host;
701
+ port;
702
+ constructor(message, code = ErrorCodes.CONNECTION_FAILED, options) {
703
+ super(message, code, {
704
+ url: options?.url,
705
+ host: options?.host,
706
+ port: options?.port,
707
+ cause: options?.cause?.message
708
+ });
709
+ this.name = "ConnectionError";
710
+ this.url = options?.url;
711
+ this.host = options?.host;
712
+ this.port = options?.port;
713
+ if (options?.cause) {
714
+ this.cause = options.cause;
715
+ }
716
+ }
717
+ static refused(url) {
718
+ return new _ConnectionError(
719
+ `Connection refused to ${url}. Ensure the bridge is running and accepting connections.`,
720
+ ErrorCodes.CONNECTION_REFUSED,
721
+ { url }
722
+ );
723
+ }
724
+ static timeout(url, timeoutMs) {
725
+ return new _ConnectionError(
726
+ `Connection to ${url} timed out after ${timeoutMs}ms. Check network connectivity and firewall settings.`,
727
+ ErrorCodes.CONNECTION_TIMEOUT,
728
+ { url }
729
+ );
730
+ }
731
+ static closed(url, reason) {
732
+ const message = reason ? `Connection to ${url} closed: ${reason}` : `Connection to ${url} closed unexpectedly`;
733
+ return new _ConnectionError(message, ErrorCodes.CONNECTION_CLOSED, { url });
734
+ }
735
+ static notConnected() {
736
+ return new _ConnectionError(
737
+ "Not connected to any peer. Call connect() first.",
738
+ ErrorCodes.NOT_CONNECTED
739
+ );
740
+ }
741
+ static alreadyConnected() {
742
+ return new _ConnectionError(
743
+ "Already connected. Disconnect first to establish a new connection.",
744
+ ErrorCodes.ALREADY_CONNECTED
745
+ );
746
+ }
747
+ };
748
+ var PeerError = class _PeerError extends BridgeError {
749
+ peerId;
750
+ constructor(message, code, peerId) {
751
+ super(message, code, { peerId });
752
+ this.name = "PeerError";
753
+ this.peerId = peerId;
754
+ }
755
+ static notFound(peerId) {
756
+ return new _PeerError(
757
+ `Peer '${peerId}' not found. The peer may have disconnected.`,
758
+ ErrorCodes.PEER_NOT_FOUND,
759
+ peerId
760
+ );
761
+ }
762
+ static noPeersConnected() {
763
+ return new _PeerError(
764
+ "No peers are connected. Wait for a peer to connect or call connectToPeer() first.",
765
+ ErrorCodes.NO_PEERS_CONNECTED
766
+ );
767
+ }
768
+ static disconnected(peerId) {
769
+ return new _PeerError(
770
+ `Peer '${peerId}' has disconnected.`,
771
+ ErrorCodes.PEER_DISCONNECTED,
772
+ peerId
773
+ );
774
+ }
775
+ };
776
+ var TaskError = class _TaskError extends BridgeError {
777
+ taskId;
778
+ constructor(message, code, taskId, context) {
779
+ super(message, code, { ...context, taskId });
780
+ this.name = "TaskError";
781
+ this.taskId = taskId;
782
+ }
783
+ static timeout(taskId, timeoutMs) {
784
+ return new _TaskError(
785
+ `Task '${taskId}' timed out after ${timeoutMs}ms. Consider increasing the task timeout or breaking the task into smaller pieces.`,
786
+ ErrorCodes.TASK_TIMEOUT,
787
+ taskId,
788
+ { timeoutMs }
789
+ );
790
+ }
791
+ static failed(taskId, reason) {
792
+ return new _TaskError(
793
+ `Task '${taskId}' failed: ${reason}`,
794
+ ErrorCodes.TASK_FAILED,
795
+ taskId
796
+ );
797
+ }
798
+ static noHandler() {
799
+ return new _TaskError(
800
+ "No task handler registered. Register a handler with onTaskReceived() before delegating tasks.",
801
+ ErrorCodes.NO_TASK_HANDLER
802
+ );
803
+ }
804
+ };
805
+ var ContextError = class _ContextError extends BridgeError {
806
+ constructor(message, code, context) {
807
+ super(message, code, context);
808
+ this.name = "ContextError";
809
+ }
810
+ static timeout(timeoutMs) {
811
+ return new _ContextError(
812
+ `Context request timed out after ${timeoutMs}ms. The peer may be processing a large amount of data.`,
813
+ ErrorCodes.CONTEXT_TIMEOUT,
814
+ { timeoutMs }
815
+ );
816
+ }
817
+ static syncFailed(reason) {
818
+ return new _ContextError(
819
+ `Context synchronization failed: ${reason}`,
820
+ ErrorCodes.CONTEXT_SYNC_FAILED
821
+ );
822
+ }
823
+ static snapshotNotFound(snapshotId) {
824
+ return new _ContextError(
825
+ `Snapshot '${snapshotId}' not found. It may have expired or never existed.`,
826
+ ErrorCodes.SNAPSHOT_NOT_FOUND,
827
+ { snapshotId }
828
+ );
829
+ }
830
+ };
831
+ var ProtocolError = class _ProtocolError extends BridgeError {
832
+ constructor(message, code = ErrorCodes.INVALID_MESSAGE, context) {
833
+ super(message, code, context);
834
+ this.name = "ProtocolError";
835
+ }
836
+ static invalidMessage(reason, data) {
837
+ return new _ProtocolError(
838
+ `Invalid message: ${reason}`,
839
+ ErrorCodes.INVALID_MESSAGE,
840
+ { data: data ? String(data).substring(0, 200) : void 0 }
841
+ );
842
+ }
843
+ static serializationError(error) {
844
+ return new _ProtocolError(
845
+ `Message serialization failed: ${error.message}`,
846
+ ErrorCodes.SERIALIZATION_ERROR,
847
+ { originalError: error.message }
848
+ );
849
+ }
850
+ };
851
+ var BridgeLifecycleError = class _BridgeLifecycleError extends BridgeError {
852
+ constructor(message, code) {
853
+ super(message, code);
854
+ this.name = "BridgeLifecycleError";
855
+ }
856
+ static alreadyStarted() {
857
+ return new _BridgeLifecycleError(
858
+ "Bridge is already started. Call stop() before starting again.",
859
+ ErrorCodes.BRIDGE_ALREADY_STARTED
860
+ );
861
+ }
862
+ static notStarted() {
863
+ return new _BridgeLifecycleError(
864
+ "Bridge is not started. Call start() first.",
865
+ ErrorCodes.BRIDGE_NOT_STARTED
866
+ );
867
+ }
868
+ static shuttingDown() {
869
+ return new _BridgeLifecycleError(
870
+ "Bridge is shutting down.",
871
+ ErrorCodes.BRIDGE_SHUTDOWN
872
+ );
873
+ }
874
+ };
875
+ function formatErrorForLogging(error) {
876
+ if (error instanceof BridgeError) {
877
+ return {
878
+ message: error.message,
879
+ code: error.code,
880
+ context: error.context,
881
+ stack: error.stack
882
+ };
883
+ }
884
+ if (error instanceof Error) {
885
+ return {
886
+ message: error.message,
887
+ stack: error.stack
888
+ };
889
+ }
890
+ return {
891
+ message: String(error)
892
+ };
893
+ }
894
+ function wrapError(error, context) {
895
+ if (error instanceof BridgeError) {
896
+ return new BridgeError(
897
+ `${context}: ${error.message}`,
898
+ error.code,
899
+ error.context
900
+ );
901
+ }
902
+ const message = error instanceof Error ? error.message : String(error);
903
+ return new BridgeError(
904
+ `${context}: ${message}`,
905
+ ErrorCodes.UNKNOWN,
906
+ { originalError: message }
907
+ );
908
+ }
909
+ function isErrorCode(error, code) {
910
+ return error instanceof BridgeError && error.code === code;
911
+ }
912
+
913
+ // src/index.ts
914
+ var VERSION = "0.1.0";
915
+ export {
916
+ ArtifactSchema,
917
+ Bridge,
918
+ BridgeError,
919
+ BridgeLifecycleError,
920
+ BridgeMessageSchema,
921
+ ConfigurationError,
922
+ ConnectionError,
923
+ ConnectionState,
924
+ ContextError,
925
+ ContextManager,
926
+ ContextSchema,
927
+ DEFAULT_CONFIG,
928
+ DirectoryTreeSchema,
929
+ ErrorCodes,
930
+ FileChunkSchema,
931
+ MessageType,
932
+ PeerError,
933
+ ProtocolError,
934
+ TaskError,
935
+ TaskRequestSchema,
936
+ TaskResultSchema,
937
+ VERSION,
938
+ WebSocketTransport,
939
+ buildDirectoryTree,
940
+ createChildLogger,
941
+ createContextRequestMessage,
942
+ createContextSyncMessage,
943
+ createLogger,
944
+ createMessage,
945
+ createNotificationMessage,
946
+ createTaskDelegateMessage,
947
+ createTaskResponseMessage,
948
+ createTransport,
949
+ deserializeMessage,
950
+ detectEnvironment,
951
+ discoverAllPeers,
952
+ discoverDdevProjects,
953
+ discoverDockerPeers,
954
+ discoverDocksalProjects,
955
+ discoverLandoProjects,
956
+ estimateTokens,
957
+ formatErrorForLogging,
958
+ getDdevProjectInfo,
959
+ getDefaultConfig,
960
+ getDocksalProjectInfo,
961
+ getHostGateway,
962
+ getLandoProjectInfo,
963
+ isDockerAvailable,
964
+ isErrorCode,
965
+ loadConfig,
966
+ loadConfigSync,
967
+ mergeConfig,
968
+ parseDockerLabels,
969
+ safeDeserializeMessage,
970
+ safeValidateMessage,
971
+ serializeMessage,
972
+ truncateToTokenLimit,
973
+ validateMessage,
974
+ wrapError
975
+ };
976
+ //# sourceMappingURL=index.js.map