@aifabrix/builder 2.40.2 → 2.41.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 (103) hide show
  1. package/README.md +6 -4
  2. package/integration/hubspot/test.js +1 -1
  3. package/lib/api/credential.api.js +40 -0
  4. package/lib/api/dev.api.js +423 -0
  5. package/lib/api/types/credential.types.js +23 -0
  6. package/lib/api/types/dev.types.js +140 -0
  7. package/lib/app/config.js +21 -0
  8. package/lib/app/down.js +2 -1
  9. package/lib/app/index.js +9 -0
  10. package/lib/app/push.js +36 -12
  11. package/lib/app/readme.js +1 -3
  12. package/lib/app/run-env-compose.js +201 -0
  13. package/lib/app/run-helpers.js +121 -118
  14. package/lib/app/run.js +148 -28
  15. package/lib/app/show.js +5 -2
  16. package/lib/build/index.js +11 -3
  17. package/lib/cli/setup-app.js +140 -14
  18. package/lib/cli/setup-dev.js +180 -17
  19. package/lib/cli/setup-environment.js +4 -2
  20. package/lib/cli/setup-external-system.js +71 -21
  21. package/lib/cli/setup-infra.js +29 -2
  22. package/lib/cli/setup-secrets.js +52 -5
  23. package/lib/cli/setup-utility.js +12 -3
  24. package/lib/commands/app-install.js +172 -0
  25. package/lib/commands/app-shell.js +75 -0
  26. package/lib/commands/app-test.js +282 -0
  27. package/lib/commands/app.js +1 -1
  28. package/lib/commands/dev-cli-handlers.js +141 -0
  29. package/lib/commands/dev-down.js +114 -0
  30. package/lib/commands/dev-init.js +309 -0
  31. package/lib/commands/secrets-list.js +118 -0
  32. package/lib/commands/secrets-remove.js +97 -0
  33. package/lib/commands/secrets-set.js +30 -17
  34. package/lib/commands/secrets-validate.js +50 -0
  35. package/lib/commands/up-dataplane.js +2 -2
  36. package/lib/commands/up-miso.js +0 -25
  37. package/lib/commands/upload.js +26 -1
  38. package/lib/core/admin-secrets.js +96 -0
  39. package/lib/core/secrets-ensure.js +378 -0
  40. package/lib/core/secrets-env-write.js +157 -0
  41. package/lib/core/secrets.js +147 -81
  42. package/lib/datasource/field-reference-validator.js +91 -0
  43. package/lib/datasource/validate.js +21 -3
  44. package/lib/deployment/environment-config.js +137 -0
  45. package/lib/deployment/environment.js +21 -98
  46. package/lib/deployment/push.js +32 -2
  47. package/lib/external-system/download.js +7 -0
  48. package/lib/external-system/test-auth.js +7 -3
  49. package/lib/external-system/test.js +5 -1
  50. package/lib/generator/index.js +174 -25
  51. package/lib/generator/wizard.js +8 -0
  52. package/lib/infrastructure/helpers.js +103 -20
  53. package/lib/infrastructure/index.js +88 -10
  54. package/lib/infrastructure/services.js +70 -15
  55. package/lib/schema/application-schema.json +24 -3
  56. package/lib/schema/external-system.schema.json +435 -413
  57. package/lib/utils/api.js +3 -3
  58. package/lib/utils/app-register-auth.js +25 -3
  59. package/lib/utils/cli-utils.js +20 -0
  60. package/lib/utils/compose-generator.js +76 -75
  61. package/lib/utils/compose-handlebars-helpers.js +43 -0
  62. package/lib/utils/compose-vector-helper.js +18 -0
  63. package/lib/utils/config-paths.js +127 -2
  64. package/lib/utils/credential-secrets-env.js +267 -0
  65. package/lib/utils/dev-cert-helper.js +122 -0
  66. package/lib/utils/device-code-helpers.js +224 -0
  67. package/lib/utils/device-code.js +37 -336
  68. package/lib/utils/docker-build.js +40 -8
  69. package/lib/utils/env-copy.js +83 -13
  70. package/lib/utils/env-map.js +35 -5
  71. package/lib/utils/env-template.js +6 -5
  72. package/lib/utils/error-formatters/http-status-errors.js +20 -1
  73. package/lib/utils/help-builder.js +15 -2
  74. package/lib/utils/infra-status.js +30 -1
  75. package/lib/utils/local-secrets.js +7 -52
  76. package/lib/utils/mutagen-install.js +195 -0
  77. package/lib/utils/mutagen.js +146 -0
  78. package/lib/utils/paths.js +43 -33
  79. package/lib/utils/port-resolver.js +28 -16
  80. package/lib/utils/remote-dev-auth.js +38 -0
  81. package/lib/utils/remote-docker-env.js +43 -0
  82. package/lib/utils/remote-secrets-loader.js +60 -0
  83. package/lib/utils/secrets-generator.js +94 -6
  84. package/lib/utils/secrets-helpers.js +33 -25
  85. package/lib/utils/secrets-path.js +2 -2
  86. package/lib/utils/secrets-utils.js +52 -1
  87. package/lib/utils/secrets-validation.js +84 -0
  88. package/lib/utils/ssh-key-helper.js +116 -0
  89. package/lib/utils/token-manager-messages.js +90 -0
  90. package/lib/utils/token-manager.js +5 -4
  91. package/lib/utils/variable-transformer.js +3 -3
  92. package/lib/validation/validator.js +65 -0
  93. package/package.json +2 -2
  94. package/scripts/install-local.js +34 -15
  95. package/templates/README.md +0 -1
  96. package/templates/applications/README.md.hbs +4 -4
  97. package/templates/applications/dataplane/application.yaml +5 -4
  98. package/templates/applications/dataplane/env.template +12 -7
  99. package/templates/applications/keycloak/env.template +2 -0
  100. package/templates/applications/miso-controller/application.yaml +1 -0
  101. package/templates/applications/miso-controller/env.template +11 -9
  102. package/templates/python/docker-compose.hbs +49 -23
  103. package/templates/typescript/docker-compose.hbs +48 -22
