btca-server 1.0.962 → 2.0.1

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 { promises as fs } from 'node:fs';
3
3
  import { mkdirSync, writeFileSync } from 'node:fs';
4
4
  import os from 'node:os';
5
5
  import path from 'node:path';
6
- import { Result } from 'better-result';
7
6
 
8
7
  import { loadNpmResource } from './npm.ts';
9
8
  import type { BtcaNpmResourceArgs } from '../types.ts';
@@ -209,12 +208,24 @@ describe('NPM Resource', () => {
209
208
  expect(resource.cleanup).toBeDefined();
210
209
 
211
210
  const resourcePath = await resource.getAbsoluteDirectoryPath();
212
- const existsBefore = await Result.tryPromise(() => fs.stat(resourcePath));
213
- expect(existsBefore.isOk()).toBe(true);
211
+ let existsBefore = false;
212
+ try {
213
+ await fs.stat(resourcePath);
214
+ existsBefore = true;
215
+ } catch {
216
+ existsBefore = false;
217
+ }
218
+ expect(existsBefore).toBe(true);
214
219
 
215
220
  await resource.cleanup?.();
216
- const existsAfter = await Result.tryPromise(() => fs.stat(resourcePath));
217
- expect(existsAfter.isOk()).toBe(false);
221
+ let existsAfter = false;
222
+ try {
223
+ await fs.stat(resourcePath);
224
+ existsAfter = true;
225
+ } catch {
226
+ existsAfter = false;
227
+ }
228
+ expect(existsAfter).toBe(false);
218
229
  });
219
230
 
