@aifabrix/builder 2.52.1 → 2.53.1

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 (225) hide show
  1. package/lib/app/deploy-config.js +3 -2
  2. package/lib/channels/add-channel.js +11 -41
  3. package/lib/channels/approval-guides/index.js +3 -0
  4. package/lib/channels/approval-guides/microsoft-copilot.js +26 -17
  5. package/lib/channels/channel-add-existing.js +72 -0
  6. package/lib/channels/channel-runtime-resolver.js +5 -1
  7. package/lib/channels/channel-status-display.js +30 -3
  8. package/lib/channels/microsoft-copilot-entra-sso-form.js +73 -2
  9. package/lib/channels/microsoft-copilot-entra-sso.js +26 -5
  10. package/lib/channels/microsoft-copilot-limits.js +22 -3
  11. package/lib/channels/microsoft-copilot-package.js +59 -14
  12. package/lib/channels/microsoft-copilot-validate.js +40 -10
  13. package/lib/cli/attach-default-help-examples.js +102 -0
  14. package/lib/cli/index.js +2 -0
  15. package/lib/cli/setup-credential-deployment.js +7 -5
  16. package/lib/cli/setup-utility-validate.js +1 -0
  17. package/lib/commands/credential-push.js +37 -5
  18. package/lib/commands/package-score.js +72 -0
  19. package/lib/commands/role-assistant.js +46 -8
  20. package/lib/deployment/deployer-poll-run.js +2 -2
  21. package/lib/deployment/deployer.js +1 -1
  22. package/lib/deployment/environment.js +2 -2
  23. package/lib/external-system/deploy.js +11 -21
  24. package/lib/integration-definition/apply.js +12 -14
  25. package/lib/integration-definition/enrich-openapi-operations.js +9 -10
  26. package/lib/lab/package-parity-score.js +417 -0
  27. package/lib/programmatic/capability-registry.js +1 -0
  28. package/lib/programmatic/help-content/channel-onboarding.md +35 -0
  29. package/lib/programmatic/help-content/cip-overview.md +27 -0
  30. package/lib/programmatic/help-content/connected-system-ui.md +70 -0
  31. package/lib/programmatic/help-content/dimensions-guide.md +23 -0
  32. package/lib/programmatic/help-content/enterprise-sync-fabrix.md +123 -0
  33. package/lib/programmatic/help-content/overview.md +25 -0
  34. package/lib/programmatic/help-content/subscription-guide.md +27 -0
  35. package/lib/programmatic/help-content/workflow.md +50 -0
  36. package/lib/programmatic/openapi-component-schemas.js +1 -0
  37. package/lib/programmatic/openapi-descriptions.js +6 -0
  38. package/lib/programmatic/openapi-envelope-examples.js +11 -0
  39. package/lib/programmatic/openapi-operation-bindings.js +5 -0
  40. package/lib/programmatic/openapi-request-schemas.js +39 -13
  41. package/lib/programmatic/openapi-result-schemas.js +30 -0
  42. package/lib/programmatic/openapi-schema-descriptions-core.js +18 -0
  43. package/lib/programmatic/openapi-schema-descriptions-operations.js +19 -0
  44. package/lib/programmatic/persist-supplied-openapi.js +180 -0
  45. package/lib/programmatic/route-handler-map.js +6 -0
  46. package/lib/programmatic/run-integration-definition.js +13 -5
  47. package/lib/role-assistant/evidence-sync.js +55 -7
  48. package/lib/role-assistant/test-runner-attachments.js +118 -0
  49. package/lib/role-assistant/test-runner-bid-apply.js +100 -21
  50. package/lib/role-assistant/test-runner-bid-document.js +1 -1
  51. package/lib/role-assistant/test-runner-start-payload.js +96 -0
  52. package/lib/role-assistant/test-runner-workhub-answers.js +11 -7
  53. package/lib/role-assistant/test-runner-workhub-helpers.js +100 -0
  54. package/lib/role-assistant/test-runner-workhub-soft.js +86 -0
  55. package/lib/role-assistant/test-runner-workhub.js +68 -105
  56. package/lib/role-assistant/test-runner.js +5 -63
  57. package/lib/role-assistant/test-suite.js +33 -40
  58. package/lib/schema/external-datasource.schema.json +13 -4
  59. package/lib/utils/compose-generator.js +10 -1
  60. package/lib/utils/compose-traefik-extra-routes.js +107 -0
  61. package/lib/utils/docker-build.js +2 -2
  62. package/lib/utils/help-builder.js +2 -1
  63. package/lib/utils/promote-dev-docker-image.js +15 -10
  64. package/package.json +14 -16
  65. package/templates/README.md +12 -1
  66. package/templates/applications/dataplane/application.yaml +8 -1
  67. package/templates/applications/dataplane/env.template +1 -1
  68. package/templates/applications/miso-controller/application.yaml +2 -1
  69. package/templates/applications/miso-controller/rbac.yaml +1 -0
  70. package/templates/channels/microsoft-copilot/ai-plugin.template.json.hbs +6 -2
  71. package/templates/channels/microsoft-copilot/entra-sso-registration.template.md.hbs +8 -5
  72. package/templates/channels/microsoft-copilot/fi-fi.template.json.hbs +7 -0
  73. package/templates/channels/microsoft-copilot/install.template.md.hbs +74 -22
  74. package/templates/channels/microsoft-copilot/manifest.template.json.hbs +29 -14
  75. package/templates/channels/microsoft-copilot/oauth-plugin-vault-required.template.md.hbs +6 -2
  76. package/templates/python/docker-compose.hbs +36 -0
  77. package/templates/typescript/docker-compose.hbs +36 -0
  78. package/.cursor/plans/Archive/157.1-security-quality-violations-by-type.csv +0 -374
  79. package/.cursor/process/README.md +0 -9
  80. package/.cursor/rules/anchor-docs.mdc +0 -15
  81. package/.cursor/rules/assistant-driven-integration-build.mdc +0 -40
  82. package/.cursor/rules/cli-layout.mdc +0 -81
  83. package/.cursor/rules/cli-product-neutral.mdc +0 -43
  84. package/.cursor/rules/deploy-rules/docker-image-ip-policy.mdc +0 -49
  85. package/.cursor/rules/docs-rules.mdc +0 -32
  86. package/.cursor/rules/plan-rules/application-lifecycle.mdc +0 -30
  87. package/.cursor/rules/plan-rules/plan-frontmatter.mdc +0 -35
  88. package/.cursor/rules/plan-rules/plan-lifecycle.mdc +0 -31
  89. package/.cursor/rules/plan-rules/plan-validation-gate.mdc +0 -34
  90. package/.cursor/rules/project-rules.mdc +0 -913
  91. package/.cursor/skills/README.md +0 -15
  92. package/.dockerignore +0 -52
  93. package/.eslintignore +0 -22
  94. package/.markdownlint.json +0 -14
  95. package/anchor-docs/README.md +0 -10
  96. package/anchor-docs/_TEMPLATE +0 -24
  97. package/babel.config.js +0 -6
  98. package/bin/builder-api-cleanup.js +0 -26
  99. package/bin/builder-api-nest.js +0 -10
  100. package/bin/builder-api-spike.js +0 -19
  101. package/enterprise-knowledge/README.md +0 -50
  102. package/enterprise-knowledge/enterprise-integration-lifecycle/README.md +0 -22
  103. package/enterprise-knowledge/enterprise-integration-lifecycle/application.json +0 -15
  104. package/enterprise-knowledge/enterprise-integration-lifecycle/deploy.js +0 -69
  105. package/enterprise-knowledge/enterprise-integration-lifecycle/enterprise-integration-lifecycle-deploy.json +0 -52
  106. package/enterprise-knowledge/enterprise-integration-lifecycle/enterprise-integration-lifecycle-system.json +0 -29
  107. package/enterprise-knowledge/enterprise-integration-lifecycle/env.template +0 -2
  108. package/integration/README.md +0 -81
  109. package/integration/github/README.md +0 -53
  110. package/integration/github/application.json +0 -21
  111. package/integration/github/deploy.js +0 -72
  112. package/integration/github/env.template +0 -11
  113. package/integration/github/github-datasource-issue-comments.json +0 -239
  114. package/integration/github/github-datasource-issues.json +0 -399
  115. package/integration/github/github-datasource-repository.json +0 -354
  116. package/integration/github/github-deploy.json +0 -1202
  117. package/integration/github/github-system.json +0 -82
  118. package/integration/hubspot-test/README.md +0 -159
  119. package/integration/hubspot-test/application.json +0 -54
  120. package/integration/hubspot-test/companies.json +0 -2048
  121. package/integration/hubspot-test/create-hubspot.js +0 -523
  122. package/integration/hubspot-test/env.template +0 -4
  123. package/integration/hubspot-test/hubspot-test-datasource-company.json +0 -138
  124. package/integration/hubspot-test/hubspot-test-datasource-contact.json +0 -146
  125. package/integration/hubspot-test/hubspot-test-datasource-deal.json +0 -146
  126. package/integration/hubspot-test/hubspot-test-datasource-users.json +0 -76
  127. package/integration/hubspot-test/hubspot-test-deploy.json +0 -2160
  128. package/integration/hubspot-test/hubspot-test-system.json +0 -74
  129. package/integration/hubspot-test/rbac.json +0 -166
  130. package/integration/hubspot-test/test-artifacts/wizard-hubspot-credential-real.yaml +0 -20
  131. package/integration/hubspot-test/test-artifacts/wizard-hubspot-env-vars.yaml +0 -9
  132. package/integration/hubspot-test/test-artifacts/wizard-invalid-add-datasource.yaml +0 -5
  133. package/integration/hubspot-test/test-artifacts/wizard-invalid-app-name.yaml +0 -5
  134. package/integration/hubspot-test/test-artifacts/wizard-invalid-credential-create.yaml +0 -7
  135. package/integration/hubspot-test/test-artifacts/wizard-invalid-credential-select.yaml +0 -7
  136. package/integration/hubspot-test/test-artifacts/wizard-invalid-known-platform.yaml +0 -4
  137. package/integration/hubspot-test/test-artifacts/wizard-invalid-missing-app.yaml +0 -4
  138. package/integration/hubspot-test/test-artifacts/wizard-invalid-missing-source.yaml +0 -2
  139. package/integration/hubspot-test/test-artifacts/wizard-invalid-mode.yaml +0 -5
  140. package/integration/hubspot-test/test-artifacts/wizard-invalid-openapi-file.yaml +0 -5
  141. package/integration/hubspot-test/test-artifacts/wizard-invalid-openapi-url.yaml +0 -4
  142. package/integration/hubspot-test/test-artifacts/wizard-invalid-source.yaml +0 -4
  143. package/integration/hubspot-test/test-artifacts/wizard-valid-for-dimension-array-test.yaml +0 -5
  144. package/integration/hubspot-test/test-artifacts/wizard-valid-for-dimension-key-test.yaml +0 -5
  145. package/integration/hubspot-test/test-artifacts/wizard-valid-for-dimension-path-test.yaml +0 -5
  146. package/integration/hubspot-test/test-artifacts/wizard-valid-for-dimension-test.yaml +0 -5
  147. package/integration/hubspot-test/test-artifacts/wizard-valid-for-rbac-test.yaml +0 -5
  148. package/integration/hubspot-test/test-artifacts/wizard-valid-for-rbac-yaml-test.yaml +0 -5
  149. package/integration/hubspot-test/test-dataplane-down-helpers.js +0 -249
  150. package/integration/hubspot-test/test-dataplane-down-tests.js +0 -386
  151. package/integration/hubspot-test/test-dataplane-down.js +0 -209
  152. package/integration/hubspot-test/test.js +0 -1588
  153. package/integration/hubspot-test/wizard-hubspot-e2e.yaml +0 -16
  154. package/integration/hubspot-test/wizard-hubspot-platform.yaml +0 -8
  155. package/integration/hubspot-test/wizard-hubspot-test-headless.yaml +0 -23
  156. package/integration/roundtrip-test-local/README.md +0 -143
  157. package/integration/roundtrip-test-local/application.yaml +0 -13
  158. package/integration/roundtrip-test-local/env.template +0 -15
  159. package/integration/roundtrip-test-local/roundtrip-test-local-datasource-roundtrip-test-company.yaml +0 -14
  160. package/integration/roundtrip-test-local/roundtrip-test-local-deploy.json +0 -61
  161. package/integration/roundtrip-test-local/roundtrip-test-local-system.yaml +0 -25
  162. package/integration/roundtrip-test-local2/README.md +0 -143
  163. package/integration/roundtrip-test-local2/application.yaml +0 -13
  164. package/integration/roundtrip-test-local2/env.template +0 -15
  165. package/integration/roundtrip-test-local2/roundtrip-test-local2-datasource-company.yaml +0 -31
  166. package/integration/roundtrip-test-local2/roundtrip-test-local2-deploy.json +0 -86
  167. package/integration/roundtrip-test-local2/roundtrip-test-local2-system.yaml +0 -25
  168. package/integration/test/wizard.yaml +0 -8
  169. package/jest.config.coverage.js +0 -37
  170. package/jest.config.default.js +0 -11
  171. package/jest.config.integration.fixtures.js +0 -22
  172. package/jest.config.integration.js +0 -33
  173. package/jest.config.isolated.js +0 -11
  174. package/jest.config.manual.js +0 -30
  175. package/jest.global-live-fabrix-hooks.js +0 -36
  176. package/jest.isolated-projects.js +0 -283
  177. package/jest.projects.js +0 -79
  178. package/lib/integration-definition/openapi-enrichment/hubspot-crm-companies.json +0 -205
  179. package/packages/builder-api/README.md +0 -120
  180. package/packages/builder-api/package.json +0 -30
  181. package/packages/builder-api/src/app.module.ts +0 -7
  182. package/packages/builder-api/src/auth/auth.module.ts +0 -5
  183. package/packages/builder-api/src/external-system/external-system.module.ts +0 -5
  184. package/packages/builder-api/src/main.ts +0 -16
  185. package/packages/builder-api/tsconfig.json +0 -15
  186. package/scripts/check-builder-api-architecture-grep.js +0 -79
  187. package/scripts/check-datasource-test-run-schema-sync.js +0 -34
  188. package/scripts/ci-fix.sh +0 -19
  189. package/scripts/ci-simulate.sh +0 -19
  190. package/scripts/diagnose-cli.js +0 -150
  191. package/scripts/docker_image_content_gate.js +0 -47
  192. package/scripts/docker_image_content_gate.sh +0 -5
  193. package/scripts/export-builder-api-evidence.js +0 -262
  194. package/scripts/install-local.js +0 -490
  195. package/scripts/lib/builder-api-docker-staging-copy.js +0 -60
  196. package/scripts/lib/builder-api-docker-staging-core.js +0 -183
  197. package/scripts/lib/docker-image-content-gate-core.js +0 -166
  198. package/scripts/lib/plan-actor.js +0 -109
  199. package/scripts/lib/plan-config.js +0 -20
  200. package/scripts/lib/plan-gates.js +0 -154
  201. package/scripts/lib/plan-meta.js +0 -104
  202. package/scripts/lib/plan-parse.js +0 -162
  203. package/scripts/lib/plan-prevalidate-gates.js +0 -337
  204. package/scripts/lib/plan-run-core.js +0 -125
  205. package/scripts/lib/plan-run-report.js +0 -119
  206. package/scripts/plan-approve.js +0 -24
  207. package/scripts/plan-archive.js +0 -49
  208. package/scripts/plan-run.js +0 -13
  209. package/scripts/plan-set-actor.js +0 -21
  210. package/scripts/plan-validate.js +0 -80
  211. package/scripts/pnpm/af.mjs +0 -125
  212. package/scripts/pnpm/check-quiet.mjs +0 -32
  213. package/scripts/pnpm/dev-install.mjs +0 -29
  214. package/scripts/pnpm/dev-reload.mjs +0 -18
  215. package/scripts/pnpm/help.mjs +0 -55
  216. package/scripts/pnpm/lib/dev-reload-core.mjs +0 -189
  217. package/scripts/pnpm/lib/dev-reload-log-capture.mjs +0 -67
  218. package/scripts/pnpm/lib/dev-reload-log-utils.mjs +0 -32
  219. package/scripts/pnpm/lib/resolve-aifabrix-bin.mjs +0 -124
  220. package/scripts/pnpm/lib/resolve-app-url.mjs +0 -183
  221. package/scripts/pnpm/lib/spinner.mjs +0 -42
  222. package/scripts/pnpm-global-remove.js +0 -48
  223. package/scripts/stage_builder_api_for_docker.js +0 -65
  224. package/scripts/sync-builder-api-template.js +0 -44
  225. package/scripts/test-dataplane-bootstrap.js +0 -331
