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
@@ -1,11 +1,11 @@
1
1
  import { promises as fs } from 'node:fs';
2
2
  import path from 'node:path';
3
3
 
4
- import { Result } from 'better-result';
4
+ import { parseJsonc } from '@btca/shared';
5
5
  import { z } from 'zod';
6
6
 
7
7
  import { CommonHints, type TaggedErrorOptions } from '../errors.ts';
8
- import { Metrics } from '../metrics/index.ts';
8
+ import { metricsError, metricsInfo } from '../metrics/index.ts';
9
9
  import { GitResourceSchema, type GitResource } from '../resources/schema.ts';
10
10
 
11
11
  // ─────────────────────────────────────────────────────────────────────────────
@@ -91,408 +91,253 @@ const expandHome = (filePath: string): string => {
91
91
  return filePath;
92
92
  };
93
93
 
94
- const stripJsonc = (content: string): string => {
95
- // Remove // and /* */ comments without touching strings.
96
- let out = '';
97
- let i = 0;
98
- let inString = false;
99
- let quote: '"' | "'" | null = null;
100
- let escaped = false;
101
-
102
- while (i < content.length) {
103
- const ch = content[i] ?? '';
104
- const next = content[i + 1] ?? '';
105
-
106
- if (inString) {
107
- out += ch;
108
- if (escaped) escaped = false;
109
- else if (ch === '\\') escaped = true;
110
- else if (quote && ch === quote) {
111
- inString = false;
112
- quote = null;
113
- }
114
- i += 1;
115
- continue;
116
- }
117
-
118
- if (ch === '/' && next === '/') {
119
- i += 2;
120
- while (i < content.length && content[i] !== '\n') i += 1;
121
- continue;
122
- }
123
-
124
- if (ch === '/' && next === '*') {
125
- i += 2;
126
- while (i < content.length) {
127
- if (content[i] === '*' && content[i + 1] === '/') {
128
- i += 2;
129
- break;
130
- }
131
- i += 1;
132
- }
133
- continue;
134
- }
135
-
136
- if (ch === '"' || ch === "'") {
137
- inString = true;
138
- quote = ch;
139
- out += ch;
140
- i += 1;
141
- continue;
142
- }
143
-
144
- out += ch;
145
- i += 1;
94
+ const readJsonFile = async (filePath: string) => {
95
+ try {
96
+ const content = await Bun.file(filePath).text();
97
+ return JSON.parse(content);
98
+ } catch {
99
+ return null;
146
100
  }
101
+ };
147
102
 
148
- // Remove trailing commas (outside strings).
149
- let normalized = '';
150
- inString = false;
151
- quote = null;
152
- escaped = false;
153
- i = 0;
154
-
155
- while (i < out.length) {
156
- const ch = out[i] ?? '';
157
-
158
- if (inString) {
159
- normalized += ch;
160
- if (escaped) escaped = false;
161
- else if (ch === '\\') escaped = true;
162
- else if (quote && ch === quote) {
163
- inString = false;
164
- quote = null;
165
- }
166
- i += 1;
167
- continue;
168
- }
169
-
170
- if (ch === '"' || ch === "'") {
171
- inString = true;
172
- quote = ch;
173
- normalized += ch;
174
- i += 1;
175
- continue;
176
- }
177
-
178
- if (ch === ',') {
179
- let j = i + 1;
180
- while (j < out.length && /\s/.test(out[j] ?? '')) j += 1;
181
- const nextNonWs = out[j] ?? '';
182
- if (nextNonWs === ']' || nextNonWs === '}') {
183
- i += 1;
184
- continue;
185
- }
186
- }
187
-
188
- normalized += ch;
189
- i += 1;
103
+ const readJsoncFile = async (filePath: string) => {
104
+ try {
105
+ const content = await Bun.file(filePath).text();
106
+ return parseJsonc(content);
107
+ } catch {
108
+ return null;
190
109
  }
191
-
192
- return normalized.trim();
193
110
  };
194
111
 
195
- const parseJsonc = (content: string): unknown => JSON.parse(stripJsonc(content));
112
+ export function getAuthPath(): string {
113
+ return `${expandHome(GLOBAL_CONFIG_DIR)}/${REMOTE_AUTH_FILENAME}`;
114
+ }
196
115
 
197
- const readJsonFile = (filePath: string) =>
198
- Result.gen(async function* () {
199
- const content = yield* Result.await(Result.tryPromise(() => Bun.file(filePath).text()));
200
- const parsed = yield* Result.try(() => JSON.parse(content));
201
- return Result.ok(parsed);
202
- });
116
+ /**
117
+ * Get the path to the remote config file in the current directory
118
+ */
119
+ export function getConfigPath(cwd: string = process.cwd()): string {
120
+ return `${cwd}/${REMOTE_CONFIG_FILENAME}`;
121
+ }
203
122
 
204
- const readJsoncFile = (filePath: string) =>
205
- Result.gen(async function* () {
206
- const content = yield* Result.await(Result.tryPromise(() => Bun.file(filePath).text()));
207
- const parsed = yield* Result.try(() => parseJsonc(content));
208
- return Result.ok(parsed);
209
- });
123
+ /**
124
+ * Check if the user is authenticated with remote
125
+ */
126
+ export async function isAuthenticated(): Promise<boolean> {
127
+ const authPath = getAuthPath();
128
+ const parsed = await readJsonFile(authPath);
129
+ if (!parsed) return false;
130
+ const authResult = RemoteAuthSchema.safeParse(parsed);
131
+ return authResult.success && !!authResult.data.apiKey;
132
+ }
210
133
 
211
- // ─────────────────────────────────────────────────────────────────────────────
212
- // Remote Config Namespace
213
- // ─────────────────────────────────────────────────────────────────────────────
134
+ /**
135
+ * Load the remote auth credentials
136
+ */
137
+ export async function loadAuth(): Promise<RemoteAuth | null> {
138
+ const authPath = getAuthPath();
139
+ const parsed = await readJsonFile(authPath);
140
+ if (!parsed) return null;
141
+ const authResult = RemoteAuthSchema.safeParse(parsed);
142
+ if (!authResult.success) {
143
+ metricsError('remote.auth.invalid', { path: authPath, error: authResult.error.message });
144
+ return null;
145
+ }
146
+ return authResult.data;
147
+ }
214
148
 
215
- export namespace RemoteConfigService {
216
- /**
217
- * Get the path to the remote auth file
218
- */
219
- export function getAuthPath(): string {
220
- return `${expandHome(GLOBAL_CONFIG_DIR)}/${REMOTE_AUTH_FILENAME}`;
149
+ /**
150
+ * Save remote auth credentials
151
+ */
152
+ export async function saveAuth(auth: RemoteAuth): Promise<void> {
153
+ const authPath = getAuthPath();
154
+ const configDir = path.dirname(authPath);
155
+ try {
156
+ await fs.mkdir(configDir, { recursive: true });
157
+ await Bun.write(authPath, JSON.stringify(auth, null, 2));
158
+ await fs.chmod(authPath, 0o600);
159
+ } catch (cause) {
160
+ throw new RemoteConfigError({
161
+ message: `Failed to save remote auth to: "${authPath}"`,
162
+ hint: 'Check that you have write permissions to the config directory.',
163
+ cause
164
+ });
221
165
  }
166
+ metricsInfo('remote.auth.saved', { path: authPath });
167
+ }
222
168
 
223
- /**
224
- * Get the path to the remote config file in the current directory
225
- */
226
- export function getConfigPath(cwd: string = process.cwd()): string {
227
- return `${cwd}/${REMOTE_CONFIG_FILENAME}`;
169
+ /**
170
+ * Delete remote auth credentials (unlink)
171
+ */
172
+ export async function deleteAuth(): Promise<void> {
173
+ const authPath = getAuthPath();
174
+ try {
175
+ await fs.unlink(authPath);
176
+ metricsInfo('remote.auth.deleted', { path: authPath });
177
+ } catch {
178
+ return;
228
179
  }
180
+ }
229
181
 
230
- /**
231
- * Check if the user is authenticated with remote
232
- */
233
- export async function isAuthenticated(): Promise<boolean> {
234
- const authPath = getAuthPath();
235
- const result = await readJsonFile(authPath);
236
- return result.match({
237
- ok: (parsed) => {
238
- const authResult = RemoteAuthSchema.safeParse(parsed);
239
- return authResult.success && !!authResult.data.apiKey;
240
- },
241
- err: () => false
182
+ /**
183
+ * Check if a remote config file exists in the current directory
184
+ */
185
+ export async function configExists(cwd: string = process.cwd()): Promise<boolean> {
186
+ const configPath = getConfigPath(cwd);
187
+ return Bun.file(configPath).exists();
188
+ }
189
+
190
+ /**
191
+ * Load the remote config from the current directory
192
+ */
193
+ export async function loadConfig(cwd: string = process.cwd()): Promise<RemoteConfig | null> {
194
+ const configPath = getConfigPath(cwd);
195
+
196
+ const parsed = await readJsoncFile(configPath);
197
+ if (!parsed) return null;
198
+
199
+ const parsedResult = RemoteConfigSchema.safeParse(parsed);
200
+ if (!parsedResult.success) {
201
+ const issues = parsedResult.error.issues
202
+ .map((i) => ` - ${i.path.join('.')}: ${i.message}`)
203
+ .join('\n');
204
+ throw new RemoteConfigError({
205
+ message: `Invalid remote config structure:\n${issues}`,
206
+ hint: `${CommonHints.CHECK_CONFIG} Required field: "project" (string).`,
207
+ cause: parsedResult.error
242
208
  });
243
209
  }
244
210
 
245
- /**
246
- * Load the remote auth credentials
247
- */
248
- export async function loadAuth(): Promise<RemoteAuth | null> {
249
- const authPath = getAuthPath();
250
- const result = await readJsonFile(authPath);
251
- return result.match({
252
- ok: (parsed) => {
253
- const authResult = RemoteAuthSchema.safeParse(parsed);
254
- if (!authResult.success) {
255
- Metrics.error('remote.auth.invalid', { path: authPath, error: authResult.error.message });
256
- return null;
257
- }
258
- return authResult.data;
259
- },
260
- err: () => null
211
+ metricsInfo('remote.config.loaded', {
212
+ path: configPath,
213
+ project: parsedResult.data.project,
214
+ resourceCount: parsedResult.data.resources.length
215
+ });
216
+
217
+ return parsedResult.data;
218
+ }
219
+
220
+ /**
221
+ * Save the remote config to the current directory
222
+ */
223
+ export async function saveConfig(config: RemoteConfig, cwd: string = process.cwd()): Promise<void> {
224
+ const configPath = getConfigPath(cwd);
225
+
226
+ const toSave = {
227
+ $schema: REMOTE_CONFIG_SCHEMA_URL,
228
+ ...config
229
+ };
230
+
231
+ try {
232
+ await Bun.write(configPath, JSON.stringify(toSave, null, '\t'));
233
+ } catch (cause) {
234
+ throw new RemoteConfigError({
235
+ message: `Failed to save remote config to: "${configPath}"`,
236
+ hint: 'Check that you have write permissions to the directory.',
237
+ cause
261
238
  });
262
239
  }
240
+ metricsInfo('remote.config.saved', {
241
+ path: configPath,
242
+ project: config.project,
243
+ resourceCount: config.resources.length
244
+ });
245
+ }
263
246
 
264
- /**
265
- * Save remote auth credentials
266
- */
267
- export async function saveAuth(auth: RemoteAuth): Promise<void> {
268
- const authPath = getAuthPath();
269
- const configDir = path.dirname(authPath);
270
-
271
- const result = await Result.gen(async function* () {
272
- yield* Result.await(
273
- Result.tryPromise({
274
- try: () => fs.mkdir(configDir, { recursive: true }),
275
- catch: (cause) =>
276
- new RemoteConfigError({
277
- message: `Failed to save remote auth to: "${authPath}"`,
278
- hint: 'Check that you have write permissions to the config directory.',
279
- cause
280
- })
281
- })
282
- );
283
-
284
- yield* Result.await(
285
- Result.tryPromise({
286
- try: () => Bun.write(authPath, JSON.stringify(auth, null, 2)),
287
- catch: (cause) =>
288
- new RemoteConfigError({
289
- message: `Failed to save remote auth to: "${authPath}"`,
290
- hint: 'Check that you have write permissions to the config directory.',
291
- cause
292
- })
293
- })
294
- );
295
-
296
- yield* Result.await(
297
- Result.tryPromise({
298
- try: () => fs.chmod(authPath, 0o600),
299
- catch: (cause) =>
300
- new RemoteConfigError({
301
- message: `Failed to save remote auth to: "${authPath}"`,
302
- hint: 'Check that you have write permissions to the config directory.',
303
- cause
304
- })
305
- })
306
- );
307
-
308
- return Result.ok(undefined);
309
- });
247
+ /**
248
+ * Create a new remote config with defaults
249
+ */
250
+ export function createDefaultConfig(projectName: string): RemoteConfig {
251
+ return {
252
+ project: projectName,
253
+ model: 'claude-haiku',
254
+ resources: []
255
+ };
256
+ }
310
257
 
311
- if (!Result.isOk(result)) throw result.error;
312
- Metrics.info('remote.auth.saved', { path: authPath });
258
+ /**
259
+ * Add a resource to the remote config
260
+ */
261
+ export async function addResource(
262
+ resource: GitResource,
263
+ cwd: string = process.cwd()
264
+ ): Promise<RemoteConfig> {
265
+ let config = await loadConfig(cwd);
266
+
267
+ if (!config) {
268
+ throw new RemoteConfigError({
269
+ message: 'No remote config found in current directory',
270
+ hint: `Create a remote config first with "btca remote init" or create a ${REMOTE_CONFIG_FILENAME} file.`
271
+ });
313
272
  }
314
273
 
315
- /**
316
- * Delete remote auth credentials (unlink)
317
- */
318
- export async function deleteAuth(): Promise<void> {
319
- const authPath = getAuthPath();
320
- const result = await Result.tryPromise(() => fs.unlink(authPath));
321
- result.match({
322
- ok: () => Metrics.info('remote.auth.deleted', { path: authPath }),
323
- err: () => undefined
274
+ // Check for duplicate
275
+ if (config.resources.some((r) => r.name === resource.name)) {
276
+ throw new RemoteConfigError({
277
+ message: `Resource "${resource.name}" already exists in remote config`,
278
+ hint: `Remove the existing resource first or use a different name.`
324
279
  });
325
280
  }
326
281
 
327
- /**
328
- * Check if a remote config file exists in the current directory
329
- */
330
- export async function configExists(cwd: string = process.cwd()): Promise<boolean> {
331
- const configPath = getConfigPath(cwd);
332
- return Bun.file(configPath).exists();
333
- }
282
+ config = {
283
+ ...config,
284
+ resources: [...config.resources, resource]
285
+ };
334
286
 
335
- /**
336
- * Load the remote config from the current directory
337
- */
338
- export async function loadConfig(cwd: string = process.cwd()): Promise<RemoteConfig | null> {
339
- const configPath = getConfigPath(cwd);
287
+ await saveConfig(config, cwd);
288
+ return config;
289
+ }
340
290
 
341
- const result = await readJsoncFile(configPath);
342
- const parsed = result.match({
343
- ok: (value) => value,
344
- err: () => null
345
- });
346
- if (!parsed) return null;
347
-
348
- const parsedResult = RemoteConfigSchema.safeParse(parsed);
349
- if (!parsedResult.success) {
350
- const issues = parsedResult.error.issues
351
- .map((i) => ` - ${i.path.join('.')}: ${i.message}`)
352
- .join('\n');
353
- throw new RemoteConfigError({
354
- message: `Invalid remote config structure:\n${issues}`,
355
- hint: `${CommonHints.CHECK_CONFIG} Required field: "project" (string).`,
356
- cause: parsedResult.error
357
- });
358
- }
359
-
360
- Metrics.info('remote.config.loaded', {
361
- path: configPath,
362
- project: parsedResult.data.project,
363
- resourceCount: parsedResult.data.resources.length
291
+ /**
292
+ * Remove a resource from the remote config
293
+ */
294
+ export async function removeResource(
295
+ name: string,
296
+ cwd: string = process.cwd()
297
+ ): Promise<RemoteConfig> {
298
+ let config = await loadConfig(cwd);
299
+
300
+ if (!config) {
301
+ throw new RemoteConfigError({
302
+ message: 'No remote config found in current directory',
303
+ hint: `Create a remote config first with "btca remote init" or create a ${REMOTE_CONFIG_FILENAME} file.`
364
304
  });
365
-
366
- return parsedResult.data;
367
305
  }
368
306
 
369
- /**
370
- * Save the remote config to the current directory
371
- */
372
- export async function saveConfig(
373
- config: RemoteConfig,
374
- cwd: string = process.cwd()
375
- ): Promise<void> {
376
- const configPath = getConfigPath(cwd);
377
-
378
- const toSave = {
379
- $schema: REMOTE_CONFIG_SCHEMA_URL,
380
- ...config
381
- };
382
-
383
- const result = await Result.tryPromise({
384
- try: () => Bun.write(configPath, JSON.stringify(toSave, null, '\t')),
385
- catch: (cause) =>
386
- new RemoteConfigError({
387
- message: `Failed to save remote config to: "${configPath}"`,
388
- hint: 'Check that you have write permissions to the directory.',
389
- cause
390
- })
391
- });
392
-
393
- if (!Result.isOk(result)) throw result.error;
394
- Metrics.info('remote.config.saved', {
395
- path: configPath,
396
- project: config.project,
397
- resourceCount: config.resources.length
307
+ const existingIndex = config.resources.findIndex((r) => r.name === name);
308
+ if (existingIndex === -1) {
309
+ throw new RemoteConfigError({
310
+ message: `Resource "${name}" not found in remote config`,
311
+ hint: `Available resources: ${config.resources.map((r) => r.name).join(', ') || 'none'}`
398
312
  });
399
313
  }
400
314
 
401
- /**
402
- * Create a new remote config with defaults
403
- */
404
- export function createDefaultConfig(projectName: string): RemoteConfig {
405
- return {
406
- project: projectName,
407
- model: 'claude-haiku',
408
- resources: []
409
- };
410
- }
315
+ config = {
316
+ ...config,
317
+ resources: config.resources.filter((r) => r.name !== name)
318
+ };
411
319
 
412
- /**
413
- * Add a resource to the remote config
414
- */
415
- export async function addResource(
416
- resource: GitResource,
417
- cwd: string = process.cwd()
418
- ): Promise<RemoteConfig> {
419
- let config = await loadConfig(cwd);
420
-
421
- if (!config) {
422
- throw new RemoteConfigError({
423
- message: 'No remote config found in current directory',
424
- hint: `Create a remote config first with "btca remote init" or create a ${REMOTE_CONFIG_FILENAME} file.`
425
- });
426
- }
427
-
428
- // Check for duplicate
429
- if (config.resources.some((r) => r.name === resource.name)) {
430
- throw new RemoteConfigError({
431
- message: `Resource "${resource.name}" already exists in remote config`,
432
- hint: `Remove the existing resource first or use a different name.`
433
- });
434
- }
435
-
436
- config = {
437
- ...config,
438
- resources: [...config.resources, resource]
439
- };
440
-
441
- await saveConfig(config, cwd);
442
- return config;
443
- }
320
+ await saveConfig(config, cwd);
321
+ return config;
322
+ }
444
323
 
445
- /**
446
- * Remove a resource from the remote config
447
- */
448
- export async function removeResource(
449
- name: string,
450
- cwd: string = process.cwd()
451
- ): Promise<RemoteConfig> {
452
- let config = await loadConfig(cwd);
453
-
454
- if (!config) {
455
- throw new RemoteConfigError({
456
- message: 'No remote config found in current directory',
457
- hint: `Create a remote config first with "btca remote init" or create a ${REMOTE_CONFIG_FILENAME} file.`
458
- });
459
- }
460
-
461
- const existingIndex = config.resources.findIndex((r) => r.name === name);
462
- if (existingIndex === -1) {
463
- throw new RemoteConfigError({
464
- message: `Resource "${name}" not found in remote config`,
465
- hint: `Available resources: ${config.resources.map((r) => r.name).join(', ') || 'none'}`
466
- });
467
- }
468
-
469
- config = {
470
- ...config,
471
- resources: config.resources.filter((r) => r.name !== name)
472
- };
473
-
474
- await saveConfig(config, cwd);
475
- return config;
324
+ /**
325
+ * Update the model in the remote config
326
+ */
327
+ export async function updateModel(
328
+ model: RemoteModelId,
329
+ cwd: string = process.cwd()
330
+ ): Promise<RemoteConfig> {
331
+ let config = await loadConfig(cwd);
332
+
333
+ if (!config) {
334
+ throw new RemoteConfigError({
335
+ message: 'No remote config found in current directory',
336
+ hint: `Create a remote config first with "btca remote init" or create a ${REMOTE_CONFIG_FILENAME} file.`
337
+ });
476
338
  }
477
339
 
478
- /**
479
- * Update the model in the remote config
480
- */
481
- export async function updateModel(
482
- model: RemoteModelId,
483
- cwd: string = process.cwd()
484
- ): Promise<RemoteConfig> {
485
- let config = await loadConfig(cwd);
486
-
487
- if (!config) {
488
- throw new RemoteConfigError({
489
- message: 'No remote config found in current directory',
490
- hint: `Create a remote config first with "btca remote init" or create a ${REMOTE_CONFIG_FILENAME} file.`
491
- });
492
- }
493
-
494
- config = { ...config, model };
495
- await saveConfig(config, cwd);
496
- return config;
497
- }
340
+ config = { ...config, model };
341
+ await saveConfig(config, cwd);
342
+ return config;
498
343
  }
@@ -7,18 +7,12 @@ export type ContextStore = {
7
7
 
8
8
  const storage = new AsyncLocalStorage<ContextStore>();
9
9
 
10
- export namespace Context {
11
- export const run = <T>(store: ContextStore, fn: () => Promise<T> | T): Promise<T> => {
12
- return Promise.resolve(storage.run(store, fn));
13
- };
10
+ export const runContext = <T>(store: ContextStore, fn: () => Promise<T> | T): Promise<T> =>
11
+ Promise.resolve(storage.run(store, fn));
14
12
 
15
- export const get = (): ContextStore | undefined => storage.getStore();
13
+ export const getContext = (): ContextStore | undefined => storage.getStore();
16
14
 
17
- export const require = (): ContextStore => {
18
- const store = storage.getStore();
19
- if (!store) throw new Error('Missing AsyncLocalStorage context');
20
- return store;
21
- };
15
+ export const requireContext = (): ContextStore =>
16
+ storage.getStore() ?? { requestId: 'unknown', txDepth: 0 };
22
17
 
23
- export const requestId = (): string => storage.getStore()?.requestId ?? 'unknown';
24
- }
18
+ export const requestId = (): string => storage.getStore()?.requestId ?? 'unknown';