220
231
  it('uses readable fsName aliases for anonymous npm resources', async () => {
@@ -1,8 +1,6 @@
1
1
  import { promises as fs } from 'node:fs';
2
2
  import path from 'node:path';
3
3
 
4
- import { Result } from 'better-result';
5
-
6
4
  import { CommonHints } from '../../errors.ts';
7
5
  import { ResourceError, resourceNameToKey } from '../helpers.ts';
8
6
  import type { BtcaFsResource, BtcaNpmResourceArgs } from '../types.ts';
@@ -42,15 +40,20 @@ type NpmPackageVersion = {
42
40
  };
43
41
 
44
42
  const cleanupDirectory = async (pathToRemove: string) => {
45
- await Result.tryPromise(() => fs.rm(pathToRemove, { recursive: true, force: true }));
43
+ try {
44
+ await fs.rm(pathToRemove, { recursive: true, force: true });
45
+ } catch {
46
+ return;
47
+ }
46
48
  };
47
49
 
48
50
  const directoryExists = async (directoryPath: string) => {
49
- const result = await Result.tryPromise(() => fs.stat(directoryPath));
50
- return result.match({
51
- ok: (stat) => stat.isDirectory(),
52
- err: () => false
53
- });
51
+ try {
52
+ const stat = await fs.stat(directoryPath);
53
+ return stat.isDirectory();
54
+ } catch {
55
+ return false;
56
+ }
54
57
  };
55
58
 
56
59
  const encodePackagePath = (packageName: string) =>
@@ -80,64 +83,61 @@ const resolveRequestedVersion = (packument: NpmPackument, requestedVersion?: str
80
83
  };
81
84
 
82
85
  const fetchJson = async <T>(url: string, resourceName: string): Promise<T> => {
83
- const response = await Result.tryPromise(() =>
84
- fetch(url, {
86
+ let response: Response;
87
+ try {
88
+ response = await fetch(url, {
85
89
  headers: {
86
90
  accept: 'application/json'
87
91
  }
88
- })
89
- );
90
-
91
- if (!Result.isOk(response)) {
92
+ });
93
+ } catch (cause) {
92
94
  throw new ResourceError({
93
95
  message: `Failed to fetch npm metadata for "${resourceName}"`,
94
96
  hint: CommonHints.CHECK_NETWORK,
95
- cause: response.error
97
+ cause
96
98
  });
97
99
  }
98
100
 
99
- if (!response.value.ok) {
101
+ if (!response.ok) {
100
102
  throw new ResourceError({
101
- message: `Failed to fetch npm metadata for "${resourceName}" (${response.value.status})`,
103
+ message: `Failed to fetch npm metadata for "${resourceName}" (${response.status})`,
102
104
  hint:
103
- response.value.status === 404
104
- ? 'Check that the npm package exists.'
105
- : CommonHints.CHECK_NETWORK,
106
- cause: new Error(`Unexpected status ${response.value.status}`)
105
+ response.status === 404 ? 'Check that the npm package exists.' : CommonHints.CHECK_NETWORK,
106
+ cause: new Error(`Unexpected status ${response.status}`)
107
107
  });
108
108
  }
109
109
 
110
- const parsed = await Result.tryPromise(() => response.value.json() as Promise<T>);
111
- if (!Result.isOk(parsed)) {
110
+ try {
111
+ return (await response.json()) as T;
112
+ } catch (cause) {
112
113
  throw new ResourceError({
113
114
  message: `Failed to parse npm metadata for "${resourceName}"`,
114
115
  hint: 'Try again. If the issue persists, the npm registry may be returning malformed data.',
115
- cause: parsed.error
116
+ cause
116
117
  });
117
118
  }
118
-
119
- return parsed.value;
120
119
  };
121
120
 
122
121
  const fetchText = async (url: string, resourceName: string) => {
123
122
  const fallbackContent = (reason: string) =>
124
123
  `<!-- npm package page unavailable for "${resourceName}" (${reason}) -->`;
125
124
 
126
- const response = await Result.tryPromise(() => fetch(url));
127
- if (!Result.isOk(response)) {
125
+ let response: Response;
126
+ try {
127
+ response = await fetch(url);
128
+ } catch {
128
129
  return fallbackContent('request failed');
129
130
  }
130
131
 
131
- if (!response.value.ok) {
132
- return fallbackContent(`status ${response.value.status}`);
132
+ if (!response.ok) {
133
+ return fallbackContent(`status ${response.status}`);
133
134
  }
134
135
 
135
- const textResult = await Result.tryPromise(() => response.value.text());
136
- if (!Result.isOk(textResult)) {
136
+ try {
137
+ return await response.text();
138
+ } catch {
137
139
  return fallbackContent('response read failed');
138
140
  }
139
-
140
- return textResult.value;
141
141
  };
142
142
 
143
143
  const readProcessOutput = async (stream: ReadableStream<Uint8Array> | null) => {
@@ -243,18 +243,12 @@ const formatResourceOverview = (args: {
243
243
  };
244
244
 
245
245
  const readCacheMeta = async (localPath: string): Promise<NpmCacheMeta | null> => {
246
- const result = await Result.gen(async function* () {
247
- const content = yield* Result.await(
248
- Result.tryPromise(() => Bun.file(path.join(localPath, NPM_CACHE_META_FILE)).text())
249
- );
250
- const parsed = yield* Result.try(() => JSON.parse(content) as NpmCacheMeta);
251
- return Result.ok(parsed);
252
- });
253
-
254
- return result.match({
255
- ok: (value) => value,
256
- err: () => null
257
- });
246
+ try {
247
+ const content = await Bun.file(path.join(localPath, NPM_CACHE_META_FILE)).text();
248
+ return JSON.parse(content) as NpmCacheMeta;
249
+ } catch {
250
+ return null;
251
+ }
258
252
  };
259
253
 
260
254
  const shouldReuseCache = async (
@@ -282,19 +276,18 @@ const installPackageFiles = async (args: {
282
276
  const installDirectory = path.join(args.localPath, NPM_INSTALL_STAGING_DIR);
283
277
  const packagePath = path.join(installDirectory, 'node_modules', ...args.packageName.split('/'));
284
278
 
285
- const createInstallDirectory = await Result.tryPromise(() =>
286
- fs.mkdir(installDirectory, { recursive: true })
287
- );
288
- if (!Result.isOk(createInstallDirectory)) {
279
+ try {
280
+ await fs.mkdir(installDirectory, { recursive: true });
281
+ } catch (cause) {
289
282
  throw new ResourceError({
290
283
  message: `Failed to prepare npm install workspace for "${args.packageName}"`,
291
284
  hint: 'Check that the btca data directory is writable.',
292
- cause: createInstallDirectory.error
285
+ cause
293
286
  });
294
287
  }
295
288
 
296
- const writeManifest = await Result.tryPromise(() =>
297
- Bun.write(
289
+ try {
290
+ await Bun.write(
298
291
  path.join(installDirectory, 'package.json'),
299
292
  JSON.stringify(
300
293
  {
@@ -304,13 +297,12 @@ const installPackageFiles = async (args: {
304
297
  null,
305
298
  2
306
299
  )
307
- )
308
- );
309
- if (!Result.isOk(writeManifest)) {
300
+ );
301
+ } catch (cause) {
310
302
  throw new ResourceError({
311
303
  message: `Failed to prepare npm install workspace for "${args.packageName}"`,
312
304
  hint: 'Check that the btca data directory is writable.',
313
- cause: writeManifest.error
305
+ cause
314
306
  });
315
307
  }
316
308
 
@@ -329,14 +321,13 @@ const installPackageFiles = async (args: {
329
321
  });
330
322
  }
331
323
 
332
- const copyResult = await Result.tryPromise(() =>
333
- fs.cp(packagePath, args.localPath, { recursive: true, force: true })
334
- );
335
- if (!Result.isOk(copyResult)) {
324
+ try {
325
+ await fs.cp(packagePath, args.localPath, { recursive: true, force: true });
326
+ } catch (cause) {
336
327
  throw new ResourceError({
337
328
  message: `Failed to copy installed npm package files for "${args.packageName}"`,
338
329
  hint: 'Check filesystem permissions and available disk space.',
339
- cause: copyResult.error
330
+ cause
340
331
  });
341
332
  }
342
333
  } finally {
@@ -431,27 +422,27 @@ const ensureNpmResource = async (config: BtcaNpmResourceArgs): Promise<string> =
431
422
  : config.resourcesDirectoryPath;
432
423
  const localPath = path.join(basePath, resourceKey);
433
424
 
434
- const mkdirResult = await Result.tryPromise({
435
- try: () => fs.mkdir(basePath, { recursive: true }),
436
- catch: (cause) =>
437
- new ResourceError({
438
- message: 'Failed to create resources directory',
439
- hint: 'Check that you have write permissions to the btca data directory.',
440
- cause
441
- })
442
- });
443
- if (!Result.isOk(mkdirResult)) throw mkdirResult.error;
425
+ try {
426
+ await fs.mkdir(basePath, { recursive: true });
427
+ } catch (cause) {
428
+ throw new ResourceError({
429
+ message: 'Failed to create resources directory',
430
+ hint: 'Check that you have write permissions to the btca data directory.',
431
+ cause
432
+ });
433
+ }
444
434
 
445
435
  const canReuse = await shouldReuseCache(config, localPath);
446
436
  if (canReuse) return localPath;
447
437
 
448
438
  await cleanupDirectory(localPath);
449
- const createResult = await Result.tryPromise(() => fs.mkdir(localPath, { recursive: true }));
450
- if (!Result.isOk(createResult)) {
439
+ try {
440
+ await fs.mkdir(localPath, { recursive: true });
441
+ } catch (cause) {
451
442
  throw new ResourceError({
452
443
  message: `Failed to prepare npm resource directory for "${config.name}"`,
453
444
  hint: 'Check that the btca data directory is writable.',
454
- cause: createResult.error
445
+ cause
455
446
  });
456
447
  }
457
448
 
@@ -1,5 +1,10 @@
1
1
  export { ResourceError } from './helpers.ts';
2
- export { Resources } from './service.ts';
2
+ export {
3
+ createResourcesService,
4
+ createAnonymousResource,
5
+ resolveResourceDefinition
6
+ } from './service.ts';
7
+ export type { ResourcesService } from './service.ts';
3
8
  export {
4
9
  GitResourceSchema,
5
10
  LocalResourceSchema,
@@ -1,4 +1,3 @@
1
- import { Result } from 'better-result';
2
1
  import { z } from 'zod';
3
2
 
4
3
  import { LIMITS } from '../validation/index.ts';
@@ -21,11 +20,13 @@ const BRANCH_NAME_REGEX = /^[a-zA-Z0-9/_.-]+$/;
21
20
  const NPM_PACKAGE_SEGMENT_REGEX = /^[a-z0-9][a-z0-9._-]*$/;
22
21
  const NPM_VERSION_OR_TAG_REGEX = /^[^\s/]+$/;
23
22
 
24
- const parseUrl = (value: string) =>
25
- Result.try(() => new URL(value)).match({
26
- ok: (url) => url,
27
- err: () => null
28
- });
23
+ const parseUrl = (value: string) => {
24
+ try {
25
+ return new URL(value);
26
+ } catch {
27
+ return null;
28
+ }
29
+ };
29
30
 
30
31
  // ─────────────────────────────────────────────────────────────────────────────
31
32
  // Field Schemas
@@ -1,6 +1,10 @@
1
1
  import { describe, expect, it } from 'bun:test';
2
2
 
3
- import { Resources, createAnonymousDirectoryKey } from './service.ts';
3
+ import {
4
+ createAnonymousDirectoryKey,
5
+ createAnonymousResource,
6
+ resolveResourceDefinition
7
+ } from './service.ts';
4
8
  import { resourceNameToKey } from './helpers.ts';
5
9
  import { type ResourceDefinition } from './schema.ts';
6
10
 
@@ -16,13 +20,13 @@ describe('Resources.resolveResourceDefinition', () => {
16
20
  const getResource = (name: string) => (name === 'svelte' ? configuredResource : undefined);
17
21
 
18
22
  it('resolves configured resources by name first', () => {
19
- const definition = Resources.resolveResourceDefinition('svelte', getResource);
23
+ const definition = resolveResourceDefinition('svelte', getResource);
20
24
  expect(definition.type).toBe('git');
21
25
  expect(definition.name).toBe('svelte');
22
26
  });
23
27
 
24
28
  it('creates anonymous git resources from valid URLs', () => {
25
- const definition = Resources.resolveResourceDefinition(
29
+ const definition = resolveResourceDefinition(
26
30
  'https://github.com/sveltejs/svelte.dev/tree/main/packages',
27
31
  () => undefined
28
32
  );
@@ -35,10 +39,7 @@ describe('Resources.resolveResourceDefinition', () => {
35
39
  });
36
40
 
37
41
  it('creates anonymous npm resources from npm references', () => {
38
- const definition = Resources.resolveResourceDefinition(
39
- 'npm:@types/node@22.10.1',
40
- () => undefined
41
- );
42
+ const definition = resolveResourceDefinition('npm:@types/node@22.10.1', () => undefined);
42
43
  expect(definition.type).toBe('npm');
43
44
  if (definition.type === 'npm') {
44
45
  expect(definition.package).toBe('@types/node');
@@ -48,7 +49,7 @@ describe('Resources.resolveResourceDefinition', () => {
48
49
  });
49
50
 
50
51
  it('creates anonymous npm resources from npm package URLs', () => {
51
- const definition = Resources.resolveResourceDefinition(
52
+ const definition = resolveResourceDefinition(
52
53
  'https://www.npmjs.com/package/react/v/19.0.0',
53
54
  () => undefined
54
55
  );
@@ -60,8 +61,8 @@ describe('Resources.resolveResourceDefinition', () => {
60
61
  });
61
62
 
62
63
  it('reuses the same cache key for repeated normalized URLs', () => {
63
- const first = Resources.createAnonymousResource('https://github.com/sveltejs/svelte.dev');
64
- const second = Resources.createAnonymousResource(
64
+ const first = createAnonymousResource('https://github.com/sveltejs/svelte.dev');
65
+ const second = createAnonymousResource(
65
66
  'https://github.com/sveltejs/svelte.dev/blob/main/packages'
66
67
  );
67
68
  expect(first).not.toBeNull();
@@ -72,8 +73,8 @@ describe('Resources.resolveResourceDefinition', () => {
72
73
  });
73
74
 
74
75
  it('uses short deterministic keys for anonymous repository paths', () => {
75
- const main = Resources.createAnonymousResource('https://github.com/sveltejs/svelte.dev');
76
- const withPath = Resources.createAnonymousResource(
76
+ const main = createAnonymousResource('https://github.com/sveltejs/svelte.dev');
77
+ const withPath = createAnonymousResource(
77
78
  'https://github.com/sveltejs/svelte.dev/tree/main/packages'
78
79
  );
79
80
  expect(main).not.toBeNull();