@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/LICENSE +21 -0
- package/README.md +690 -0
- package/dist/chunk-BRH476VK.js +1993 -0
- package/dist/chunk-BRH476VK.js.map +1 -0
- package/dist/cli.d.ts +40 -0
- package/dist/cli.js +844 -0
- package/dist/cli.js.map +1 -0
- package/dist/index.d.ts +1848 -0
- package/dist/index.js +976 -0
- package/dist/index.js.map +1 -0
- package/package.json +71 -0
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
|