btca-server 2.0.2 → 2.0.4

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.
@@ -141,20 +141,11 @@ export type ConfigService = {
141
141
  provider: string,
142
142
  model: string,
143
143
  providerOptions?: ProviderOptionsConfig
144
- ) => Promise<{ provider: string; model: string; savedTo: ConfigScope }>;
145
- updateModelEffect: (
146
- provider: string,
147
- model: string,
148
- providerOptions?: ProviderOptionsConfig
149
144
  ) => Effect.Effect<{ provider: string; model: string; savedTo: ConfigScope }, unknown>;
150
- addResource: (resource: ResourceDefinition) => Promise<ResourceDefinition>;
151
- addResourceEffect: (resource: ResourceDefinition) => Effect.Effect<ResourceDefinition, unknown>;
152
- removeResource: (name: string) => Promise<void>;
153
- removeResourceEffect: (name: string) => Effect.Effect<void, unknown>;
154
- clearResources: () => Promise<{ cleared: number }>;
155
- clearResourcesEffect: () => Effect.Effect<{ cleared: number }, unknown>;
156
- reload: () => Promise<void>;
157
- reloadEffect: () => Effect.Effect<void, unknown>;
145
+ addResource: (resource: ResourceDefinition) => Effect.Effect<ResourceDefinition, unknown>;
146
+ removeResource: (name: string) => Effect.Effect<void, unknown>;
147
+ clearResources: () => Effect.Effect<{ cleared: number }, unknown>;
148
+ reload: () => Effect.Effect<void, unknown>;
158
149
  };
159
150
 
160
151
  export type Service = ConfigService;
@@ -495,234 +486,222 @@ const makeService = (
495
486
  }
496
487
  };
497
488
 
