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.
Files changed (43) hide show
  1. package/package.json +3 -3
  2. package/src/agent/agent.test.ts +31 -24
  3. package/src/agent/index.ts +8 -2
  4. package/src/agent/loop.ts +303 -346
  5. package/src/agent/service.ts +252 -233
  6. package/src/agent/types.ts +2 -2
  7. package/src/collections/index.ts +2 -1
  8. package/src/collections/service.ts +352 -345
  9. package/src/config/config.test.ts +3 -1
  10. package/src/config/index.ts +615 -727
  11. package/src/config/remote.ts +214 -369
  12. package/src/context/index.ts +6 -12
  13. package/src/context/transaction.ts +23 -30
  14. package/src/effect/errors.ts +45 -0
  15. package/src/effect/layers.ts +26 -0
  16. package/src/effect/runtime.ts +19 -0
  17. package/src/effect/services.ts +154 -0
  18. package/src/index.ts +291 -369
  19. package/src/metrics/index.ts +46 -46
  20. package/src/pricing/models-dev.ts +104 -106
  21. package/src/providers/auth.ts +159 -200
  22. package/src/providers/index.ts +19 -2
  23. package/src/providers/model.ts +115 -135
  24. package/src/providers/openai.ts +3 -3
  25. package/src/resources/impls/git.ts +123 -146
  26. package/src/resources/impls/npm.test.ts +16 -5
  27. package/src/resources/impls/npm.ts +66 -75
  28. package/src/resources/index.ts +6 -1
  29. package/src/resources/schema.ts +7 -6
  30. package/src/resources/service.test.ts +13 -12
  31. package/src/resources/service.ts +153 -112
  32. package/src/stream/index.ts +1 -1
  33. package/src/stream/service.test.ts +5 -5
  34. package/src/stream/service.ts +282 -293
  35. package/src/tools/glob.ts +126 -141
  36. package/src/tools/grep.ts +205 -210
  37. package/src/tools/index.ts +8 -4
  38. package/src/tools/list.ts +118 -140
  39. package/src/tools/read.ts +209 -235
  40. package/src/tools/virtual-sandbox.ts +91 -83
  41. package/src/validation/index.ts +18 -22
  42. package/src/vfs/virtual-fs.test.ts +37 -25
  43. package/src/vfs/virtual-fs.ts +218 -216
