open-managed-agents 0.1.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 (197) hide show
  1. package/LICENSE +93 -0
  2. package/README.md +248 -0
  3. package/bin/oma.mjs +499 -0
  4. package/dist/scripts/alpha-openai-compatible-fixture.mjs +134 -0
  5. package/dist/scripts/alpha-smoke-models.mjs +92 -0
  6. package/dist/scripts/alpha-smoke.mjs +581 -0
  7. package/dist/scripts/oma-doctor.js +230 -0
  8. package/dist/scripts/oma-models.js +299 -0
  9. package/dist/scripts/oma-workspaces.js +133 -0
  10. package/dist/src/control-plane/admin/auth.js +43 -0
  11. package/dist/src/control-plane/admin/routes.js +76 -0
  12. package/dist/src/control-plane/admin/service.js +102 -0
  13. package/dist/src/control-plane/admission.js +90 -0
  14. package/dist/src/control-plane/agents/routes.js +63 -0
  15. package/dist/src/control-plane/agents/service.js +687 -0
  16. package/dist/src/control-plane/agents/store.js +391 -0
  17. package/dist/src/control-plane/agents/types.js +1 -0
  18. package/dist/src/control-plane/api-constants.js +3 -0
  19. package/dist/src/control-plane/app.js +907 -0
  20. package/dist/src/control-plane/console/auth.js +68 -0
  21. package/dist/src/control-plane/console/static.js +112 -0
  22. package/dist/src/control-plane/deployment-runtime-config.js +328 -0
  23. package/dist/src/control-plane/deployment-runtime-event-coordinator.js +25 -0
  24. package/dist/src/control-plane/deployment-runtime-turn-guard.js +14 -0
  25. package/dist/src/control-plane/deployment-session-coordinator.js +25 -0
  26. package/dist/src/control-plane/deployment-session-output-coordinator.js +27 -0
  27. package/dist/src/control-plane/deployment-storage.js +354 -0
  28. package/dist/src/control-plane/egress/image.js +48 -0
  29. package/dist/src/control-plane/egress/policy.js +571 -0
  30. package/dist/src/control-plane/egress/presets.js +77 -0
  31. package/dist/src/control-plane/egress/proxy.js +72 -0
  32. package/dist/src/control-plane/egress/ssrf.js +106 -0
  33. package/dist/src/control-plane/egress/utils/debug.js +14 -0
  34. package/dist/src/control-plane/egress/vendor/http-proxy.js +335 -0
  35. package/dist/src/control-plane/egress/vendor/mitm-ca.js +226 -0
  36. package/dist/src/control-plane/egress/vendor/mitm-leaf.js +124 -0
  37. package/dist/src/control-plane/egress/vendor/parent-proxy.js +438 -0
  38. package/dist/src/control-plane/egress/vendor/request-filter.js +105 -0
  39. package/dist/src/control-plane/egress/vendor/sandbox-config.js +1 -0
  40. package/dist/src/control-plane/egress/vendor/tls-terminate-proxy.js +269 -0
  41. package/dist/src/control-plane/environments/routes.js +54 -0
  42. package/dist/src/control-plane/environments/service.js +112 -0
  43. package/dist/src/control-plane/environments/store.js +132 -0
  44. package/dist/src/control-plane/environments/types.js +1 -0
  45. package/dist/src/control-plane/errors.js +81 -0
  46. package/dist/src/control-plane/events/broadcaster.js +217 -0
  47. package/dist/src/control-plane/events/constants.js +2 -0
  48. package/dist/src/control-plane/events/persist.js +39 -0
  49. package/dist/src/control-plane/events/request.js +190 -0
  50. package/dist/src/control-plane/events/routes.js +159 -0
  51. package/dist/src/control-plane/events/runtime-helpers.js +134 -0
  52. package/dist/src/control-plane/events/service.js +1985 -0
  53. package/dist/src/control-plane/events/session-guards.js +29 -0
  54. package/dist/src/control-plane/events/sse.js +6 -0
  55. package/dist/src/control-plane/events/store.js +772 -0
  56. package/dist/src/control-plane/events/tool-persistence.js +102 -0
  57. package/dist/src/control-plane/events/types.js +21 -0
  58. package/dist/src/control-plane/files/routes.js +121 -0
  59. package/dist/src/control-plane/files/service.js +65 -0
  60. package/dist/src/control-plane/files/store-common.js +87 -0
  61. package/dist/src/control-plane/files/store-local.js +488 -0
  62. package/dist/src/control-plane/files/store-memory.js +270 -0
  63. package/dist/src/control-plane/files/store.js +2 -0
  64. package/dist/src/control-plane/files/types.js +7 -0
  65. package/dist/src/control-plane/http.js +25 -0
  66. package/dist/src/control-plane/ids.js +43 -0
  67. package/dist/src/control-plane/logging.js +263 -0
  68. package/dist/src/control-plane/models/auth-storage-backend.js +238 -0
  69. package/dist/src/control-plane/models/catalog.js +78 -0
  70. package/dist/src/control-plane/models/config-security.js +376 -0
  71. package/dist/src/control-plane/models/deployment-config.js +93 -0
  72. package/dist/src/control-plane/models/routes.js +32 -0
  73. package/dist/src/control-plane/models/service.js +138 -0
  74. package/dist/src/control-plane/observability/instruments.js +65 -0
  75. package/dist/src/control-plane/observability/metrics.js +125 -0
  76. package/dist/src/control-plane/observability/routes.js +95 -0
  77. package/dist/src/control-plane/openapi/document.js +282 -0
  78. package/dist/src/control-plane/openapi/routes.js +78 -0
  79. package/dist/src/control-plane/request-idempotency.js +85 -0
  80. package/dist/src/control-plane/secrets/envelope.js +132 -0
  81. package/dist/src/control-plane/secrets/master-key.js +44 -0
  82. package/dist/src/control-plane/secrets/routes.js +20 -0
  83. package/dist/src/control-plane/secrets/service.js +71 -0
  84. package/dist/src/control-plane/secrets/store.js +212 -0
  85. package/dist/src/control-plane/secrets/types.js +3 -0
  86. package/dist/src/control-plane/sessions/pi/custom-tools.js +146 -0
  87. package/dist/src/control-plane/sessions/pi/mcp/bridge.js +527 -0
  88. package/dist/src/control-plane/sessions/pi/mcp/client.js +157 -0
  89. package/dist/src/control-plane/sessions/pi/mcp/credential.js +28 -0
  90. package/dist/src/control-plane/sessions/pi/mcp/fetch.js +69 -0
  91. package/dist/src/control-plane/sessions/pi/mcp/probe.js +202 -0
  92. package/dist/src/control-plane/sessions/pi/mcp/runtime.js +13 -0
  93. package/dist/src/control-plane/sessions/pi/runner.js +1104 -0
  94. package/dist/src/control-plane/sessions/pi/sandbox/cma-glob.js +388 -0
  95. package/dist/src/control-plane/sessions/pi/sandbox/cma-grep.js +124 -0
  96. package/dist/src/control-plane/sessions/pi/sandbox/docker-egress.js +347 -0
  97. package/dist/src/control-plane/sessions/pi/sandbox/docker.js +1589 -0
  98. package/dist/src/control-plane/sessions/pi/sandbox/glob.js +48 -0
  99. package/dist/src/control-plane/sessions/pi/sandbox/image.js +7 -0
  100. package/dist/src/control-plane/sessions/pi/sandbox/microsandbox.js +1677 -0
  101. package/dist/src/control-plane/sessions/pi/sandbox/provider.js +561 -0
  102. package/dist/src/control-plane/sessions/pi/sandbox/selection.js +168 -0
  103. package/dist/src/control-plane/sessions/pi/span-normalizer.js +82 -0
  104. package/dist/src/control-plane/sessions/pi/tool-permissions.js +392 -0
  105. package/dist/src/control-plane/sessions/pi/translator.js +100 -0
  106. package/dist/src/control-plane/sessions/request.js +164 -0
  107. package/dist/src/control-plane/sessions/resources.js +63 -0
  108. package/dist/src/control-plane/sessions/routes.js +88 -0
  109. package/dist/src/control-plane/sessions/serialize.js +24 -0
  110. package/dist/src/control-plane/sessions/service.js +733 -0
  111. package/dist/src/control-plane/sessions/store.js +538 -0
  112. package/dist/src/control-plane/sessions/types.js +1 -0
  113. package/dist/src/control-plane/skills/archive.js +169 -0
  114. package/dist/src/control-plane/skills/routes.js +41 -0
  115. package/dist/src/control-plane/skills/service.js +29 -0
  116. package/dist/src/control-plane/skills/store.js +233 -0
  117. package/dist/src/control-plane/skills/types.js +5 -0
  118. package/dist/src/control-plane/sqlite-transaction.js +15 -0
  119. package/dist/src/control-plane/vaults/mcp-oauth-validate.js +165 -0
  120. package/dist/src/control-plane/vaults/oauth-refresh-ticker.js +43 -0
  121. package/dist/src/control-plane/vaults/oauth-refresh.js +448 -0
  122. package/dist/src/control-plane/vaults/routes.js +66 -0
  123. package/dist/src/control-plane/vaults/service.js +553 -0
  124. package/dist/src/control-plane/vaults/store.js +691 -0
  125. package/dist/src/control-plane/vaults/types.js +1 -0
  126. package/dist/src/control-plane/wake-loop.js +65 -0
  127. package/dist/src/control-plane/wiring.js +92 -0
  128. package/dist/src/control-plane/workspace.js +4 -0
  129. package/dist/src/control-plane/workspaces/store.js +158 -0
  130. package/dist/src/egress-proxy-main.js +103 -0
  131. package/dist/src/main.js +124 -0
  132. package/dist/src/types/agents.js +1 -0
  133. package/dist/src/types/common.js +1 -0
  134. package/dist/src/types/environments.js +1 -0
  135. package/dist/src/types/events.js +121 -0
  136. package/dist/src/types/files.js +1 -0
  137. package/dist/src/types/json.js +19 -0
  138. package/dist/src/types/sessions.js +1 -0
  139. package/dist/ui/managed-agents-console/README.md +52 -0
  140. package/dist/ui/managed-agents-console/docs/agents.md +23 -0
  141. package/dist/ui/managed-agents-console/docs/console.md +23 -0
  142. package/dist/ui/managed-agents-console/docs/dreams.md +5 -0
  143. package/dist/ui/managed-agents-console/docs/environments.md +27 -0
  144. package/dist/ui/managed-agents-console/docs/events.md +19 -0
  145. package/dist/ui/managed-agents-console/docs/files.md +17 -0
  146. package/dist/ui/managed-agents-console/docs/github.md +11 -0
  147. package/dist/ui/managed-agents-console/docs/integrations.md +19 -0
  148. package/dist/ui/managed-agents-console/docs/memory.md +11 -0
  149. package/dist/ui/managed-agents-console/docs/migration.md +17 -0
  150. package/dist/ui/managed-agents-console/docs/multiagent.md +11 -0
  151. package/dist/ui/managed-agents-console/docs/outcomes.md +11 -0
  152. package/dist/ui/managed-agents-console/docs/overview.md +41 -0
  153. package/dist/ui/managed-agents-console/docs/permissions.md +24 -0
  154. package/dist/ui/managed-agents-console/docs/quickstart.md +51 -0
  155. package/dist/ui/managed-agents-console/docs/reference.md +27 -0
  156. package/dist/ui/managed-agents-console/docs/sandbox-reference.md +19 -0
  157. package/dist/ui/managed-agents-console/docs/sandbox-security.md +15 -0
  158. package/dist/ui/managed-agents-console/docs/scheduled-deployments.md +5 -0
  159. package/dist/ui/managed-agents-console/docs/self-hosted-sandboxes.md +15 -0
  160. package/dist/ui/managed-agents-console/docs/session-operations.md +21 -0
  161. package/dist/ui/managed-agents-console/docs/sessions.md +21 -0
  162. package/dist/ui/managed-agents-console/docs/skills.md +15 -0
  163. package/dist/ui/managed-agents-console/docs/tools.md +23 -0
  164. package/dist/ui/managed-agents-console/docs/vaults.md +19 -0
  165. package/dist/ui/managed-agents-console/docs/webhooks.md +11 -0
  166. package/dist/ui/managed-agents-console/index.html +47 -0
  167. package/dist/ui/managed-agents-console/serve.mjs +116 -0
  168. package/dist/ui/managed-agents-console/src/agents-files.jsx +273 -0
  169. package/dist/ui/managed-agents-console/src/api.js +1043 -0
  170. package/dist/ui/managed-agents-console/src/app.jsx +633 -0
  171. package/dist/ui/managed-agents-console/src/auth.jsx +215 -0
  172. package/dist/ui/managed-agents-console/src/console.css +733 -0
  173. package/dist/ui/managed-agents-console/src/data.js +188 -0
  174. package/dist/ui/managed-agents-console/src/detail.jsx +719 -0
  175. package/dist/ui/managed-agents-console/src/docs.jsx +200 -0
  176. package/dist/ui/managed-agents-console/src/environments.jsx +365 -0
  177. package/dist/ui/managed-agents-console/src/forms.jsx +494 -0
  178. package/dist/ui/managed-agents-console/src/icons.jsx +48 -0
  179. package/dist/ui/managed-agents-console/src/skills.jsx +21 -0
  180. package/dist/ui/managed-agents-console/src/sse.js +185 -0
  181. package/dist/ui/managed-agents-console/src/states.jsx +90 -0
  182. package/dist/ui/managed-agents-console/src/tweaks-panel.jsx +541 -0
  183. package/dist/ui/managed-agents-console/src/ui.jsx +190 -0
  184. package/dist/ui/managed-agents-console/src/vaults-data.js +89 -0
  185. package/dist/ui/managed-agents-console/src/vaults.jsx +171 -0
  186. package/dist/ui/managed-agents-console/vendor/babel.min.js +4 -0
  187. package/dist/ui/managed-agents-console/vendor/react-dom.production.min.js +267 -0
  188. package/dist/ui/managed-agents-console/vendor/react.production.min.js +31 -0
  189. package/dist/ui/openapi-docs/VENDOR.md +27 -0
  190. package/dist/ui/openapi-docs/index.html +16 -0
  191. package/dist/ui/openapi-docs/swagger-initializer.js +14 -0
  192. package/dist/ui/openapi-docs/vendor/LICENSE +202 -0
  193. package/dist/ui/openapi-docs/vendor/NOTICE +2 -0
  194. package/dist/ui/openapi-docs/vendor/swagger-ui-bundle.js +2 -0
  195. package/dist/ui/openapi-docs/vendor/swagger-ui-standalone-preset.js +2 -0
  196. package/dist/ui/openapi-docs/vendor/swagger-ui.css +3 -0
  197. package/package.json +53 -0