498
- const service: ConfigService = {
499
- resourcesDirectory,
500
- configPath,
501
- get resources() {
502
- return getMergedResources();
503
- },
504
- get model() {
505
- return getActiveConfig().model ?? DEFAULT_MODEL;
506
- },
507
- get provider() {
508
- return getActiveConfig().provider ?? DEFAULT_PROVIDER;
509
- },
510
- get providerTimeoutMs() {
511
- return getActiveConfig().providerTimeoutMs;
512
- },
513
- get maxSteps() {
514
- return getActiveConfig().maxSteps ?? DEFAULT_MAX_STEPS;
515
- },
516
- getProviderOptions: (providerId: string) => getMergedProviderOptions()[providerId],
517
- getResource: (name: string) => getMergedResources().find((r) => r.name === name),
489
+ const updateModelPromise = async (
490
+ provider: string,
491
+ model: string,
492
+ providerOptions?: ProviderOptionsConfig
493
+ ): Promise<{ provider: string; model: string; savedTo: ConfigScope }> => {
494
+ if (!isProviderSupported(provider)) {
495
+ const available = getSupportedProviders();
496
+ throw new ConfigError({
497
+ message: `Provider "${provider}" is not supported`,
498
+ hint: `Available providers: ${available.join(', ')}. Open an issue to request this provider: https://github.com/davis7dotsh/better-context/issues.`
499
+ });
500
+ }
501
+ const mutableConfig = getMutableConfig();
502
+ const existingProviderOptions = mutableConfig.providerOptions ?? {};
503
+ const nextProviderOptions = providerOptions
504
+ ? {
505
+ ...existingProviderOptions,
506
+ [provider]: {
507
+ ...(existingProviderOptions[provider] ?? {}),
508
+ ...providerOptions
509
+ }
510
+ }
511
+ : existingProviderOptions;
512
+ const updated = {
513
+ ...mutableConfig,
514
+ provider,
515
+ model,
516
+ ...(providerOptions ? { providerOptions: nextProviderOptions } : {})
517
+ };
518
518
 
519
- updateModel: async (
520
- provider: string,
521
- model: string,
522
- providerOptions?: ProviderOptionsConfig
523
- ) => {
524
- if (!isProviderSupported(provider)) {
525
- const available = getSupportedProviders();
519
+ if (provider === 'openai-compat') {
520
+ const merged = currentProjectConfig
521
+ ? mergeProviderOptions(currentGlobalConfig, updated)
522
+ : mergeProviderOptions(updated, null);
523
+ const compat = merged['openai-compat'];
524
+ const baseURL = compat?.baseURL?.trim();
525
+ const name = compat?.name?.trim();
526
+ if (!baseURL || !name) {
526
527
  throw new ConfigError({
527
- message: `Provider "${provider}" is not supported`,
528
- hint: `Available providers: ${available.join(', ')}. Open an issue to request this provider: https://github.com/davis7dotsh/better-context/issues.`
528
+ message: 'openai-compat requires baseURL and name',
529
+ hint: 'Run "btca connect -p openai-compat" to configure baseURL and name.'
529
530
  });
530
531
  }
531
- const mutableConfig = getMutableConfig();
532
- const existingProviderOptions = mutableConfig.providerOptions ?? {};
533
- const nextProviderOptions = providerOptions
534
- ? {
535
- ...existingProviderOptions,
536
- [provider]: {
537
- ...(existingProviderOptions[provider] ?? {}),
538
- ...providerOptions
539
- }
540
- }
541
- : existingProviderOptions;
542
- const updated = {
543
- ...mutableConfig,
544
- provider,
545
- model,
546
- ...(providerOptions ? { providerOptions: nextProviderOptions } : {})
547
- };
532
+ }
533
+ setMutableConfig(updated);
534
+ await saveConfig(configPath, updated);
535
+ metricsInfo('config.model.updated', { provider, model });
536
+ return {
537
+ provider,
538
+ model,
539
+ savedTo: currentProjectConfig ? 'project' : 'global'
540
+ };
541
+ };
548
542
 
549
- if (provider === 'openai-compat') {
550
- const merged = currentProjectConfig
551
- ? mergeProviderOptions(currentGlobalConfig, updated)
552
- : mergeProviderOptions(updated, null);
553
- const compat = merged['openai-compat'];
554
- const baseURL = compat?.baseURL?.trim();
555
- const name = compat?.name?.trim();
556
- if (!baseURL || !name) {
557
- throw new ConfigError({
558
- message: 'openai-compat requires baseURL and name',
559
- hint: 'Run "btca connect -p openai-compat" to configure baseURL and name.'
560
- });
561
- }
562
- }
563
- setMutableConfig(updated);
564
- await saveConfig(configPath, updated);
565
- metricsInfo('config.model.updated', { provider, model });
566
- return {
567
- provider,
568
- model,
569
- savedTo: currentProjectConfig ? 'project' : 'global'
570
- };
571
- },
543
+ const addResourcePromise = async (resource: ResourceDefinition): Promise<ResourceDefinition> => {
544
+ const mergedResources = getMergedResources();
545
+ if (mergedResources.some((r) => r.name === resource.name)) {
546
+ throw new ConfigError({
547
+ message: `Resource "${resource.name}" already exists`,
548
+ hint: `Choose a different name or remove the existing resource first with "btca remove ${resource.name}".`
549
+ });
550
+ }
551
+
552
+ const mutableConfig = getMutableConfig();
553
+ const updated = {
554
+ ...mutableConfig,
555
+ resources: [...mutableConfig.resources, resource]
556
+ };
557
+ setMutableConfig(updated);
558
+ await saveConfig(configPath, updated);
559
+ metricsInfo('config.resource.added', { name: resource.name, type: resource.type });
560
+ return resource;
561
+ };
572
562
 
573
- addResource: async (resource: ResourceDefinition) => {
574
- // Check for duplicate name in merged resources
575
- const mergedResources = getMergedResources();
576
- if (mergedResources.some((r) => r.name === resource.name)) {
563
+ const removeResourcePromise = async (name: string): Promise<void> => {
564
+ const mergedResources = getMergedResources();
565
+ const exists = mergedResources.some((r) => r.name === name);
566
+ if (!exists) {
567
+ const available = mergedResources.map((r) => r.name);
568
+ throw new ConfigError({
569
+ message: `Resource "${name}" not found`,
570
+ hint:
571
+ available.length > 0
572
+ ? `Available resources: ${available.join(', ')}. ${CommonHints.LIST_RESOURCES}`
573
+ : `No resources configured. ${CommonHints.ADD_RESOURCE}`
574
+ });
575
+ }
576
+
577
+ const mutableConfig = getMutableConfig();
578
+ const isInMutableConfig = mutableConfig.resources.some((r) => r.name === name);
579
+
580
+ if (currentProjectConfig) {
581
+ const isInGlobal = currentGlobalConfig.resources.some((r) => r.name === name);
582
+ const isInProject = currentProjectConfig.resources.some((r) => r.name === name);
583
+
584
+ if (isInProject) {
585
+ const updated = {
586
+ ...currentProjectConfig,
587
+ resources: currentProjectConfig.resources.filter((r) => r.name !== name)
588
+ };
589
+ currentProjectConfig = updated;
590
+ await saveConfig(configPath, updated);
591
+ metricsInfo('config.resource.removed', { name, from: 'project' });
592
+ } else if (isInGlobal) {
577
593
  throw new ConfigError({
578
- message: `Resource "${resource.name}" already exists`,
579
- hint: `Choose a different name or remove the existing resource first with "btca remove ${resource.name}".`
594
+ message: `Resource "${name}" is defined in the global config`,
595
+ hint: `To remove this resource globally, edit the global config at "${expandHome(GLOBAL_CONFIG_DIR)}/${GLOBAL_CONFIG_FILENAME}" or run the command without a project config present.`
596
+ });
597
+ }
598
+ } else {
599
+ if (!isInMutableConfig) {
600
+ throw new ConfigError({
601
+ message: `Resource "${name}" not found in config`,
602
+ hint: CommonHints.LIST_RESOURCES
580
603
  });
581
604
  }
582
-
583
- // Add only to the mutable config (project if exists, else global)
584
- const mutableConfig = getMutableConfig();
585
605
  const updated = {
586
606
  ...mutableConfig,
587
- resources: [...mutableConfig.resources, resource]
607
+ resources: mutableConfig.resources.filter((r) => r.name !== name)
588
608
  };
589
609
  setMutableConfig(updated);
590
610
  await saveConfig(configPath, updated);
591
- metricsInfo('config.resource.added', { name: resource.name, type: resource.type });
592
- return resource;
593
- },
594
-
595
- removeResource: async (name: string) => {
596
- const mergedResources = getMergedResources();
597
- const exists = mergedResources.some((r) => r.name === name);
598
- if (!exists) {
599
- const available = mergedResources.map((r) => r.name);
600
- throw new ConfigError({
601
- message: `Resource "${name}" not found`,
602
- hint:
603
- available.length > 0
604
- ? `Available resources: ${available.join(', ')}. ${CommonHints.LIST_RESOURCES}`
605
- : `No resources configured. ${CommonHints.ADD_RESOURCE}`
606
- });
607
- }
611
+ metricsInfo('config.resource.removed', { name, from: 'global' });
612
+ }
613
+ };
608
614
 
609
- const mutableConfig = getMutableConfig();
610
- const isInMutableConfig = mutableConfig.resources.some((r) => r.name === name);
611
-
612
- if (currentProjectConfig) {
613
- // We have a project config
614
- const isInGlobal = currentGlobalConfig.resources.some((r) => r.name === name);
615
- const isInProject = currentProjectConfig.resources.some((r) => r.name === name);
616
-
617
- if (isInProject) {
618
- // Resource is in project config - just remove it
619
- const updated = {
620
- ...currentProjectConfig,
621
- resources: currentProjectConfig.resources.filter((r) => r.name !== name)
622
- };
623
- currentProjectConfig = updated;
624
- await saveConfig(configPath, updated);
625
- metricsInfo('config.resource.removed', { name, from: 'project' });
626
- } else if (isInGlobal) {
627
- // Resource is only in global config
628
- // User wants to remove a global resource from project context
629
- // We can't modify global config from project context, so throw an error
630
- throw new ConfigError({
631
- message: `Resource "${name}" is defined in the global config`,
632
- hint: `To remove this resource globally, edit the global config at "${expandHome(GLOBAL_CONFIG_DIR)}/${GLOBAL_CONFIG_FILENAME}" or run the command without a project config present.`
633
- });
634
- }
635
- } else {
636
- // No project config, modify global directly
637
- if (!isInMutableConfig) {
638
- // This shouldn't happen given the exists check above, but be safe
639
- throw new ConfigError({
640
- message: `Resource "${name}" not found in config`,
641
- hint: CommonHints.LIST_RESOURCES
642
- });
643
- }
644
- const updated = {
645
- ...mutableConfig,
646
- resources: mutableConfig.resources.filter((r) => r.name !== name)
647
- };
648
- setMutableConfig(updated);
649
- await saveConfig(configPath, updated);
650
- metricsInfo('config.resource.removed', { name, from: 'global' });
651
- }
652
- },
615
+ const clearResourcesPromise = async (): Promise<{ cleared: number }> => {
616
+ let clearedCount = 0;
653
617
 
654
- clearResources: async () => {
655
- // Clear the resources directory
656
- let clearedCount = 0;
618
+ let resourcesDir: string[] = [];
619
+ try {
620
+ resourcesDir = await fs.readdir(resourcesDirectory);
621
+ } catch {
622
+ resourcesDir = [];
623
+ }
657
624
 
658
- let resourcesDir: string[] = [];
625
+ for (const item of resourcesDir) {
659
626
  try {
660
- resourcesDir = await fs.readdir(resourcesDirectory);
627
+ await fs.rm(`${resourcesDirectory}/${item}`, { recursive: true, force: true });
628
+ clearedCount++;
661
629
  } catch {
662
- resourcesDir = [];
630
+ break;
663
631
  }
632
+ }
664
633
 
665
- for (const item of resourcesDir) {
666
- try {
667
- await fs.rm(`${resourcesDirectory}/${item}`, { recursive: true, force: true });
668
- clearedCount++;
669
- } catch {
670
- break;
671
- }
672
- }
634
+ metricsInfo('config.resources.cleared', { count: clearedCount });
635
+ return { cleared: clearedCount };
636
+ };
673
637
 
674
- metricsInfo('config.resources.cleared', { count: clearedCount });
675
- return { cleared: clearedCount };
676
- },
638
+ const reloadPromise = async (): Promise<void> => {
639
+ metricsInfo('config.reload.start', { configPath });
677
640
 
678
- reload: async () => {
679
- // Reload the config file from disk
680
- // configPath points to either project config (if it existed at startup) or global config
681
- metricsInfo('config.reload.start', { configPath });
641
+ const configExists = await Bun.file(configPath).exists();
642
+ if (!configExists) {
643
+ metricsInfo('config.reload.skipped', { reason: 'file not found', configPath });
644
+ return;
645
+ }
682
646
 
683
- const configExists = await Bun.file(configPath).exists();
684
- if (!configExists) {
685
- metricsInfo('config.reload.skipped', { reason: 'file not found', configPath });
686
- return;
687
- }
647
+ const reloaded = await loadConfigFromPath(configPath);
688
648
 
689
- const reloaded = await loadConfigFromPath(configPath);
649
+ if (currentProjectConfig !== null) {
650
+ currentProjectConfig = reloaded;
651
+ } else {
652
+ currentGlobalConfig = reloaded;
653
+ }
690
654
 
691
- // Update the appropriate config based on what we had at startup
692
- if (currentProjectConfig !== null) {
693
- currentProjectConfig = reloaded;
694
- } else {
695
- currentGlobalConfig = reloaded;
696
- }
655
+ metricsInfo('config.reload.done', {
656
+ resources: reloaded.resources.length,
657
+ configPath
658
+ });
659
+ };
697
660
 
698
- metricsInfo('config.reload.done', {
699
- resources: reloaded.resources.length,
700
- configPath
701
- });
661
+ const service: ConfigService = {
662
+ resourcesDirectory,
663
+ configPath,
664
+ get resources() {
665
+ return getMergedResources();
702
666
  },
703
- updateModelEffect: (provider, model, providerOptions) =>
667
+ get model() {
668
+ return getActiveConfig().model ?? DEFAULT_MODEL;
669
+ },
670
+ get provider() {
671
+ return getActiveConfig().provider ?? DEFAULT_PROVIDER;
672
+ },
673
+ get providerTimeoutMs() {
674
+ return getActiveConfig().providerTimeoutMs;
675
+ },
676
+ get maxSteps() {
677
+ return getActiveConfig().maxSteps ?? DEFAULT_MAX_STEPS;
678
+ },
679
+ getProviderOptions: (providerId: string) => getMergedProviderOptions()[providerId],
680
+ getResource: (name: string) => getMergedResources().find((r) => r.name === name),
681
+
682
+ updateModel: (provider, model, providerOptions) =>
704
683
  Effect.tryPromise({
705
- try: () => service.updateModel(provider, model, providerOptions),
684
+ try: () => updateModelPromise(provider, model, providerOptions),
706
685
  catch: (cause) => cause
707
686
  }),
708
- addResourceEffect: (resource) =>
687
+ addResource: (resource) =>
709
688
  Effect.tryPromise({
710
- try: () => service.addResource(resource),
689
+ try: () => addResourcePromise(resource),
711
690
  catch: (cause) => cause
712
691
  }),
713
- removeResourceEffect: (name) =>
692
+ removeResource: (name) =>
714
693
  Effect.tryPromise({
715
- try: () => service.removeResource(name),
694
+ try: () => removeResourcePromise(name),
716
695
  catch: (cause) => cause
717
696
  }),
718
- clearResourcesEffect: () =>
697
+ clearResources: () =>
719
698
  Effect.tryPromise({
720
- try: () => service.clearResources(),
699
+ try: () => clearResourcesPromise(),
721
700
  catch: (cause) => cause
722
701
  }),
723
- reloadEffect: () =>
702
+ reload: () =>
724
703
  Effect.tryPromise({
725
- try: () => service.reload(),
704
+ try: () => reloadPromise(),
726
705
  catch: (cause) => cause
727
706
  })
728
707
  };
@@ -97,58 +97,50 @@ export const getDefaultResourceNames: Effect.Effect<string[], never, ConfigServi
97
97
 
98
98
  export const reloadConfig: Effect.Effect<void, unknown, ConfigService> = Effect.flatMap(
99
99
  configService,
100
- (config) => config.reloadEffect()
100
+ (config) => config.reload()
101
101
  );
102
102
 
103
- export const listProviders: Effect.Effect<
104
- Awaited<ReturnType<AgentServiceShape['listProviders']>>,
105
- unknown,
106
- AgentService
107
- > = Effect.flatMap(agentService, (agent) => agent.listProvidersEffect());
103
+ export const listProviders = Effect.flatMap(agentService, (agent) => agent.listProviders());
108
104
 
109
105
  export const loadCollection = (args: {
110
106
  resourceNames: readonly string[];
111
107
  quiet?: boolean;
112
108
  }): Effect.Effect<
113
- Awaited<ReturnType<CollectionsServiceShape['load']>>,
109
+ Awaited<ReturnType<CollectionsServiceShape['loadPromise']>>,
114
110
  unknown,
115
111
  CollectionsService
116
- > => Effect.flatMap(collectionsService, (collections) => collections.loadEffect(args));
112
+ > => Effect.flatMap(collectionsService, (collections) => collections.load(args));
117
113
 
118
114
  export const askQuestion = (args: {
119
- collection: Awaited<ReturnType<CollectionsServiceShape['load']>>;
115
+ collection: Awaited<ReturnType<CollectionsServiceShape['loadPromise']>>;
120
116
  question: string;
121
- }): Effect.Effect<Awaited<ReturnType<AgentServiceShape['ask']>>, unknown, AgentService> =>
122
- Effect.flatMap(agentService, (agent) => agent.askEffect(args));
117
+ }) => Effect.flatMap(agentService, (agent) => agent.ask(args));
123
118
 
124
119
  export const askQuestionStream = (args: {
125
- collection: Awaited<ReturnType<CollectionsServiceShape['load']>>;
120
+ collection: Awaited<ReturnType<CollectionsServiceShape['loadPromise']>>;
126
121
  question: string;
127
- }): Effect.Effect<Awaited<ReturnType<AgentServiceShape['askStream']>>, unknown, AgentService> =>
128
- Effect.flatMap(agentService, (agent) => agent.askStreamEffect(args));
122
+ }) => Effect.flatMap(agentService, (agent) => agent.askStream(args));
129
123
 