@@ -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
- export namespace VirtualFs {
33
- const defaultId = 'default';
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
- const normalize = (filePath: string): string => {
46
- const resolved = posix.resolve('/', filePath);
47
- return resolved === '' ? '/' : resolved;
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
- const toStat = (stat: MaybeStat): VfsStat => {
51
- const isFile = typeof stat.isFile === 'function' ? stat.isFile() : Boolean(stat.isFile);
52
- const isDirectory =
53
- typeof stat.isDirectory === 'function' ? stat.isDirectory() : Boolean(stat.isDirectory);
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
- export const create = () => {
69
- const vfsId = randomUUID();
70
- instances.set(vfsId, new InMemoryFs());
71
- return vfsId;
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
- export const has = (vfsId?: string) => {
75
- const key = vfsId ?? defaultId;
76
- return instances.has(key);
77
- };
66
+ export const createVirtualFs = () => {
67
+ const vfsId = randomUUID();
68
+ instances.set(vfsId, new InMemoryFs());
69
+ return vfsId;
70
+ };
78
71
 
79
- export const reset = (vfsId?: string) => {
80
- const key = vfsId ?? defaultId;
81
- instances.set(key, new InMemoryFs());
82
- };
72
+ export const hasVirtualFs = (vfsId?: string) => {
73
+ const key = vfsId ?? defaultId;
74
+ return instances.has(key);
75
+ };
83
76
 
84
- export const dispose = (vfsId?: string) => {
85
- const key = vfsId ?? defaultId;
86
- instances.delete(key);
87
- };
77
+ export const resetVirtualFs = (vfsId?: string) => {
78
+ const key = vfsId ?? defaultId;
79
+ instances.set(key, new InMemoryFs());
80
+ };
88
81
 
89
- export const disposeAll = () => {
90
- instances.clear();
91
- instances.set(defaultId, new InMemoryFs());
92
- };
82
+ export const disposeVirtualFs = (vfsId?: string) => {
83
+ const key = vfsId ?? defaultId;
84
+ instances.delete(key);
85
+ };
93
86
 
94
- export function resolve(filePath: string): string {
95
- return normalize(filePath);
96
- }
87
+ export const disposeAllVirtualFs = () => {
88
+ instances.clear();
89
+ instances.set(defaultId, new InMemoryFs());
90
+ };
97
91
 
98
- export async function mkdir(filePath: string, options?: { recursive?: boolean }, vfsId?: string) {
99
- await getInstance(vfsId).mkdir(normalize(filePath), options);
100
- }
92
+ export const resolveVirtualFsPath = (filePath: string): string => normalizeVfsPath(filePath);
101
93
 
102
- export async function rm(
103
- filePath: string,
104
- options?: { recursive?: boolean; force?: boolean },
105
- vfsId?: string
106
- ) {
107
- await getInstance(vfsId).rm(normalize(filePath), options);
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
- export async function exists(filePath: string, vfsId?: string) {
111
- const result = await Result.tryPromise(() => getInstance(vfsId).stat(normalize(filePath)));
112
- return result.match({
113
- ok: () => true,
114
- err: () => false
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
- export async function stat(filePath: string, vfsId?: string) {
119
- const stats = (await getInstance(vfsId).stat(normalize(filePath))) as MaybeStat;
120
- return toStat(stats);
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
- export async function readdir(filePath: string, vfsId?: string) {
124
- const resolvedResult = await Result.tryPromise(() => realpath(filePath, vfsId));
125
- const resolved = resolvedResult.match({
126
- ok: (value) => value,
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
- export async function readFile(filePath: string, vfsId?: string) {
148
- return getInstance(vfsId).readFile(normalize(filePath));
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
- export async function readFileBuffer(filePath: string, vfsId?: string) {
152
- return getInstance(vfsId).readFileBuffer(normalize(filePath));
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
- export async function writeFile(
156
- filePath: string,
157
- data: string | ArrayBufferView,
158
- vfsId?: string
159
- ) {
160
- const content =
161
- typeof data === 'string'
162
- ? data
163
- : new Uint8Array(data.buffer as ArrayBufferLike, data.byteOffset, data.byteLength);
164
- await getInstance(vfsId).writeFile(normalize(filePath), content);
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
- export async function symlink(targetPath: string, linkPath: string, vfsId?: string) {
168
- const target = posix.isAbsolute(targetPath) ? normalize(targetPath) : targetPath;
169
- await getInstance(vfsId).symlink(target, normalize(linkPath));
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
- export async function realpath(filePath: string, vfsId?: string) {
173
- const resolved = normalize(filePath);
174
- const maybe = getInstance(vfsId) as unknown as {
175
- realpath?: (path: string) => Promise<string>;
176
- };
177
- if (typeof maybe.realpath === 'function') {
178
- return maybe.realpath(resolved);
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
- export async function listFilesRecursive(rootPath: string, vfsId?: string) {
184
- const files: string[] = [];
185
- const stack: string[] = [normalize(rootPath)];
186
-
187
- while (stack.length > 0) {
188
- const current = stack.pop();
189
- if (!current) continue;
190
- const entriesResult = await Result.tryPromise(() => readdir(current, vfsId));
191
- const entries = entriesResult.match({
192
- ok: (value) => value,
193
- err: () => []
194
- });
195
- if (entries.length === 0) continue;
196
-
197
- for (const entry of entries) {
198
- const entryPath = normalize(posix.join(current, entry.name));
199
- if (entry.isDirectory) {
200
- stack.push(entryPath);
201
- } else if (entry.isFile) {
202
- files.push(entryPath);
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
- export async function importDirectoryFromDisk(args: {
211
- sourcePath: string;
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
- if (dirent.isSymbolicLink()) {
246
- const targetResult = await Result.tryPromise(() => fs.readlink(srcPath));
247
- const target = targetResult.match({
248
- ok: (value) => value,
249
- err: () => null
250
- });
251
- if (target) {
252
- const linkResult = await Result.tryPromise(() => symlink(target, destPath, vfsId));
253
- linkResult.match({
254
- ok: () => undefined,
255
- err: () => undefined
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
- if (dirent.isFile()) {
262
- const bufferResult = await Result.tryPromise(() => fs.readFile(srcPath));
263
- const buffer = bufferResult.match({
264
- ok: (value) => value,
265
- err: () => null
266
- });
267
- if (buffer) {
268
- const writeResult = await Result.tryPromise(() => writeFile(destPath, buffer, vfsId));
269
- writeResult.match({
270
- ok: () => undefined,
271
- err: () => undefined
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
- await mkdir(dest, { recursive: true }, vfsId);
279
- await walk(base);
280
- }
281
- }
281
+ await mkdirVirtualFs(dest, { recursive: true }, vfsId);
282
+ await walk(base);
283
+ };