@aifabrix/builder 2.44.4 → 2.44.6

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 (214) hide show
  1. package/.cursor/rules/cli-layout.mdc +1 -1
  2. package/.cursor/rules/project-rules.mdc +1 -1
  3. package/.npmrc.token +1 -1
  4. package/README.md +15 -23
  5. package/integration/hubspot-test/README.md +2 -0
  6. package/integration/hubspot-test/test.js +5 -3
  7. package/jest.projects.js +68 -17
  8. package/lib/api/controller-health.api.js +49 -0
  9. package/lib/api/dimension-values.api.js +82 -0
  10. package/lib/api/dimensions.api.js +114 -0
  11. package/lib/api/external-systems.api.js +1 -0
  12. package/lib/api/integration-clients.api.js +168 -0
  13. package/lib/api/types/dimension-values.types.js +28 -0
  14. package/lib/api/types/dimensions.types.js +31 -0
  15. package/lib/api/types/integration-clients.types.js +45 -0
  16. package/lib/api/types/wizard.types.js +2 -1
  17. package/lib/api/validation-runner.js +46 -25
  18. package/lib/app/deploy-config.js +11 -1
  19. package/lib/app/deploy-status-display.js +3 -3
  20. package/lib/app/deploy.js +36 -14
  21. package/lib/app/display.js +15 -11
  22. package/lib/app/push.js +46 -23
  23. package/lib/app/register.js +1 -1
  24. package/lib/app/restart-display.js +95 -0
  25. package/lib/app/rotate-secret.js +1 -1
  26. package/lib/app/run-container-start.js +12 -6
  27. package/lib/app/run-env-compose.js +30 -1
  28. package/lib/app/run-helpers.js +44 -12
  29. package/lib/app/run-reload-sync.js +148 -0
  30. package/lib/app/run-resolve-image.js +51 -1
  31. package/lib/app/run.js +99 -73
  32. package/lib/build/index.js +75 -45
  33. package/lib/cli/doctor-check.js +117 -0
  34. package/lib/cli/index.js +8 -2
  35. package/lib/cli/infra-guided.js +445 -0
  36. package/lib/cli/setup-app.help.js +1 -1
  37. package/lib/cli/setup-app.js +20 -2
  38. package/lib/cli/setup-app.test-commands.js +9 -5
  39. package/lib/cli/setup-auth.js +26 -0
  40. package/lib/cli/setup-dev-path-commands.js +50 -3
  41. package/lib/cli/setup-infra.js +138 -61
  42. package/lib/cli/setup-integration-client.js +182 -0
  43. package/lib/cli/setup-parameters.js +21 -2
  44. package/lib/cli/setup-platform.js +102 -0
  45. package/lib/cli/setup-secrets.js +18 -6
  46. package/lib/cli/setup-utility.js +97 -33
  47. package/lib/commands/datasource-capability-dimension-cli.js +128 -0
  48. package/lib/commands/datasource-capability-output.js +29 -0
  49. package/lib/commands/datasource-capability-relate-cli.js +140 -0
  50. package/lib/commands/datasource-capability.js +411 -0
  51. package/lib/commands/datasource-unified-test-cli.options.js +1 -1
  52. package/lib/commands/datasource.js +53 -13
  53. package/lib/commands/dev-down.js +3 -3
  54. package/lib/commands/dev-infra-gate.js +32 -0
  55. package/lib/commands/dev-init.js +13 -7
  56. package/lib/commands/dimension-value.js +179 -0
  57. package/lib/commands/dimension.js +330 -0
  58. package/lib/commands/integration-client.js +430 -0
  59. package/lib/commands/login-device.js +65 -30
  60. package/lib/commands/login.js +21 -10
  61. package/lib/commands/parameters-validate.js +78 -13
  62. package/lib/commands/repair-datasource-auto-rbac.js +166 -0
  63. package/lib/commands/repair-datasource-keys.js +10 -5
  64. package/lib/commands/repair-datasource.js +19 -7
  65. package/lib/commands/repair-env-template.js +4 -1
  66. package/lib/commands/repair-openapi-sync.js +172 -0
  67. package/lib/commands/repair-persist.js +102 -0
  68. package/lib/commands/repair-rbac-extract.js +27 -0
  69. package/lib/commands/repair-rbac-migrate.js +186 -0
  70. package/lib/commands/repair-rbac.js +225 -19
  71. package/lib/commands/repair-system-alignment.js +246 -0
  72. package/lib/commands/repair-system-permissions.js +168 -0
  73. package/lib/commands/repair.js +120 -354
  74. package/lib/commands/secure.js +1 -1
  75. package/lib/commands/setup-modes.js +455 -0
  76. package/lib/commands/setup-prompts.js +388 -0
  77. package/lib/commands/setup.js +149 -0
  78. package/lib/commands/teardown.js +228 -0
  79. package/lib/commands/test-e2e-external.js +4 -3
  80. package/lib/commands/up-common.js +97 -12
  81. package/lib/commands/up-dataplane.js +33 -11
  82. package/lib/commands/up-miso.js +7 -11
  83. package/lib/commands/upload.js +109 -23
  84. package/lib/commands/wizard-core-helpers.js +14 -11
  85. package/lib/commands/wizard-core.js +58 -15
  86. package/lib/commands/wizard-dataplane.js +2 -2
  87. package/lib/commands/wizard-entity-selection.js +72 -14
  88. package/lib/commands/wizard-headless.js +7 -3
  89. package/lib/commands/wizard-helpers.js +13 -1
  90. package/lib/commands/wizard.js +210 -61
  91. package/lib/constants/infra-compose-service-names.js +40 -0
  92. package/lib/core/env-reader.js +16 -3
  93. package/lib/core/secrets-admin-env.js +101 -0
  94. package/lib/core/secrets-ensure-infra.js +34 -1
  95. package/lib/core/secrets-ensure.js +88 -66
  96. package/lib/core/secrets-env-content.js +432 -0
  97. package/lib/core/secrets-env-write.js +27 -1
  98. package/lib/core/secrets-load.js +248 -0
  99. package/lib/core/secrets-names.js +32 -0
  100. package/lib/core/secrets.js +17 -757
  101. package/lib/datasource/capability/basic-exposure.js +76 -0
  102. package/lib/datasource/capability/capability-diff-slice.js +41 -0
  103. package/lib/datasource/capability/capability-key.js +34 -0
  104. package/lib/datasource/capability/capability-resolve.js +172 -0
  105. package/lib/datasource/capability/capability-storage-keys.js +22 -0
  106. package/lib/datasource/capability/copy-operations.js +348 -0
  107. package/lib/datasource/capability/copy-test-payload.js +139 -0
  108. package/lib/datasource/capability/create-operations.js +235 -0
  109. package/lib/datasource/capability/dimension-operations.js +151 -0
  110. package/lib/datasource/capability/dimension-validate.js +219 -0
  111. package/lib/datasource/capability/json-pointer.js +31 -0
  112. package/lib/datasource/capability/reference-rewrite.js +51 -0
  113. package/lib/datasource/capability/relate-operations.js +325 -0
  114. package/lib/datasource/capability/relate-validate.js +219 -0
  115. package/lib/datasource/capability/remove-operations.js +275 -0
  116. package/lib/datasource/capability/run-capability-copy.js +152 -0
  117. package/lib/datasource/capability/run-capability-diff.js +135 -0
  118. package/lib/datasource/capability/run-capability-dimension.js +291 -0
  119. package/lib/datasource/capability/run-capability-edit.js +377 -0
  120. package/lib/datasource/capability/run-capability-relate.js +193 -0
  121. package/lib/datasource/capability/run-capability-remove.js +105 -0
  122. package/lib/datasource/capability/templates/minimal-fetch.json +18 -0
  123. package/lib/datasource/capability/validate-capability-slice.js +35 -0
  124. package/lib/datasource/list.js +136 -23
  125. package/lib/datasource/log-viewer.js +2 -4
  126. package/lib/datasource/unified-validation-run.js +51 -16
  127. package/lib/datasource/validate.js +53 -1
  128. package/lib/deployment/deploy-poll-ui.js +60 -0
  129. package/lib/deployment/deployer-status.js +29 -3
  130. package/lib/deployment/deployer.js +48 -30
  131. package/lib/deployment/environment.js +7 -2
  132. package/lib/deployment/poll-interval.js +72 -0
  133. package/lib/deployment/push.js +11 -9
  134. package/lib/external-system/deploy.js +4 -1
  135. package/lib/external-system/download.js +61 -32
  136. package/lib/external-system/sync-deploy-manifest.js +33 -0
  137. package/lib/generator/wizard-prompts.js +7 -1
  138. package/lib/generator/wizard.js +34 -0
  139. package/lib/infrastructure/index.js +49 -19
  140. package/lib/infrastructure/orphan-infra-docker-teardown.js +177 -0
  141. package/lib/parameters/infra-kv-discovery.js +29 -4
  142. package/lib/parameters/infra-parameter-catalog.js +6 -3
  143. package/lib/parameters/infra-parameter-validate.js +67 -19
  144. package/lib/resolvers/datasource-resolver.js +53 -0
  145. package/lib/resolvers/dimension-file.js +52 -0
  146. package/lib/resolvers/manifest-resolver.js +133 -0
  147. package/lib/schema/external-datasource.schema.json +183 -53
  148. package/lib/schema/external-system.schema.json +23 -10
  149. package/lib/schema/infra.parameter.yaml +26 -11
  150. package/lib/schema/wizard-config.schema.json +2 -2
  151. package/lib/utils/aifabrix-config-dir-walk.js +40 -0
  152. package/lib/utils/aifabrix-runtime-config-dir.js +26 -3
  153. package/lib/utils/app-run-containers.js +2 -2
  154. package/lib/utils/bash-secret-env.js +59 -0
  155. package/lib/utils/cli-secrets-error-format.js +78 -0
  156. package/lib/utils/cli-test-layout-chalk.js +31 -9
  157. package/lib/utils/cli-utils.js +4 -36
  158. package/lib/utils/datasource-test-run-display.js +8 -0
  159. package/lib/utils/dev-hosts-helper.js +3 -2
  160. package/lib/utils/dev-init-ssh-merge.js +2 -1
  161. package/lib/utils/docker-build.js +17 -9
  162. package/lib/utils/docker-reload-mount.js +127 -0
  163. package/lib/utils/external-readme.js +117 -4
  164. package/lib/utils/external-system-local-test-tty.js +3 -2
  165. package/lib/utils/external-system-readiness-core.js +45 -12
  166. package/lib/utils/external-system-readiness-deploy-display.js +3 -3
  167. package/lib/utils/external-system-readiness-display-internals.js +33 -3
  168. package/lib/utils/external-system-readiness-display.js +10 -1
  169. package/lib/utils/file-upload.js +40 -3
  170. package/lib/utils/health-check-db-init.js +107 -0
  171. package/lib/utils/health-check-public-warn.js +69 -0
  172. package/lib/utils/health-check-url.js +19 -4
  173. package/lib/utils/health-check.js +135 -105
  174. package/lib/utils/help-builder.js +5 -1
  175. package/lib/utils/image-name.js +34 -7
  176. package/lib/utils/integration-file-backup.js +74 -0
  177. package/lib/utils/mutagen-install.js +30 -3
  178. package/lib/utils/paths.js +108 -25
  179. package/lib/utils/postgres-wipe.js +212 -0
  180. package/lib/utils/register-aifabrix-shell-env.js +15 -0
  181. package/lib/utils/remote-dev-auth.js +21 -5
  182. package/lib/utils/remote-docker-env.js +9 -1
  183. package/lib/utils/remote-secrets-loader.js +42 -3
  184. package/lib/utils/resolve-docker-image-ref.js +9 -3
  185. package/lib/utils/secrets-ancestor-paths.js +47 -0
  186. package/lib/utils/secrets-helpers.js +17 -10
  187. package/lib/utils/secrets-kv-refs.js +42 -0
  188. package/lib/utils/secrets-kv-scope.js +19 -2
  189. package/lib/utils/secrets-materialize-local.js +134 -0
  190. package/lib/utils/secrets-path.js +24 -10
  191. package/lib/utils/secrets-utils.js +2 -2
  192. package/lib/utils/system-builder-root.js +34 -0
  193. package/lib/utils/url-declarative-resolve-build.js +6 -1
  194. package/lib/utils/url-declarative-runtime-base-path.js +32 -0
  195. package/lib/utils/url-declarative-vdir-inactive-env.js +2 -1
  196. package/lib/utils/urls-local-registry.js +73 -20
  197. package/lib/utils/validation-poll-ui.js +81 -0
  198. package/lib/utils/validation-run-poll.js +29 -5
  199. package/lib/utils/with-muted-logger.js +53 -0
  200. package/package.json +1 -1
  201. package/templates/applications/dataplane/application.yaml +1 -1
  202. package/templates/applications/dataplane/rbac.yaml +10 -10
  203. package/templates/applications/keycloak/env.template +8 -6
  204. package/templates/applications/miso-controller/application.yaml +7 -0
  205. package/templates/applications/miso-controller/env.template +7 -7
  206. package/templates/applications/miso-controller/rbac.yaml +9 -9
  207. package/templates/external-system/README.md.hbs +89 -102
  208. package/.nyc_output/55e9d034-ddab-4579-a706-e02a91d75c91.json +0 -1
  209. package/.nyc_output/processinfo/55e9d034-ddab-4579-a706-e02a91d75c91.json +0 -1
  210. package/.nyc_output/processinfo/index.json +0 -1
  211. package/lib/api/service-users.api.js +0 -150
  212. package/lib/api/types/service-users.types.js +0 -65
  213. package/lib/cli/setup-service-user.js +0 -187
  214. package/lib/commands/service-user.js +0 -429