package/README.md CHANGED
@@ -34,7 +34,7 @@ Install the AI Fabrix platform and test it locally. Then add external integratio
34
34
  npm install -g @aifabrix/builder
35
35
  ```
36
36
 
37
- **Alias:** You can use `aifx` instead of `aifabrix` in any command.
37
+ **Alias:** You can use `af` instead of `aifabrix` in any command.
38
38
 
39
39
  ---
40
40
 
@@ -48,6 +48,8 @@ Get the platform running locally so you can try it.
48
48
  aifabrix up-infra
49
49
  ```
50
50
 
51
+ First-time run creates required infra secrets automatically. Use `aifabrix up-infra --adminPwd <password>` to set a custom admin password for Postgres, pgAdmin, and Redis Commander.
52
+
51
53
  2. **Start the platform** (Keycloak, Miso Controller, Dataplane) from community images:
52
54
 
53
55
  ```bash
@@ -60,12 +62,12 @@ Get the platform running locally so you can try it.
60
62
 
61
63
  - **OpenAI:** set your API key:
62
64
  ```bash
63
- aifabrix secrets set secrets-openaiApiKeyVault <your-openai-secret-key>
65
+ aifabrix secret set secrets-openaiApiKeyVault <your-openai-secret-key>
64
66
  ```
65
67
  - **Azure OpenAI:** set endpoint and API key:
66
68
  ```bash
