@aifabrix/builder 2.51.1 → 2.52.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 (69) hide show
  1. package/lib/app/run-container-start.js +4 -0
  2. package/lib/app/run-deploy-job.js +135 -27
  3. package/lib/app/run-env-compose.js +32 -0
  4. package/lib/app/run-env-recovery.js +35 -0
  5. package/lib/app/run-helpers.js +5 -6
  6. package/lib/app/run.js +11 -3
  7. package/lib/channels/approval-guides/chatgpt.js +12 -9
  8. package/lib/channels/channel-targets.js +18 -1
  9. package/lib/channels/chatgpt-runtime-contract.js +1 -1
  10. package/lib/cli/infra-guided-footers.js +1 -1
  11. package/lib/cli/infra-guided-public-url-warn.js +55 -0
  12. package/lib/cli/infra-guided.js +65 -56
  13. package/lib/cli/setup-app.help.js +4 -0
  14. package/lib/cli/setup-app.test-commands.js +59 -28
  15. package/lib/cli/setup-external-system.js +50 -12
  16. package/lib/commands/channel-add-setup.js +8 -2
  17. package/lib/commands/channel.js +16 -5
  18. package/lib/commands/deploy-gate-orchestrator.js +3 -2
  19. package/lib/commands/governance-verify-external.js +2 -5
  20. package/lib/commands/lifecycle-external.js +14 -7
  21. package/lib/commands/role-assistant.js +66 -8
  22. package/lib/commands/test-governance-external.js +2 -5
  23. package/lib/commands/up-miso.js +61 -24
  24. package/lib/commands/upload-data-sync-finalize.js +20 -3
  25. package/lib/commands/verify-governance-external.js +2 -5
  26. package/lib/commands/verify-operations-external.js +2 -5
  27. package/lib/commands/verify-operations-step-plan.js +31 -0
  28. package/lib/commands/verify-trust-command-action.js +2 -5
  29. package/lib/datasource/datasource-identifier.js +4 -3
  30. package/lib/datasource/integration-context.js +3 -3
  31. package/lib/datasource/resolve-app.js +90 -8
  32. package/lib/datasource/test-e2e.js +2 -2
  33. package/lib/datasource/unified-validation-run-body.js +15 -1
  34. package/lib/datasource/unified-validation-run-resolve.js +2 -2
  35. package/lib/external-system/deploy-helpers.js +9 -5
  36. package/lib/external-system/deploy.js +2 -1
  37. package/lib/external-system/test-system-level.js +21 -3
  38. package/lib/external-system/test.js +4 -4
  39. package/lib/generator/builders.js +9 -15
  40. package/lib/role-assistant/knowledge-markdown-frontmatter.js +75 -0
  41. package/lib/role-assistant/knowledge-sync.js +25 -5
  42. package/lib/role-assistant/test-runner-workhub-answers.js +44 -26
  43. package/lib/role-assistant/test-runner-workhub.js +21 -7
  44. package/lib/schema/application-schema.json +10 -28
  45. package/lib/schema/infra.parameter.yaml +0 -20
  46. package/lib/utils/certify-external-workspace.js +40 -0
  47. package/lib/utils/docker-run-ephemeral.js +77 -17
  48. package/lib/utils/health-check-public-warn.js +31 -13
  49. package/lib/utils/health-check.js +11 -1
  50. package/lib/utils/paths.js +13 -15
  51. package/lib/utils/role-assistant-paths.js +76 -14
  52. package/lib/utils/upload-sync-options.js +7 -4
  53. package/lib/validation/reserved-system-key-prefixes.js +56 -0
  54. package/lib/validation/validate.js +54 -19
  55. package/package.json +1 -1
  56. package/templates/applications/builder-api/application.yaml +3 -3
  57. package/templates/applications/dataplane/application.yaml +3 -4
  58. package/templates/applications/dataplane/env.template +2 -2
  59. package/templates/applications/dataplane/rbac.yaml +10 -10
  60. package/templates/applications/keycloak/application.yaml +2 -2
  61. package/templates/applications/keycloak/env.template +5 -2
  62. package/templates/applications/miso-controller/application.yaml +3 -3
  63. package/templates/applications/miso-controller/env.template +16 -3
  64. package/templates/channels/chatgpt/install.template.md.hbs +75 -37
  65. package/templates/channels/chatgpt/integration/README.md +3 -2
  66. package/templates/channels/chatgpt/manifest.template.json.hbs +3 -3
  67. package/templates/channels/slack/install.template.md.hbs +6 -1
  68. package/templates/python/docker-compose.hbs +10 -0
  69. package/templates/typescript/docker-compose.hbs +10 -0