@@ -0,0 +1,133 @@
1
+ /**
2
+ * Shared remote manifest lookup helpers (dataplane).
3
+ *
4
+ * This module does not decide *when* to fetch; callers can treat failures as
5
+ * \"not authenticated\" or \"remote unavailable\" depending on their UX needs.
6
+ *
7
+ * @fileoverview Remote manifest fetch helpers
8
+ * @author AI Fabrix Team
9
+ * @version 2.0.0
10
+ */
11
+
12
+ 'use strict';
13
+
14
+ const { resolveDataplaneAndAuth } = require('../commands/upload');
15
+ const { getExternalSystemConfig } = require('../api/external-systems.api');
16
+ const { getDatasourceConfig } = require('../api/datasources-core.api');
17
+
18
+ /**
19
+ * @param {any} maybeEnvelope
20
+ * @returns {{ isFailure: boolean, status: number, errorType: string|undefined, message: string|undefined }}
21
+ */
22
+ function unwrapApiFailure(maybeEnvelope) {
23
+ if (!maybeEnvelope || typeof maybeEnvelope !== 'object') {
24
+ return { isFailure: false, status: 0, errorType: undefined, message: undefined };
25
+ }
26
+
27
+ if (maybeEnvelope.success !== false) {
28
+ return { isFailure: false, status: 0, errorType: undefined, message: undefined };
29
+ }
30
+
31
+ return {
32
+ isFailure: true,
33
+ status: Number(maybeEnvelope.status) || 0,
34
+ errorType: maybeEnvelope.errorType,
35
+ message: maybeEnvelope.error || maybeEnvelope.formattedError || maybeEnvelope.message
36
+ };
37
+ }
38
+
39
+ /**
40
+ * @param {{ status: number, errorType: string|undefined, message: string|undefined }} failure
41
+ * @returns {string|undefined}
42
+ */
43
+ function failureCodeFrom(failure) {
44
+ if (failure.errorType === 'notfound' || failure.status === 404) return 'not_found';
45
+ if (/Authentication required\./i.test(String(failure.message || ''))) return 'not_authenticated';
46
+ return undefined;
47
+ }
48
+
49
+ /**
50
+ * Fetch running manifest for one external system key (includes dataSources[]).
51
+ *
52
+ * @param {string} systemKey
53
+ * @param {{ silent?: boolean }} [opts]
54
+ * @returns {Promise<{ ok: true, dataplaneUrl: string, authConfig: Object, manifest: any } | { ok: false, error: string, code?: string }>}
55
+ */
56
+ async function tryFetchRunningManifest(systemKey, opts = {}) {
57
+ try {
58
+ const { dataplaneUrl, authConfig } = await resolveDataplaneAndAuth(String(systemKey || '').trim(), {
59
+ silent: opts.silent === true
60
+ });
61
+ const res = await getExternalSystemConfig(dataplaneUrl, systemKey, authConfig);
62
+ const manifest = res?.data?.data ?? res?.data ?? res;
63
+ return { ok: true, dataplaneUrl, authConfig, manifest };
64
+ } catch (e) {
65
+ const msg = e?.message || String(e);
66
+ const code = /Authentication required\./i.test(msg) ? 'not_authenticated' : undefined;
67
+ return { ok: false, error: msg, code };
68
+ }
69
+ }
70
+
71
+ /**
72
+ * @param {any} runningManifest
73
+ * @param {string} datasourceKey
74
+ * @returns {any|null}
75
+ */
76
+ function findDatasourceInRunningManifest(runningManifest, datasourceKey) {
77
+ const dsKey = String(datasourceKey || '').trim();
78
+ const dataSources = runningManifest?.dataSources;
79
+ if (!Array.isArray(dataSources)) return null;
80
+ return dataSources.find((d) => d && d.key === dsKey) || null;
81
+ }
82
+
83
+ module.exports = {
84
+ tryFetchRunningManifest,
85
+ findDatasourceInRunningManifest,
86
+ /**
87
+ * Fetch one datasource config by key/id using dataplane auth resolved from a systemKey.
88
+ *
89
+ * This is useful for cross-system FK target validation because datasource keys are globally unique.
90
+ *
91
+ * @param {string} systemKey - any system key usable for auth resolution
92
+ * @param {string} datasourceKey - datasource key to fetch config for
93
+ * @param {{ silent?: boolean }} [opts]
94
+ * @returns {Promise<{ ok: true, datasourceConfig: any } | { ok: false, error: string, code?: string }>}
95
+ */
96
+ async tryFetchDatasourceConfig(systemKey, datasourceKey, opts = {}) {
97
+ try {
98
+ const trimmedSystemKey = String(systemKey || '').trim();
99
+ const trimmedDatasourceKey = String(datasourceKey || '').trim();
100
+
101
+ const { dataplaneUrl, authConfig } = await resolveDataplaneAndAuth(trimmedSystemKey, {
102
+ silent: opts.silent === true
103
+ });
104
+
105
+ const res = await getDatasourceConfig(dataplaneUrl, trimmedDatasourceKey, authConfig);
106
+ const outerFailure = unwrapApiFailure(res);
107
+ if (outerFailure.isFailure) {
108
+ return {
109
+ ok: false,
110
+ error: outerFailure.message || `Failed to fetch datasource config: ${trimmedDatasourceKey}`,
111
+ code: failureCodeFrom(outerFailure)
112
+ };
113
+ }
114
+
115
+ const cfg = res?.data?.data ?? res?.data ?? res;
116
+ const innerFailure = unwrapApiFailure(cfg);
117
+ if (innerFailure.isFailure) {
118
+ return {
119
+ ok: false,
120
+ error: innerFailure.message || `Failed to fetch datasource config: ${trimmedDatasourceKey}`,
121
+ code: failureCodeFrom(innerFailure)
122
+ };
123
+ }
124
+
125
+ return { ok: true, datasourceConfig: cfg };
126
+ } catch (e) {
127
+ const msg = e?.message || String(e);
128
+ const code = /Authentication required\./i.test(msg) ? 'not_authenticated' : undefined;
129
+ return { ok: false, error: msg, code };
130
+ }
131
+ }
132
+ };
133
+
@@ -2,17 +2,17 @@
2
2
  "$schema":"https://json-schema.org/draft/2020-12/schema",
