@shipfox/api-agent 4.0.0 → 6.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 (90) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/CHANGELOG.md +48 -0
  3. package/dist/core/custom-model-provider-config-service.d.ts +4 -0
  4. package/dist/core/custom-model-provider-config-service.d.ts.map +1 -1
  5. package/dist/core/custom-model-provider-config-service.js +19 -16
  6. package/dist/core/custom-model-provider-config-service.js.map +1 -1
  7. package/dist/core/model-provider-config-service.d.ts +4 -0
  8. package/dist/core/model-provider-config-service.d.ts.map +1 -1
  9. package/dist/core/model-provider-config-service.js +10 -8
  10. package/dist/core/model-provider-config-service.js.map +1 -1
  11. package/dist/core/resolve-runtime-credentials.d.ts +6 -2
  12. package/dist/core/resolve-runtime-credentials.d.ts.map +1 -1
  13. package/dist/core/resolve-runtime-credentials.js +10 -5
  14. package/dist/core/resolve-runtime-credentials.js.map +1 -1
  15. package/dist/core/secrets-client.d.ts +4 -0
  16. package/dist/core/secrets-client.d.ts.map +1 -0
  17. package/dist/core/secrets-client.js +6 -0
  18. package/dist/core/secrets-client.js.map +1 -0
  19. package/dist/index.d.ts +4 -1
  20. package/dist/index.d.ts.map +1 -1
  21. package/dist/index.js +19 -13
  22. package/dist/index.js.map +1 -1
  23. package/dist/presentation/e2eRoutes/create-model-provider.d.ts +2 -1
  24. package/dist/presentation/e2eRoutes/create-model-provider.d.ts.map +1 -1
  25. package/dist/presentation/e2eRoutes/create-model-provider.js +46 -45
  26. package/dist/presentation/e2eRoutes/create-model-provider.js.map +1 -1
  27. package/dist/presentation/e2eRoutes/index.d.ts +2 -0
  28. package/dist/presentation/e2eRoutes/index.d.ts.map +1 -1
  29. package/dist/presentation/e2eRoutes/index.js +9 -6
  30. package/dist/presentation/e2eRoutes/index.js.map +1 -1
  31. package/dist/presentation/index.d.ts +1 -1
  32. package/dist/presentation/index.d.ts.map +1 -1
  33. package/dist/presentation/index.js +1 -1
  34. package/dist/presentation/index.js.map +1 -1
  35. package/dist/presentation/inter-module.d.ts +7 -0
  36. package/dist/presentation/inter-module.d.ts.map +1 -0
  37. package/dist/presentation/inter-module.js +45 -0
  38. package/dist/presentation/inter-module.js.map +1 -0
  39. package/dist/presentation/routes/create-custom-model-provider.d.ts +2 -1
  40. package/dist/presentation/routes/create-custom-model-provider.d.ts.map +1 -1
  41. package/dist/presentation/routes/create-custom-model-provider.js +39 -35
  42. package/dist/presentation/routes/create-custom-model-provider.js.map +1 -1
  43. package/dist/presentation/routes/delete-model-provider-config.d.ts +2 -1
  44. package/dist/presentation/routes/delete-model-provider-config.d.ts.map +1 -1
  45. package/dist/presentation/routes/delete-model-provider-config.js +33 -29
  46. package/dist/presentation/routes/delete-model-provider-config.js.map +1 -1
  47. package/dist/presentation/routes/discover-custom-model-provider-models-by-slug.d.ts +2 -1
  48. package/dist/presentation/routes/discover-custom-model-provider-models-by-slug.d.ts.map +1 -1
  49. package/dist/presentation/routes/discover-custom-model-provider-models-by-slug.js +32 -28
  50. package/dist/presentation/routes/discover-custom-model-provider-models-by-slug.js.map +1 -1
  51. package/dist/presentation/routes/errors.d.ts.map +1 -1
  52. package/dist/presentation/routes/errors.js +13 -12
  53. package/dist/presentation/routes/errors.js.map +1 -1
  54. package/dist/presentation/routes/index.d.ts +2 -0
  55. package/dist/presentation/routes/index.d.ts.map +1 -1
  56. package/dist/presentation/routes/index.js +32 -29
  57. package/dist/presentation/routes/index.js.map +1 -1
  58. package/dist/presentation/routes/update-custom-model-provider.d.ts +2 -1
  59. package/dist/presentation/routes/update-custom-model-provider.d.ts.map +1 -1
  60. package/dist/presentation/routes/update-custom-model-provider.js +41 -37
  61. package/dist/presentation/routes/update-custom-model-provider.js.map +1 -1
  62. package/dist/presentation/routes/upsert-model-provider-config.d.ts +2 -1
  63. package/dist/presentation/routes/upsert-model-provider-config.d.ts.map +1 -1
  64. package/dist/presentation/routes/upsert-model-provider-config.js +46 -42
  65. package/dist/presentation/routes/upsert-model-provider-config.js.map +1 -1
  66. package/dist/tsconfig.test.tsbuildinfo +1 -1
  67. package/package.json +30 -61
  68. package/src/core/custom-model-provider-config-service.ts +50 -34
  69. package/src/core/model-provider-config-service.test.ts +1 -3
  70. package/src/core/model-provider-config-service.ts +18 -15
  71. package/src/core/resolve-runtime-credentials.test.ts +3 -3
  72. package/src/core/resolve-runtime-credentials.ts +22 -8
  73. package/src/core/secrets-client.ts +13 -0
  74. package/src/index.ts +13 -8
  75. package/src/presentation/e2eRoutes/create-model-provider.ts +41 -39
  76. package/src/presentation/e2eRoutes/index.ts +6 -4
  77. package/src/presentation/index.ts +1 -1
  78. package/src/presentation/inter-module.ts +78 -0
  79. package/src/presentation/routes/create-custom-model-provider.ts +36 -30
  80. package/src/presentation/routes/delete-model-provider-config.ts +25 -22
  81. package/src/presentation/routes/discover-custom-model-provider-models-by-slug.ts +31 -25
  82. package/src/presentation/routes/errors.ts +11 -11
  83. package/src/presentation/routes/index.ts +32 -27
  84. package/src/presentation/routes/model-provider-config.test.ts +1 -1
  85. package/src/presentation/routes/update-custom-model-provider.ts +40 -34
  86. package/src/presentation/routes/upsert-model-provider-config.ts +43 -37
  87. package/test/fixtures/secrets-client.ts +80 -0
  88. package/test/globalSetup.ts +0 -9
  89. package/test/setup.ts +8 -0
  90. package/tsconfig.build.tsbuildinfo +1 -1
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@shipfox/api-agent",
3
3
  "license": "MIT",
