btca-server 1.0.961 → 2.0.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/package.json +3 -3
- package/src/agent/agent.test.ts +31 -24
- package/src/agent/index.ts +8 -2
- package/src/agent/loop.ts +303 -346
- package/src/agent/service.ts +252 -233
- package/src/agent/types.ts +2 -2
- package/src/collections/index.ts +2 -1
- package/src/collections/service.ts +352 -345
- package/src/config/config.test.ts +3 -1
- package/src/config/index.ts +615 -727
- package/src/config/remote.ts +214 -369
- package/src/context/index.ts +6 -12
- package/src/context/transaction.ts +23 -30
- package/src/effect/errors.ts +45 -0
- package/src/effect/layers.ts +26 -0
- package/src/effect/runtime.ts +19 -0
- package/src/effect/services.ts +154 -0
- package/src/index.ts +291 -369
- package/src/metrics/index.ts +46 -46
- package/src/pricing/models-dev.ts +104 -106
- package/src/providers/auth.ts +159 -200
- package/src/providers/index.ts +19 -2
- package/src/providers/model.ts +115 -135
- package/src/providers/openai.ts +3 -3
- package/src/resources/impls/git.ts +123 -146
- package/src/resources/impls/npm.test.ts +16 -5
- package/src/resources/impls/npm.ts +66 -75
- package/src/resources/index.ts +6 -1
- package/src/resources/schema.ts +7 -6
- package/src/resources/service.test.ts +13 -12
- package/src/resources/service.ts +153 -112
- package/src/stream/index.ts +1 -1
- package/src/stream/service.test.ts +5 -5
- package/src/stream/service.ts +282 -293
- package/src/tools/glob.ts +126 -141
- package/src/tools/grep.ts +205 -210
- package/src/tools/index.ts +8 -4
- package/src/tools/list.ts +118 -140
- package/src/tools/read.ts +209 -235
- package/src/tools/virtual-sandbox.ts +91 -83
- package/src/validation/index.ts +18 -22
- package/src/vfs/virtual-fs.test.ts +37 -25
- package/src/vfs/virtual-fs.ts +218 -216
package/src/vfs/virtual-fs.ts
CHANGED
|
@@ -3,7 +3,6 @@ import * as fs from 'node:fs/promises';
|
|
|
3
3
|
import type { Dirent } from 'node:fs';
|
|
4
4
|
import * as path from 'node:path';
|
|
5
5
|
|
|
6
|
-
import { Result } from 'better-result';
|
|
7
6
|
import { InMemoryFs } from 'just-bash';
|
|
8
7
|
|
|
9
8
|
const posix = path.posix;
|
|
@@ -29,253 +28,256 @@ type MaybeStat = {
|
|
|
29
28
|
mtimeMs?: number;
|
|
30
29
|
};
|
|
31
30
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
const instances = new Map<string, InMemoryFs>([[defaultId, new InMemoryFs()]]);
|
|
35
|
-
|
|
36
|
-
const getInstance = (vfsId?: string) => {
|
|
37
|
-
const key = vfsId ?? defaultId;
|
|
38
|
-
const existing = instances.get(key);
|
|
39
|
-
if (existing) return existing;
|
|
40
|
-
const created = new InMemoryFs();
|
|
41
|
-
instances.set(key, created);
|
|
42
|
-
return created;
|
|
43
|
-
};
|
|
31
|
+
const defaultId = 'default';
|
|
32
|
+
const instances = new Map<string, InMemoryFs>([[defaultId, new InMemoryFs()]]);
|
|
44
33
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
34
|
+
const getInstance = (vfsId?: string) => {
|
|
35
|
+
const key = vfsId ?? defaultId;
|
|
36
|
+
const existing = instances.get(key);
|
|
37
|
+
if (existing) return existing;
|
|
38
|
+
const created = new InMemoryFs();
|
|
39
|
+
instances.set(key, created);
|
|
40
|
+
return created;
|
|
41
|
+
};
|
|
49
42
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
const mtimeMs =
|
|
55
|
-
typeof stat.mtimeMs === 'number'
|
|
56
|
-
? stat.mtimeMs
|
|
57
|
-
: stat.mtime instanceof Date
|
|
58
|
-
? stat.mtime.getTime()
|
|
59
|
-
: 0;
|
|
60
|
-
return {
|
|
61
|
-
isFile,
|
|
62
|
-
isDirectory,
|
|
63
|
-
size: stat.size ?? 0,
|
|
64
|
-
mtimeMs
|
|
65
|
-
};
|
|
66
|
-
};
|
|
43
|
+
const normalizeVfsPath = (filePath: string): string => {
|
|
44
|
+
const resolved = posix.resolve('/', filePath);
|
|
45
|
+
return resolved === '' ? '/' : resolved;
|
|
46
|
+
};
|
|
67
47
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
48
|
+
const toStat = (stat: MaybeStat): VfsStat => {
|
|
49
|
+
const isFile = typeof stat.isFile === 'function' ? stat.isFile() : Boolean(stat.isFile);
|
|
50
|
+
const isDirectory =
|
|
51
|
+
typeof stat.isDirectory === 'function' ? stat.isDirectory() : Boolean(stat.isDirectory);
|
|
52
|
+
const mtimeMs =
|
|
53
|
+
typeof stat.mtimeMs === 'number'
|
|
54
|
+
? stat.mtimeMs
|
|
55
|
+
: stat.mtime instanceof Date
|
|
56
|
+
? stat.mtime.getTime()
|
|
57
|
+
: 0;
|
|
58
|
+
return {
|
|
59
|
+
isFile,
|
|
60
|
+
isDirectory,
|
|
61
|
+
size: stat.size ?? 0,
|
|
62
|
+
mtimeMs
|
|
72
63
|
};
|
|
64
|
+
};
|
|
73
65
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
66
|
+
export const createVirtualFs = () => {
|
|
67
|
+
const vfsId = randomUUID();
|
|
68
|
+
instances.set(vfsId, new InMemoryFs());
|
|
69
|
+
return vfsId;
|
|
70
|
+
};
|
|
78
71
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
72
|
+
export const hasVirtualFs = (vfsId?: string) => {
|
|
73
|
+
const key = vfsId ?? defaultId;
|
|
74
|
+
return instances.has(key);
|
|
75
|
+
};
|
|
83
76
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
77
|
+
export const resetVirtualFs = (vfsId?: string) => {
|
|
78
|
+
const key = vfsId ?? defaultId;
|
|
79
|
+
instances.set(key, new InMemoryFs());
|
|
80
|
+
};
|
|
88
81
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
82
|
+
export const disposeVirtualFs = (vfsId?: string) => {
|
|
83
|
+
const key = vfsId ?? defaultId;
|
|
84
|
+
instances.delete(key);
|
|
85
|
+
};
|
|
93
86
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
87
|
+
export const disposeAllVirtualFs = () => {
|
|
88
|
+
instances.clear();
|
|
89
|
+
instances.set(defaultId, new InMemoryFs());
|
|
90
|
+
};
|
|
97
91
|
|
|
98
|
-
|
|
99
|
-
await getInstance(vfsId).mkdir(normalize(filePath), options);
|
|
100
|
-
}
|
|
92
|
+
export const resolveVirtualFsPath = (filePath: string): string => normalizeVfsPath(filePath);
|
|
101
93
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
94
|
+
export const mkdirVirtualFs = async (
|
|
95
|
+
filePath: string,
|
|
96
|
+
options?: { recursive?: boolean },
|
|
97
|
+
vfsId?: string
|
|
98
|
+
) => {
|
|
99
|
+
await getInstance(vfsId).mkdir(normalizeVfsPath(filePath), options);
|
|
100
|
+
};
|
|
109
101
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
102
|
+
export const rmVirtualFs = async (
|
|
103
|
+
filePath: string,
|
|
104
|
+
options?: { recursive?: boolean; force?: boolean },
|
|
105
|
+
vfsId?: string
|
|
106
|
+
) => {
|
|
107
|
+
await getInstance(vfsId).rm(normalizeVfsPath(filePath), options);
|
|
108
|
+
};
|
|
117
109
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
110
|
+
export const existsInVirtualFs = async (filePath: string, vfsId?: string) => {
|
|
111
|
+
try {
|
|
112
|
+
await getInstance(vfsId).stat(normalizeVfsPath(filePath));
|
|
113
|
+
return true;
|
|
114
|
+
} catch {
|
|
115
|
+
return false;
|
|
121
116
|
}
|
|
117
|
+
};
|
|
122
118
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
err: () => normalize(filePath)
|
|
128
|
-
});
|
|
129
|
-
const entries = (await getInstance(vfsId).readdir(resolved)) as string[];
|
|
130
|
-
const result: VfsDirEntry[] = [];
|
|
131
|
-
for (const name of entries) {
|
|
132
|
-
const entryPath = normalize(posix.join(filePath, name));
|
|
133
|
-
const entryStatResult = await Result.tryPromise(() => stat(entryPath, vfsId));
|
|
134
|
-
const entryStat = entryStatResult.match({
|
|
135
|
-
ok: (value) => value,
|
|
136
|
-
err: () => null
|
|
137
|
-
});
|
|
138
|
-
result.push({
|
|
139
|
-
name,
|
|
140
|
-
isFile: entryStat?.isFile ?? false,
|
|
141
|
-
isDirectory: entryStat?.isDirectory ?? false
|
|
142
|
-
});
|
|
143
|
-
}
|
|
144
|
-
return result;
|
|
145
|
-
}
|
|
119
|
+
export const statVirtualFs = async (filePath: string, vfsId?: string) => {
|
|
120
|
+
const stats = (await getInstance(vfsId).stat(normalizeVfsPath(filePath))) as MaybeStat;
|
|
121
|
+
return toStat(stats);
|
|
122
|
+
};
|
|
146
123
|
|
|
147
|
-
|
|
148
|
-
|
|
124
|
+
export const readdirVirtualFs = async (filePath: string, vfsId?: string) => {
|
|
125
|
+
let resolved = normalizeVfsPath(filePath);
|
|
126
|
+
try {
|
|
127
|
+
resolved = await realpathVirtualFs(filePath, vfsId);
|
|
128
|
+
} catch {
|
|
129
|
+
resolved = normalizeVfsPath(filePath);
|
|
149
130
|
}
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
131
|
+
const entries = (await getInstance(vfsId).readdir(resolved)) as string[];
|
|
132
|
+
const result: VfsDirEntry[] = [];
|
|
133
|
+
for (const name of entries) {
|
|
134
|
+
const entryPath = normalizeVfsPath(posix.join(filePath, name));
|
|
135
|
+
let entryStat: VfsStat | null = null;
|
|
136
|
+
try {
|
|
137
|
+
entryStat = await statVirtualFs(entryPath, vfsId);
|
|
138
|
+
} catch {
|
|
139
|
+
entryStat = null;
|
|
140
|
+
}
|
|
141
|
+
result.push({
|
|
142
|
+
name,
|
|
143
|
+
isFile: entryStat?.isFile ?? false,
|
|
144
|
+
isDirectory: entryStat?.isDirectory ?? false
|
|
145
|
+
});
|
|
153
146
|
}
|
|
147
|
+
return result;
|
|
148
|
+
};
|
|
154
149
|
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
)
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
150
|
+
export const readVirtualFsFile = async (filePath: string, vfsId?: string) =>
|
|
151
|
+
getInstance(vfsId).readFile(normalizeVfsPath(filePath));
|
|
152
|
+
|
|
153
|
+
export const readVirtualFsFileBuffer = async (filePath: string, vfsId?: string) =>
|
|
154
|
+
getInstance(vfsId).readFileBuffer(normalizeVfsPath(filePath));
|
|
155
|
+
|
|
156
|
+
export const writeVirtualFsFile = async (
|
|
157
|
+
filePath: string,
|
|
158
|
+
data: string | ArrayBufferView,
|
|
159
|
+
vfsId?: string
|
|
160
|
+
) => {
|
|
161
|
+
const content =
|
|
162
|
+
typeof data === 'string'
|
|
163
|
+
? data
|
|
164
|
+
: new Uint8Array(data.buffer as ArrayBufferLike, data.byteOffset, data.byteLength);
|
|
165
|
+
await getInstance(vfsId).writeFile(normalizeVfsPath(filePath), content);
|
|
166
|
+
};
|
|
166
167
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
168
|
+
export const symlinkVirtualFs = async (targetPath: string, linkPath: string, vfsId?: string) => {
|
|
169
|
+
const target = posix.isAbsolute(targetPath) ? normalizeVfsPath(targetPath) : targetPath;
|
|
170
|
+
await getInstance(vfsId).symlink(target, normalizeVfsPath(linkPath));
|
|
171
|
+
};
|
|
171
172
|
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
}
|
|
180
|
-
return resolved;
|
|
173
|
+
export const realpathVirtualFs = async (filePath: string, vfsId?: string) => {
|
|
174
|
+
const resolved = normalizeVfsPath(filePath);
|
|
175
|
+
const maybe = getInstance(vfsId) as unknown as {
|
|
176
|
+
realpath?: (path: string) => Promise<string>;
|
|
177
|
+
};
|
|
178
|
+
if (typeof maybe.realpath === 'function') {
|
|
179
|
+
return maybe.realpath(resolved);
|
|
181
180
|
}
|
|
181
|
+
return resolved;
|
|
182
|
+
};
|
|
182
183
|
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
184
|
+
export const listVirtualFsFilesRecursive = async (rootPath: string, vfsId?: string) => {
|
|
185
|
+
const files: string[] = [];
|
|
186
|
+
const stack: string[] = [normalizeVfsPath(rootPath)];
|
|
187
|
+
|
|
188
|
+
while (stack.length > 0) {
|
|
189
|
+
const current = stack.pop();
|
|
190
|
+
if (!current) continue;
|
|
191
|
+
let entries: VfsDirEntry[] = [];
|
|
192
|
+
try {
|
|
193
|
+
entries = await readdirVirtualFs(current, vfsId);
|
|
194
|
+
} catch {
|
|
195
|
+
entries = [];
|
|
196
|
+
}
|
|
197
|
+
if (entries.length === 0) continue;
|
|
198
|
+
|
|
199
|
+
for (const entry of entries) {
|
|
200
|
+
const entryPath = normalizeVfsPath(posix.join(current, entry.name));
|
|
201
|
+
if (entry.isDirectory) {
|
|
202
|
+
stack.push(entryPath);
|
|
203
|
+
} else if (entry.isFile) {
|
|
204
|
+
files.push(entryPath);
|
|
204
205
|
}
|
|
205
206
|
}
|
|
206
|
-
|
|
207
|
-
return files;
|
|
208
207
|
}
|
|
209
208
|
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
destinationPath: string;
|
|
213
|
-
ignore?: (relativePath: string) => boolean;
|
|
214
|
-
vfsId?: string;
|
|
215
|
-
}) {
|
|
216
|
-
const base = path.resolve(args.sourcePath);
|
|
217
|
-
const dest = normalize(args.destinationPath);
|
|
218
|
-
const ignore = args.ignore ?? (() => false);
|
|
219
|
-
const vfsId = args.vfsId;
|
|
220
|
-
|
|
221
|
-
const walk = async (currentPath: string): Promise<void> => {
|
|
222
|
-
const relative = path.relative(base, currentPath);
|
|
223
|
-
if (relative && ignore(relative)) return;
|
|
224
|
-
const direntsResult = await Result.tryPromise(() =>
|
|
225
|
-
fs.readdir(currentPath, { withFileTypes: true })
|
|
226
|
-
);
|
|
227
|
-
const dirents = direntsResult.match({
|
|
228
|
-
ok: (value) => value,
|
|
229
|
-
err: () => []
|
|
230
|
-
});
|
|
231
|
-
if (dirents.length === 0) return;
|
|
232
|
-
|
|
233
|
-
for (const dirent of dirents) {
|
|
234
|
-
const srcPath = path.join(currentPath, dirent.name);
|
|
235
|
-
const relPath = path.relative(base, srcPath);
|
|
236
|
-
if (ignore(relPath)) continue;
|
|
237
|
-
const destPath = normalize(posix.join(dest, relPath.split(path.sep).join('/')));
|
|
238
|
-
|
|
239
|
-
if (dirent.isDirectory()) {
|
|
240
|
-
await mkdir(destPath, { recursive: true }, vfsId);
|
|
241
|
-
await walk(srcPath);
|
|
242
|
-
continue;
|
|
243
|
-
}
|
|
209
|
+
return files;
|
|
210
|
+
};
|
|
244
211
|
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
212
|
+
export const importDirectoryIntoVirtualFs = async (args: {
|
|
213
|
+
sourcePath: string;
|
|
214
|
+
destinationPath: string;
|
|
215
|
+
ignore?: (relativePath: string) => boolean;
|
|
216
|
+
vfsId?: string;
|
|
217
|
+
}) => {
|
|
218
|
+
const base = path.resolve(args.sourcePath);
|
|
219
|
+
const dest = normalizeVfsPath(args.destinationPath);
|
|
220
|
+
const ignore = args.ignore ?? (() => false);
|
|
221
|
+
const vfsId = args.vfsId;
|
|
222
|
+
|
|
223
|
+
const walk = async (currentPath: string): Promise<void> => {
|
|
224
|
+
const relative = path.relative(base, currentPath);
|
|
225
|
+
if (relative && ignore(relative)) return;
|
|
226
|
+
let dirents: Dirent[] = [];
|
|
227
|
+
try {
|
|
228
|
+
dirents = await fs.readdir(currentPath, { withFileTypes: true });
|
|
229
|
+
} catch {
|
|
230
|
+
dirents = [];
|
|
231
|
+
}
|
|
232
|
+
if (dirents.length === 0) return;
|
|
233
|
+
|
|
234
|
+
for (const dirent of dirents) {
|
|
235
|
+
const srcPath = path.join(currentPath, dirent.name);
|
|
236
|
+
const relPath = path.relative(base, srcPath);
|
|
237
|
+
if (ignore(relPath)) continue;
|
|
238
|
+
const destPath = normalizeVfsPath(posix.join(dest, relPath.split(path.sep).join('/')));
|
|
239
|
+
|
|
240
|
+
if (dirent.isDirectory()) {
|
|
241
|
+
await mkdirVirtualFs(destPath, { recursive: true }, vfsId);
|
|
242
|
+
await walk(srcPath);
|
|
243
|
+
continue;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
if (dirent.isSymbolicLink()) {
|
|
247
|
+
let target: string | null = null;
|
|
248
|
+
try {
|
|
249
|
+
target = await fs.readlink(srcPath);
|
|
250
|
+
} catch {
|
|
251
|
+
target = null;
|
|
252
|
+
}
|
|
253
|
+
if (target) {
|
|
254
|
+
try {
|
|
255
|
+
await symlinkVirtualFs(target, destPath, vfsId);
|
|
256
|
+
} catch {
|
|
257
|
+
// Ignore invalid symlink entries while importing.
|
|
257
258
|
}
|
|
258
|
-
continue;
|
|
259
259
|
}
|
|
260
|
+
continue;
|
|
261
|
+
}
|
|
260
262
|
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
263
|
+
if (dirent.isFile()) {
|
|
264
|
+
let buffer: Buffer | null = null;
|
|
265
|
+
try {
|
|
266
|
+
buffer = await fs.readFile(srcPath);
|
|
267
|
+
} catch {
|
|
268
|
+
buffer = null;
|
|
269
|
+
}
|
|
270
|
+
if (buffer) {
|
|
271
|
+
try {
|
|
272
|
+
await writeVirtualFsFile(destPath, buffer, vfsId);
|
|
273
|
+
} catch {
|
|
274
|
+
// Ignore individual file write errors while importing.
|
|
273
275
|
}
|
|
274
276
|
}
|
|
275
277
|
}
|
|
276
|
-
}
|
|
278
|
+
}
|
|
279
|
+
};
|
|
277
280
|
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
}
|
|
281
|
+
await mkdirVirtualFs(dest, { recursive: true }, vfsId);
|
|
282
|
+
await walk(base);
|
|
283
|
+
};
|