3
3
  "$id":"aifabrix://schema/external-datasource.schema.json",
4
4
  "title":"External Data Source",
5
- "description":"Configuration for AI Fabrix ExternalDataSource entities. Includes metadata schema, data dimensions, transformation mappings, OpenAPI/MCP exposure, execution logic, and sync behavior.",
5
+ "description":"Configuration for AI Fabrix ExternalDataSource entities. Includes metadata schema, data dimensions, transformation mappings, OpenAPI/MCP exposure, execution logic, and sync behavior. Root externalSpec is the datasource-level authoritative reference for where to obtain the vendor or tooling API specification used for import and validation; external-system.schema.json openapi/mcp are system-level connectivity hints only.",
6
6
  "metadata":{
7
7
  "key":"external-datasource-schema",
8
8
  "name":"External Data Source Configuration Schema",
9
9
  "description":"JSON schema for validating ExternalDataSource configuration files",
10
- "version":"2.4.6",
10
+ "version":"2.4.8",
11
11
  "type":"schema",
12
12
  "category":"integration",
13
13
  "author":"AI Fabrix Team",
14
14
  "createdAt":"2024-01-01T00:00:00Z",
15
- "updatedAt":"2026-04-20T00:00:00Z",
15
+ "updatedAt":"2026-05-10T00:00:00Z",
16
16
  "compatibility":{
17
17
  "minVersion":"1.0.0",
18
18
  "maxVersion":"3.0.0",
@@ -183,6 +183,27 @@
183
183
  "Added optional fetch.httpResponseNormalization for manifest-driven GET response shaping (302 Location follow, JSON redirect envelopes, nested JSON string URL follow-up)."
184
184
  ],
185
185
  "breaking":false
186
+ },
187
+ {
188
+ "version":"2.4.8",
189
+ "date":"2026-05-10T00:00:00Z",
190
+ "changes":[
191
+ "Documented roles: root externalSpec is the datasource-level authoritative vendor/API specification provenance (fetch, import, validation); config.openapi is the runtime connector (operations, documentKey) and is the behavioral source of truth; external-system openapi/mcp remain connectivity/bootstrap only.",
192
+ "Clarified externalSpec and openapi property descriptions to distinguish vendor spec location from generated or persisted AI-facing OpenAPI material managed by dataplane."
193
+ ],
194
+ "breaking":false
195
+ },
196
+ {
197
+ "version":"2.4.7",
198
+ "date":"2026-05-08T00:00:00Z",
199
+ "changes":[
200
+ "externalSpec: optional root object with required type (openapi | mcp) plus documentKey, url, relativePath for vendor or tooling provenance (wizard/import).",
201
+ "fieldMappings.attributes.<name>: added optional writePath (provider-payload target dot-path), direction (read|write|readwrite), and passthrough (boolean) to support bidirectional mapping authoring; compiler auto-derives writePath from invertible '{{raw.<dot.path>}}' expressions.",
202
+ "fieldMappings.attributes.<name>: short-form sugar accepted - a string equal to a {{...}} expression is normalized to { expression: '<value>' } at compile time.",
203
+ "Rejected body and rawBody as aliases for writePath (engine-neutral writePath is the only write-target identifier).",
204
+ "validate_raw_path: replaced provider-aware traversal with one generic JSON-Schema rule (descend via properties.<seg>; accept remaining path under additionalProperties: true; otherwise emit one actionable error)."
205
+ ],
206
+ "breaking":false
186
207
  }