4
- "version": "4.0.0",
4
+ "version": "6.0.0",
5
5
  "repository": {
6
6
  "type": "git",
7
7
  "url": "git+https://github.com/ShipfoxHQ/shipfox.git",
@@ -13,62 +13,28 @@
13
13
  "types": "dist/index.d.ts",
14
14
  "imports": {
15
15
  "#test/*": "./test/*",
16
- "#*": {
17
- "workspace-source": "./src/*",
18
- "development": "./src/*",
19
- "default": "./dist/*"
20
- }
16
+ "#*": "./dist/*"
21
17
  },
22
18
  "exports": {
23
19
  ".": {
24
- "development": {
25
- "types": "./src/index.ts",
26
- "default": "./src/index.ts"
27
- },
28
- "default": {
29
- "types": "./dist/index.d.ts",
30
- "default": "./dist/index.js"
31
- }
20
+ "types": "./dist/index.d.ts",
21
+ "default": "./dist/index.js"
32
22
  },
33
23
  "./core/errors": {
34
- "development": {
35
- "types": "./src/core/errors.ts",
36
- "default": "./src/core/errors.ts"
37
- },
38
- "default": {
39
- "types": "./dist/core/errors.d.ts",
40
- "default": "./dist/core/errors.js"
41
- }
24
+ "types": "./dist/core/errors.d.ts",
25
+ "default": "./dist/core/errors.js"
42
26
  },
43
27
  "./core/resolve-agent-config": {
44
- "development": {
45
- "types": "./src/core/resolve-agent-config.ts",
46
- "default": "./src/core/resolve-agent-config.ts"
47
- },
48
- "default": {
49
- "types": "./dist/core/resolve-agent-config.d.ts",
50
- "default": "./dist/core/resolve-agent-config.js"
51
- }
28
+ "types": "./dist/core/resolve-agent-config.d.ts",
29
+ "default": "./dist/core/resolve-agent-config.js"
52
30
  },
53
31
  "./core/resolve-runtime-credentials": {
54
- "development": {
55
- "types": "./src/core/resolve-runtime-credentials.ts",
56
- "default": "./src/core/resolve-runtime-credentials.ts"
57
- },
58
- "default": {
59
- "types": "./dist/core/resolve-runtime-credentials.d.ts",
60
- "default": "./dist/core/resolve-runtime-credentials.js"
61
- }
32
+ "types": "./dist/core/resolve-runtime-credentials.d.ts",
33
+ "default": "./dist/core/resolve-runtime-credentials.js"
62
34
  },
63
35
  "./core/workspace-agent-defaults-resolver": {
64
- "development": {
65
- "types": "./src/core/workspace-agent-defaults-resolver.ts",
66
- "default": "./src/core/workspace-agent-defaults-resolver.ts"
67
- },
68
- "default": {
69
- "types": "./dist/core/workspace-agent-defaults-resolver.d.ts",
70
- "default": "./dist/core/workspace-agent-defaults-resolver.js"
71
- }
36
+ "types": "./dist/core/workspace-agent-defaults-resolver.d.ts",
37
+ "default": "./dist/core/workspace-agent-defaults-resolver.js"
72
38
  }
73
39
  },
74
40
  "dependencies": {
@@ -76,31 +42,34 @@
76
42
  "@opentelemetry/api": "^1.9.1",
77
43
  "drizzle-orm": "^0.45.2",
78
44
  "zod": "^4.4.3",
79
- "@shipfox/api-agent-dto": "3.0.0",
80
- "@shipfox/api-auth-context": "3.0.0",
81
- "@shipfox/api-secrets": "4.0.0",
82
- "@shipfox/config": "1.2.1",
83
- "@shipfox/node-drizzle": "0.3.0",
84
- "@shipfox/node-egress-guard": "0.1.0",
85
- "@shipfox/node-fastify": "0.2.2",
86
- "@shipfox/node-module": "0.3.1",
87
- "@shipfox/node-opentelemetry": "0.5.1",
88
- "@shipfox/node-postgres": "0.4.1",
89
- "@shipfox/redact": "0.2.1"
45
+ "@shipfox/api-agent-dto": "6.0.0",
46
+ "@shipfox/api-auth-context": "6.0.0",
47
+ "@shipfox/api-secrets-dto": "6.0.0",
48
+ "@shipfox/inter-module": "0.2.0",
49
+ "@shipfox/config": "1.2.2",
50
+ "@shipfox/node-drizzle": "0.3.2",
51
+ "@shipfox/node-egress-guard": "0.1.1",
52
+ "@shipfox/node-fastify": "0.2.4",
53
+ "@shipfox/node-module": "0.4.0",
54
+ "@shipfox/node-opentelemetry": "0.5.2",
55
+ "@shipfox/node-postgres": "0.4.2",
56
+ "@shipfox/redact": "0.2.3"
90
57
  },
91
58
  "devDependencies": {
92
59
  "@types/pg": "^8.15.5",
93
60
  "drizzle-kit": "^0.31.10",
94
- "@shipfox/biome": "1.8.1",
95
- "@shipfox/swc": "1.2.5",
61
+ "@shipfox/biome": "1.8.2",
62
+ "@shipfox/depcruise": "1.0.2",
63
+ "@shipfox/swc": "1.2.6",
96
64
  "@shipfox/ts-config": "1.3.8",
97
- "@shipfox/typescript": "1.1.6",
98
- "@shipfox/vitest": "1.2.2"
65
+ "@shipfox/typescript": "1.1.7",
66
+ "@shipfox/vitest": "1.2.3"
99
67
  },
100
68
  "scripts": {
101
69
  "build": "shipfox-swc",
102
70
  "check": "shipfox-biome-check",
103
71
  "check:fix": "shipfox-biome-check --write",
72
+ "depcruise": "shipfox-depcruise",
104
73
  "test": "shipfox-vitest-run",
105
74
  "test:watch": "shipfox-vitest-watch",
106
75
  "type": "shipfox-tsc-check",
@@ -10,7 +10,6 @@ import {
10
10
  type UpdateCustomModelProviderBodyDto,
11
11
  type UpdateCustomModelProviderHeaderRequestDto,
12
12
  } from '@shipfox/api-agent-dto';
13
- import {deleteSecrets, getSecretsByNamespace, setSecrets} from '@shipfox/api-secrets';
14
13
  import {assertEgressAllowed} from '@shipfox/node-egress-guard';
15
14
  import {
16
15
  deleteModelProviderConfig,
@@ -38,6 +37,7 @@ import {
38
37
  probeCustomModelProviderCredentials,
39
38
  runProviderProbe,
40
39
  } from './model-provider-validation.js';
40
+ import {type AgentSecretsClient, requireAgentSecretsClient} from './secrets-client.js';
41
41
 
42
42
  export interface CreateCustomModelProviderConfigParams {
43
43
  workspaceId: string;
@@ -55,6 +55,7 @@ export interface UpdateCustomModelProviderConfigParams {
55
55
 
56
56
  export interface CustomModelProviderConfigServiceOptions {
57
57
  probe?: typeof probeCustomModelProviderCredentials | undefined;
58
+ secrets?: AgentSecretsClient | undefined;
58
59
  }
59
60
 
60
61
  interface SplitHeaders {
@@ -69,6 +70,7 @@ export async function createCustomModelProviderConfig(
69
70
  options: CustomModelProviderConfigServiceOptions = {},
70
71
  ): Promise<ModelProviderConfig> {
71
72
  const probe = options.probe ?? probeCustomModelProviderCredentials;
73
+ const secrets = requireAgentSecretsClient(options.secrets);
72
74
 
73
75
  const existing = await getModelProviderConfig({
74
76
  workspaceId: params.workspaceId,
@@ -123,7 +125,7 @@ export async function createCustomModelProviderConfig(
123
125
  }
124
126
 
125
127
  try {
126
- await setSecrets({
128
+ await secrets.setSecrets({
127
129
  workspaceId: params.workspaceId,
128
130
  namespace: agentSystemNamespace(params.body.slug),
129
131
  values: customCredentialsToStoreValues(secretCredentials),
@@ -134,7 +136,7 @@ export async function createCustomModelProviderConfig(
134
136
  workspaceId: params.workspaceId,
135
137
  providerId: params.body.slug,
136
138
  });
137
- await deleteSecrets({
139
+ await secrets.deleteSecrets({
138
140
  workspaceId: params.workspaceId,
139
141
  namespace: agentSystemNamespace(params.body.slug),
140
142
  });
@@ -147,9 +149,10 @@ export async function updateCustomModelProviderConfig(
147
149
  options: CustomModelProviderConfigServiceOptions = {},
148
150
  ): Promise<ModelProviderConfig> {
149
151
  const probe = options.probe ?? probeCustomModelProviderCredentials;
152
+ const secrets = requireAgentSecretsClient(options.secrets);
150
153
 
151
154
  const existing = await getExistingCustomConfig(params.workspaceId, params.providerId);
152
- const existingSecrets = await getCustomProviderSecrets({
155
+ const existingSecrets = await getCustomProviderSecrets(secrets, {
153
156
  workspaceId: params.workspaceId,
154
157
  providerId: params.providerId,
155
158
  });
@@ -204,7 +207,7 @@ export async function updateCustomModelProviderConfig(
204
207
  });
205
208
 
206
209
  const namespace = agentSystemNamespace(params.providerId);
207
- await setSecrets({
210
+ await secrets.setSecrets({
208
211
  workspaceId: params.workspaceId,
209
212
  namespace,
210
213
  values: customCredentialsToStoreValues(newSecretCredentials),
@@ -227,7 +230,7 @@ export async function updateCustomModelProviderConfig(
227
230
  defaultThinking: existing.defaultThinking,
228
231
  });
229
232
  } catch (error) {
230
- await rollbackCustomProviderSecretUpdate({
233
+ await rollbackCustomProviderSecretUpdate(secrets, {
231
234
  workspaceId: params.workspaceId,
232
235
  namespace,
233
236
  existingSecrets,
@@ -236,7 +239,7 @@ export async function updateCustomModelProviderConfig(
236
239
  throw error;
237
240
  }
238
241
 
239
- await pruneStaleCustomHeaderSecrets({
242
+ await pruneStaleCustomHeaderSecrets(secrets, {
240
243
  workspaceId: params.workspaceId,
241
244
  providerId: params.providerId,
242
245
  existingSecrets,
@@ -247,13 +250,17 @@ export async function updateCustomModelProviderConfig(
247
250
  return updated;
248
251
  }
249
252
 
250
- export async function resolveCustomModelProviderDiscoveryParams(params: {
251
- workspaceId: string;
252
- providerId: ModelProviderRef;
253
- body: DiscoverCustomModelProviderModelsBySlugBodyDto;
254
- }): Promise<DiscoverCustomModelProviderModelsBodyDto> {
253
+ export async function resolveCustomModelProviderDiscoveryParams(
254
+ params: {
255
+ workspaceId: string;
256
+ providerId: ModelProviderRef;
257
+ body: DiscoverCustomModelProviderModelsBySlugBodyDto;
258
+ },
259
+ options: {secrets?: AgentSecretsClient | undefined} = {},
260
+ ): Promise<DiscoverCustomModelProviderModelsBodyDto> {
261
+ const secrets = requireAgentSecretsClient(options.secrets);
255
262
  const existing = await getExistingCustomConfig(params.workspaceId, params.providerId);
256
- const existingSecrets = await getCustomProviderSecrets({
263
+ const existingSecrets = await getCustomProviderSecrets(secrets, {
257
264
  workspaceId: params.workspaceId,
258
265
  providerId: params.providerId,
259
266
  });
@@ -434,11 +441,14 @@ function selectProbeModel(
434
441
  return model;
435
442
  }
436
443
 
437
- async function getCustomProviderSecrets(params: {
438
- workspaceId: string;
439
- providerId: ModelProviderRef;
440
- }): Promise<Record<string, string>> {
441
- const values = await getSecretsByNamespace({
444
+ async function getCustomProviderSecrets(
445
+ secrets: AgentSecretsClient,
446
+ params: {
447
+ workspaceId: string;
448
+ providerId: ModelProviderRef;
449
+ },
450
+ ): Promise<Record<string, string>> {
451
+ const {values} = await secrets.getSecretsByNamespace({
442
452
  workspaceId: params.workspaceId,
443
453
  namespace: agentSystemNamespace(params.providerId),
444
454
  });
@@ -474,12 +484,15 @@ function normalizedUrl(value: string): string {
474
484
  return normalizeCustomProviderBaseUrl(value);
475
485
  }
476
486
 
477
- async function rollbackCustomProviderSecretUpdate(params: {
478
- workspaceId: string;
479
- namespace: string;
480
- existingSecrets: Record<string, string>;
481
- attemptedSecrets: Record<string, string>;
482
- }): Promise<void> {
487
+ async function rollbackCustomProviderSecretUpdate(
488
+ secrets: AgentSecretsClient,
489
+ params: {
490
+ workspaceId: string;
491
+ namespace: string;
492
+ existingSecrets: Record<string, string>;
493
+ attemptedSecrets: Record<string, string>;
494
+ },
495
+ ): Promise<void> {
483
496
  const attemptedKeys = Object.keys(params.attemptedSecrets);
484
497
  if (attemptedKeys.length === 0) return;
485
498
 
@@ -490,7 +503,7 @@ async function rollbackCustomProviderSecretUpdate(params: {
490
503
  }),
491
504
  );
492
505
  if (Object.keys(secretsToRestore).length > 0) {
493
- await setSecrets({
506
+ await secrets.setSecrets({
494
507
  workspaceId: params.workspaceId,
495
508
  namespace: params.namespace,
496
509
  values: customCredentialsToStoreValues(secretsToRestore),
@@ -500,20 +513,23 @@ async function rollbackCustomProviderSecretUpdate(params: {
500
513
  const newKeys = attemptedKeys.filter((key) => params.existingSecrets[key] === undefined);
501
514
  if (newKeys.length === 0) return;
502
515
 
503
- await deleteSecrets({
516
+ await secrets.deleteSecrets({
504
517
  workspaceId: params.workspaceId,
505
518
  namespace: params.namespace,
506
519
  keys: newKeys.map(customCredentialToStoreKey),
507
520
  });
508
521
  }
509
522
 
510
- async function pruneStaleCustomHeaderSecrets(params: {
511
- workspaceId: string;
512
- providerId: ModelProviderRef;
513
- existingSecrets: Record<string, string>;
514
- newSecrets: Record<string, string>;
515
- replaceHeaders: boolean;
516
- }): Promise<void> {
523
+ async function pruneStaleCustomHeaderSecrets(
524
+ secrets: AgentSecretsClient,
525
+ params: {
526
+ workspaceId: string;
527
+ providerId: ModelProviderRef;
528
+ existingSecrets: Record<string, string>;
529
+ newSecrets: Record<string, string>;
530
+ replaceHeaders: boolean;
531
+ },
532
+ ): Promise<void> {
517
533
  if (!params.replaceHeaders) return;
518
534
 
519
535
  const nextKeys = new Set(Object.keys(params.newSecrets));
@@ -522,7 +538,7 @@ async function pruneStaleCustomHeaderSecrets(params: {
522
538
  );
523
539
  if (staleKeys.length === 0) return;
524
540
 
525
- await deleteSecrets({
541
+ await secrets.deleteSecrets({
526
542
  workspaceId: params.workspaceId,
527
543
  namespace: agentSystemNamespace(params.providerId),
528
544
  keys: staleKeys.map(customCredentialToStoreKey),
@@ -1,7 +1,7 @@
1
1
  import crypto from 'node:crypto';
2
- import * as secretStore from '@shipfox/api-secrets';
3
2
  import * as modelProviderDb from '#db/index.js';
4
3
  import {getModelProviderConfig, upsertModelProviderConfig} from '#db/index.js';
4
+ import {getSecretsByNamespace, setSecrets} from '#test/fixtures/secrets-client.js';
5
5
  import {agentSystemNamespace} from './credential-fingerprints.js';
6
6
  import {
7
7
  createCustomModelProviderConfig,
@@ -23,8 +23,6 @@ import {
23
23
  updateModelProviderConfigDefaultModel,
24
24
  } from './model-provider-config-service.js';
25
25
 
26
- const {getSecretsByNamespace, setSecrets} = secretStore;
27
-
28
26
  describe('testAndSaveModelProviderConfig', () => {
29
27
  let workspaceId: string;
30
28
 
@@ -5,7 +5,6 @@ import {
5
5
  modelProviderCredentialKeysMatch,
6
6
  type SupportedModelProviderId,
7
7
  } from '@shipfox/api-agent-dto';
8
- import {deleteSecrets, getSecretsByNamespace, setSecrets} from '@shipfox/api-secrets';
9
8
  import {
10
9
  deleteModelProviderConfig as deleteModelProviderConfigRow,
11
10
  getModelProviderConfig,
@@ -22,6 +21,7 @@ import {
22
21
  } from './errors.js';
23
22
  import {buildModelProviderCatalog} from './model-provider-catalog.js';
24
23
  import {probeModelProviderCredentials, runProviderProbe} from './model-provider-validation.js';
24
+ import {type AgentSecretsClient, requireAgentSecretsClient} from './secrets-client.js';
25
25
 
26
26
  export interface TestAndSaveModelProviderConfigParams {
27
27
  workspaceId: string;
@@ -35,6 +35,7 @@ export interface TestAndSaveModelProviderConfigParams {
35
35
 
36
36
  export interface TestAndSaveModelProviderConfigOptions {
37
37
  probe?: typeof probeModelProviderCredentials;
38
+ secrets?: AgentSecretsClient | undefined;
38
39
  pruneStaleSecrets?:
39
40
  | ((params: {workspaceId: string; namespace: string; expectedKeys: string[]}) => Promise<void>)
40
41
  | undefined;
@@ -51,6 +52,7 @@ export async function testAndSaveModelProviderConfig(
51
52
  options: TestAndSaveModelProviderConfigOptions = {},
52
53
  ): Promise<ModelProviderConfig> {
53
54
  const probe = options.probe ?? probeModelProviderCredentials;
55
+ const secrets = requireAgentSecretsClient(options.secrets);
54
56
  const entry = getModelProviderEntry(params.providerId);
55
57
  if (entry === undefined || entry.support_status !== 'supported') {
56
58
  throw new UnsupportedModelProviderError(params.providerId);
@@ -83,10 +85,11 @@ export async function testAndSaveModelProviderConfig(
83
85
  secrets: Object.values(params.credentials),
84
86
  signal: params.signal,
85
87
  });
86
- const pruneStaleSecrets = options.pruneStaleSecrets ?? pruneStaleProviderCredentialSecrets;
88
+ const pruneStaleSecrets =
89
+ options.pruneStaleSecrets ?? ((input) => pruneStaleProviderCredentialSecrets(secrets, input));
87
90
  const namespace = agentSystemNamespace(params.providerId);
88
91
  const values = credentialsToStoreValues(params.providerId, params.credentials);
89
- await setSecrets({
92
+ await secrets.setSecrets({
90
93
  workspaceId: params.workspaceId,
91
94
  namespace,
92
95
  values,
@@ -154,12 +157,13 @@ export async function updateModelProviderConfigDefaultModel(
154
157
  return config;
155
158
  }
156
159
 
157
- export async function deleteModelProviderConfig(params: {
158
- workspaceId: string;
159
- providerId: ModelProviderRef;
160
- }): Promise<boolean> {
160
+ export async function deleteModelProviderConfig(
161
+ params: {workspaceId: string; providerId: ModelProviderRef},
162
+ options: {secrets?: AgentSecretsClient | undefined} = {},
163
+ ): Promise<boolean> {
164
+ const secrets = requireAgentSecretsClient(options.secrets);
161
165
  const deleted = await deleteModelProviderConfigRow(params);
162
- await deleteSecrets({
166
+ await secrets.deleteSecrets({
163
167
  workspaceId: params.workspaceId,
164
168
  namespace: agentSystemNamespace(params.providerId),
165
169
  });
@@ -186,12 +190,11 @@ function resolveDefaultModel(
186
190
  return {probeModel: model, storedModel: requestedModel ?? null};
187
191
  }
188
192
 
189
- async function pruneStaleProviderCredentialSecrets(params: {
190
- workspaceId: string;
191
- namespace: string;
192
- expectedKeys: string[];
193
- }): Promise<void> {
194
- const stored = await getSecretsByNamespace({
193
+ async function pruneStaleProviderCredentialSecrets(
194
+ secrets: AgentSecretsClient,
195
+ params: {workspaceId: string; namespace: string; expectedKeys: string[]},
196
+ ): Promise<void> {
197
+ const {values: stored} = await secrets.getSecretsByNamespace({
195
198
  workspaceId: params.workspaceId,
196
199
  namespace: params.namespace,
197
200
  });
@@ -199,7 +202,7 @@ async function pruneStaleProviderCredentialSecrets(params: {
199
202
  const staleKeys = Object.keys(stored).filter((key) => !expected.has(key));
200
203
  if (staleKeys.length === 0) return;
201
204
 
202
- await deleteSecrets({
205
+ await secrets.deleteSecrets({
203
206
  workspaceId: params.workspaceId,
204
207
  namespace: params.namespace,
205
208
  keys: staleKeys,
@@ -1,6 +1,6 @@
1
1
  import type {ModelProviderRef} from '@shipfox/api-agent-dto';
2
- import {SecretDecryptionError, setSecrets} from '@shipfox/api-secrets';
3
2
  import {deleteModelProviderConfig, upsertModelProviderConfig} from '#db/index.js';
3
+ import {setSecrets} from '#test/fixtures/secrets-client.js';
4
4
  import {agentSystemNamespace, customCredentialsToStoreValues} from './credential-fingerprints.js';
5
5
  import {ModelProviderConfigNotFoundError} from './errors.js';
6
6
  import {resolveRuntimeCredentials} from './resolve-runtime-credentials.js';
@@ -245,7 +245,7 @@ describe('resolveRuntimeCredentials', () => {
245
245
  });
246
246
 
247
247
  it('does not expose credential material on store decryption errors', async () => {
248
- const error = new SecretDecryptionError();
248
+ const error = new Error('Secret decryption failed');
249
249
  await upsertModelProviderConfig({
250
250
  workspaceId,
251
251
  providerId: 'anthropic',
@@ -264,7 +264,7 @@ describe('resolveRuntimeCredentials', () => {
264
264
  {getCredentialBag: vi.fn().mockRejectedValue(error)},
265
265
  );
266
266
 
267
- await expect(result).rejects.toThrow(SecretDecryptionError);
267
+ await expect(result).rejects.toBe(error);
268
268
  try {
269
269
  await result;
270
270
  } catch (error) {
@@ -7,7 +7,8 @@ import {
7
7
  modelProviderCredentialKeysMatch,
8
8
  type SupportedModelProviderId,
9
9
  } from '@shipfox/api-agent-dto';
10
- import {getSecretsByNamespace, SecretDecryptionError} from '@shipfox/api-secrets';
10
+ import {secretsInterModuleContract} from '@shipfox/api-secrets-dto/inter-module';
11
+ import {isInterModuleKnownError} from '@shipfox/inter-module';
11
12
  import {config} from '#config.js';
12
13
  import {getModelProviderConfig} from '#db/index.js';
13
14
  import {agentRuntimeConfigResolvedCount} from '#metrics/index.js';
@@ -18,6 +19,7 @@ import {
18
19
  } from './credential-fingerprints.js';
19
20
  import type {ModelProviderConfig} from './entities/model-provider-config.js';
20
21
  import {ModelProviderConfigNotFoundError} from './errors.js';
22
+ import {type AgentSecretsClient, requireAgentSecretsClient} from './secrets-client.js';
21
23
 
22
24
  export interface ResolveRuntimeCredentialsParams {
23
25
  workspaceId: string;
@@ -34,7 +36,10 @@ interface RuntimeCredentialsConfig {
34
36
 
35
37
  interface ResolveRuntimeCredentialsOptions {
36
38
  runtimeConfig?: RuntimeCredentialsConfig | undefined;
37
- getCredentialBag?: typeof getSecretsByNamespace | undefined;
39
+ secrets?: AgentSecretsClient | undefined;
40
+ getCredentialBag?:
41
+ | ((params: {workspaceId: string; namespace: string}) => Promise<Record<string, string>>)
42
+ | undefined;
38
43
  }
39
44
 
40
45
  export async function resolveRuntimeCredentials(
@@ -42,7 +47,7 @@ export async function resolveRuntimeCredentials(
42
47
  options?: ResolveRuntimeCredentialsOptions,
43
48
  ): Promise<AgentRuntimeCredentialsResponseDto> {
44
49
  const runtimeConfig = options?.runtimeConfig ?? config;
45
- const getCredentialBag = options?.getCredentialBag ?? getSecretsByNamespace;
50
+ const secrets = options?.secrets;
46
51
  const providerConfig = await getModelProviderConfig({
47
52
  workspaceId: params.workspaceId,
48
53
  providerId: params.provider,
@@ -50,10 +55,17 @@ export async function resolveRuntimeCredentials(
50
55
 
51
56
  if (providerConfig) {
52
57
  try {
53
- const values = await getCredentialBag({
54
- workspaceId: params.workspaceId,
55
- namespace: agentSystemNamespace(params.provider),
56
- });
58
+ const values = options?.getCredentialBag
59
+ ? await options.getCredentialBag({
60
+ workspaceId: params.workspaceId,
61
+ namespace: agentSystemNamespace(params.provider),
62
+ })
63
+ : (
64
+ await requireAgentSecretsClient(secrets).getSecretsByNamespace({
65
+ workspaceId: params.workspaceId,
66
+ namespace: agentSystemNamespace(params.provider),
67
+ })
68
+ ).values;
57
69
  if (providerConfig.kind === 'custom') {
58
70
  const credentials = storeValuesToCustomRuntimeCredentials(values);
59
71
  agentRuntimeConfigResolvedCount.add(1, {source: 'workspace', outcome: 'resolved'});
@@ -68,7 +80,9 @@ export async function resolveRuntimeCredentials(
68
80
  agentRuntimeConfigResolvedCount.add(1, {source: 'workspace', outcome: 'resolved'});
69
81
  return toResponse(params, credentials, providerConfig);
70
82
  } catch (error) {
71
- if (error instanceof SecretDecryptionError) {
83
+ if (
84
+ isInterModuleKnownError(secretsInterModuleContract.methods.getSecretsByNamespace, error)
85
+ ) {
72
86
  agentRuntimeConfigResolvedCount.add(1, {
73
87
  source: 'workspace',
74
88
  outcome: 'decryption_failed',
@@ -0,0 +1,13 @@
1
+ import type {SecretsInterModuleClient} from '@shipfox/api-secrets-dto/inter-module';
2
+
3
+ export type AgentSecretsClient = Pick<
4
+ SecretsInterModuleClient,
5
+ 'deleteSecrets' | 'getSecretsByNamespace' | 'setSecrets'
6
+ >;
7
+
8
+ export function requireAgentSecretsClient(
9
+ secrets: AgentSecretsClient | undefined,
10
+ ): AgentSecretsClient {
11
+ if (secrets) return secrets;
12
+ throw new Error('Agent Secrets client is not configured.');
13
+ }
package/src/index.ts CHANGED
@@ -1,7 +1,9 @@
1
1
  import type {ShipfoxModule} from '@shipfox/node-module';
2
+ import type {AgentSecretsClient} from '#core/secrets-client.js';
2
3
  import {db, migrationsPath} from '#db/index.js';
3
- import {agentE2eRoutes} from '#presentation/e2eRoutes/index.js';
4
- import {routes} from '#presentation/index.js';
4
+ import {createAgentE2eRoutes} from '#presentation/e2eRoutes/index.js';
5
+ import {createAgentInterModulePresentation} from '#presentation/inter-module.js';
6
+ import {createAgentRoutes} from '#presentation/routes/index.js';
5
7
 
6
8
  export {
7
9
  type AgentConfigResolutionContext,
@@ -35,9 +37,12 @@ export {
35
37
  upsertModelProviderConfig,
36
38
  } from '#db/index.js';
37
39
 
38
- export const agentModule: ShipfoxModule = {
39
- name: 'agent',
40
- database: {db, migrationsPath},
41
- routes,
42
- e2eRoutes: [agentE2eRoutes],
43
- };
40
+ export function createAgentModule(params: {secrets: AgentSecretsClient}): ShipfoxModule {
41
+ return {
42
+ name: 'agent',
43
+ database: {db, migrationsPath},
44
+ routes: createAgentRoutes(params.secrets),
45
+ e2eRoutes: [createAgentE2eRoutes(params.secrets)],
46
+ interModulePresentations: [createAgentInterModulePresentation(params)],
47
+ };
48
+ }