@@ -1,41 +1,35 @@
1
1
  /**
2
2
  * Role Assistant suite discovery for multi-case CLI runs.
3
- * @fileoverview Discover YAML cases under tests/role-assistant/ra-* suite folders
3
+ * @fileoverview Discover YAML cases under tests/ra-* / package tests/
4
4
  */
5
5
 
6
6
  'use strict';
7
7
 
8
8
  const path = require('path');
9
9
  const fs = require('../internal/fs-real-sync');
10
- const { bareRoleKey } = require('../utils/role-assistant-paths');
10
+ const { bareRoleKey, roleAssistantSystemKey } = require('../utils/role-assistant-paths');
11
11
 
12
- function loadNodeFs() {
13
- if (global.__AIFABRIX_NODE_FS_UNMOCKED__) {
14
- return global.__AIFABRIX_NODE_FS_UNMOCKED__;
15
- }
16
- return typeof jest !== 'undefined' && typeof jest.requireActual === 'function'
17
- ? jest.requireActual('node:fs')
18
- : require('node:fs');
19
- }
20
-
21
- const nodeFs = loadNodeFs();
22
-
23
- function exists(p) {
24
- return nodeFs.existsSync(p) || fs.existsSync(p);
25
- }
26
-
27
- function readdir(p) {
28
- if (nodeFs.existsSync(p)) {
29
- return nodeFs.readdirSync(p, { withFileTypes: true });
30
- }
31
- return fs.readdirSync(p).map((name) => ({
32
- name,
33
- isDirectory: () => fs.statSync(path.join(p, name)).isDirectory()
34
- }));
12
+ /**
13
+ * Candidate case roots (first existing wins).
14
+ * 1. tests/<ra-key>/<role-key>/
15
+ * 2. tests/role-assistant/<ra-key>/ (legacy)
16
+ * 3. role-assistant/<ra-key>/tests/
17
+ * @param {string} roleKey
18
+ * @param {string} cwd
19
+ * @returns {string[]}
20
+ */
21
+ function listCasesRootCandidates(roleKey, cwd) {
22
+ const bare = bareRoleKey(roleKey);
23
+ const raKey = roleAssistantSystemKey(bare);
24
+ return [
25
+ path.join(cwd, 'tests', raKey, bare),
26
+ path.join(cwd, 'tests', 'role-assistant', raKey),
27
+ path.join(cwd, 'role-assistant', raKey, 'tests')
28
+ ];
35
29
  }
36
30
 
37
31
  /**
38
- * Resolve cases root for a role: prefer tests/role-assistant/ra-{bare}, else package tests/.
32
+ * Resolve cases root for a role.
39
33
  * @param {string} roleKey
40
34
  * @param {{ casesRoot?: string, cwd?: string }} [options]
41
35
  * @returns {string}
@@ -45,16 +39,14 @@ function resolveCasesRoot(roleKey, options = {}) {
45
39
  return path.resolve(String(options.casesRoot));
46
40
  }
47
41
  const cwd = options.cwd || process.cwd();
48
- const bare = bareRoleKey(roleKey);
49
- const preferred = path.join(cwd, 'tests', 'role-assistant', `ra-${bare}`);
50
- if (exists(preferred)) {
51
- return preferred;
52
- }
53
- const packageTests = path.join(cwd, 'role-assistant', `ra-${bare}`, 'tests');
54
- if (exists(packageTests)) {
55
- return packageTests;
42
+ const candidates = listCasesRootCandidates(roleKey, cwd);
43
+ for (const candidate of candidates) {
44
+ if (fs.existsSync(candidate)) {
45
+ return candidate;
46
+ }
56
47
  }
57
- return preferred;
48
+ // Prefer package path in errors — primary authoring location when repo-root tests/ is absent.
49
+ return candidates[candidates.length - 1];
58
50
  }
59
51
 
60
52
  /**
@@ -63,12 +55,12 @@ function resolveCasesRoot(roleKey, options = {}) {
63
55
  * @returns {Array<{ suite: string, file: string, absPath: string, caseIdHint: string }>}
64
56
  */
65
57
  function discoverSuiteCaseFiles(casesRoot, options = {}) {
66
- if (!exists(casesRoot)) {
58
+ if (!fs.existsSync(casesRoot)) {
67
59
  return [];
68
60
  }
69
61
  const suiteFilter = options.suite ? String(options.suite).trim() : '';
70
62
  const rows = [];
71
- for (const ent of readdir(casesRoot)) {
63
+ for (const ent of fs.readdirSync(casesRoot, { withFileTypes: true })) {
72
64
  if (!ent.isDirectory() || String(ent.name).startsWith('.')) {
73
65
  continue;
74
66
  }
@@ -76,9 +68,9 @@ function discoverSuiteCaseFiles(casesRoot, options = {}) {
76
68
  continue;
77
69
  }
78
70
  const suitePath = path.join(casesRoot, ent.name);
79
- for (const fileEnt of readdir(suitePath)) {
80
- const name = fileEnt.name || fileEnt;
81
- if (typeof name !== 'string' || !/\.(ya?ml)$/i.test(name)) {
71
+ for (const fileEnt of fs.readdirSync(suitePath, { withFileTypes: true })) {
72
+ const name = fileEnt.name;
73
+ if (!fileEnt.isFile() || !/\.(ya?ml)$/i.test(name)) {
82
74
  continue;
83
75
  }
84
76
  const absPath = path.join(suitePath, name);
@@ -113,6 +105,7 @@ function filterDiscoveredCases(rows, caseSel) {
113
105
  }
114
106
 
115
107
  module.exports = {
108
+ listCasesRootCandidates,
116
109
  resolveCasesRoot,
117
110
  discoverSuiteCaseFiles,
118
111
  filterDiscoveredCases
@@ -7,12 +7,12 @@
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.9.6",
10
+ "version":"2.9.7",
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-08-07T00:00:00Z",
15
+ "updatedAt":"2026-08-21T00:00:00Z",
16
16
  "compatibility":{
17
17
  "minVersion":"1.0.0",
18
18
  "maxVersion":"3.0.0",
@@ -31,6 +31,14 @@
31
31
  "external-system.schema.json"
32
32
  ],
33
33
  "changelog":[
34
+ {
35
+ "version":"2.9.7",
36
+ "date":"2026-08-21T00:00:00Z",
37
+ "changes":[
38
+ "Added record to CIP fetch.source for governed local record-storage operations."
39
+ ],
40
+ "breaking":false
41
+ },
34
42
  {
35
43
  "version":"2.9.5",
36
44
  "date":"2026-07-31T00:00:00Z",
@@ -2534,9 +2542,10 @@
2534
2542
  "openapi",
2535
2543
  "http",
2536
2544
  "datasource",
2537
- "viewpoint"
2545
+ "viewpoint",
2546
+ "record"
2538
2547
  ],
2539
- "description":"If 'openapi', refer to openapi.operations.*. If 'http', use explicit method/path. If 'datasource', execute another datasource operation. If 'viewpoint', execute saved SQL in fetch.query against records storage (storageType view)."
2548
+ "description":"If 'openapi', refer to openapi.operations.*. If 'http', use explicit method/path. If 'datasource', execute another datasource operation. If 'viewpoint', execute saved SQL in fetch.query against records storage (storageType view). If 'record', read or persist governed rows in local record storage with no vendor HTTP; read versus persist follows the CIP operation shape."
2540
2549
  },
2541
2550
  "openapiRef":{
2542
2551
  "type":"string",
@@ -31,6 +31,7 @@ const {
31
31
  buildTraefikIngressBase,
32
32
  computeTraefikStripPathPrefix
33
33
  } = require('./compose-traefik-ingress-base');
34
+ const { buildTraefikExtraRouters } = require('./compose-traefik-extra-routes');
34
35
  const { parseDeveloperIdNum } = require('./declarative-url-ports');
35
36
 
36
37
  registerComposeHelpers();
@@ -250,13 +251,20 @@ function buildTraefikConfig(config, devId, scopeOpts = null, remoteServer = null
250
251
  // Traefik must only attach routers to `web` for developer-id > 0; `websecure`+tls labels without
251
252
  // matching certs break routing on Traefik 3.6+ for HTTP traffic (404 on PathPrefix).
252
253
  const terminateTlsAtTraefik = base.tls === true && devNum === 0;
254
+ const stripPathPrefix = computeTraefikStripPathPrefix(base.path, resolvedHealth);
255
+ const extra = buildTraefikExtraRouters(config.frontDoorRouting, base.path);
253
256
  return {
254
257
  enabled: true,
255
258
  host: base.host,
256
259
  path: base.path,
257
260
  tls: terminateTlsAtTraefik,
258
261
  certStore: terminateTlsAtTraefik ? base.certStore || null : null,
259
- stripPathPrefix: computeTraefikStripPathPrefix(base.path, resolvedHealth)
262
+ stripPathPrefix,
263
+ passThroughRouters: extra.passThroughRouters,
264
+ aliasRouters: extra.aliasRouters.map((r) => ({
265
+ ...r,
266
+ stripPrimary: stripPathPrefix === true && base.path !== '/'
267
+ }))
260
268
  };
261
269
  }
262
270
 
@@ -424,6 +432,7 @@ module.exports = {
424
432
  getImageName,
425
433
  derivePathFromPattern,
426
434
  buildTraefikConfig,
435
+ buildTraefikExtraRouters,
427
436
  computeAlignedHealthCheckPath,
428
437
  resolveHealthCheckPathWithFrontDoorVdir,
429
438
  buildDevUsername,
@@ -0,0 +1,107 @@
1
+ /**
2
+ * Extra Traefik routers from frontDoorRouting.requiresRuleSet + ruleSetConditions.
3
+ *
4
+ * @fileoverview Schema-legal pass-through routes (plan 171.0). No invented keys.
5
+ * @author AI Fabrix Team
6
+ * @version 1.1.0
7
+ */
8
+
9
+ /**
10
+ * Normalize a route pattern to a PathPrefix value (no trailing slash / wildcards).
11
+ * @param {unknown} pattern
12
+ * @returns {string|null}
13
+ */
14
+ function normalizeRoutePrefix(pattern) {
15
+ if (!pattern || typeof pattern !== 'string') {
16
+ return null;
17
+ }
18
+ const trimmed = pattern.trim();
19
+ if (!trimmed.startsWith('/')) {
20
+ return null;
21
+ }
22
+ if (trimmed === '/' || trimmed === '/*') {
23
+ return '/';
24
+ }
25
+ const withoutWildcards = trimmed.replace(/\/\*+$/g, '').replace(/\*+$/g, '');
26
+ const withoutTrailing = withoutWildcards.replace(/\/+$/g, '');
27
+ return withoutTrailing || '/';
28
+ }
29
+
30
+ /**
31
+ * Safe Traefik label id fragment from a path prefix.
32
+ * @param {string} prefix
33
+ * @param {number} index
34
+ * @returns {string}
35
+ */
36
+ function routeIdFromPrefix(prefix, index) {
37
+ const slug = String(prefix || 'x')
38
+ .replace(/^\//, '')
39
+ .replace(/[^a-zA-Z0-9]+/g, '-')
40
+ .replace(/^-+|-+$/g, '')
41
+ .slice(0, 40);
42
+ return `${slug || 'route'}-${index}`;
43
+ }
44
+
45
+ /**
46
+ * Collect PathPrefix values from ruleSetConditions matchValues.
47
+ * @param {Object} frontDoor
48
+ * @returns {string[]}
49
+ */
50
+ function matchValuesFromRuleSetConditions(frontDoor) {
51
+ if (frontDoor.requiresRuleSet !== true) {
52
+ return [];
53
+ }
54
+ const conditions = Array.isArray(frontDoor.ruleSetConditions)
55
+ ? frontDoor.ruleSetConditions
56
+ : [];
57
+ const prefixes = [];
58
+ conditions.forEach((condition) => {
59
+ if (!condition || typeof condition !== 'object') {
60
+ return;
61
+ }
62
+ const values = Array.isArray(condition.matchValues) ? condition.matchValues : [];
63
+ values.forEach((raw) => {
64
+ const pathPrefix = normalizeRoutePrefix(raw);
65
+ if (pathPrefix && pathPrefix !== '/') {
66
+ prefixes.push(pathPrefix);
67
+ }
68
+ });
69
+ });
70
+ return prefixes;
71
+ }
72
+
73
+ /**
74
+ * Build Traefik extra-route descriptors from frontDoorRouting (schema fields only).
75
+ * @param {Object|null|undefined} frontDoor
76
+ * @param {string} _primaryPath - Resolved primary PathPrefix (unused; reserved for callers)
77
+ * @returns {{ passThroughRouters: object[], aliasRouters: object[] }}
78
+ */
79
+ function buildTraefikExtraRouters(frontDoor, _primaryPath) {
80
+ const passThroughRouters = [];
81
+ const aliasRouters = [];
82
+ if (!frontDoor || typeof frontDoor !== 'object') {
83
+ return { passThroughRouters, aliasRouters };
84
+ }
85
+
86
+ const prefixes = matchValuesFromRuleSetConditions(frontDoor);
87
+ const seen = new Set();
88
+ prefixes.forEach((pathPrefix, index) => {
89
+ if (seen.has(pathPrefix)) {
90
+ return;
91
+ }
92
+ seen.add(pathPrefix);
93
+ passThroughRouters.push({
94
+ id: routeIdFromPrefix(pathPrefix, index),
95
+ pathPrefix
96
+ });
97
+ });
98
+
99
+ return { passThroughRouters, aliasRouters };
100
+ }
101
+
102
+ module.exports = {
103
+ normalizeRoutePrefix,
104
+ routeIdFromPrefix,
105
+ matchValuesFromRuleSetConditions,
106
+ buildTraefikExtraRouters
107
+ };
@@ -253,12 +253,12 @@ async function executeBuild(imageName, dockerfilePath, contextPath, tag, options
253
253
  );
254
254
  await executeDockerBuild(imageName, dockerfilePath, contextPath, tag, buildArgs, noCache);
255
255
 
256
- // Also apply floating `main` when building a version tag (matches dev-promote dual tags).
256
+ // Also apply floating `latest` when building a version tag (matches dev-promote dual tags).
257
257
  if (options && options.tag && options.tag !== 'latest' && options.tag !== 'main') {
258
258
  const { execFileWithDockerEnv } = require('./docker-exec');
259
259
  const { getDockerExecEnv } = require('./remote-docker-env');
260
260
  const env = await getDockerExecEnv();
261
- await execFileWithDockerEnv('docker', ['tag', `${imageName}:${tag}`, `${imageName}:main`], { env });
261
+ await execFileWithDockerEnv('docker', ['tag', `${imageName}:${tag}`, `${imageName}:latest`], { env });
262
262
  }
263
263
  }
264
264
 
@@ -98,7 +98,8 @@ const CATEGORIES = [
98
98
  { name: 'validate', term: 'validate [appKey|systemKey|file]' },
99
99
  { name: 'validate-json', term: 'validate-json [appKey|systemKey]' },
100
100
  { name: 'parameters', term: 'parameters validate' },
101
- { name: 'diff', term: 'diff <file1> <file2>' }
101
+ { name: 'diff', term: 'diff <file1> <file2>' },
102
+ { name: 'package-score', term: 'package-score --reference <dir> --candidate <dir>' }
102
103
  ]
103
104
  },
104
105
  {
@@ -2,9 +2,9 @@
2
2
  * Promote a developer-scoped local Docker image to the manifest base repository (tag only).
3
3
  *
4
4
  * @fileoverview Used by `aifabrix build --base --from-dev` and dev-promote workflows.
5
- * Promote always applies two base tags: `main` (floating) and the version tag (TAG / package version).
5
+ * Promote always applies two base tags: `latest` (floating) and the version tag (TAG / package version).
6
6
  * @author AI Fabrix Team
7
- * @version 1.1.0
7
+ * @version 1.2.0
8
8
  */
9
9
 
10
10
  'use strict';
@@ -16,8 +16,11 @@ const { execWithDockerEnv } = require('./docker-exec');
16
16
  const logger = require('./logger');
17
17
  const { formatSuccessLine } = require('./cli-test-layout-chalk');
18
18
 
19
- /** Floating base tag applied on every promote alongside the version tag. */
20
- const PROMOTE_MAIN_TAG = 'main';
19
+ /** Floating base tag applied on every promote alongside the version tag (matches setup manifests). */
20
+ const PROMOTE_FLOATING_TAG = 'latest';
21
+
22
+ /** @deprecated Use {@link PROMOTE_FLOATING_TAG}. Kept for callers that imported the old name. */
23
+ const PROMOTE_MAIN_TAG = PROMOTE_FLOATING_TAG;
21
24
 
22
25
  /**
23
26
  * @param {string} value
@@ -72,22 +75,23 @@ async function dockerTagImageRef(sourceRef, targetRef) {
72
75
  */
73
76
  function resolvePromoteVersionTag(options = {}) {
74
77
  const raw = String(options.tag || '').trim();
75
- if (!raw || raw === 'latest' || raw === PROMOTE_MAIN_TAG) {
76
- return 'latest';
78
+ // `main` accepted as legacy alias for the floating tag.
79
+ if (!raw || raw === PROMOTE_FLOATING_TAG || raw === 'main') {
80
+ return PROMOTE_FLOATING_TAG;
77
81
  }
78
82
  return raw;
79
83
  }
80
84
 
81
85
  /**
82
- * Base tags to apply on promote: version first, then `main` when distinct.
86
+ * Base tags to apply on promote: version first, then `latest` when distinct.
83
87
  * @param {Object} options
84
88
  * @returns {string[]}
85
89
  */
86
90
  function resolvePromoteBaseTags(options = {}) {
87
91
  const versionTag = resolvePromoteVersionTag(options);
88
92
  const tags = [versionTag];
89
- if (versionTag !== PROMOTE_MAIN_TAG) {
90
- tags.push(PROMOTE_MAIN_TAG);
93
+ if (versionTag !== PROMOTE_FLOATING_TAG) {
94
+ tags.push(PROMOTE_FLOATING_TAG);
91
95
  }
92
96
  return tags;
93
97
  }
@@ -182,7 +186,7 @@ async function allBaseTagsMatchDev(baseName, baseTags, devId) {
182
186
 
183
187
  /**
184
188
  * Tag developer-scoped image as manifest base when present locally.
185
- * Always applies the version tag and `main`.
189
+ * Always applies the version tag and `latest`.
186
190
  * @param {string} appName
187
191
  * @param {Object} options - build options (`base`, `tag`, `registry`, `fromDev`)
188
192
  * @returns {Promise<string|null>} primary base image ref (version) when promoted; null when missing
@@ -236,6 +240,7 @@ function shouldAttemptDevPromote(options) {
236
240
  }
237
241
 
238
242
  module.exports = {
243
+ PROMOTE_FLOATING_TAG,
239
244
  PROMOTE_MAIN_TAG,
240
245
  tryPromoteDevImageToBase,
241
246
  shouldAttemptDevPromote,
package/package.json CHANGED
@@ -1,12 +1,19 @@
1
1
  {
2
2
  "name": "@aifabrix/builder",
3
- "version": "2.52.1",
3
+ "version": "2.53.1",
4
4
  "description": "AI Fabrix Builder — CLI and developer scripts (pnpm + af)",
5
5
  "main": "lib/index.js",
6
6
  "bin": {
7
7
  "aifabrix": "bin/aifabrix.js",
8
8
  "af": "bin/aifabrix.js"
9
9
  },
10
+ "files": [
11
+ "bin/aifabrix.js",
12
+ "lib/",
13
+ "templates/",
14
+ "README.md",
15
+ "LICENSE"
16
+ ],
10
17
  "scripts": {
11
18
  "help": "node scripts/pnpm/help.mjs",
12
19
  "dev-install": "node scripts/pnpm/dev-install.mjs",
@@ -21,32 +28,23 @@
21
28
  "lint:fix": "eslint . --ext .js --fix",
22
29
  "test": "node tests/scripts/test-wrapper.js",
23
30
  "build": "npm run lint && npm run test",
24
- "build:ci": "npm run lint && npm run check:schema-sync && npm run check:flags && npm run test:ci",
31
+ "build:ci": "npm run lint && npm run check:schema-sync && npm run check:flags && npm run check:cli-help && npm run test:ci",
25
32
  "test:ci": "bash tests/scripts/ci-simulate.sh",
26
- "install:local": "node scripts/install-local.js && which aifabrix && which af",
27
- "uninstall:local": "node scripts/install-local.js uninstall && which aifabrix && which af",
28
- "sync-json": "node scripts/sync-builder-api-template.js",
29
- "builder-api:build": "tsc -p packages/builder-api/tsconfig.json",
30
- "builder-api:nest": "node bin/builder-api-nest.js",
31
- "builder-api:cleanup": "node bin/builder-api-cleanup.js",
32
- "check:builder-api-arch": "node scripts/check-builder-api-architecture-grep.js",
33
33
  "check:schema-sync": "node scripts/check-datasource-test-run-schema-sync.js",
34
34
  "check:flags": "jest tests/lib/schema/flag-map-validation-run.test.js --runInBand --config jest.config.default.js",
35
- "test:154": "npm run check:builder-api-arch && jest --runInBand tests/lib/programmatic/ tests/packages/builder-api/ tests/lib/commands/up-builder-api.test.js tests/lib/commands/up-common.test.js tests/lib/commands/repair-deploy-script.test.js tests/lib/generator/deploy-script.test.js tests/lib/generator/wizard-generator.test.js tests/lib/scripts/sync-builder-api-template.test.js tests/lib/platform-integrations-manifest.test.js",
36
- "test:154-docker-ip": "jest --runInBand tests/scripts/stage_builder_api_for_docker.test.js tests/scripts/docker_image_content_gate.test.js",
37
- "stage-builder-api-docker": "node scripts/stage_builder_api_for_docker.js",
38
- "docker-image-build-builder-api": "npm run stage-builder-api-docker && docker build -f builder/builder-api/Dockerfile -t builder-api:gate .",
39
- "docker-image-gate-builder-api": "npm run docker-image-build-builder-api && node scripts/docker_image_content_gate.js builder-api:gate builder-api",
35
+ "check:cli-help": "node scripts/check-cli-help-examples.js",
40
36
  "test:manual": "jest --config jest.config.manual.js --runInBand",
41
37
  "test:integration": "jest --config jest.config.integration.js --runInBand",
42
38
  "test:integration:fixtures": "jest --config jest.config.integration.fixtures.js --runInBand",
43
39
  "test:hubspot-wizard": "node integration/hubspot-test/test.js",
44
40
  "test:hubspot-dataplane-down": "node integration/hubspot-test/test-dataplane-down.js",
45
41
  "test:coverage": "cross-env RUN_COVERAGE=1 node tests/scripts/test-wrapper.js",
46
- "test:watch": "jest --watch",
47
- "diagnose:cli": "node scripts/diagnose-cli.js",
42
+ "docker-image-gate-builder-api": "node scripts/stage_builder_api_for_docker.js && docker build -f builder/builder-api/Dockerfile -t builder-api:gate . && node scripts/docker_image_content_gate.js builder-api:gate builder-api",
43
+ "install:local": "node scripts/install-local.js && which aifabrix && which af",
44
+ "uninstall:local": "node scripts/install-local.js uninstall && which aifabrix && which af",
48
45
  "install:global": "npm install -g . --force && which aifabrix && which af && aifabrix --version",
49
46
  "uninstall:global": "npm uninstall -g @aifabrix/builder",
47
+ "sync-json": "node scripts/sync-builder-api-template.js",
50
48
  "pack": "npm run build && npm pack",
51
49
  "validate": "npm run build",
52
50
  "prepublishOnly": "npm run validate"
@@ -15,7 +15,18 @@ Application templates are folder-based and located under `templates/applications
15
15
  - `templates/applications/keycloak/` - Keycloak application template
16
16
  - `templates/applications/builder-api/` - Builder API platform webapp (published from `builder/builder-api/` via `npm run sync-json`)
17
17
 
18
- **Platform SSOT (builder-api, plan 154.2):** Edit `builder/builder-api/` in the aifabrix-builder repo, then run `npm run sync-json` to publish `application.yaml`, `Dockerfile`, and `env.template` into `templates/applications/builder-api/`. Install with `aifabrix up-platform` or `aifabrix up-builder-api` — not hand-editing the template copy.
18
+ **Platform template sync (`pnpm run sync-json`):** Publishes local `builder/builder-api/` (`application.yaml`, `Dockerfile`, `env.template`) and, when sibling checkouts exist, pulls the same file sets as miso/dataplane sync-json:
19
+
20
+ | SSOT | → template |
21
+ | --- | --- |
22
+ | `aifabrix-builder/builder/builder-api/` | `templates/applications/builder-api/` |
23
+ | `aifabrix-miso/builder/miso-controller/` (`env.template`, `rbac.yaml`, `application.yaml`) | `templates/applications/miso-controller/` |
24
+ | `aifabrix-miso/builder/keycloak/` (`env.template`, `application.yaml`) | `templates/applications/keycloak/` |
25
+ | `aifabrix-dataplane/builder/dataplane/` (`env.template`, `rbac.yaml`, `application.yaml`) | `templates/applications/dataplane/` |
26
+
27
+ **Schema master (dataplane):** Also copies dataplane `app/schemas/json/` core integration schemas + `type/*` and `configs/deployment-rules.yaml` into `lib/schema/` (same contract as dataplane `make sync-json`). Do not hand-edit those mirrors — edit dataplane SSOT, then sync. Builder-owned schemas in `lib/schema/` (wizard, infra, etc.) are left alone.
28
+
29
+ Overrides: `AIFABRIX_MISO_SRC`, `AIFABRIX_DATAPLANE_SRC` / `AIFABRIX_DATAPLANE_ROOT` (repo roots). Install with `aifabrix up-platform` / `up-builder-api` — do not hand-edit the template copies as primary.
19
30
 
20
31
  **Template Validation:**
21
32
  - Template folder must exist in `templates/applications/<name>/`
@@ -5,7 +5,7 @@ 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.26
8
+ version: 2.0.31
9
9
  # Image Configuration
10
10
  # Set tag to match your build (example: aifabrix build dataplane -t 1.0.0).
11
11
  # Registry is required so the controller can pull the image (avoids docker-not-found on the controller host).
@@ -29,6 +29,13 @@ frontDoorRouting:
29
29
  internalDockerUseOriginOnly: false
30
30
  host: ${DEV_USERNAME}.${REMOTE_HOST}
31
31
  tls: ${TLS_ENABLED}
32
+ # Origin RFC 9728 PRM (outside /data PathPrefix) — Builder Traefik pass-through (plan 171.0).
33
+ requiresRuleSet: true
34
+ ruleSetConditions:
35
+ - type: UrlPath
36
+ operator: BeginsWith
37
+ matchValues:
38
+ - /.well-known/oauth-protected-resource/data
32
39
 
33
40
  # Azure Requirements
34
41
  requires:
@@ -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.26
47
+ VERSION=2.0.31
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
@@ -4,7 +4,8 @@ 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.29'
7
+ version: '2.0.34'
8
+
8
9
  # Image Configuration
9
10
  # Set tag to match your build (e.g. aifabrix build miso-controller -t 1.0.0 then tag: 1.0.0)
10
11
  # Registry is required so the controller can pull the image (avoids "docker: not found" on the controller host).
@@ -156,6 +156,7 @@ permissions:
156
156
  roles:
157
157
  [
158
158
  'aifabrix-platform-admin',
159
+ 'aifabrix-infrastructure-admin',
159
160
  'aifabrix-deployment-admin',
160
161
  'aifabrix-developer',
161
162
  'aifabrix-observer'
@@ -3,19 +3,23 @@
3
3
  "schema_version": "v2.4",
4
4
  "name_for_human": "{{pluginNameForHuman}}",
5
5
  "description_for_human": "{{pluginDescriptionForHuman}}",
6
- "description_for_model": "Governed enterprise work tools for Role Assistants. Prefer discovery tool names only; resolve Role Assistants dynamically for the signed-in user.",
6
+ "description_for_model": "Governed enterprise work tools for Role Assistants. Prefer discovery tool names only; resolve Role Assistants dynamically for the signed-in user. For listing available Role Assistants use work.roles; use assistant.describe only when an assistantKey is already known.",
7
7
  "namespace": "{{pluginNamespace}}",
8
8
  "privacy_policy_url": "{{{dataplaneBaseUrl}}}/privacy",
9
9
  "legal_info_url": "{{{dataplaneBaseUrl}}}/terms",
10
+ "functions": [],
10
11
  "runtimes": [
11
12
  {
12
13
  "type": "RemoteMCPServer",
13
14
  "spec": {
14
15
  "url": "{{{mcpUrl}}}"
15
16
  },
17
+ "run_for_functions": [
18
+ "*"
19
+ ],
16
20
  "auth": {
17
21
  "type": "OAuthPluginVault",
18
- "reference_id": "{{oauthPluginVaultReferenceId}}"
22
+ "reference_id": "{{{oauthPluginVaultReferenceId}}}"
19
23
  }
20
24
  }
21
25
  ]
@@ -6,22 +6,25 @@ Direct URL:
6
6
 
7
7
  Open **Tools → Microsoft Entra SSO client ID registration → Register client ID** (or New).
8
8
 
9
+ Do this **before** organisation publish. The registration / auth-config ID becomes `--oauth-plugin-vault-reference-id`. It is **not** the Entra Client (application) ID.
10
+
9
11
  ### Values to paste into the form
10
12
 
11
13
  | Field | Value |
12
14
  | --- | --- |
13
15
  | Registration name | `{{appName}}` |
14
- | Base URL | {{#if entraSsoBaseUrl}}`{{{entraSsoBaseUrl}}}`{{else}}*(missing — dataplane URL not resolved)*{{/if}} |
15
- | Restrict usage by organization | My organization only (DEV) |
16
- | Restrict usage by Teams app | Any Teams app (until the Teams app id is stable) |
16
+ | Base URL | {{#if entraSsoBaseUrl}}`{{{entraSsoBaseUrl}}}`{{else}}*(missing — Remote MCP URL not resolved)*{{/if}} |
17
+ | Restrict usage by organization | My organization only |
18
+ | Restrict usage by Teams app | Stable Teams app / manifest ID `{{packageAppId}}` (known before Import — bind to this ID at registration; do not use “Any Teams app”) |
17
19
  | Client (application) ID | {{#if entraSsoClientId}}`{{entraSsoClientId}}`{{else}}*(missing — Entra SSO enterprise app (client) id from controller Keycloak status `entraIdentityProvider.enterpriseAppId`; complete Entra onboarding if empty)*{{/if}} |
18
20
  | Scope | {{#if entraSsoScope}}`{{entraSsoScope}}`{{else}}*(missing — set Runtime MCP delegated scopes on Trust source `microsoft-entra-id` `allowedScopes`; not openid/profile alone)*{{/if}} |
19
21
 
20
22
  ### Related (for your records)
21
23
 
22
24
  {{#if mcpUrl}}- **Remote MCP URL (package):** `{{{mcpUrl}}}`{{/if}}
25
+ {{#if packageAppId}}- **Teams app / manifest ID:** `{{packageAppId}}` (preserve on Import app — do not create a new unrelated app){{/if}}
23
26
  {{#if entraIssuer}}- **Entra issuer (Trust source):** `{{{entraIssuer}}}`{{/if}}
24
27
  {{#if entraTenantId}}- **Tenant id:** `{{entraTenantId}}`{{/if}}
25
- {{#if entraEnterpriseAppId}}- **Enterprise app id (Keycloak):** `{{entraEnterpriseAppId}}`{{/if}}
28
+ {{#if entraEnterpriseAppId}}- **Entra Client (application) ID (Keycloak enterprise app):** `{{entraEnterpriseAppId}}`{{/if}}
26
29
 
27
- After Save, copy the **Microsoft Entra SSO registration ID** (auth config id). That value is `--oauth-plugin-vault-reference-id`.
30
+ After Save, copy the **Microsoft Entra SSO registration ID** (auth-config id). Pass that value as `--oauth-plugin-vault-reference-id` and re-run `channel add` so `ai-plugin.json` embeds `OAuthPluginVault.reference_id`.
@@ -0,0 +1,7 @@
1
+ {
2
+ "$schema": "https://developer.microsoft.com/en-us/json-schemas/teams/v1.21/MicrosoftTeams.Localization.schema.json",
3
+ "name.short": "{{shortName}}",
4
+ "name.full": "{{fullName}}",
5
+ "description.short": "{{fiShortDescription}}",
6
+ "description.full": "{{fiFullDescription}}"
7
+ }