187
208
  ]
188
209
  },
@@ -283,69 +304,131 @@
283
304
  "properties":{
284
305
  "attributes":{
285
306
  "type":"object",
286
- "description":"Key = normalized attribute name. Value = transformation configuration.",
307
+ "description":"Key = normalized attribute name. Value = transformation configuration. Short-form: a string equal to a {{...}} expression is normalized to { expression: '...' } by the compiler.",
287
308
  "propertyNames":{
288
309
  "pattern":"^[a-z][a-zA-Z0-9]*$"
289
310
  },
290
311
  "additionalProperties":{
291
- "type":"object",
292
- "properties":{
293
- "expression":{
312
+ "oneOf":[
313
+ {
294
314
  "type":"string",
295
- "description":"Pipe-based DSL expression. Allowed roots: raw.<path>, fk.<fk>.metadata.<field>, fk.<fk>.dimension.<dimension>[.label], dimension.<dimension>[.label]. FK traversal in DSL is one-hop only.",
315
+ "description":"Short-form sugar: an expression string is normalized by the compiler to { expression: '<value>' }.",
296
316
  "maxLength":512,
297
317
  "pattern":"^\\s*\\{\\{(raw\\.[a-zA-Z0-9_.]+|fk\\.[a-z][a-zA-Z0-9]*\\.metadata\\.[a-z][a-zA-Z0-9]*|fk\\.[a-z][a-zA-Z0-9]*\\.dimension\\.[a-zA-Z0-9_]+(\\.label)?|dimension\\.[a-zA-Z0-9_]+(\\.label)?)\\}\\}(\\s*\\|\\s*[a-zA-Z0-9_]+(\\([^)]*\\))?)*\\s*$"
298
318
  },
299
- "description":{
300
- "type":"string",
301
- "description":"Technical description of the normalized field."
302
- },
303
- "semantic":{
319
+ {
304
320
  "type":"object",
305
- "description":"Semantic metadata for AI agents and schema generation.",
306
321
  "properties":{
307
- "title":{
322
+ "expression":{
308
323
  "type":"string",
309
- "description":"Human-friendly label for this field."
324
+ "description":"Pipe-based DSL expression. Allowed roots: raw.<path>, fk.<fk>.metadata.<field>, fk.<fk>.dimension.<dimension>[.label], dimension.<dimension>[.label]. FK traversal in DSL is one-hop only.",
325
+ "maxLength":512,
326
+ "pattern":"^\\s*\\{\\{(raw\\.[a-zA-Z0-9_.]+|fk\\.[a-z][a-zA-Z0-9]*\\.metadata\\.[a-z][a-zA-Z0-9]*|fk\\.[a-z][a-zA-Z0-9]*\\.dimension\\.[a-zA-Z0-9_]+(\\.label)?|dimension\\.[a-zA-Z0-9_]+(\\.label)?)\\}\\}(\\s*\\|\\s*[a-zA-Z0-9_]+(\\([^)]*\\))?)*\\s*$"
310
327
  },
311
328
  "description":{
312
329
  "type":"string",
313
- "description":"Business-level description of the field and how it is used."
330
+ "description":"Technical description of the normalized field."
314
331
  },
315
- "example":{
316
- "description":"Example value for this field.",
317
- "type":["string","number","boolean","object","array","null"]
332
+ "writePath":{
333
+ "type":"string",
334
+ "description":"Optional. Provider-payload target path for write operations. Dot-separated identifiers (e.g. 'properties.name'). When omitted and the expression is a single, function-free '{{raw.<dot.path>}}', the compiler derives writePath from raw.<dot.path>. When the expression is non-invertible (function chain, fk., dimension., literal, multiple references), writePath is null unless explicitly set.",
335
+ "pattern":"^[a-zA-Z_][a-zA-Z0-9_]*(\\.[a-zA-Z_][a-zA-Z0-9_]*)*$",
336
+ "maxLength":256
318
337
  },
319
- "categories":{
320
- "type":"array",
321
- "description":"Logical categories/tags (e.g. ['sales', 'revenue']).",
322
- "items":{
323
- "type":"string"
324
- }
338
+ "direction":{
339
+ "type":"string",
340
+ "description":"Optional. Mapping direction. 'readwrite' (default for invertible mappings, including those with explicit writePath), 'read' (default for non-invertible expressions or fields listed in exposed.readonly), 'write' (write-only).",
341
+ "enum":["read","write","readwrite"]
325
342
  },
326
- "synonyms":{
327
- "type":"array",
328
- "description":"Optional list of synonyms used in natural language (e.g. 'opportunity', 'sales-case').",
329
- "items":{
330
- "type":"string"
331
- }
343
+ "passthrough":{
344
+ "type":"boolean",
345
+ "description":"Optional. When true, declares an explicit dynamic-keys passthrough into the provider payload. The mapper writes the exposed value verbatim at writePath. Requires writePath. There is no implicit deep-merge of unknown keys; passthrough is the only mechanism for forwarding arbitrary provider-bag keys.",
346
+ "default":false
347
+ },
348
+ "semantic":{
349
+ "type":"object",
350
+ "description":"Semantic metadata for AI agents and schema generation.",
351
+ "properties":{
352
+ "title":{
353
+ "type":"string",
354
+ "description":"Human-friendly label for this field."
355
+ },
356
+ "description":{
357
+ "type":"string",
358
+ "description":"Business-level description of the field and how it is used."
359
+ },
360
+ "example":{
361
+ "description":"Example value for this field.",
362
+ "type":["string","number","boolean","object","array","null"]
363
+ },
364
+ "categories":{
365
+ "type":"array",
366
+ "description":"Logical categories/tags (e.g. ['sales', 'revenue']).",
367
+ "items":{
368
+ "type":"string"
369
+ }
370
+ },
371
+ "synonyms":{
372
+ "type":"array",
373
+ "description":"Optional list of synonyms used in natural language (e.g. 'opportunity', 'sales-case').",
374
+ "items":{
375
+ "type":"string"
376
+ }
377
+ }
378
+ },
379
+ "additionalProperties":false
380
+ },
381
+ "lineage":{
382
+ "$ref":"#/$defs/fieldMappingLineage"
332
383
  }
333
384
  },
385
+ "required":[
386
+ "expression"
387
+ ],
388
+ "not":{
389
+ "anyOf":[
390
+ {"required":["body"]},
391
+ {"required":["rawBody"]}
392
+ ]
393
+ },
334
394
  "additionalProperties":false
335
- },
336
- "lineage":{
337
- "$ref":"#/$defs/fieldMappingLineage"
338
395
  }
339
- },
340
- "required":[
341
- "expression"
342
- ],
343
- "additionalProperties":false
396
+ ]
344
397
  }
345
398
  }
346
399
  },
347
400
  "additionalProperties":false
348
401
  },
402
+ "externalSpec":{
403
+ "type":"object",
404
+ "description":"Optional datasource-level provenance for the authoritative vendor or tooling API specification: where to fetch or resolve the external OpenAPI document or MCP surface (type, url, relativePath, optional documentKey). Used for import paths and lightweight validation against declared operations—not system-level connectivity (see external-system openapi/mcp). The generated or materialized AI-facing OpenAPI derived from config.openapi.operations is a separate dataplane-managed artifact keyed by openapi.documentKey.",
405
+ "properties":{
406
+ "type":{
407
+ "type":"string",
408
+ "enum":[
409
+ "openapi",
410
+ "mcp"
411
+ ],
412
+ "description":"Whether this provenance describes an OpenAPI document (openapi) or an MCP server/tooling surface (mcp)."
413
+ },
414
+ "documentKey":{
415
+ "type":"string",
416
+ "description":"Optional key when external source aligns with openapi.documentKey."
417
+ },
418
+ "url":{
419
+ "type":"string",
420
+ "description":"Optional absolute URL of the external OpenAPI document or MCP descriptor, depending on type."
421
+ },
422
+ "relativePath":{
423
+ "type":"string",
424
+ "description":"Optional path relative to the integration bundle root for an external OpenAPI JSON file or MCP artifact, depending on type."
425
+ }
426
+ },
427
+ "required":[
428
+ "type"
429
+ ],
430
+ "additionalProperties":false
431
+ },
349
432
  "exposed":{
350
433
  "type":"object",
351
434
  "description":"Defines public API exposure contract. v2.4.0 freeze requires exposed.schema as canonical response shape.",
@@ -500,7 +583,7 @@
500
583
  },
501
584
  "openapi":{
502
585
  "type":"object",
503
- "description":"OpenAPI-driven connector configuration. Only includes endpoints selected during wizard onboarding. To add more endpoints, run the wizard again.",
586
+ "description":"OpenAPI-driven connector configuration: authoritative runtime operation bindings and AI-facing documentKey. Operations are selected during wizard onboarding; dataplane may generate or refresh the persisted OpenAPI artifact for that documentKey from these operations when needed. Where the vendor OpenAPI file is obtained from is described by root externalSpec, not this block.",
504
587
  "properties":{
505
588
  "enabled":{
506
589
  "type":"boolean",
@@ -551,8 +634,13 @@
551
634
  "type":"boolean",
552
635
  "default":false,
553
636
  "description":"When true and no explicit operation security scopes are configured, the runtime resolves required RBAC permission as '<resourceType>:<operation>' for each operation (resourceType from the datasource config, default document), matching builder repair RBAC permission names. Explicit operation-level scopes (openapi.operations.*.security/permissions) take precedence and disable autoRbac for that operation."
637
+ },
638
+ "operationRef":{
639
+ "type":"string",
640
+ "description":"Optional OpenAPI JSON Pointer to the primary operation (e.g. list) used by wizard-generated configs for document resolution."
554
641
  }
555
- }
642
+ },
643
+ "additionalProperties":false
556
644
  },
557
645
  "validation":{
558
646
  "type":"object",
@@ -694,12 +782,17 @@
694
782
  },
695
783
  "primaryKey":{
696
784
  "type":"object",
697
- "description":"Optional key/value payload for primary-key focused operation tests.",
785
+ "description":"Concrete primary-key field values for list/get/mutations, keyed by the same names as the datasource manifest primaryKey array. Authors may supply a subset of PK fields. Optional primaryKey.search (string legacy filter or JSON filter object) resolves a row via list when implemented for the engine.",
698
786
  "additionalProperties":true
699
787
  },
788
+ "useCopyForMutations":{
789
+ "type":"boolean",
790
+ "default":true,
791
+ "description":"When true (default if scenarios include create), after create the executor merges returned identifiers into the effective primary key for later get/update/delete in that run. When false, mutations use the static manifest primaryKey (destructive on shared fixtures). Ignored when no create scenario exists."
792
+ },
700
793
  "scenarios":{
701
794
  "type":"array",
702
- "description":"Optional operation-specific test scenarios.",
795
+ "description":"Ordered scenario steps for capacity E2E; execution order is array order (optional order field is a diagnostics hint only).",
703
796
  "items":{
704
797
  "type":"object",
705
798
  "required":[
@@ -710,6 +803,11 @@
710
803
  "type":"string",
711
804
  "pattern":"^[a-z][a-zA-Z0-9]*$"
712
805
  },
806
+ "enabled":{
807
+ "type":"boolean",
808
+ "default":true,
809
+ "description":"When false, the executor skips this scenario and records an explicit skip (scenario_disabled) in evidence."
810
+ },
713
811
  "input":{
714
812
  "type":"object",
715
813
  "additionalProperties":true
@@ -756,15 +854,15 @@
756
854
  },
757
855
  "additionalProperties":false
758
856
  },
759
- "capabilities":{
760
- "type":"array",
761
- "description":"Supported operations list.",
762
- "items":{
763
- "type":"string",
764
- "pattern":"^[a-z][a-zA-Z0-9]*$"
765
- },
766
- "uniqueItems":true
767
- },
857
+ "capabilities":{
858
+ "description":"Supported operations list.",
859
+ "type":"array",
860
+ "items":{
861
+ "type":"string",
862
+ "pattern":"^[a-z][a-zA-Z0-9_]*$"
863
+ },
864
+ "uniqueItems":true
865
+ },
768
866
  "execution":{
769
867
  "type":"object",
770
868
  "description":"Execution engine configuration for this datasource (CIP, Python, or datasource-chaining). CIP is the native declarative mode.",
@@ -1524,6 +1622,29 @@
1524
1622
  }
1525
1623
  ]
1526
1624
  },
1625
+ "restRuntime":{
1626
+ "type":"object",
1627
+ "description":"Canonical REST path shape for manifest-defined (non-core) operations. Core CRUD keys ignore this block.",
1628
+ "additionalProperties":false,
1629
+ "properties":{
1630
+ "pathKind":{
1631
+ "type":"string",
1632
+ "enum":[
1633
+ "collection",
1634
+ "collectionSubresource",
1635
+ "item",
1636
+ "itemSubresource"
1637
+ ]
1638
+ },
1639
+ "subpathSegments":{
1640
+ "type":"array",
1641
+ "items":{
1642
+ "type":"string"
1643
+ },
1644
+ "description":"Trailing path segments after the resource segment or record identifier (e.g. basic → /{resource}/basic)."
1645
+ }
1646
+ }
1647
+ },
1527
1648
  "security":{
1528
1649
  "description":"OpenAPI security requirements. Supports object/list/string forms consumed by RBAC coverage validation.",
1529
1650
  "oneOf":[
@@ -1755,6 +1876,15 @@
1755
1876
  "identity":{
1756
1877
  "$ref":"#/$defs/identitySpec"
1757
1878
  },
1879
+ "shape":{
1880
+ "type":"string",
1881
+ "enum":[
1882
+ "create",
1883
+ "update",
1884
+ "delete"
1885
+ ],
1886
+ "description":"Optional semantic classification for this operation when automatic inference from fetch steps (method/path) is insufficient."
1887
+ },
1758
1888
  "steps":{
1759
1889
  "type":"array",
1760
1890
  "minItems":1,
@@ -2,17 +2,17 @@
2
2
  "$schema":"http://json-schema.org/draft-07/schema#",
3
3
  "$id":"aifabrix://schema/external-system.schema.json",
4
4
  "title":"AI Fabrix External System Configuration Schema",
5
- "description":"Schema for configuring an external system connected to the AI Fabrix Dataplane. This defines authentication, OpenAPI/MCP bindings, field mappings defaults, metadata handling and portal inputs.",
5
+ "description":"Schema for configuring an external system connected to the AI Fabrix Dataplane. This defines authentication, system-level OpenAPI/MCP connectivity hints (not the datasource-grade vendor contract; see external-datasource.schema.json root externalSpec), field mappings defaults, metadata handling and portal inputs.",
6
6
  "metadata":{
7
7
  "key":"external-system-schema",
8
8
  "name":"External System Configuration Schema",
9
9
  "description":"JSON schema for validating ExternalSystem configuration files",
10
- "version":"1.6.2",
10
+ "version":"1.6.3",
11
11
  "type":"schema",
12
12
  "category":"integration",
13
13
  "author":"AI Fabrix Team",
14
14
  "createdAt":"2024-01-01T00:00:00Z",
15
- "updatedAt":"2026-04-23T00:00:00Z",
15
+ "updatedAt":"2026-05-10T00:00:00Z",
16
16
  "compatibility":{
17
17
  "minVersion":"1.4.0",
18
18
  "maxVersion":"2.0.0",
@@ -29,6 +29,15 @@
29
29
 
30
30
  ],
31
31
  "changelog":[
32
+ {
33
+ "version":"1.6.3",
34
+ "date":"2026-05-10T00:00:00Z",
35
+ "changes":[
36
+ "Documented split: system openapi/mcp objects are connectivity and bootstrap hints (specUrl, serverUrl, documentKey); datasource-level vendor/API provenance belongs in external-datasource externalSpec.",
37
+ "Declared optional openapi.autoDiscoverEntities (wizard/tooling); tightened openapi and mcp to additionalProperties: false."
38
+ ],
39
+ "breaking":false
40
+ },
32
41
  {
33
42
  "version":"1.6.2",
34
43
  "date":"2026-04-23T00:00:00Z",
@@ -154,7 +163,7 @@
154
163
  "mcp",
155
164
  "custom"
156
165
  ],
157
- "description":"Integration type: OpenAPI-driven, MCP-driven, or custom Python connector."
166
+ "description":"Integration type: openapi and mcp indicate how the system is wired at a high level; per-datasource vendor specifications and import provenance are authored on each ExternalDataSource (external-datasource.schema.json externalSpec), not duplicated as the full contract here."
158
167
  },
159
168
  "enabled":{
160
169
  "type":"boolean",
@@ -235,23 +244,27 @@
235
244
  },
236
245
  "openapi":{
237
246
  "type":"object",
238
- "description":"OpenAPI integration configuration",
247
+ "description":"System-level connectivity and bootstrap hints for OpenAPI-backed systems (e.g. default specUrl for discovery, documentKey alignment with shared registry entries). Not the authoritative per-datasource vendor OpenAPI contract; that is declared on the datasource via externalSpec and validated against imported material.",
239
248
  "properties":{
240
249
  "documentKey":{
241
250
  "type":"string",
242
- "description":"Key of the OpenAPI document in the registry"
251
+ "description":"Optional registry-oriented key associated with this system's default OpenAPI document (wizard/bootstrap)."
243
252
  },
244
253
  "specUrl":{
245
254
  "type":"string",
246
- "description":"URL to the OpenAPI specification",
255
+ "description":"Optional absolute URL to a representative OpenAPI specification for onboarding or tooling (not a substitute for datasource externalSpec).",
247
256
  "pattern":"^(http|https)://.*$"
257
+ },
258
+ "autoDiscoverEntities":{
259
+ "type":"boolean",
260
+ "description":"Optional wizard or tooling hint to discover entities from the system default spec. Not part of the per-datasource vendor contract (see external-datasource externalSpec)."
248
261
  }
249
262
  },
250
- "additionalProperties":true
263
+ "additionalProperties":false
251
264
  },
252
265
  "mcp":{
253
266
  "type":"object",
254
- "description":"Model Context Protocol integration configuration",
267
+ "description":"System-level MCP connectivity hints (server URL, tool prefix). Not the datasource-level MCP security or operation contract; those live on ExternalDataSource configuration.",
255
268
  "properties":{
256
269
  "serverUrl":{
257
270
  "type":"string",
@@ -264,7 +277,7 @@
264
277
  "pattern":"^[a-zA-Z0-9_-]+$"
265
278
  }
266
279
  },
267
- "additionalProperties":true
280
+ "additionalProperties":false
268
281
  },
269
282
  "dataSources":{
270
283
  "type":"array",
@@ -11,6 +11,10 @@ defaults:
11
11
  userPassword: user123
12
12
  # Always ensured on up-infra even when no workspace env.template references these kv:// keys (bootstrap defaults).
13
13
  standardUpInfraEnsureKeys:
14
+ # Docker Postgres admin + Redis — required for admin-secrets / infra compose; not guaranteed in every app env.template
15
+ - postgres-passwordKeyVault
16
+ - redis-url
17
+ - redis-passwordKeyVault
14
18
  - databases-miso-controller-0-urlKeyVault
15
19
  - databases-miso-controller-0-passwordKeyVault
16
20
  - databases-miso-controller-1-urlKeyVault
@@ -113,6 +117,17 @@ parameters:
113
117
  vaultSecretName: keycloak-client-secretKeyVault
114
118
  notes: Per-app OAuth client secret from Keycloak registration; generated on first ensure.
115
119
 
120
+ # Keycloak events: webhook signature secret used by miso-controller (KEYCLOAK_EVENTS_SECRET).
121
+ # This must exist on first boot because up-platform runs containers from templates (no prior resolve).
122
+ - key: keycloak-events-secretKeyVault
123
+ scope: shared-service
124
+ generator:
125
+ type: randomBytes32
126
+ ensureOn: [upInfra, resolveApp]
127
+ azure:
128
+ notes: >-
129
+ Shared secret for Keycloak event signature verification. Generated on first ensure for local bootstrap.
130
+
116
131
  - key: keycloak-default-passwordKeyVault
117
132
  scope: shared-service
118
133
  generator:
@@ -236,6 +251,16 @@ parameters:
236
251
  notes: >-
237
252
  Per-app OAuth client id from controller registration; literal default matches local Keycloak client naming.
238
253
 
254
+ - key: miso-controller-client-secretKeyVault
255
+ scope: app
256
+ generator:
257
+ type: randomBytes32
258
+ ensureOn: [upInfra, resolveApp]
259
+ azure:
260
+ notes: >-
261
+ Per-app OAuth client secret used by miso-controller for service-to-service auth (MISO_CLIENTSECRET).
262
+ Generated on first ensure for local bootstrap.
263
+
239
264
  # Dataplane ↔ controller OAuth (builder/dataplane env.template MISO_CLIENTID / MISO_CLIENTSECRET).
240
265
  - key: dataplane-client-idKeyVault
241
266
  scope: app
@@ -317,22 +342,12 @@ parameters:
317
342
  azure:
318
343
  notes: Empty until set; user-supplied Azure OpenAI API key (not auto-generated).
319
344
 
320
- # Legacy unprefixed name (scaffold / old env.template); prefer *KeyVault suffix or {appKey}-secrets-apiKeyVault (keyvault.md).
321
- - key: api-key
322
- scope: app
323
- generator:
324
- type: randomBytes32
325
- ensureOn: [resolveApp]
326
- azure:
327
- notes: >-
328
- Legacy kv://api-key; new apps should use kv://api-keyKeyVault or {appKey}-secrets-apiKeyVault.
329
-
330
345
  # Legacy unprefixed name; prefer kv://{appKey}-secrets-apiKeyVault in env.template (keyvault.md secrets.apiKeyVault).
331
346
  - key: miso-controller-secrets-apiKeyVault
332
347
  scope: app
333
348
  generator:
334
349
  type: randomBytes32
335
- ensureOn: [resolveApp]
350
+ ensureOn: [upInfra, resolveApp]
336
351
  azure:
337
352
  notes: >-
338
353
  Prefer {appKey}-secrets-apiKeyVault locally; dataplane shares miso-controller's entry for pipeline Bearer bypass.
@@ -184,7 +184,7 @@
184
184
  "type": "string",
185
185
  "description": "User intent (any descriptive text, e.g., 'sales-focused CRM integration')",
186
186
  "minLength": 1,
187
- "maxLength": 500
187
+ "maxLength": 1000
188
188
  },
189
189
  "fieldOnboardingLevel": {
190
190
  "type": "string",
@@ -214,7 +214,7 @@
214
214
  },
215
215
  "debug": {
216
216
  "type": "boolean",
217
- "description": "When true, capture detailed generation steps and save to debug.log (dataplane returns debugLog)",
217
+ "description": "When true, capture detailed generation steps and save to integration/<app>/logs/debug.log (dataplane returns debugLog)",
218
218
  "default": false
219
219
  }
220
220
  }