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
@@ -7,7 +7,6 @@
7
7
  * - Command injection via branch names
8
8
  * - DoS via unbounded input sizes
9
9
  */
10
- import { Result } from 'better-result';
11
10
 
12
11
  // ─────────────────────────────────────────────────────────────────────────────
13
12
  // Regex Patterns
@@ -73,7 +72,13 @@ const ok = (): ValidationResult => ({ valid: true });
73
72
  const okWithValue = <T>(value: T): ValidationResultWithValue<T> => ({ valid: true, value });
74
73
  const fail = (error: string): ValidationResult => ({ valid: false, error });
75
74
  const failWithValue = <T>(error: string): ValidationResultWithValue<T> => ({ valid: false, error });
76
- const parseUrl = (value: string) => Result.try(() => new URL(value));
75
+ const parseUrl = (value: string) => {
76
+ try {
77
+ return new URL(value);
78
+ } catch {
79
+ return null;
80
+ }
81
+ };
77
82
  const isWsl = () =>
78
83
  process.platform === 'linux' &&
79
84
  (Boolean(process.env.WSL_DISTRO_NAME) ||
@@ -173,10 +178,7 @@ export const validateBranchName = (branch: string): ValidationResult => {
173
178
  * Non-GitHub URLs are returned unchanged.
174
179
  */
175
180
  export const normalizeGitHubUrl = (url: string): string => {
176
- const parsed = parseUrl(url).match({
177
- ok: (value) => value,
178
- err: () => null
179
- });
181
+ const parsed = parseUrl(url);
180
182
  if (!parsed) return url;
181
183
 
182
184
  const hostname = parsed.hostname.toLowerCase();
@@ -221,10 +223,7 @@ export const validateGitUrl = (url: string): ValidationResultWithValue<string> =
221
223
  return failWithValue('Git URL cannot be empty');
222
224
  }
223
225
 
224
- const parsed = parseUrl(url).match({
225
- ok: (value) => value,
226
- err: () => null
227
- });
226
+ const parsed = parseUrl(url);
228
227
  if (!parsed) return failWithValue(`Invalid URL format: "${url}"`);
229
228
 
230
229
  // Only allow HTTPS protocol
@@ -321,10 +320,13 @@ const toNpmReference = (parsed: { packageName: string; version?: string }): Pars
321
320
  };
322
321
 
323
322
  const safeDecodeUriComponent = (value: string): string | null =>
324
- Result.try(() => decodeURIComponent(value)).match({
325
- ok: (decoded) => decoded,
326
- err: () => null
327
- });
323
+ (() => {
324
+ try {
325
+ return decodeURIComponent(value);
326
+ } catch {
327
+ return null;
328
+ }
329
+ })();
328
330
 
329
331
  const parseNpmSpecReference = (reference: string): ParsedNpmReference | null => {
330
332
  if (!reference.startsWith('npm:')) return null;
@@ -339,10 +341,7 @@ const parseNpmSpecReference = (reference: string): ParsedNpmReference | null =>
339
341
  };
340
342
 
341
343
  const parseNpmUrlReference = (reference: string): ParsedNpmReference | null => {
342
- const parsedUrl = parseUrl(reference).match({
343
- ok: (value) => value,
344
- err: () => null
345
- });
344
+ const parsedUrl = parseUrl(reference);
346
345
  if (!parsedUrl) return null;
347
346
  if (parsedUrl.protocol !== 'https:') return null;
348
347
 
@@ -375,10 +374,7 @@ export const parseNpmReference = (reference: string): ParsedNpmReference | null
375
374
  parseNpmSpecReference(reference) ?? parseNpmUrlReference(reference);
376
375
 
377
376
  const isNpmPackageUrl = (reference: string): boolean => {
378
- const parsedUrl = parseUrl(reference).match({
379
- ok: (value) => value,
380
- err: () => null
381
- });
377
+ const parsedUrl = parseUrl(reference);
382
378
  if (!parsedUrl || parsedUrl.protocol !== 'https:') return false;
383
379
  const hostname = parsedUrl.hostname.toLowerCase();
384
380
  if (hostname !== 'npmjs.com' && hostname !== 'www.npmjs.com') return false;
@@ -4,11 +4,23 @@ import { promises as fs } from 'node:fs';
4
4
  import os from 'node:os';
5
5
  import path from 'node:path';
6
6
 
7
- import { GlobTool } from '../tools/glob.ts';
8
- import { GrepTool } from '../tools/grep.ts';
9
- import { ListTool } from '../tools/list.ts';
10
- import { ReadTool } from '../tools/read.ts';
11
- import { VirtualFs } from './virtual-fs.ts';
7
+ import { executeGlobTool } from '../tools/glob.ts';
8
+ import { executeGrepTool } from '../tools/grep.ts';
9
+ import { executeListTool } from '../tools/list.ts';
10
+ import { executeReadTool } from '../tools/read.ts';
11
+ import {
12
+ createVirtualFs,
13
+ disposeVirtualFs,
14
+ existsInVirtualFs,
15
+ importDirectoryIntoVirtualFs,
16
+ listVirtualFsFilesRecursive,
17
+ mkdirVirtualFs,
18
+ readVirtualFsFile,
19
+ readdirVirtualFs,
20
+ rmVirtualFs,
21
+ statVirtualFs,
22
+ writeVirtualFsFile
23
+ } from './virtual-fs.ts';
12
24
 
13
25
  const posix = path.posix;
14
26
 
@@ -16,7 +28,7 @@ const createRoot = () => `/vfs-test-${randomUUID()}`;
16
28
 
17
29
  const cleanupVirtual = async (root: string, vfsId?: string) => {
18
30
  try {
19
- await VirtualFs.rm(root, { recursive: true, force: true }, vfsId);
31
+ await rmVirtualFs(root, { recursive: true, force: true }, vfsId);
20
32
  } catch {
21
33
  // ignore cleanup failures
22
34
  }
@@ -25,37 +37,37 @@ const cleanupVirtual = async (root: string, vfsId?: string) => {
25
37
  describe('VirtualFs (just-bash)', () => {
26
38
  it('supports basic in-memory file operations', async () => {
27
39
  const root = createRoot();
28
- const vfsId = VirtualFs.create();
40
+ const vfsId = createVirtualFs();
29
41
  try {
30
42
  const dir = posix.join(root, 'dir');
31
43
  const file = posix.join(dir, 'hello.txt');
32
44
 
33
- await VirtualFs.mkdir(dir, { recursive: true }, vfsId);
34
- await VirtualFs.writeFile(file, 'Hello virtual', vfsId);
45
+ await mkdirVirtualFs(dir, { recursive: true }, vfsId);
46
+ await writeVirtualFsFile(file, 'Hello virtual', vfsId);
35
47
 
36
- const text = await VirtualFs.readFile(file, vfsId);
48
+ const text = await readVirtualFsFile(file, vfsId);
37
49
  expect(text).toBe('Hello virtual');
38
50
 
39
- const fileStat = await VirtualFs.stat(file, vfsId);
51
+ const fileStat = await statVirtualFs(file, vfsId);
40
52
  expect(fileStat.isFile).toBe(true);
41
53
 
42
- const dirStat = await VirtualFs.stat(dir, vfsId);
54
+ const dirStat = await statVirtualFs(dir, vfsId);
43
55
  expect(dirStat.isDirectory).toBe(true);
44
56
 
45
- const entries = await VirtualFs.readdir(dir, vfsId);
57
+ const entries = await readdirVirtualFs(dir, vfsId);
46
58
  expect(entries.some((entry) => entry.name === 'hello.txt')).toBe(true);
47
59
 
48
- const files = await VirtualFs.listFilesRecursive(root, vfsId);
60
+ const files = await listVirtualFsFilesRecursive(root, vfsId);
49
61
  expect(files).toContain(file);
50
62
  } finally {
51
63
  await cleanupVirtual(root, vfsId);
52
- VirtualFs.dispose(vfsId);
64
+ disposeVirtualFs(vfsId);
53
65
  }
54
66
  });
55
67
 
56
68
  it('imports from disk and works with virtual tools', async () => {
57
69
  const root = createRoot();
58
- const vfsId = VirtualFs.create();
70
+ const vfsId = createVirtualFs();
59
71
  const sourceDir = await fs.mkdtemp(path.join(os.tmpdir(), 'btca-just-bash-'));
60
72
  try {
61
73
  const resourceName = `repo-${randomUUID()}`;
@@ -68,9 +80,9 @@ describe('VirtualFs (just-bash)', () => {
68
80
  await fs.mkdir(path.join(sourceDir, '.git'), { recursive: true });
69
81
  await fs.writeFile(path.join(sourceDir, '.git', 'HEAD'), 'ref: refs/heads/main');
70
82
 
71
- await VirtualFs.mkdir(collectionPath, { recursive: true }, vfsId);
83
+ await mkdirVirtualFs(collectionPath, { recursive: true }, vfsId);
72
84
 
73
- await VirtualFs.importDirectoryFromDisk({
85
+ await importDirectoryIntoVirtualFs({
74
86
  sourcePath: sourceDir,
75
87
  destinationPath: resourcePath,
76
88
  vfsId,
@@ -82,26 +94,26 @@ describe('VirtualFs (just-bash)', () => {
82
94
  }
83
95
  });
84
96
 
85
- expect(await VirtualFs.exists(posix.join(resourcePath, 'README.md'), vfsId)).toBe(true);
86
- expect(await VirtualFs.exists(posix.join(resourcePath, '.git', 'HEAD'), vfsId)).toBe(false);
97
+ expect(await existsInVirtualFs(posix.join(resourcePath, 'README.md'), vfsId)).toBe(true);
98
+ expect(await existsInVirtualFs(posix.join(resourcePath, '.git', 'HEAD'), vfsId)).toBe(false);
87
99
 
88
100
  const context = { basePath: collectionPath, vfsId };
89
101
 
90
- const listResult = await ListTool.execute({ path: '.' }, context);
102
+ const listResult = await executeListTool({ path: '.' }, context);
91
103
  expect(listResult.metadata.entries.some((entry) => entry.name === resourceName)).toBe(true);
92
104
 
93
- const readResult = await ReadTool.execute({ path: `${resourceName}/README.md` }, context);
105
+ const readResult = await executeReadTool({ path: `${resourceName}/README.md` }, context);
94
106
  expect(readResult.output).toContain('needle');
95
107
 
96
- const globResult = await GlobTool.execute({ pattern: '**/*.ts' }, context);
108
+ const globResult = await executeGlobTool({ pattern: '**/*.ts' }, context);
97
109
  expect(globResult.output.split('\n')).toContain(`${resourceName}/src/index.ts`);
98
110
 
99
- const grepResult = await GrepTool.execute({ pattern: 'needle' }, context);
111
+ const grepResult = await executeGrepTool({ pattern: 'needle' }, context);
100
112
  expect(grepResult.output).toContain(`${resourceName}/README.md`);
101
113
  } finally {
102
114
  await cleanupVirtual(root, vfsId);
103
115
  await fs.rm(sourceDir, { recursive: true, force: true });
104
- VirtualFs.dispose(vfsId);
116
+ disposeVirtualFs(vfsId);
105
117
  }
106
118
  });
107
119
  });