@@ -0,0 +1,553 @@
1
+ import { isIP } from "node:net";
2
+ import { isBlockedAddress } from "../egress/ssrf.js";
3
+ import { newVaultCredentialId, newVaultId } from "../ids.js";
4
+ import { conflict, invalidRequest, notFound } from "../errors.js";
5
+ import { nextRefreshAt, OAUTH_REFRESH_LONG_LIVED_RECHECK_MS, } from "./oauth-refresh.js";
6
+ const MAX_DISPLAY_NAME_LENGTH = 255;
7
+ const MAX_METADATA_PAIRS = 16;
8
+ const MAX_METADATA_KEY_LENGTH = 64;
9
+ const MAX_METADATA_VALUE_LENGTH = 512;
10
+ const MAX_CREDENTIALS_PER_VAULT = 20;
11
+ const MAX_TOKEN_ENDPOINT_LENGTH = 2048;
12
+ // Floor matches scrubKnownSecrets' MIN_KNOWN_SECRET_LENGTH (logging.ts):
13
+ // a token the scrubber cannot safely redact must not be storable, or a
14
+ // hostile server echoing the bare token would bypass the #170 scrub
15
+ // (review #170, Codex). OMA tightening; real bearer tokens are far longer.
16
+ const MIN_TOKEN_LENGTH = 8;
17
+ export class DefaultVaultService {
18
+ store;
19
+ opts;
20
+ constructor(store, opts = {}) {
21
+ this.store = store;
22
+ this.opts = opts;
23
+ }
24
+ createVault(workspaceId, input) {
25
+ const req = parseVaultCreate(input);
26
+ const now = new Date().toISOString();
27
+ return toManagedVault(this.store.createVault({
28
+ row: {
29
+ id: newVaultId(),
30
+ workspace_id: workspaceId,
31
+ type: "vault",
32
+ display_name: req.displayName,
33
+ metadata: req.metadata,
34
+ created_at: now,
35
+ updated_at: now,
36
+ archived_at: null,
37
+ },
38
+ }));
39
+ }
40
+ retrieveVault(workspaceId, vaultId) {
41
+ const row = this.store.retrieveVaultAny(workspaceId, vaultId);
42
+ if (!row)
43
+ throw notFound(`Vault ${vaultId} not found`);
44
+ return toManagedVault(row);
45
+ }
46
+ listVaults(workspaceId, opts = {}) {
47
+ const page = this.store.listVaults(workspaceId, opts);
48
+ return {
49
+ data: page.data.map(toManagedVault),
50
+ has_more: page.has_more,
51
+ next_page: page.next_page,
52
+ };
53
+ }
54
+ updateVault(workspaceId, vaultId, input) {
55
+ const updates = parseVaultUpdate(input);
56
+ const row = this.store.updateVault(workspaceId, vaultId, updates, new Date().toISOString());
57
+ if (!row)
58
+ throw notFound(`Vault ${vaultId} not found`);
59
+ return toManagedVault(row);
60
+ }
61
+ archiveVault(workspaceId, vaultId) {
62
+ const row = this.store.archiveVault(workspaceId, vaultId, new Date().toISOString());
63
+ if (!row)
64
+ throw notFound(`Vault ${vaultId} not found`);
65
+ return toManagedVault(row);
66
+ }
67
+ deleteVault(workspaceId, vaultId) {
68
+ if (!this.store.deleteVault(workspaceId, vaultId)) {
69
+ throw notFound(`Vault ${vaultId} not found`);
70
+ }
71
+ return { id: vaultId, type: "vault_deleted" };
72
+ }
73
+ createCredential(workspaceId, vaultId, input) {
74
+ const vault = this.store.retrieveVault(workspaceId, vaultId);
75
+ if (!vault)
76
+ throw notFound(`Vault ${vaultId} not found`);
77
+ if (this.store.countActiveCredentials(workspaceId, vaultId) >=
78
+ MAX_CREDENTIALS_PER_VAULT) {
79
+ throw invalidRequest(`Vault ${vaultId} cannot have more than ${MAX_CREDENTIALS_PER_VAULT} active credentials`);
80
+ }
81
+ const req = parseCredentialCreate(input, this.opts.allowInsecureTokenEndpoint);
82
+ const now = new Date().toISOString();
83
+ try {
84
+ const created = toManagedCredential(this.store.createCredential({
85
+ row: {
86
+ id: newVaultCredentialId(),
87
+ workspace_id: workspaceId,
88
+ vault_id: vault.id,
89
+ type: "vault_credential",
90
+ display_name: req.displayName,
91
+ metadata: req.metadata,
92
+ auth: req.auth,
93
+ auth_version: 1,
94
+ created_at: now,
95
+ updated_at: now,
96
+ archived_at: null,
97
+ },
98
+ token: req.secretValue,
99
+ nextRefreshAt: oauthSchedule(new Date(now), req.auth, req.auth.type === "mcp_oauth" && req.auth.refresh !== undefined),
100
+ }));
101
+ if (req.auth.type === "mcp_oauth" && req.auth.refresh !== undefined) {
102
+ this.opts.onSchedulingChanged?.();
103
+ }
104
+ return created;
105
+ }
106
+ catch (error) {
107
+ if (isSqliteUniqueConstraint(error)) {
108
+ throw conflict(`An active credential for that MCP server already exists in vault ${vaultId}`);
109
+ }
110
+ if (error instanceof Error && error.message.startsWith("Secrets require")) {
111
+ throw invalidRequest(error.message);
112
+ }
113
+ throw error;
114
+ }
115
+ }
116
+ retrieveCredential(workspaceId, vaultId, credentialId) {
117
+ this.assertVaultExists(workspaceId, vaultId);
118
+ const row = this.store.retrieveCredentialAny(workspaceId, vaultId, credentialId);
119
+ if (!row)
120
+ throw notFound(`Vault credential ${credentialId} not found`);
121
+ return toManagedCredential(row);
122
+ }
123
+ listCredentials(workspaceId, vaultId, opts = {}) {
124
+ this.assertVaultExists(workspaceId, vaultId);
125
+ const page = this.store.listCredentials(workspaceId, vaultId, opts);
126
+ return {
127
+ data: page.data.map(toManagedCredential),
128
+ has_more: page.has_more,
129
+ next_page: page.next_page,
130
+ };
131
+ }
132
+ updateCredential(workspaceId, vaultId, credentialId, input) {
133
+ this.assertVaultExists(workspaceId, vaultId);
134
+ const existing = this.store.retrieveCredential(workspaceId, vaultId, credentialId);
135
+ if (!existing)
136
+ throw notFound(`Vault credential ${credentialId} not found`);
137
+ const req = parseCredentialUpdate(input, existing);
138
+ try {
139
+ const currentOauth = this.store.readOauthRefreshState(workspaceId, vaultId, credentialId);
140
+ const updatedAt = new Date().toISOString();
141
+ const nextAuth = req.auth?.type === "mcp_oauth"
142
+ ? {
143
+ ...existing.auth,
144
+ ...(req.auth.expiresAt === undefined
145
+ ? {}
146
+ : req.auth.expiresAt === null
147
+ ? { expires_at: undefined }
148
+ : { expires_at: req.auth.expiresAt }),
149
+ }
150
+ : existing.auth;
151
+ const row = this.store.updateCredential(workspaceId, vaultId, credentialId, req, updatedAt, req.auth === undefined
152
+ ? undefined
153
+ : {
154
+ nextRefreshAt: oauthSchedule(new Date(updatedAt), nextAuth, req.auth.type === "mcp_oauth" &&
155
+ (req.auth.refreshToken !== undefined ||
156
+ currentOauth?.secrets.refreshToken !== undefined)),
157
+ });
158
+ if (!row)
159
+ throw notFound(`Vault credential ${credentialId} not found`);
160
+ if (req.auth !== undefined)
161
+ this.opts.onSchedulingChanged?.();
162
+ return toManagedCredential(row);
163
+ }
164
+ catch (error) {
165
+ if (isSqliteUniqueConstraint(error)) {
166
+ throw conflict(`An active credential for that MCP server already exists in vault ${vaultId}`);
167
+ }
168
+ if (error instanceof Error && error.message.startsWith("Secrets require")) {
169
+ throw invalidRequest(error.message);
170
+ }
171
+ throw error;
172
+ }
173
+ }
174
+ archiveCredential(workspaceId, vaultId, credentialId) {
175
+ this.assertVaultExists(workspaceId, vaultId);
176
+ const row = this.store.archiveCredential(workspaceId, vaultId, credentialId, new Date().toISOString());
177
+ if (!row)
178
+ throw notFound(`Vault credential ${credentialId} not found`);
179
+ return toManagedCredential(row);
180
+ }
181
+ deleteCredential(workspaceId, vaultId, credentialId) {
182
+ this.assertVaultExists(workspaceId, vaultId);
183
+ if (!this.store.deleteCredential(workspaceId, vaultId, credentialId)) {
184
+ throw notFound(`Vault credential ${credentialId} not found`);
185
+ }
186
+ }
187
+ assertVaultsUsable(workspaceId, vaultIds) {
188
+ const seen = new Set();
189
+ for (const vaultId of vaultIds) {
190
+ if (seen.has(vaultId))
191
+ continue;
192
+ seen.add(vaultId);
193
+ const row = this.store.retrieveVault(workspaceId, vaultId);
194
+ if (!row)
195
+ throw invalidRequest(`Vault ${vaultId} not found`);
196
+ }
197
+ }
198
+ resolveCredential(workspaceId, vaultIds, serverUrl) {
199
+ return this.store.resolveCredential(workspaceId, vaultIds, serverUrl);
200
+ }
201
+ readCredentialRuntimeMetadata(workspaceId, vaultId, credentialId) {
202
+ return this.store.readCredentialRuntimeMetadata(workspaceId, vaultId, credentialId);
203
+ }
204
+ readOauthValidationSnapshot(workspaceId, vaultId, credentialId) {
205
+ return this.store.readOauthRefreshState(workspaceId, vaultId, credentialId);
206
+ }
207
+ assertVaultExists(workspaceId, vaultId) {
208
+ const row = this.store.retrieveVaultAny(workspaceId, vaultId);
209
+ if (!row)
210
+ throw notFound(`Vault ${vaultId} not found`);
211
+ return row;
212
+ }
213
+ }
214
+ function oauthSchedule(now, auth, hasRefreshToken) {
215
+ if (auth.type !== "mcp_oauth" || auth.refresh === undefined || !hasRefreshToken) {
216
+ return null;
217
+ }
218
+ return auth.expires_at === undefined
219
+ ? new Date(now.getTime() + OAUTH_REFRESH_LONG_LIVED_RECHECK_MS).toISOString()
220
+ : nextRefreshAt(now, new Date(auth.expires_at)).toISOString();
221
+ }
222
+ function parseVaultCreate(input) {
223
+ const obj = objectInput(input);
224
+ return {
225
+ displayName: displayNameField(obj, "display_name", { required: true }),
226
+ metadata: metadataField(obj.metadata),
227
+ };
228
+ }
229
+ function parseVaultUpdate(input) {
230
+ const obj = objectInput(input);
231
+ const updates = {};
232
+ if (obj.display_name !== undefined) {
233
+ updates.displayName = displayNameField(obj, "display_name", { required: true });
234
+ }
235
+ if (obj.metadata !== undefined) {
236
+ updates.metadata = metadataField(obj.metadata);
237
+ }
238
+ return updates;
239
+ }
240
+ function parseCredentialCreate(input, allowInsecureTokenEndpoint) {
241
+ const obj = objectInput(input);
242
+ const parsed = parseCredentialAuthCreate(obj.auth, allowInsecureTokenEndpoint);
243
+ return {
244
+ displayName: obj.display_name === undefined
245
+ ? null
246
+ : nullableDisplayNameField(obj, "display_name"),
247
+ metadata: metadataField(obj.metadata),
248
+ auth: parsed.auth,
249
+ secretValue: parsed.secretValue,
250
+ };
251
+ }
252
+ function parseCredentialUpdate(input, existing) {
253
+ const obj = objectInput(input);
254
+ const updates = {};
255
+ if (obj.display_name !== undefined) {
256
+ updates.displayName = nullableDisplayNameField(obj, "display_name");
257
+ }
258
+ if (obj.metadata !== undefined) {
259
+ updates.metadata = metadataField(obj.metadata);
260
+ }
261
+ if (obj.auth !== undefined) {
262
+ updates.auth = parseCredentialAuthUpdate(obj.auth, existing);
263
+ }
264
+ return updates;
265
+ }
266
+ function parseCredentialAuthCreate(input, allowInsecureTokenEndpoint) {
267
+ const auth = authObject(input);
268
+ const type = auth.type;
269
+ if (type === "static_bearer") {
270
+ return {
271
+ auth: {
272
+ type,
273
+ mcp_server_url: mcpServerUrlField(auth, "mcp_server_url", {
274
+ required: true,
275
+ }),
276
+ },
277
+ secretValue: staticBearerTokenField(auth),
278
+ };
279
+ }
280
+ const refresh = oauthRefreshCreate(auth.refresh, allowInsecureTokenEndpoint);
281
+ const accessToken = secretField(auth, "access_token", { required: true });
282
+ const expiresAt = expiresAtField(auth.expires_at);
283
+ return {
284
+ auth: {
285
+ type,
286
+ mcp_server_url: mcpServerUrlField(auth, "mcp_server_url", {
287
+ required: true,
288
+ }),
289
+ ...(expiresAt === undefined ? {} : { expires_at: expiresAt }),
290
+ ...(refresh === undefined ? {} : { refresh: refresh.metadata }),
291
+ },
292
+ secretValue: JSON.stringify({
293
+ access_token: accessToken,
294
+ ...(refresh?.refreshToken === undefined
295
+ ? {}
296
+ : { refresh_token: refresh.refreshToken }),
297
+ ...(refresh?.clientSecret === undefined
298
+ ? {}
299
+ : { client_secret: refresh.clientSecret }),
300
+ }),
301
+ };
302
+ }
303
+ function parseCredentialAuthUpdate(input, existing) {
304
+ const auth = authObject(input);
305
+ if (auth.type !== existing.auth.type) {
306
+ throw invalidRequest("`auth.type` is immutable");
307
+ }
308
+ if (auth.mcp_server_url !== undefined) {
309
+ throw invalidRequest("`auth.mcp_server_url` is immutable");
310
+ }
311
+ if (auth.type === "static_bearer") {
312
+ return { type: auth.type, token: staticBearerTokenField(auth) };
313
+ }
314
+ if (auth.token_endpoint !== undefined) {
315
+ throw invalidRequest("`auth.token_endpoint` is immutable");
316
+ }
317
+ if (auth.client_id !== undefined) {
318
+ throw invalidRequest("`auth.client_id` is immutable");
319
+ }
320
+ const out = { type: "mcp_oauth" };
321
+ if (auth.access_token !== undefined) {
322
+ out.accessToken = secretField(auth, "access_token", { required: true });
323
+ }
324
+ if (auth.expires_at !== undefined) {
325
+ out.expiresAt = expiresAtField(auth.expires_at) ?? null;
326
+ }
327
+ if (auth.refresh !== undefined) {
328
+ const refresh = objectField(auth, "refresh");
329
+ for (const field of [
330
+ "token_endpoint",
331
+ "client_id",
332
+ "scope",
333
+ "token_endpoint_auth",
334
+ ]) {
335
+ if (refresh[field] !== undefined) {
336
+ throw invalidRequest(`\`auth.refresh.${field}\` is immutable`);
337
+ }
338
+ }
339
+ out.refreshToken = secretField(refresh, "refresh_token", { required: true });
340
+ }
341
+ if (out.accessToken === undefined &&
342
+ out.expiresAt === undefined &&
343
+ out.refreshToken === undefined) {
344
+ throw invalidRequest("`auth` must include an updateable field");
345
+ }
346
+ return out;
347
+ }
348
+ function staticBearerTokenField(auth) {
349
+ return secretField(auth, "token", { required: true });
350
+ }
351
+ function secretField(obj, field, opts = {}) {
352
+ const value = stringField(obj, field, opts);
353
+ if (value.length < MIN_TOKEN_LENGTH) {
354
+ throw invalidRequest(`\`${field}\` must be at least ${MIN_TOKEN_LENGTH} characters`);
355
+ }
356
+ return value;
357
+ }
358
+ function oauthRefreshCreate(input, allowInsecureTokenEndpoint) {
359
+ if (input === undefined)
360
+ return undefined;
361
+ const refresh = objectField({ refresh: input }, "refresh");
362
+ const tokenEndpoint = tokenEndpointField(refresh, "token_endpoint", allowInsecureTokenEndpoint);
363
+ const clientId = stringField(refresh, "client_id", { required: true });
364
+ const scope = refresh.scope === undefined
365
+ ? undefined
366
+ : stringField(refresh, "scope", { required: true });
367
+ const tokenEndpointAuth = objectField(refresh, "token_endpoint_auth");
368
+ const authType = stringField(tokenEndpointAuth, "type", { required: true });
369
+ if (authType !== "none" &&
370
+ authType !== "client_secret_basic" &&
371
+ authType !== "client_secret_post") {
372
+ throw invalidRequest("`auth.refresh.token_endpoint_auth.type` must be one of `none`, `client_secret_basic`, or `client_secret_post`");
373
+ }
374
+ const refreshToken = secretField(refresh, "refresh_token", { required: true });
375
+ const clientSecret = authType === "none"
376
+ ? clientSecretAbsent(tokenEndpointAuth)
377
+ : secretField(tokenEndpointAuth, "client_secret", { required: true });
378
+ return {
379
+ metadata: {
380
+ token_endpoint: tokenEndpoint,
381
+ client_id: clientId,
382
+ ...(scope === undefined ? {} : { scope }),
383
+ token_endpoint_auth: { type: authType },
384
+ },
385
+ refreshToken,
386
+ ...(clientSecret === undefined ? {} : { clientSecret }),
387
+ };
388
+ }
389
+ function tokenEndpointField(obj, field, allowInsecureTokenEndpoint) {
390
+ const value = stringField(obj, field, { required: true });
391
+ if (value.length > MAX_TOKEN_ENDPOINT_LENGTH) {
392
+ throw invalidRequest(`\`${field}\` must be at most ${MAX_TOKEN_ENDPOINT_LENGTH} characters`);
393
+ }
394
+ try {
395
+ const url = new URL(value);
396
+ const explicitlyAllowed = allowInsecureTokenEndpoint?.(url) === true;
397
+ if (url.protocol !== "https:" && !explicitlyAllowed) {
398
+ throw invalidRequest(`\`${field}\` must be an https URL`);
399
+ }
400
+ if (url.username || url.password || url.hash) {
401
+ throw invalidRequest(`\`${field}\` must not include userinfo or fragments`);
402
+ }
403
+ if (isBlockedIpLiteral(url.hostname) && !explicitlyAllowed) {
404
+ throw invalidRequest(`\`${field}\` host is not allowed`);
405
+ }
406
+ return value;
407
+ }
408
+ catch (error) {
409
+ if (error instanceof Error && error.name === "ApiError")
410
+ throw error;
411
+ throw invalidRequest(`\`${field}\` must be an https URL`);
412
+ }
413
+ }
414
+ function clientSecretAbsent(obj) {
415
+ if (obj.client_secret !== undefined) {
416
+ throw invalidRequest("`auth.refresh.token_endpoint_auth.client_secret` is not allowed when type is `none`");
417
+ }
418
+ return undefined;
419
+ }
420
+ function isBlockedIpLiteral(hostname) {
421
+ const normalized = hostname.replace(/^\[|\]$/g, "");
422
+ const family = isIP(normalized);
423
+ if (family !== 0)
424
+ return isBlockedAddress(normalized, family);
425
+ return false;
426
+ }
427
+ function expiresAtField(input) {
428
+ if (input === undefined)
429
+ return undefined;
430
+ if (typeof input !== "string" || input.length === 0) {
431
+ throw invalidRequest("`auth.expires_at` must be a non-empty string");
432
+ }
433
+ const time = Date.parse(input);
434
+ if (!Number.isFinite(time)) {
435
+ throw invalidRequest("`auth.expires_at` must be an ISO-8601 timestamp");
436
+ }
437
+ if (time <= Date.now()) {
438
+ throw invalidRequest("auth.expires_at must be in the future.");
439
+ }
440
+ return input;
441
+ }
442
+ function objectField(obj, field) {
443
+ const value = obj[field];
444
+ if (!isObject(value))
445
+ throw invalidRequest(`\`${field}\` must be a JSON object`);
446
+ return value;
447
+ }
448
+ function authObject(input) {
449
+ if (!isObject(input))
450
+ throw invalidRequest("`auth` must be a JSON object");
451
+ const type = stringField(input, "type", { required: true });
452
+ if (type !== "static_bearer" && type !== "mcp_oauth") {
453
+ throw invalidRequest("Only `static_bearer` and `mcp_oauth` credentials are supported");
454
+ }
455
+ return { ...input, type };
456
+ }
457
+ function objectInput(input) {
458
+ if (!isObject(input))
459
+ throw invalidRequest("Request body must be a JSON object");
460
+ return input;
461
+ }
462
+ function isObject(value) {
463
+ return typeof value === "object" && value !== null && !Array.isArray(value);
464
+ }
465
+ function displayNameField(obj, field, opts = {}) {
466
+ const value = stringField(obj, field, opts);
467
+ if (value.length > MAX_DISPLAY_NAME_LENGTH) {
468
+ throw invalidRequest(`\`${field}\` must be at most ${MAX_DISPLAY_NAME_LENGTH} characters`);
469
+ }
470
+ return value;
471
+ }
472
+ function nullableDisplayNameField(obj, field) {
473
+ const value = obj[field];
474
+ if (value === null)
475
+ return null;
476
+ return displayNameField(obj, field, { required: true });
477
+ }
478
+ function mcpServerUrlField(obj, field, opts = {}) {
479
+ const value = stringField(obj, field, opts);
480
+ try {
481
+ const url = new URL(value);
482
+ if (url.protocol !== "http:" && url.protocol !== "https:") {
483
+ throw invalidRequest(`\`${field}\` must be an http(s) URL`);
484
+ }
485
+ return value;
486
+ }
487
+ catch (error) {
488
+ if (error instanceof Error && error.name === "ApiError")
489
+ throw error;
490
+ throw invalidRequest(`\`${field}\` must be an http(s) URL`);
491
+ }
492
+ }
493
+ function stringField(obj, field, opts = {}) {
494
+ const value = obj[field];
495
+ if (typeof value === "string" && value.length > 0)
496
+ return value;
497
+ if (value === undefined && opts.required !== true)
498
+ return "";
499
+ throw invalidRequest(`\`${field}\` must be a non-empty string`);
500
+ }
501
+ function metadataField(input) {
502
+ if (input === undefined)
503
+ return {};
504
+ if (!isObject(input))
505
+ throw invalidRequest("`metadata` must be a JSON object");
506
+ const entries = Object.entries(input);
507
+ if (entries.length > MAX_METADATA_PAIRS) {
508
+ throw invalidRequest(`\`metadata\` must have at most ${MAX_METADATA_PAIRS} pairs`);
509
+ }
510
+ const out = {};
511
+ for (const [key, value] of entries) {
512
+ if (key.length === 0 || key.length > MAX_METADATA_KEY_LENGTH) {
513
+ throw invalidRequest(`\`metadata\` keys must be 1-${MAX_METADATA_KEY_LENGTH} characters`);
514
+ }
515
+ if (typeof value !== "string") {
516
+ throw invalidRequest("`metadata` values must be strings");
517
+ }
518
+ if (value.length > MAX_METADATA_VALUE_LENGTH) {
519
+ throw invalidRequest(`\`metadata\` values must be at most ${MAX_METADATA_VALUE_LENGTH} characters`);
520
+ }
521
+ out[key] = value;
522
+ }
523
+ return out;
524
+ }
525
+ function toManagedVault(row) {
526
+ return {
527
+ id: row.id,
528
+ type: "vault",
529
+ display_name: row.display_name,
530
+ metadata: row.metadata,
531
+ created_at: row.created_at,
532
+ updated_at: row.updated_at,
533
+ archived_at: row.archived_at,
534
+ };
535
+ }
536
+ function toManagedCredential(row) {
537
+ return {
538
+ id: row.id,
539
+ type: "vault_credential",
540
+ vault_id: row.vault_id,
541
+ display_name: row.display_name,
542
+ metadata: row.metadata,
543
+ auth: row.auth,
544
+ created_at: row.created_at,
545
+ updated_at: row.updated_at,
546
+ archived_at: row.archived_at,
547
+ };
548
+ }
549
+ function isSqliteUniqueConstraint(error) {
550
+ return (error instanceof Error &&
551
+ (error.code === "SQLITE_CONSTRAINT_UNIQUE" ||
552
+ error.message.includes("UNIQUE constraint failed")));
553
+ }