130
124
  export const updateModelConfig = (args: {
131
125
  provider: string;
132
126
  model: string;
133
127
  providerOptions?: Parameters<ConfigServiceShape['updateModel']>[2];
134
- }): Effect.Effect<Awaited<ReturnType<ConfigServiceShape['updateModel']>>, unknown, ConfigService> =>
128
+ }) =>
135
129
  Effect.flatMap(configService, (config) =>
136
- config.updateModelEffect(args.provider, args.model, args.providerOptions)
130
+ config.updateModel(args.provider, args.model, args.providerOptions)
137
131
  );
138
132
 
139
133
  export const addConfigResource = (
140
134
  resource: ResourceDefinition
141
135
  ): Effect.Effect<ResourceDefinition, unknown, ConfigService> =>
142
- Effect.flatMap(configService, (config) => config.addResourceEffect(resource));
136
+ Effect.flatMap(configService, (config) => config.addResource(resource));
143
137
 
144
138
  export const removeConfigResource = (name: string): Effect.Effect<void, unknown, ConfigService> =>
145
- Effect.flatMap(configService, (config) => config.removeResourceEffect(name));
139
+ Effect.flatMap(configService, (config) => config.removeResource(name));
146
140
 
147
- export const clearConfigResources: Effect.Effect<
148
- Awaited<ReturnType<ConfigServiceShape['clearResources']>>,
149
- unknown,
150
- ConfigService
151
- > = Effect.flatMap(configService, (config) => config.clearResourcesEffect());
141
+ export const clearConfigResources = Effect.flatMap(configService, (config) =>
142
+ config.clearResources()
143
+ );
152
144
 