67
- aifabrix secrets set azure-openaiapi-urlKeyVault <your-azure-openai-endpoint-url>
68
- aifabrix secrets set secrets-azureOpenaiApiKeyVault <your-azure-openai-secret-key>
69
+ aifabrix secret set azure-openaiapi-urlKeyVault <your-azure-openai-endpoint-url>
70
+ aifabrix secret set secrets-azureOpenaiApiKeyVault <your-azure-openai-secret-key>
69
71
  ```
70
72
 
71
73
  Secrets are stored in `~/.aifabrix/secrets.local.yaml` or the file from `aifabrix-secrets` in your config (e.g. `builder/secrets.local.yaml`).
@@ -281,7 +281,7 @@ async function loadEnvFile(envPath, options) {
281
281
  /**
282
282
  * Load test config (controller, environment, dataplane, openapi file).
283
283
  * Reads integration/hubspot/.env; missing CONTROLLER_URL/ENVIRONMENT fall back to
284
- * the same resolution as the CLI (aifx auth status) so tests use the same controller.
284
+ * the same resolution as the CLI (af auth status) so tests use the same controller.
285
285
  * @async
286
286
  * @function loadTestConfigFromEnv
287
287
  * @returns {Promise<Object>} Context with controllerUrl, environment, dataplaneUrl, openapiFile
@@ -0,0 +1,40 @@
1
+ /**
2
+ * @fileoverview Credential API functions (Dataplane secret store)
3
+ * @author AI Fabrix Team
4
+ * @version 2.0.0
5
+ */
6
+
7
+ const { ApiClient } = require('./index');
8
+
9
+ const CREDENTIAL_SECRET_ENDPOINT = '/api/v1/credential/secret';
10
+
11
+ /**
12
+ * Store credential secrets in the dataplane secret store.
13
+ * Values are encrypted at rest by the dataplane; send plain values only (no kv:// as value).
14
+ *
15
+ * POST /api/v1/credential/secret
16
+ * @requiresPermission {Dataplane} credential:create
17
+ * @async
18
+ * @function storeCredentialSecrets
19
+ * @param {string} dataplaneUrl - Dataplane base URL
20
+ * @param {Object} authConfig - Authentication configuration (Bearer token required)
21
+ * @param {Array<{ key: string, value: string }>} items - Secret items (key = kv path, value = plain)
22
+ * @returns {Promise<{ stored?: number, success?: boolean, error?: string }>} Secret store response
23
+ * @throws {Error} If request fails (non-2xx) and caller may handle 403/401 as warning
24
+ */
25
+ async function storeCredentialSecrets(dataplaneUrl, authConfig, items) {
26
+ if (!dataplaneUrl || typeof dataplaneUrl !== 'string') {
27
+ throw new Error('dataplaneUrl is required and must be a string');
28
+ }
29
+ if (!items || !Array.isArray(items) || items.length === 0) {
30
+ return { stored: 0 };
31
+ }
32
+ const client = new ApiClient(dataplaneUrl, authConfig);
33
+ return await client.post(CREDENTIAL_SECRET_ENDPOINT, {
34
+ body: items
35
+ });
36
+ }
37
+
38
+ module.exports = {
39
+ storeCredentialSecrets
40
+ };
@@ -0,0 +1,423 @@
1
+ /**
2
+ * @fileoverview Builder Server (dev) API – issue-cert, settings, users, SSH keys, secrets.
3
+ * First call: issue-cert is public (no client cert). All other routes require client certificate:
4
+ * when clientKeyPem is provided, requests use mTLS (TLS client cert); otherwise X-Client-Cert header only.
5
+ * @author AI Fabrix Team
6
+ * @version 2.0.0
7
+ */
8
+
9
+ const https = require('https');
10
+ const { makeApiCall } = require('../utils/api');
11
+
12
+ const DEFAULT_TIMEOUT_MS = 15000;
13
+
14
+ /**
15
+ * Encode PEM for use in X-Client-Cert header. HTTP header values must not contain newlines;
16
+ * we send certPem as base64: Buffer.from(pem, 'utf8').toString('base64').
17
+ * Server should decode with Buffer.from(headerVal, 'base64').toString('utf8').
18
+ * @param {string} clientCertPem - PEM-encoded client certificate
19
+ * @returns {string} Base64-encoded PEM for header
20
+ */
21
+ function encodeCertForHeader(clientCertPem) {
22
+ if (!clientCertPem || typeof clientCertPem !== 'string') return '';
23
+ return Buffer.from(clientCertPem, 'utf8').toString('base64');
24
+ }
25
+
26
+ /**
27
+ * Normalize base URL (no trailing slash)
28
+ * @param {string} serverUrl - Base URL of Builder Server
29
+ * @returns {string} Normalized URL
30
+ */
31
+ function normalizeBaseUrl(serverUrl) {
32
+ if (!serverUrl || typeof serverUrl !== 'string') {
33
+ throw new Error('remote-server URL is required and must be a string');
34
+ }
35
+ return serverUrl.trim().replace(/\/+$/, '');
36
+ }
37
+
38
+ /**
39
+ * Build full URL for an endpoint path
40
+ * @param {string} baseUrl - Normalized base URL
41
+ * @param {string} path - Path (e.g. /api/dev/issue-cert)
42
+ * @returns {string} Full URL
43
+ */
44
+ function buildUrl(baseUrl, path) {
45
+ const p = path.startsWith('/') ? path : `/${path}`;
46
+ return `${baseUrl}${p}`;
47
+ }
48
+
49
+ /**
50
+ * Make request to Builder Server. Throws on !success with message from response or error.
51
+ * @param {string} url - Full URL
52
+ * @param {Object} options - Fetch options (method, headers, body)
53
+ * @returns {Promise<Object>} result.data when success
54
+ */
55
+ async function request(url, options = {}) {
56
+ const fetchOptions = {
57
+ method: options.method || 'GET',
58
+ headers: { 'Content-Type': 'application/json', ...options.headers },
59
+ signal: AbortSignal.timeout(DEFAULT_TIMEOUT_MS)
60
+ };
61
+ if (options.body !== undefined) {
62
+ fetchOptions.body = typeof options.body === 'string' ? options.body : JSON.stringify(options.body);
63
+ }
64
+ const result = await makeApiCall(url, fetchOptions);
65
+ if (!result.success) {
66
+ const msg = result.formattedError || result.error || result.message || `Request failed (${result.status})`;
67
+ const err = new Error(msg);
68
+ err.status = result.status;
69
+ err.errorData = result.errorData;
70
+ throw err;
71
+ }
72
+ return result.data;
73
+ }
74
+
75
+ /**
76
+ * Build request options and TLS agent for mTLS request.
77
+ * @param {string} url - Full URL
78
+ * @param {Object} options - { method, headers, body }
79
+ * @param {string} certPem - PEM client certificate
80
+ * @param {string} keyPem - PEM client key
81
+ * @returns {{ urlObj: URL, method: string, headers: Object, body: string|undefined, agent: https.Agent }}
82
+ */
83
+ function buildMtlsRequestOptions(url, options, certPem, keyPem) {
84
+ const urlObj = new URL(url);
85
+ const method = (options.method || 'GET').toUpperCase();
86
+ const headers = { 'Content-Type': 'application/json', ...options.headers };
87
+ let body = options.body;
88
+ if (body !== undefined && typeof body !== 'string') {
89
+ body = JSON.stringify(body);
90
+ }
91
+ if (body) {
92
+ headers['Content-Length'] = Buffer.byteLength(body, 'utf8');
93
+ }
94
+ const tlsOptions = { cert: certPem, key: keyPem, rejectUnauthorized: true };
95
+ const agent = new https.Agent(tlsOptions);
96
+ return { urlObj, method, headers, body, agent, tlsOptions };
97
+ }
98
+
99
+ /**
100
+ * Handle mTLS response: collect body, parse JSON, resolve or reject.
101
+ * @param {import('http').IncomingMessage} res - HTTP response
102
+ * @param {Function} resolve - Promise resolve
103
+ * @param {Function} reject - Promise reject
104
+ */
105
+ function handleMtlsResponse(res, resolve, reject) {
106
+ const chunks = [];
107
+ res.on('data', (c) => chunks.push(c));
108
+ res.on('end', () => {
109
+ const raw = Buffer.concat(chunks).toString('utf8');
110
+ let data;
111
+ try {
112
+ data = raw ? JSON.parse(raw) : {};
113
+ } catch {
114
+ data = raw;
115
+ }
116
+ if (res.statusCode < 200 || res.statusCode >= 300) {
117
+ const msg = (data && (data.message || data.error)) || res.statusMessage || `Request failed (${res.statusCode})`;
118
+ const err = new Error(msg);
119
+ err.status = res.statusCode;
120
+ err.errorData = data;
121
+ reject(err);
122
+ } else {
123
+ resolve(data);
124
+ }
125
+ });
126
+ }
127
+
128
+ /**
129
+ * Make request with mTLS (TLS client certificate). Uses Node https module so the client cert
130
+ * is presented in the TLS handshake. Also sends X-Client-Cert header for backends that read it.
131
+ * @param {string} url - Full URL (https only)
132
+ * @param {Object} options - { method, headers, body }
133
+ * @param {string} certPem - PEM-encoded client certificate
134
+ * @param {string} keyPem - PEM-encoded client private key
135
+ * @returns {Promise<Object>} response data when success
136
+ */
137
+ function requestWithCertImpl(url, options, certPem, keyPem) {
138
+ return new Promise((resolve, reject) => {
139
+ const urlObj = new URL(url);
140
+ if (urlObj.protocol !== 'https:') {
141
+ reject(new Error('mTLS request requires https URL'));
142
+ return;
143
+ }
144
+ const { method, headers, body, agent, tlsOptions } = buildMtlsRequestOptions(url, options, certPem, keyPem);
145
+ const req = https.request(
146
+ {
147
+ hostname: urlObj.hostname,
148
+ port: urlObj.port || 443,
149
+ path: urlObj.pathname + urlObj.search,
150
+ method,
151
+ headers,
152
+ agent,
153
+ ...tlsOptions
154
+ },
155
+ (res) => handleMtlsResponse(res, resolve, reject)
156
+ );
157
+ req.on('error', reject);
158
+ req.setTimeout(DEFAULT_TIMEOUT_MS, () => {
159
+ req.destroy();
160
+ reject(new Error(`Request timed out after ${DEFAULT_TIMEOUT_MS}ms`));
161
+ });
162
+ if (body) {
163
+ req.write(body, 'utf8');
164
+ }
165
+ req.end();
166
+ });
167
+ }
168
+
169
+ /**
170
+ * Issue developer certificate (public; no client cert). POST /api/dev/issue-cert
171
+ * @requiresPermission {BuilderServer} Public (no auth required)
172
+ * @param {string} serverUrl - Builder Server base URL
173
+ * @param {Object} body - IssueCertDto: developerId, pin, csr
174
+ * @returns {Promise<Object>} IssueCertResponseDto: certificate, validDays, validNotAfter
175
+ */
176
+ async function issueCert(serverUrl, body) {
177
+ const base = normalizeBaseUrl(serverUrl);
178
+ return request(buildUrl(base, '/api/dev/issue-cert'), { method: 'POST', body });
179
+ }
180
+
181
+ /**
182
+ * Get health. GET /health (public)
183
+ * @requiresPermission {BuilderServer} Public (no auth required)
184
+ * @param {string} serverUrl - Builder Server base URL
185
+ * @returns {Promise<Object>} HealthResponseDto: status, checks (dataDir, encryptionKey, ca, users, tokens)
186
+ */
187
+ async function getHealth(serverUrl) {
188
+ const base = normalizeBaseUrl(serverUrl);
189
+ return request(buildUrl(base, '/health'));
190
+ }
191
+
192
+ /**
193
+ * Get developer settings (cert-authenticated). GET /api/dev/settings
194
+ * When clientKeyPem is provided, uses mTLS (TLS client cert); otherwise X-Client-Cert header only.
195
+ * @requiresPermission {BuilderServer} Client certificate (X-Client-Cert or mTLS)
196
+ * @param {string} serverUrl - Builder Server base URL
197
+ * @param {string} clientCertPem - PEM-encoded client certificate
198
+ * @param {string} [clientKeyPem] - PEM-encoded client private key (enables mTLS when provided)
199
+ * @returns {Promise<Object>} SettingsResponseDto
200
+ */
201
+ async function getSettings(serverUrl, clientCertPem, clientKeyPem) {
202
+ if (!clientCertPem || typeof clientCertPem !== 'string') {
203
+ throw new Error('Client certificate PEM is required for getSettings');
204
+ }
205
+ const base = normalizeBaseUrl(serverUrl);
206
+ const url = buildUrl(base, '/api/dev/settings');
207
+ const reqOptions = { method: 'GET', headers: { 'X-Client-Cert': encodeCertForHeader(clientCertPem) } };
208
+ if (clientKeyPem && typeof clientKeyPem === 'string') {
209
+ return requestWithCertImpl(url, reqOptions, clientCertPem, clientKeyPem);
210
+ }
211
+ return request(url, reqOptions);
212
+ }
213
+
214
+ /**
215
+ * List developers. GET /api/dev/users
216
+ * @requiresPermission {BuilderServer} Client certificate (X-Client-Cert)
217
+ * @param {string} serverUrl - Builder Server base URL
218
+ * @param {string} clientCertPem - PEM client certificate
219
+ * @returns {Promise<Object[]>} Array of UserResponseDto (empty when none)
220
+ */
221
+ async function listUsers(serverUrl, clientCertPem) {
222
+ const base = normalizeBaseUrl(serverUrl);
223
+ const data = await request(buildUrl(base, '/api/dev/users'), {
224
+ method: 'GET',
225
+ headers: { 'X-Client-Cert': encodeCertForHeader(clientCertPem) }
226
+ });
227
+ return Array.isArray(data) ? data : [];
228
+ }
229
+
230
+ /**
231
+ * Create developer. POST /api/dev/users
232
+ * @requiresPermission {BuilderServer} Client certificate (X-Client-Cert)
233
+ * @param {string} serverUrl - Builder Server base URL
234
+ * @param {string} clientCertPem - PEM client certificate
235
+ * @param {Object} body - CreateUserDto: developerId, name, email, optional groups
236
+ * @returns {Promise<Object>} UserResponseDto
237
+ */
238
+ async function createUser(serverUrl, clientCertPem, body) {
239
+ const base = normalizeBaseUrl(serverUrl);
240
+ return request(buildUrl(base, '/api/dev/users'), {
241
+ method: 'POST',
242
+ headers: { 'X-Client-Cert': encodeCertForHeader(clientCertPem) },
243
+ body
244
+ });
245
+ }
246
+
247
+ /**
248
+ * Update developer. PATCH /api/dev/users/:id
249
+ * @requiresPermission {BuilderServer} Client certificate (X-Client-Cert)
250
+ * @param {string} serverUrl - Builder Server base URL
251
+ * @param {string} clientCertPem - PEM client certificate
252
+ * @param {string} id - Developer ID
253
+ * @param {Object} body - UpdateUserDto: at least one of name, email, groups
254
+ * @returns {Promise<Object>} UserResponseDto
255
+ */
256
+ async function updateUser(serverUrl, clientCertPem, id, body) {
257
+ const base = normalizeBaseUrl(serverUrl);
258
+ return request(buildUrl(base, `/api/dev/users/${encodeURIComponent(id)}`), {
259
+ method: 'PATCH',
260
+ headers: { 'X-Client-Cert': encodeCertForHeader(clientCertPem) },
261
+ body
262
+ });
263
+ }
264
+
265
+ /**
266
+ * Delete developer. DELETE /api/dev/users/:id
267
+ * @requiresPermission {BuilderServer} Client certificate (X-Client-Cert)
268
+ * @param {string} serverUrl - Builder Server base URL
269
+ * @param {string} clientCertPem - PEM client certificate
270
+ * @param {string} id - Developer ID
271
+ * @returns {Promise<Object>} DeletedResponseDto
272
+ */
273
+ async function deleteUser(serverUrl, clientCertPem, id) {
274
+ const base = normalizeBaseUrl(serverUrl);
275
+ return request(buildUrl(base, `/api/dev/users/${encodeURIComponent(id)}`), {
276
+ method: 'DELETE',
277
+ headers: { 'X-Client-Cert': encodeCertForHeader(clientCertPem) }
278
+ });
279
+ }
280
+
281
+ /**
282
+ * Create or regenerate one-time PIN. POST /api/dev/users/:id/pin
283
+ * @requiresPermission {BuilderServer} Client certificate (X-Client-Cert)
284
+ * @param {string} serverUrl - Builder Server base URL
285
+ * @param {string} clientCertPem - PEM client certificate
286
+ * @param {string} id - Developer ID
287
+ * @returns {Promise<Object>} CreatePinResponseDto: pin, expiresAt
288
+ */
289
+ async function createPin(serverUrl, clientCertPem, id) {
290
+ const base = normalizeBaseUrl(serverUrl);
291
+ return request(buildUrl(base, `/api/dev/users/${encodeURIComponent(id)}/pin`), {
292
+ method: 'POST',
293
+ headers: { 'X-Client-Cert': encodeCertForHeader(clientCertPem) }
294
+ });
295
+ }
296
+
297
+ /**
298
+ * List SSH keys for developer. GET /api/dev/users/:id/ssh-keys
299
+ * @requiresPermission {BuilderServer} Client certificate (X-Client-Cert)
300
+ * @param {string} serverUrl - Builder Server base URL
301
+ * @param {string} clientCertPem - PEM client certificate
302
+ * @param {string} id - Developer ID
303
+ * @returns {Promise<Object[]>} Array of SshKeyItemDto
304
+ */
305
+ async function listSshKeys(serverUrl, clientCertPem, id) {
306
+ const base = normalizeBaseUrl(serverUrl);
307
+ const data = await request(buildUrl(base, `/api/dev/users/${encodeURIComponent(id)}/ssh-keys`), {
308
+ method: 'GET',
309
+ headers: { 'X-Client-Cert': encodeCertForHeader(clientCertPem) }
310
+ });
311
+ return Array.isArray(data) ? data : [];
312
+ }
313
+
314
+ /**
315
+ * Add SSH public key for developer. POST /api/dev/users/:id/ssh-keys
316
+ * When clientKeyPem is provided, uses mTLS (TLS client cert); otherwise X-Client-Cert header only.
317
+ * @requiresPermission {BuilderServer} Client certificate (X-Client-Cert or mTLS)
318
+ * @param {string} serverUrl - Builder Server base URL
319
+ * @param {string} clientCertPem - PEM client certificate
320
+ * @param {string} id - Developer ID
321
+ * @param {Object} body - AddSshKeyDto: publicKey, optional label
322
+ * @param {string} [clientKeyPem] - PEM-encoded client private key (enables mTLS when provided)
323
+ * @returns {Promise<Object>} SshKeyItemDto
324
+ */
325
+ async function addSshKey(serverUrl, clientCertPem, id, body, clientKeyPem) {
326
+ const base = normalizeBaseUrl(serverUrl);
327
+ const url = buildUrl(base, `/api/dev/users/${encodeURIComponent(id)}/ssh-keys`);
328
+ const reqOptions = {
329
+ method: 'POST',
330
+ headers: { 'X-Client-Cert': encodeCertForHeader(clientCertPem) },
331
+ body
332
+ };
333
+ if (clientKeyPem && typeof clientKeyPem === 'string') {
334
+ return requestWithCertImpl(url, reqOptions, clientCertPem, clientKeyPem);
335
+ }
336
+ return request(url, reqOptions);
337
+ }
338
+
339
+ /**
340
+ * Remove SSH key by fingerprint. DELETE /api/dev/users/:id/ssh-keys/:fingerprint
341
+ * @requiresPermission {BuilderServer} Client certificate (X-Client-Cert)
342
+ * @param {string} serverUrl - Builder Server base URL
343
+ * @param {string} clientCertPem - PEM client certificate
344
+ * @param {string} id - Developer ID
345
+ * @param {string} fingerprint - Key fingerprint
346
+ * @returns {Promise<Object>} DeletedResponseDto
347
+ */
348
+ async function removeSshKey(serverUrl, clientCertPem, id, fingerprint) {
349
+ const base = normalizeBaseUrl(serverUrl);
350
+ return request(buildUrl(base, `/api/dev/users/${encodeURIComponent(id)}/ssh-keys/${encodeURIComponent(fingerprint)}`), {
351
+ method: 'DELETE',
352
+ headers: { 'X-Client-Cert': encodeCertForHeader(clientCertPem) }
353
+ });
354
+ }
355
+
356
+ /**
357
+ * List secrets. GET /api/dev/secrets
358
+ * @requiresPermission {BuilderServer} Client certificate (X-Client-Cert)
359
+ * @param {string} serverUrl - Builder Server base URL
360
+ * @param {string} clientCertPem - PEM client certificate
361
+ * @returns {Promise<Object[]>} Array of SecretItemDto: name, value
362
+ */
363
+ async function listSecrets(serverUrl, clientCertPem) {
364
+ const base = normalizeBaseUrl(serverUrl);
365
+ const data = await request(buildUrl(base, '/api/dev/secrets'), {
366
+ method: 'GET',
367
+ headers: { 'X-Client-Cert': encodeCertForHeader(clientCertPem) }
368
+ });
369
+ return Array.isArray(data) ? data : [];
370
+ }
371
+
372
+ /**
373
+ * Add or update secret. POST /api/dev/secrets
374
+ * @requiresPermission {BuilderServer} Client certificate (X-Client-Cert)
375
+ * @param {string} serverUrl - Builder Server base URL
376
+ * @param {string} clientCertPem - PEM client certificate
377
+ * @param {Object} body - AddSecretDto: key, value
378
+ * @returns {Promise<Object>} AddSecretResponseDto
379
+ */
380
+ async function addSecret(serverUrl, clientCertPem, body) {
381
+ const base = normalizeBaseUrl(serverUrl);
382
+ return request(buildUrl(base, '/api/dev/secrets'), {
383
+ method: 'POST',
384
+ headers: { 'X-Client-Cert': encodeCertForHeader(clientCertPem) },
385
+ body
386
+ });
387
+ }
388
+
389
+ /**
390
+ * Delete secret by key. DELETE /api/dev/secrets/:key
391
+ * @requiresPermission {BuilderServer} Client certificate (X-Client-Cert)
392
+ * @param {string} serverUrl - Builder Server base URL
393
+ * @param {string} clientCertPem - PEM client certificate
394
+ * @param {string} key - Secret key
395
+ * @returns {Promise<Object>} DeleteSecretResponseDto
396
+ */
397
+ async function deleteSecret(serverUrl, clientCertPem, key) {
398
+ const base = normalizeBaseUrl(serverUrl);
399
+ return request(buildUrl(base, `/api/dev/secrets/${encodeURIComponent(key)}`), {
400
+ method: 'DELETE',
401
+ headers: { 'X-Client-Cert': encodeCertForHeader(clientCertPem) }
402
+ });
403
+ }
404
+
405
+ module.exports = {
406
+ issueCert,
407
+ getHealth,
408
+ getSettings,
409
+ listUsers,
410
+ createUser,
411
+ updateUser,
412
+ deleteUser,
413
+ createPin,
414
+ listSshKeys,
415
+ addSshKey,
416
+ removeSshKey,
417
+ listSecrets,
418
+ addSecret,
419
+ deleteSecret,
420
+ normalizeBaseUrl,
421
+ buildUrl,
422
+ encodeCertForHeader
423
+ };
@@ -0,0 +1,23 @@
1
+ /**
2
+ * @fileoverview Credential API type definitions (Dataplane secret store)
3
+ * @author AI Fabrix Team
4
+ * @version 2.0.0
5
+ */
6
+
7
+ /**
8
+ * Single secret item for Dataplane credential secret store.
9
+ * Key is the kv:// path; value must be plain (resolved), never a kv:// reference.
10
+ * @typedef {Object} SecretStoreItem
11
+ * @property {string} key - kv:// path (e.g. kv://secrets/client-secret)
12
+ * @property {string} value - Plain secret value (encrypted at rest by dataplane)
13
+ */
14
+
15
+ /**
16
+ * Response from POST /api/v1/credential/secret (Dataplane).
17
+ * @typedef {Object} SecretStoreResponse
18
+ * @property {number} [stored] - Number of secrets stored
19
+ * @property {boolean} [success] - Request success flag
20
+ * @property {string} [error] - Error message when success is false
21
+ */
22
+
23
+ module.exports = {};
@@ -0,0 +1,140 @@
1
+ /**
2
+ * @fileoverview Builder Server (dev) API type definitions – issue-cert, settings, users, SSH keys, secrets
3
+ * @author AI Fabrix Team
4
+ * @version 2.0.0
5
+ */
6
+
7
+ /**
8
+ * Issue certificate request (POST /api/dev/issue-cert). Public; no client cert.
9
+ * @typedef {Object} IssueCertDto
10
+ * @property {string} developerId - Developer ID (must match user for whom PIN was created)
11
+ * @property {string} pin - One-time PIN from POST /api/dev/users/:id/pin
12
+ * @property {string} csr - PEM-encoded Certificate Signing Request
13
+ */
14
+
15
+ /**
16
+ * Issue certificate response (POST /api/dev/issue-cert)
17
+ * @typedef {Object} IssueCertResponseDto
18
+ * @property {string} certificate - PEM-encoded X.509 certificate
19
+ * @property {number} validDays - Validity in days
20
+ * @property {string} validNotAfter - ISO 8601 validity end (UTC)
21
+ * @property {string} [caCertificate] - Optional PEM-encoded CA certificate (for remote Docker TLS; saved as ca.pem)
22
+ * @property {string} [ca] - Optional alias for caCertificate
23
+ */
24
+
25
+ /**
26
+ * Developer settings (GET /api/dev/settings). Cert-authenticated.
27
+ * @typedef {Object} SettingsResponseDto
28
+ * @property {string} user-mutagen-folder - Server path to workspace root (no app segment)
29
+ * @property {string} secrets-encryption - Encryption key (hex)
30
+ * @property {string} aifabrix-secrets - Path or URL for secrets
31
+ * @property {string} aifabrix-env-config - Env config path
32
+ * @property {string} remote-server - Builder-server base URL
33
+ * @property {string} docker-endpoint - Docker API endpoint
34
+ * @property {string} sync-ssh-user - SSH user for Mutagen
35
+ * @property {string} sync-ssh-host - SSH host for Mutagen
36
+ */
37
+
38
+ /**
39
+ * User list item (GET /api/dev/users)
40
+ * @typedef {Object} UserResponseDto
41
+ * @property {string} id - Developer ID
42
+ * @property {string} name - Display name
43
+ * @property {string} email - Email
44
+ * @property {string} createdAt - ISO 8601
45
+ * @property {boolean} certificateIssued - Whether cert was issued
46
+ * @property {string} [certificateValidNotAfter] - Cert validity end (optional)
47
+ * @property {string[]} groups - Access groups (admin, secret-manager, developer)
48
+ */
49
+
50
+ /**
51
+ * Create user request (POST /api/dev/users)
52
+ * @typedef {Object} CreateUserDto
53
+ * @property {string} developerId - Unique developer ID (numeric string)
54
+ * @property {string} name - Display name
55
+ * @property {string} email - Email
56
+ * @property {string[]} [groups] - Default [developer]
57
+ */
58
+
59
+ /**
60
+ * Update user request (PATCH /api/dev/users/:id). At least one field.
61
+ * @typedef {Object} UpdateUserDto
62
+ * @property {string} [name] - Display name
63
+ * @property {string} [email] - Email
64
+ * @property {string[]} [groups] - Access groups
65
+ */
66
+
67
+ /**
68
+ * Create PIN response (POST /api/dev/users/:id/pin)
69
+ * @typedef {Object} CreatePinResponseDto
70
+ * @property {string} pin - One-time PIN
71
+ * @property {string} expiresAt - ISO 8601
72
+ */
73
+
74
+ /**
75
+ * Add SSH key request (POST /api/dev/users/:id/ssh-keys)
76
+ * @typedef {Object} AddSshKeyDto
77
+ * @property {string} publicKey - SSH public key line
78
+ * @property {string} [label] - Optional label
79
+ */
80
+
81
+ /**
82
+ * SSH key item (list/add response)
83
+ * @typedef {Object} SshKeyItemDto
84
+ * @property {string} fingerprint - Key fingerprint
85
+ * @property {string} [label] - Optional label
86
+ * @property {string} [createdAt] - ISO 8601
87
+ */
88
+
89
+ /**
90
+ * Deleted response (DELETE endpoints)
91
+ * @typedef {Object} DeletedResponseDto
92
+ * @property {string} deleted - ID or key of deleted resource
93
+ */
94
+
95
+ /**
96
+ * Secret item (GET /api/dev/secrets)
97
+ * @typedef {Object} SecretItemDto
98
+ * @property {string} name - Secret key
99
+ * @property {string} value - Decrypted value
100
+ */
101
+
102
+ /**
103
+ * Add secret request (POST /api/dev/secrets)
104
+ * @typedef {Object} AddSecretDto
105
+ * @property {string} key - Secret key
106
+ * @property {string} value - Secret value
107
+ */
108
+
109
+ /**
110
+ * Add secret response
111
+ * @typedef {Object} AddSecretResponseDto
112
+ * @property {string} key - Key that was added/updated
113
+ */
114
+
115
+ /**
116
+ * Delete secret response
117
+ * @typedef {Object} DeleteSecretResponseDto
118
+ * @property {string} deleted - Key that was removed
119
+ */
120
+
121
+ /**
122
+ * Health response (GET /health)
123
+ * @typedef {Object} HealthResponseDto
124
+ * @property {string} status - Overall status, e.g. "ok"
125
+ * @property {Object} checks - Per-component health checks
126
+ * @property {string} checks.dataDir - Data directory check ("ok" or error)
127
+ * @property {string} checks.encryptionKey - Encryption key check ("ok" or error)
128
+ * @property {string} checks.ca - CA certificate check ("ok" or error)
129
+ * @property {string} checks.users - Users store check ("ok" or error)
130
+ * @property {string} checks.tokens - Tokens store check ("ok" or error)
131
+ */
132
+
133
+ /**
134
+ * Error response (all error responses)
135
+ * @typedef {Object} ErrorResponseDto
136
+ * @property {number} statusCode - HTTP status
137
+ * @property {string} error - Short error type
138
+ * @property {string} message - Human-readable message
139
+ * @property {string} [code] - Optional machine-readable code
140
+ */