@@ -1022,23 +1022,17 @@ async function getResolveAppPath(appName, options = {}) {
1022
1022
  return { appPath: result.appPath, envOnly: false };
1023
1023
  }
1024
1024
 
1025
- /**
1026
- * @param {string} walkDir - Candidate workspace root (walk upward from cwd)
1027
- * @param {string} cwd - Resolved process.cwd()
1028
- * @param {ReturnType<typeof nodeFs>} disk
1029
- * @returns {string|null}
1030
- */
1031
- function tryIntegrationAppKeyAtWalkStep(walkDir, cwd, disk) {
1032
- const integrationDir = path.join(walkDir, 'integration');
1025
+ function tryAuthoringAppKeyAtWalkStep(walkDir, cwd, disk, folderName) {
1026
+ const authoringDir = path.join(walkDir, folderName);
1033
1027
  try {
1034
- if (!disk.existsSync(integrationDir)) {
1028
+ if (!disk.existsSync(authoringDir)) {
1035
1029
  return null;
1036
1030
  }
1037
- const intNorm = path.resolve(integrationDir);
1038
- if (cwd !== intNorm && !cwd.startsWith(intNorm + path.sep)) {
1031
+ const rootNorm = path.resolve(authoringDir);
1032
+ if (cwd !== rootNorm && !cwd.startsWith(rootNorm + path.sep)) {
1039
1033
  return null;
1040
1034
  }
1041
- const rel = path.relative(intNorm, cwd);
1035
+ const rel = path.relative(rootNorm, cwd);
1042
1036
  if (!rel || rel.startsWith('..') || path.isAbsolute(rel)) {
1043
1037
  return null;
1044
1038
  }
@@ -1049,16 +1043,20 @@ function tryIntegrationAppKeyAtWalkStep(walkDir, cwd, disk) {
1049
1043
  }
1050
1044
 
1051
1045
  /**
1052
- * Resolve app folder name when cwd is under some ancestor's `integration/<systemKey>/`.
1046
+ * Resolve app folder name when cwd is under integration/, role-assistant/, or
1047
+ * enterprise-knowledge/<systemKey>/.
1053
1048
  * Walks up from cwd (does not use {@link getIntegrationBuilderBaseDir}) so detection still works
1054
- * when the canonical base is config/home but the shell is inside a checkout's integration tree.
1049
+ * when the canonical base is config/home but the shell is inside a checkout's tree.
1055
1050
  */
1056
1051
  function resolveIntegrationAppKeyFromCwd() {
1057
1052
  const cwd = path.resolve(process.cwd());
1058
1053
  const disk = nodeFs();
1059
1054
  let p = cwd;
1060
1055
  for (let i = 0; i < 64; i += 1) {
1061
- const key = tryIntegrationAppKeyAtWalkStep(p, cwd, disk);
1056
+ const key =
1057
+ tryAuthoringAppKeyAtWalkStep(p, cwd, disk, 'integration') ||
1058
+ tryAuthoringAppKeyAtWalkStep(p, cwd, disk, 'role-assistant') ||
1059
+ tryAuthoringAppKeyAtWalkStep(p, cwd, disk, 'enterprise-knowledge');
1062
1060
  if (key) {
1063
1061
  return key;
1064
1062
  }
@@ -3,7 +3,7 @@
3
3
  *
4
4
  * @fileoverview Paths under role-assistant/ for Role Assistant packages
5
5
  * @author AI Fabrix Team
6
- * @version 1.0.0
6
+ * @version 1.1.0
7
7
  */
8
8
 
9
9
  'use strict';
@@ -14,6 +14,7 @@ const { nodeFs } = require('../internal/node-fs');
14
14
 
15
15
  const WORKSPACE_ROLE_ASSISTANT = 'role-assistant';
16
16
  const SYSTEM_KIND_DIGITAL_WORKER = 'digitalWorker';
17
+ const ROLE_ASSISTANT_SYSTEM_PREFIX = 'ra-';
17
18
 
18
19
  function getMaterializationParent() {
19
20
  const work = process.env.AIFABRIX_WORK;
@@ -63,7 +64,64 @@ function isExistingDirectory(disk, dirPath) {
63
64
  }
64
65
 
65
66
  /**
66
- * Resolves role-assistant/<roleKey> with cwd-first behavior.
67
+ * Bare business roleKey (strips leading ra- if present).
68
+ * @param {string} roleKeyOrSystemKey
69
+ * @returns {string}
70
+ */
71
+ function bareRoleKey(roleKeyOrSystemKey) {
72
+ const raw = String(roleKeyOrSystemKey || '').trim();
73
+ if (raw.toLowerCase().startsWith(ROLE_ASSISTANT_SYSTEM_PREFIX)) {
74
+ return raw.slice(ROLE_ASSISTANT_SYSTEM_PREFIX.length);
75
+ }
76
+ return raw;
77
+ }
78
+
79
+ /**
80
+ * Folder / ExternalSystem key for a Role Assistant: ra-{roleKey}.
81
+ * @param {string} roleKeyOrSystemKey
82
+ * @returns {string}
83
+ */
84
+ function roleAssistantSystemKey(roleKeyOrSystemKey) {
85
+ const bare = bareRoleKey(roleKeyOrSystemKey);
86
+ if (!bare) {
87
+ return '';
88
+ }
89
+ return `${ROLE_ASSISTANT_SYSTEM_PREFIX}${bare}`;
90
+ }
91
+
92
+ /**
93
+ * Candidate folder names under role-assistant/ (prefer ra-, then legacy bare).
94
+ * @param {string} roleKeyOrSystemKey
95
+ * @returns {string[]}
96
+ */
97
+ function roleAssistantFolderCandidates(roleKeyOrSystemKey) {
98
+ const withRa = roleAssistantSystemKey(roleKeyOrSystemKey);
99
+ const bare = bareRoleKey(roleKeyOrSystemKey);
100
+ const out = [];
101
+ if (withRa) {
102
+ out.push(withRa);
103
+ }
104
+ if (bare && bare !== withRa) {
105
+ out.push(bare);
106
+ }
107
+ return out;
108
+ }
109
+
110
+ function firstExistingUnderRoot(disk, root, folderNames) {
111
+ if (!root) {
112
+ return null;
113
+ }
114
+ for (const name of folderNames) {
115
+ const candidate = path.join(root, name);
116
+ if (isExistingDirectory(disk, candidate)) {
117
+ return candidate;
118
+ }
119
+ }
120
+ return null;
121
+ }
122
+
123
+ /**
124
+ * Resolves role-assistant/ra-{roleKey} (preferred) or legacy bare folder.
67
125
  * @param {string} roleKey
68
126
  * @returns {string}
69
127
  */
@@ -72,27 +130,31 @@ function getRoleAssistantPath(roleKey) {
72
130
  throw new Error('Role key is required and must be a string');
73
131
  }
74
132
  const disk = nodeFs();
133
+ const folders = roleAssistantFolderCandidates(roleKey);
134
+ const preferred = roleAssistantSystemKey(roleKey);
75
135
  const cwdRoot = getCwdRoleAssistantRoot();
76
- const matPath = path.join(getMaterializationParent(), WORKSPACE_ROLE_ASSISTANT, roleKey);
77
- if (cwdRoot) {
78
- const cwdPath = path.join(cwdRoot, roleKey);
79
- if (isExistingDirectory(disk, cwdPath)) {
80
- return cwdPath;
81
- }
82
- }
83
- if (isExistingDirectory(disk, matPath)) {
84
- return matPath;
136
+ const matRoot = path.join(getMaterializationParent(), WORKSPACE_ROLE_ASSISTANT);
137
+
138
+ const existing =
139
+ firstExistingUnderRoot(disk, cwdRoot, folders) ||
140
+ firstExistingUnderRoot(disk, matRoot, folders);
141
+ if (existing) {
142
+ return existing;
85
143
  }
86
144
  if (cwdRoot) {
87
- return path.join(cwdRoot, roleKey);
145
+ return path.join(cwdRoot, preferred);
88
146
  }
89
- return matPath;
147
+ return path.join(matRoot, preferred);
90
148
  }
91
149
 
92
150
  module.exports = {
93
151
  WORKSPACE_ROLE_ASSISTANT,
94
152
  SYSTEM_KIND_DIGITAL_WORKER,
153
+ ROLE_ASSISTANT_SYSTEM_PREFIX,
95
154
  getCwdRoleAssistantRoot,
96
155
  getRoleAssistantRoot,
97
- getRoleAssistantPath
156
+ getRoleAssistantPath,
157
+ bareRoleKey,
158
+ roleAssistantSystemKey,
159
+ roleAssistantFolderCandidates
98
160
  };
@@ -10,7 +10,7 @@
10
10
 
11
11
  /**
12
12
  * @param {Object} [options] - CLI options from test / sync callers
13
- * @returns {{ minimal: true, verbose: boolean, silentResolve: true, syncMode: true, force?: true, authConfig?: Object, dataplaneUrl?: string, environment?: string }}
13
+ * @returns {{ minimal: true, verbose: boolean, silentResolve: boolean, syncMode: boolean, force?: true, authConfig?: Object, dataplaneUrl?: string, environment?: string }}
14
14
  */
15
15
  function buildMinimalUploadSyncOptions(options = {}) {
16
16
  const deployPreflight = options.uploadContext === 'deploy-preflight';
@@ -18,11 +18,14 @@ function buildMinimalUploadSyncOptions(options = {}) {
18
18
  minimal: true,
19
19
  // CLI -v on verify-* / test-* is for run output, not preflight upload banners.
20
20
  verbose: false,
21
- silentResolve: true,
22
- syncMode: true,
21
+ // Deploy preflight should match `af upload` progress (URL + Publishing spinner).
22
+ // Verify/test --sync stays silent so parent command owns the progress line.
23
+ silentResolve: !deployPreflight,
24
+ syncMode: !deployPreflight,
23
25
  skipDataSync: true,
26
+ // Explicit suppress wins; deploy-preflight never suppresses the Publishing spinner.
24
27
  suppressPipelineSpinner:
25
- options.suppressPipelineSpinner === true || deployPreflight === true
28
+ options.suppressPipelineSpinner === true && !deployPreflight
26
29
  };
27
30
  if (options.force === true) {
28
31
  out.force = true;
@@ -0,0 +1,56 @@
1
+ /**
2
+ * Frozen ExternalSystem key prefixes (Role Assistant / Enterprise Knowledge).
3
+ *
4
+ * @fileoverview Reject hand-authored ra-/ek- keys under integration/
5
+ * @author AI Fabrix Team
6
+ * @version 1.0.0
7
+ */
8
+
9
+ 'use strict';
10
+
11
+ const RESERVED_SYSTEM_KEY_PREFIXES = ['ra-', 'ek-'];
12
+
13
+ /**
14
+ * @param {string|null|undefined} systemKey
15
+ * @returns {boolean}
16
+ */
17
+ function hasReservedPlatformSystemKeyPrefix(systemKey) {
18
+ const normalized = String(systemKey || '')
19
+ .trim()
20
+ .toLowerCase();
21
+ if (!normalized) {
22
+ return false;
23
+ }
24
+ return RESERVED_SYSTEM_KEY_PREFIXES.some((prefix) => normalized.startsWith(prefix));
25
+ }
26
+
27
+ /**
28
+ * Connected Systems under integration/ must not use reserved prefixes.
29
+ * Role Assistant packages live under role-assistant/ra-*.
30
+ *
31
+ * @param {string|null|undefined} systemKey
32
+ * @param {string|null|undefined} workspaceBaseDir - integration | role-assistant | …
33
+ * @returns {string|null} Error message or null when allowed
34
+ */
35
+ function reservedSystemKeyError(systemKey, workspaceBaseDir) {
36
+ if (!hasReservedPlatformSystemKeyPrefix(systemKey)) {
37
+ return null;
38
+ }
39
+ const workspace = String(workspaceBaseDir || '')
40
+ .trim()
41
+ .toLowerCase();
42
+ if (workspace === 'role-assistant' || workspace === 'enterprise-knowledge') {
43
+ return null;
44
+ }
45
+ return (
46
+ `System key '${systemKey}' uses a reserved prefix (ra- or ek-). ` +
47
+ 'Role Assistant packages use role-assistant/ra-{roleKey}/; ' +
48
+ 'Enterprise Knowledge uses ek- keys from platform generators only.'
49
+ );
50
+ }
51
+
52
+ module.exports = {
53
+ RESERVED_SYSTEM_KEY_PREFIXES,
54
+ hasReservedPlatformSystemKeyPrefix,
55
+ reservedSystemKeyError
56
+ };
@@ -22,6 +22,7 @@ const { generateControllerManifest } = require('../generator/external-controller
22
22
  const { validateControllerManifest } = require('./external-manifest-validator');
23
23
  const { runExternalValidateWithOptionalCertSync } = require('./validate-external-cert-sync');
24
24
  const { validateExternalFile } = require('./validate-external-file');
25
+ const { reservedSystemKeyError } = require('./reserved-system-key-prefixes');
25
26
 
26
27
  /**
27
28
  * Validates a file path (detects type and validates)
@@ -276,6 +277,52 @@ async function validateManifestStep(appName, options = {}) {
276
277
  }
277
278
  }
278
279
 
280
+ /**
281
+ * Early-fail payload when system key uses a reserved prefix.
282
+ * @param {string} reservedError
283
+ * @param {string} appPath
284
+ * @returns {Object}
285
+ */
286
+ function reservedSystemKeyValidationResult(reservedError, appPath) {
287
+ return {
288
+ valid: false,
289
+ errors: [reservedError],
290
+ warnings: [],
291
+ steps: {
292
+ application: { valid: false, errors: [reservedError], warnings: [] },
293
+ components: { valid: true, errors: [], warnings: [], files: [], skipped: true },
294
+ manifest: { valid: true, errors: [], warnings: [], skipped: true }
295
+ },
296
+ appPath
297
+ };
298
+ }
299
+
300
+ /**
301
+ * Aggregate step results into the complete validation response.
302
+ * @param {Object} steps
303
+ * @param {string} appPath
304
+ * @returns {Object}
305
+ */
306
+ function aggregateExternalValidationSteps(steps, appPath) {
307
+ const allErrors = [
308
+ ...steps.application.errors,
309
+ ...steps.components.errors,
310
+ ...steps.manifest.errors
311
+ ];
312
+ const allWarnings = [
313
+ ...steps.application.warnings,
314
+ ...steps.components.warnings,
315
+ ...steps.manifest.warnings
316
+ ];
317
+ return {
318
+ valid: allErrors.length === 0,
319
+ errors: allErrors,
320
+ warnings: allWarnings,
321
+ steps,
322
+ appPath
323
+ };
324
+ }
325
+
279
326
  /**
280
327
  * Validates external system completely (components + full manifest)
281
328
  * Performs step-by-step validation: application config → components → full manifest
@@ -295,7 +342,12 @@ async function validateExternalSystemComplete(appName, options = {}) {
295
342
  throw new Error('App name is required and must be a string');
296
343
  }
297
344
 
298
- const { appPath } = await detectAppType(appName, options);
345
+ const { appPath, baseDir } = await detectAppType(appName, options);
346
+ const reservedError = reservedSystemKeyError(appName, baseDir);
347
+ if (reservedError) {
348
+ return reservedSystemKeyValidationResult(reservedError, appPath);
349
+ }
350
+
299
351
  const steps = {
300
352
  application: { valid: false, errors: [], warnings: [] },
301
353
  components: { valid: false, errors: [], warnings: [], files: [] },
@@ -303,14 +355,9 @@ async function validateExternalSystemComplete(appName, options = {}) {
303
355
  };
304
356
 
305
357
  const externalOptions = { ...options };
306
-
307
- // Step 1: Validate Application Config
308
358
  steps.application = await validateApplicationStep(appName, externalOptions);
309
-
310
- // Step 2: Validate Individual Components
311
359
  steps.components = await validateComponentsStep(appName, externalOptions);
312
360
 
313
- // If components have errors, return early (don't validate manifest)
314
361
  if (!steps.components.valid) {
315
362
  steps.manifest = { valid: true, errors: [], warnings: [], skipped: true };
316
363
  return {
@@ -322,20 +369,8 @@ async function validateExternalSystemComplete(appName, options = {}) {
322
369
  };
323
370
  }
324
371
 
325
- // Step 3 & 4: Generate and Validate Full Manifest (only if Step 2 passes)
326
372
  steps.manifest = await validateManifestStep(appName, { ...externalOptions, appPath });
327
-
328
- // Aggregate Results
329
- const allErrors = [...steps.application.errors, ...steps.components.errors, ...steps.manifest.errors];
330
- const allWarnings = [...steps.application.warnings, ...steps.components.warnings, ...steps.manifest.warnings];
331
-
332
- return {
333
- valid: allErrors.length === 0,
334
- errors: allErrors,
335
- warnings: allWarnings,
336
- steps,
337
- appPath
338
- };
373
+ return aggregateExternalValidationSteps(steps, appPath);
339
374
  }
340
375
 
341
376
  async function validateAppOrFile(appOrFile, options = {}) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aifabrix/builder",
3
- "version": "2.51.1",
3
+ "version": "2.52.0",
4
4
  "description": "AI Fabrix Builder — CLI and developer scripts (pnpm + af)",
5
5
  "main": "lib/index.js",
6
6
  "bin": {
@@ -2,7 +2,7 @@
2
2
  app:
3
3
  key: builder-api
4
4
  displayName: 'AI Fabrix Builder API'
5
- description: 'Stateless HTTP orchestration for external-system workspace lifecycle. Provider-neutral integration flows via Dataplane Enterprise MCP and governance APIs (154.0 / 154.1).'
5
+ description: 'Business Transformation services — HTTP orchestration for connected-system workspace lifecycle via Dataplane Enterprise MCP and governance APIs.'
6
6
  type: webapp
7
7
  language: typescript
8
8
  version: 1.0.0
@@ -11,8 +11,8 @@ app:
11
11
  image:
12
12
  name: aifabrix/builder-api
13
13
  tag: latest
14
- registry: aifabrixdevacr.azurecr.io
15
- registryMode: acr
14
+ registry: aifabrixacr.azurecr.io
15
+ registryMode: external
16
16
 
17
17
  # Port Configuration (container port; host port = port + developer_id * 100)
18
18
  port: 3005
@@ -5,16 +5,15 @@ app:
5
5
  description: "AI Fabrix Dataplane is a secure, in-tenant integration and automation layer that supplies governed, normalized, and explainable enterprise data to AI agents. Using CIP as a declarative standard, it enforces RBAC and ABAC, executes integrations, and exposes trusted data via MCP and OpenAPI."
6
6
  type: webapp
7
7
  language: python # Explicitly specify Python language
8
- version: 2.0.15
9
-
8
+ version: 2.0.20
10
9
  # Image Configuration
11
10
  # Set tag to match your build (example: aifabrix build dataplane -t 1.0.0).
12
11
  # Registry is required so the controller can pull the image (avoids docker-not-found on the controller host).
13
12
  image:
14
13
  name: aifabrix/dataplane
15
14
  tag: latest
16
- registry: aifabrixdevacr.azurecr.io
17
- registryMode: acr
15
+ registry: aifabrixacr.azurecr.io
16
+ registryMode: external
18
17
 
19
18
  # Port Configuration
20
19
  port: 3001
@@ -44,7 +44,7 @@ API_KEY_BYPASS_ROLES=full-access
44
44
 
45
45
  # API Configuration
46
46
  API_V1_STR=/api/v1
47
- VERSION=2.0.15
47
+ VERSION=2.0.20
48
48
  # Minimum supported aifabrix-builder CLI semver (advertised on /health and /api/v1/health; empty = no gate)
49
49
  # MIN_BUILDER_CLI_VERSION=2.45.0
50
50
  MIN_BUILDER_CLI_VERSION=2.45.5
@@ -284,7 +284,7 @@ VALIDATION_AUTO_ISSUE_INTEGRATION_CERTIFICATE=true
284
284
  CIP_EXECUTION_MAX_RESPONSE_SIZE_MB=100.0
285
285
  CIP_EXECUTION_MAX_RECORDS=100000
286
286
  CIP_EXECUTION_OPERATION_TIMEOUT=300.0
287
- CIP_EXECUTION_HTTP_TIMEOUT=30.0
287
+ CIP_EXECUTION_HTTP_TIMEOUT=120.0
288
288
  CIP_EXECUTION_MAX_RETRIES=3
289
289
  CIP_EXECUTION_RETRY_BACKOFF_FACTOR=2.0
290
290
  CIP_EXECUTION_RETRY_INITIAL_DELAY=1.0
@@ -5,47 +5,47 @@
5
5
  # MisoClient validates environment access automatically based on user/application permissions.
6
6
 
7
7
  roles:
8
- - name: "AI Fabrix Platform Admin"
8
+ - name: "Platform Admin"
9
9
  value: "aifabrix-platform-admin"
10
10
  description: "Full platform infrastructure management and enterprise controller access"
11
11
  groups: ["AI-Fabrix-Platform-Admins"]
12
12
 
13
- - name: "AI Fabrix Security Admin"
13
+ - name: "Security Admin"
14
14
  value: "aifabrix-security-admin"
15
15
  description: "Security and compliance management for enterprise controller"
16
16
  groups: ["AI-Fabrix-Security-Admins"]
17
17
 
18
- - name: "AI Fabrix Deployment Admin"
18
+ - name: "Deployment Admin"
19
19
  value: "aifabrix-deployment-admin"
20
20
  description: "Application deployment orchestration and environment management"
21
21
  groups: ["AI-Fabrix-Deployment-Admins"]
22
22
 
23
- - name: "AI Fabrix Compliance Admin"
23
+ - name: "Compliance Admin"
24
24
  value: "aifabrix-compliance-admin"
25
25
  description: "ISO 27001 compliance monitoring and audit management"
26
26
  groups: ["AI-Fabrix-Compliance-Admins"]
27
27
 
28
- - name: "AI Fabrix Developer"
28
+ - name: "Developer"
29
29
  value: "aifabrix-developer"
30
30
  description: "Developer access to deploy applications via CI pipelines"
31
31
  groups: ["AI-Fabrix-Developers"]
32
32
 
33
- - name: "AI Fabrix Business Transformation"
34
- value: "aifabrix-business-transformation"
33
+ - name: "Business Transformation"
34
+ value: "business-transformation"
35
35
  description: "Built-in assistant: business intention → governed Connected Systems via Runtime and builder-api"
36
36
  groups: ["AI-Fabrix-Business-Transformation"]
37
37
 
38
- - name: "AI Fabrix Observer"
38
+ - name: "Observer"
39
39
  value: "aifabrix-observer"
40
40
  description: "Read-only access to monitoring, logs, and compliance reports"
41
41
  groups: ["AI-Fabrix-Observers"]
42
42
 
43
- - name: "AI Fabrix AI Trust Operator"
43
+ - name: "AI Trust Operator"
44
44
  value: "aifabrix-ai-trust-operator"
45
45
  description: "Run AI Trust access evaluation and explain visibility for governed users"
46
46
  groups: ["AI-Fabrix-AI-Trust-Operators"]
47
47
 
48
- - name: "AI Fabrix External System Discoverer"
48
+ - name: "External System Discoverer"
49
49
  value: "aifabrix-external-system-discoverer"
50
50
  description: "Discover business context for external systems via REST and MCP"
51
51
  groups: ["AI-Fabrix-External-System-Discoverers"]
@@ -10,8 +10,8 @@ app:
10
10
  image:
11
11
  name: aifabrix/keycloak
12
12
  tag: 'latest'
13
- registry: aifabrixdevacr.azurecr.io
14
- registryMode: acr
13
+ registry: aifabrixacr.azurecr.io
14
+ registryMode: external
15
15
 
16
16
  # Port Configuration (base for host; host port = 8082 + developer_id*100 from ~/.aifabrix/config.yaml)
17
17
  port: 8082
@@ -9,11 +9,14 @@
9
9
  KC_BOOTSTRAP_ADMIN_USERNAME=admin
10
10
  KC_BOOTSTRAP_ADMIN_PASSWORD=kv://keycloak-admin-passwordKeyVault
11
11
  KC_HOSTNAME_STRICT=false
12
- KC_HTTP_ENABLED=${HTTP_ENABLED}
12
+ # Follows tlsEnabled in ~/.aifabrix/config.yaml (same as ${TLS_ENABLED}).
13
+ # Traefik/edge HTTPS on → true (proxy forwards HTTP to Keycloak :8080).
14
+ # Single container without HTTPS (tlsEnabled false) → false.
15
+ KC_HTTP_ENABLED=${TLS_ENABLED}
13
16
  # When Traefik + frontDoorRouting.enabled are on, same vdir as url://public. If the front door is off, env generation
14
17
  # rewrites url://vdir-public to "/" before expansion (empty path is invalid — Keycloak 26 SRCFG00040).
15
18
  # Docker-only templates may use url://vdir-internal instead.
16
- KC_HTTP_RELATIVE_PATH=url://vdir-public
19
+ KC_HTTP_RELATIVE_PATH=/auth
17
20
 
18
21
  # =============================================================================
19
22
  # HOSTNAME / ISSUER (Docker vs localhost)
@@ -4,7 +4,7 @@ app:
4
4
  displayName: 'Miso Controller'
5
5
  description: 'Miso is the AI Fabrix in-tenant controller and portal layer for securely operating enterprise AI apps inside a customer’s Azure tenant. It provides Entra ID SSO, RBAC, audit logs, environment/app configuration via schemas, and safe secret handling via Key Vault references—ensuring governance, traceability, and predictable UX across portal, SDK, and CLI.'
6
6
  type: webapp
7
- version: '2.0.10'
7
+ version: '2.0.20'
8
8
 
9
9
  # Image Configuration
10
10
  # Set tag to match your build (e.g. aifabrix build miso-controller -t 1.0.0 then tag: 1.0.0)
@@ -12,8 +12,8 @@ app:
12
12
  image:
13
13
  name: aifabrix/miso-controller
14
14
  tag: latest
15
- registry: aifabrixdevacr.azurecr.io
16
- registryMode: acr
15
+ registry: aifabrixacr.azurecr.io
16
+ registryMode: external
17
17
 
18
18
  # Port Configuration (container port; host port = 3000 + developer_id*100 from ~/.aifabrix/config.yaml)
19
19
  port: 3000
@@ -49,6 +49,10 @@ ONBOARDING_ADMIN_EMAIL=kv://miso-controller-admin-emailKeyVault
49
49
  # When true, supersedes ONBOARDING_CREATE_DEV_ENV for which extra environments are created
50
50
  ONBOARDING_FULL_PLATFORM=false
51
51
 
52
+ # When true, run Azure env deploy for onboarding envs (SDK modules). Default false —
53
+ # Marketplace already provisions infra; TA owns customer env deploy.
54
+ ONBOARDING_CREATE_MISO_ENV=false
55
+
52
56
  # Set to true to create both miso and dev environments during onboarding
53
57
  # When false (default), only the miso environment is created (ignored when ONBOARDING_FULL_PLATFORM=true)
54
58
  ONBOARDING_CREATE_DEV_ENV=true
@@ -236,6 +240,14 @@ SKIP_DB_REDEPLOY=true
236
240
  # Bootstrap (SKIP_FIRST_TIME_SETUP=false) is separate from the activation wizard.
237
241
  DEPLOYMENT=database
238
242
 
243
+ # Dev/test: open /tenant-activation on Platform Apps (last step). Default false.
244
+ # Does not skip server-side TA — use only when env is already activated/deployed.
245
+ TA_SKIP_TO_APP_DEPLOY=false
246
+
247
+ # Dev/test: show TA Entra "Completing setup" countdown (120s cap). Default false.
248
+ # Exposed to UI via GET /api/v1/tenant/status → showTaCountdown (restart controller after change).
249
+ SHOW_TA_COUNT_DOWN=false
250
+
239
251
  # =============================================================================
240
252
  # SECURITY & ENCRYPTION
241
253
  # =============================================================================
@@ -287,9 +299,10 @@ ENTRA_GRAPH_DELEGATED_REDIRECT_URI=https://aifabrix-pro-mori-web-we.azurewebsite
287
299
  # =============================================================================
288
300
  # MORI SERVICE CONFIGURATION
289
301
  # =============================================================================
290
-
291
- # Shared pro Mori (Entra handoff + Graph callback). Prefer this over url://mori-controller-public
292
- # unless a local Mori app is registered and running for the developer.
302
+ # Shared Mori (handoff poll + Entra callback). Local `url://mori-controller-public`
303
+ # only works when a local Mori app is registered and running for this developer.
304
+ # MORI_CALLBACK_API_KEY must be the same secret as Mori MORI_CONTROLLER_CALLBACK_API_KEY
305
+ # (both resolve from kv://mori-callback-api-keyKeyVault — do not generate separate values).
293
306
  MORI_BASE_URL=https://aifabrix-pro-mori-web-we.azurewebsites.net
294
307
  MORI_AUTH_METHOD=kv://mori-controller-auth-methodKeyVault
295
308
  MORI_CALLBACK_API_KEY=kv://mori-callback-api-keyKeyVault