153
145
  export const loadedResourceCollectionKey = (resourceNames: readonly string[]) =>
154
146
  getCollectionKey(resourceNames);
@@ -41,13 +41,13 @@ export type ResourcesService = {
41
41
  options?: {
42
42
  quiet?: boolean;
43
43
  }
44
- ) => Promise<BtcaFsResource>;
45
- loadEffect: (
44
+ ) => Effect.Effect<BtcaFsResource, ResourceError, never>;
45
+ loadPromise: (
46
46
  name: string,
47
47
  options?: {
48
48
  quiet?: boolean;
49
49
  }
50
- ) => Effect.Effect<BtcaFsResource, ResourceError>;
50
+ ) => Promise<BtcaFsResource>;
51
51
  };
52
52
 
53
53
  const normalizeSearchPaths = (definition: GitResource): string[] => {
@@ -154,55 +154,56 @@ export const resolveResourceDefinition = (
154
154
  };
155
155
 
156
156
  export const createResourcesService = (config: ConfigServiceShape): ResourcesService => {
157
- const loadEffect: ResourcesService['loadEffect'] = (name, options) =>
158
- Effect.gen(function* () {
159
- const quiet = options?.quiet ?? false;
160
- const definition = yield* Effect.try({
161
- try: () => resolveResourceDefinition(name, config.getResource),
162
- catch: (cause) =>
163
- cause instanceof ResourceError
164
- ? cause
165
- : new ResourceError({
166
- message: `Failed to resolve resource "${name}"`,
167
- hint: `${CommonHints.LIST_RESOURCES} ${CommonHints.ADD_RESOURCE}`,
168
- cause
169
- })
170
- });
171
-
172
- if (isGitResource(definition)) {
173
- return yield* Effect.tryPromise({
174
- try: () =>
175
- loadGitResource(definitionToGitArgs(definition, config.resourcesDirectory, quiet)),
176
- catch: (cause) =>
177
- cause instanceof ResourceError
178
- ? cause
179
- : new ResourceError({
180
- message: `Failed to load git resource "${name}"`,
181
- hint: CommonHints.CLEAR_CACHE,
182
- cause
183
- })
157
+ const loadPromise: ResourcesService['loadPromise'] = async (name, options) => {
158
+ const quiet = options?.quiet ?? false;
159
+ const definition = resolveResourceDefinition(name, config.getResource);
160
+
161
+ if (isGitResource(definition)) {
162
+ try {
163
+ return await loadGitResource(
164
+ definitionToGitArgs(definition, config.resourcesDirectory, quiet)
165
+ );
166
+ } catch (cause) {
167
+ if (cause instanceof ResourceError) throw cause;
168
+ throw new ResourceError({
169
+ message: `Failed to load git resource "${name}"`,
170
+ hint: CommonHints.CLEAR_CACHE,
171
+ cause
184
172
  });
185
173
  }
174
+ }
186
175
 
187
- if (isNpmResource(definition)) {
188
- return yield* Effect.tryPromise({
189
- try: () => loadNpmResource(definitionToNpmArgs(definition, config.resourcesDirectory)),
190
- catch: (cause) =>
191
- cause instanceof ResourceError
192
- ? cause
193
- : new ResourceError({
194
- message: `Failed to load npm resource "${name}"`,
195
- hint: CommonHints.CLEAR_CACHE,
196
- cause
197
- })
176
+ if (isNpmResource(definition)) {
177
+ try {
178
+ return await loadNpmResource(definitionToNpmArgs(definition, config.resourcesDirectory));
179
+ } catch (cause) {
180
+ if (cause instanceof ResourceError) throw cause;
181
+ throw new ResourceError({
182
+ message: `Failed to load npm resource "${name}"`,
183
+ hint: CommonHints.CLEAR_CACHE,
184
+ cause
198
185
  });
199
186
  }
187
+ }
188
+
189
+ return loadLocalResource(definitionToLocalArgs(definition));
190
+ };
200
191
 
201
- return loadLocalResource(definitionToLocalArgs(definition));
192
+ const load: ResourcesService['load'] = (name, options) =>
193
+ Effect.tryPromise({
194
+ try: () => loadPromise(name, options),
195
+ catch: (cause) =>
196
+ cause instanceof ResourceError
197
+ ? cause
198
+ : new ResourceError({
199
+ message: `Failed to resolve resource "${name}"`,
200
+ hint: `${CommonHints.LIST_RESOURCES} ${CommonHints.ADD_RESOURCE}`,
201
+ cause
202
+ })
202
203
  });
203
204
 
204
205
  return {
205
- load: (name, options) => Effect.runPromise(loadEffect(name, options)),
206
- loadEffect
206
+ load,
207
+ loadPromise
207
208
  };
208
209
  };