@aifabrix/builder 2.52.0 → 2.52.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.
- package/jest.isolated-projects.js +25 -2
- package/jest.projects.js +4 -1
- package/lib/api/datasources-extended.api.js +8 -8
- package/lib/api/keycloak.api.js +92 -0
- package/lib/api/token-providers.api.js +77 -0
- package/lib/api/types/keycloak.types.js +23 -0
- package/lib/api/types/token-providers.types.js +65 -0
- package/lib/app/down.js +16 -1
- package/lib/channels/add-channel.js +126 -19
- package/lib/channels/approval-guides/chatgpt.js +1 -1
- package/lib/channels/approval-guides/microsoft-copilot.js +16 -12
- package/lib/channels/channel-add-ensure.js +128 -0
- package/lib/channels/channel-artifact-generator.js +11 -1
- package/lib/channels/channel-branding.js +94 -0
- package/lib/channels/channel-credential.js +8 -0
- package/lib/channels/channel-discovery.js +330 -0
- package/lib/channels/channel-external-system.js +1 -1
- package/lib/channels/channel-runtime-resolver.js +103 -14
- package/lib/channels/channel-status-display.js +7 -1
- package/lib/channels/microsoft-copilot-channel-add.js +138 -0
- package/lib/channels/microsoft-copilot-entra-sso-form.js +180 -0
- package/lib/channels/microsoft-copilot-entra-sso.js +264 -0
- package/lib/channels/microsoft-copilot-icons.js +74 -0
- package/lib/channels/microsoft-copilot-limits.js +84 -0
- package/lib/channels/microsoft-copilot-package.js +130 -49
- package/lib/channels/microsoft-copilot-validate.js +197 -0
- package/lib/cli/index.js +2 -0
- package/lib/cli/infra-guided.js +13 -6
- package/lib/cli/setup-infra.js +31 -4
- package/lib/commands/channel-add-setup.js +28 -9
- package/lib/commands/channel.js +14 -10
- package/lib/commands/integration-definition.js +158 -0
- package/lib/commands/repair-auth-apply.js +19 -5
- package/lib/commands/repair-datasource-sync-operations.js +4 -1
- package/lib/commands/role-assistant-test-output.js +200 -0
- package/lib/commands/role-assistant.js +125 -166
- package/lib/datasource/capability/capability-resolve.js +15 -5
- package/lib/datasource/capability/copy-operations.js +14 -4
- package/lib/infrastructure/index.js +19 -4
- package/lib/integration-definition/apply.js +478 -0
- package/lib/integration-definition/canonical-json.js +51 -0
- package/lib/integration-definition/emit-suite-bid.js +94 -0
- package/lib/integration-definition/enrich-crm-field-mappings.js +240 -0
- package/lib/integration-definition/enrich-openapi-operations.js +434 -0
- package/lib/integration-definition/from-discovery.js +339 -0
- package/lib/integration-definition/hash.js +37 -0
- package/lib/integration-definition/index.js +37 -0
- package/lib/integration-definition/openapi-enrichment/hubspot-crm-companies.json +205 -0
- package/lib/integration-definition/resource-type-catalog.js +59 -0
- package/lib/integration-definition/system-kind-enum.js +40 -0
- package/lib/integration-definition/validate.js +148 -0
- package/lib/programmatic/builder-api-request.js +5 -1
- package/lib/programmatic/builder-help-governance.js +3 -2
- package/lib/programmatic/capability-registry.js +19 -0
- package/lib/programmatic/openapi-component-schemas.js +6 -1
- package/lib/programmatic/openapi-descriptions.js +40 -0
- package/lib/programmatic/openapi-envelope-examples.js +269 -0
- package/lib/programmatic/openapi-operation-bindings.js +45 -0
- package/lib/programmatic/openapi-request-schemas.js +14 -0
- package/lib/programmatic/openapi-result-schemas-overflow.js +101 -0
- package/lib/programmatic/openapi-result-schemas.js +46 -99
- package/lib/programmatic/openapi-schema-descriptions-core.js +7 -4
- package/lib/programmatic/openapi-schema-descriptions-help.js +1 -1
- package/lib/programmatic/openapi-schema-descriptions-operations.js +24 -0
- package/lib/programmatic/openapi-schema-helpers.js +9 -9
- package/lib/programmatic/openapi-spec.js +11 -1
- package/lib/programmatic/orchestration-openapi-meta.js +10 -0
- package/lib/programmatic/route-handler-map.js +29 -1
- package/lib/programmatic/run-channel-package.js +236 -0
- package/lib/programmatic/run-integration-definition.js +69 -0
- package/lib/role-assistant/knowledge-sync-logging.js +58 -0
- package/lib/role-assistant/knowledge-sync.js +88 -93
- package/lib/role-assistant/test-cases-bid.js +155 -0
- package/lib/role-assistant/test-cases-evaluate.js +9 -1
- package/lib/role-assistant/test-cases-honesty.js +24 -10
- package/lib/role-assistant/test-cases.js +28 -0
- package/lib/role-assistant/test-runner-bid-apply.js +122 -0
- package/lib/role-assistant/test-runner-bid-document.js +278 -0
- package/lib/role-assistant/test-runner-workhub-answers.js +245 -20
- package/lib/role-assistant/test-runner-workhub.js +100 -18
- package/lib/role-assistant/test-suite.js +119 -0
- package/lib/schema/application-schema.json +56 -6
- package/lib/schema/external-datasource.schema.json +103 -17
- package/lib/schema/external-system.schema.json +54 -3
- package/lib/schema/integration-definition.schema.json +195 -0
- package/lib/utils/authentication-shape.js +6 -0
- package/lib/utils/docker-build.js +3 -3
- package/lib/utils/help-builder.js +1 -0
- package/lib/utils/promote-dev-docker-image.js +97 -18
- package/lib/utils/transient-developer-id.js +28 -0
- package/package.json +1 -1
- package/scripts/pnpm/af.mjs +24 -9
- package/scripts/pnpm/help.mjs +1 -1
- package/templates/applications/dataplane/application.yaml +3 -2
- package/templates/applications/dataplane/env.template +7 -6
- package/templates/applications/miso-controller/application.yaml +23 -9
- package/templates/applications/miso-controller/env.template +8 -2
- package/templates/channels/README.md +11 -1
- package/templates/channels/brand/color-192.png +0 -0
- package/templates/channels/brand/dark/128.png +0 -0
- package/templates/channels/brand/dark/192.png +0 -0
- package/templates/channels/brand/dark/256.png +0 -0
- package/templates/channels/brand/dark/32.png +0 -0
- package/templates/channels/brand/dark/512.png +0 -0
- package/templates/channels/brand/dark/64.png +0 -0
- package/templates/channels/brand/light/128.png +0 -0
- package/templates/channels/brand/light/192.png +0 -0
- package/templates/channels/brand/light/256.png +0 -0
- package/templates/channels/brand/light/32.png +0 -0
- package/templates/channels/brand/light/512.png +0 -0
- package/templates/channels/brand/light/64.png +0 -0
- package/templates/channels/brand/mono-white/128.png +0 -0
- package/templates/channels/brand/mono-white/192.png +0 -0
- package/templates/channels/brand/mono-white/256.png +0 -0
- package/templates/channels/brand/mono-white/32.png +0 -0
- package/templates/channels/brand/mono-white/512.png +0 -0
- package/templates/channels/brand/mono-white/64.png +0 -0
- package/templates/channels/brand/outline-32.png +0 -0
- package/templates/channels/chatgpt/install.template.md.hbs +6 -4
- package/templates/channels/chatgpt/integration/README.md +4 -5
- package/templates/channels/chatgpt/integration/chatgpt-system.json +1 -16
- package/templates/channels/microsoft-copilot/ai-plugin.template.json.hbs +10 -16
- package/templates/channels/microsoft-copilot/declarative-agent.template.json.hbs +5 -3
- package/templates/channels/microsoft-copilot/entra-sso-registration.template.md.hbs +27 -0
- package/templates/channels/microsoft-copilot/install.template.md.hbs +39 -20
- package/templates/channels/microsoft-copilot/integration/README.md +2 -3
- package/templates/channels/microsoft-copilot/integration/microsoft-copilot-system.json +1 -22
- package/templates/channels/microsoft-copilot/manifest.template.json.hbs +7 -7
- package/templates/channels/microsoft-copilot/oauth-plugin-vault-required.template.md.hbs +11 -0
- package/templates/channels/slack/integration/README.md +2 -4
- package/templates/channels/teams-bot/install.template.md.hbs +4 -0
- package/templates/channels/teams-bot/integration/README.md +3 -5
- package/templates/channels/web-embed/integration/web-embed-system.json +1 -16
- /package/.cursor/plans/{Done → Archive}/157.1-security-quality-violations-by-type.csv +0 -0
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
|
+
"$id": "aifabrix://schema/integration-definition.schema.json",
|
|
4
|
+
"title": "AI Fabrix Builder Integration Definition (BID)",
|
|
5
|
+
"description": "Canonical BT → Builder integration definition. Schema name aifabrix.builder.integrationDefinition v1.0.0.",
|
|
6
|
+
"metadata": {
|
|
7
|
+
"key": "integration-definition-schema",
|
|
8
|
+
"name": "Builder Integration Definition",
|
|
9
|
+
"version": "1.0.0",
|
|
10
|
+
"schemaName": "aifabrix.builder.integrationDefinition",
|
|
11
|
+
"type": "schema",
|
|
12
|
+
"category": "integration",
|
|
13
|
+
"author": "AI Fabrix Team",
|
|
14
|
+
"createdAt": "2026-08-04T00:00:00Z",
|
|
15
|
+
"updatedAt": "2026-08-04T00:00:00Z"
|
|
16
|
+
},
|
|
17
|
+
"type": "object",
|
|
18
|
+
"additionalProperties": false,
|
|
19
|
+
"required": [
|
|
20
|
+
"schemaName",
|
|
21
|
+
"schemaVersion",
|
|
22
|
+
"definitionId",
|
|
23
|
+
"definitionHash",
|
|
24
|
+
"meta",
|
|
25
|
+
"packageIdentity",
|
|
26
|
+
"system",
|
|
27
|
+
"datasources",
|
|
28
|
+
"relationships",
|
|
29
|
+
"sync",
|
|
30
|
+
"capabilities",
|
|
31
|
+
"authority",
|
|
32
|
+
"acceptance"
|
|
33
|
+
],
|
|
34
|
+
"properties": {
|
|
35
|
+
"schemaName": {
|
|
36
|
+
"type": "string",
|
|
37
|
+
"const": "aifabrix.builder.integrationDefinition"
|
|
38
|
+
},
|
|
39
|
+
"schemaVersion": {
|
|
40
|
+
"type": "string",
|
|
41
|
+
"pattern": "^[0-9]+\\.[0-9]+\\.[0-9]+$",
|
|
42
|
+
"description": "BID schema semver; implementers must accept 1.0.0."
|
|
43
|
+
},
|
|
44
|
+
"definitionId": {
|
|
45
|
+
"type": "string",
|
|
46
|
+
"format": "uuid"
|
|
47
|
+
},
|
|
48
|
+
"definitionHash": {
|
|
49
|
+
"type": "string",
|
|
50
|
+
"pattern": "^sha256:[a-f0-9]{64}$",
|
|
51
|
+
"description": "SHA-256 of canonicalized BID body excluding this field."
|
|
52
|
+
},
|
|
53
|
+
"meta": {
|
|
54
|
+
"type": "object",
|
|
55
|
+
"additionalProperties": false,
|
|
56
|
+
"required": ["suiteRunId", "producedAt", "btAssistantKey"],
|
|
57
|
+
"properties": {
|
|
58
|
+
"suiteRunId": { "type": "string", "minLength": 1 },
|
|
59
|
+
"producedAt": { "type": "string", "format": "date-time" },
|
|
60
|
+
"btAssistantKey": { "type": "string", "minLength": 1 },
|
|
61
|
+
"answerFingerprint": { "type": "string" },
|
|
62
|
+
"knowledgePackRefs": {
|
|
63
|
+
"type": "array",
|
|
64
|
+
"items": { "type": "string", "minLength": 1 }
|
|
65
|
+
},
|
|
66
|
+
"humanAnswerRefs": {
|
|
67
|
+
"type": "array",
|
|
68
|
+
"items": { "type": "string", "minLength": 1 }
|
|
69
|
+
},
|
|
70
|
+
"replaceSystemKey": { "type": "boolean", "default": false }
|
|
71
|
+
}
|
|
72
|
+
},
|
|
73
|
+
"packageIdentity": {
|
|
74
|
+
"type": "object",
|
|
75
|
+
"additionalProperties": false,
|
|
76
|
+
"required": ["systemKey", "packageVersion", "displayName"],
|
|
77
|
+
"properties": {
|
|
78
|
+
"systemKey": { "type": "string", "pattern": "^[a-z0-9-]+$", "minLength": 3 },
|
|
79
|
+
"packageVersion": { "type": "string", "pattern": "^[0-9]+\\.[0-9]+\\.[0-9]+$" },
|
|
80
|
+
"displayName": { "type": "string", "minLength": 1 }
|
|
81
|
+
}
|
|
82
|
+
},
|
|
83
|
+
"system": {
|
|
84
|
+
"type": "object",
|
|
85
|
+
"additionalProperties": true,
|
|
86
|
+
"required": ["key", "systemKind", "connectionRefs"],
|
|
87
|
+
"properties": {
|
|
88
|
+
"key": { "type": "string", "pattern": "^[a-z0-9-]+$" },
|
|
89
|
+
"displayName": { "type": "string" },
|
|
90
|
+
"description": { "type": "string" },
|
|
91
|
+
"systemKind": { "type": "string", "minLength": 1 },
|
|
92
|
+
"type": { "type": "string", "enum": ["openapi", "mcp", "custom"] },
|
|
93
|
+
"connectionRefs": {
|
|
94
|
+
"type": "object",
|
|
95
|
+
"description": "kv:// or credential key refs only — no plaintext secrets.",
|
|
96
|
+
"additionalProperties": { "type": "string", "minLength": 1 }
|
|
97
|
+
},
|
|
98
|
+
"openapiSourceRef": { "type": "string" }
|
|
99
|
+
}
|
|
100
|
+
},
|
|
101
|
+
"datasources": {
|
|
102
|
+
"type": "array",
|
|
103
|
+
"minItems": 1,
|
|
104
|
+
"items": {
|
|
105
|
+
"type": "object",
|
|
106
|
+
"additionalProperties": true,
|
|
107
|
+
"required": ["key", "displayName", "entityType", "resourceType"],
|
|
108
|
+
"properties": {
|
|
109
|
+
"key": { "type": "string", "pattern": "^[a-z0-9-]+$", "minLength": 3 },
|
|
110
|
+
"displayName": { "type": "string", "minLength": 1 },
|
|
111
|
+
"entityType": {
|
|
112
|
+
"type": "string",
|
|
113
|
+
"enum": [
|
|
114
|
+
"documentStorage",
|
|
115
|
+
"vectorStore",
|
|
116
|
+
"recordStorage",
|
|
117
|
+
"messageService",
|
|
118
|
+
"serviceContract",
|
|
119
|
+
"none"
|
|
120
|
+
]
|
|
121
|
+
},
|
|
122
|
+
"resourceType": { "type": "string", "pattern": "^[a-z0-9-]+$" },
|
|
123
|
+
"capabilityKeys": {
|
|
124
|
+
"type": "array",
|
|
125
|
+
"items": { "type": "string", "pattern": "^[a-z][a-zA-Z0-9]*$" }
|
|
126
|
+
},
|
|
127
|
+
"propertyKeys": {
|
|
128
|
+
"type": "array",
|
|
129
|
+
"description": "Vendor property names to sync/index (CRM objects → CIP query.properties + fieldMappings).",
|
|
130
|
+
"items": { "type": "string", "pattern": "^[a-zA-Z][a-zA-Z0-9_]*$", "minLength": 1 }
|
|
131
|
+
},
|
|
132
|
+
"openapiSourceRef": { "type": "string" }
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
},
|
|
136
|
+
"relationships": {
|
|
137
|
+
"type": "array",
|
|
138
|
+
"items": {
|
|
139
|
+
"type": "object",
|
|
140
|
+
"additionalProperties": true,
|
|
141
|
+
"required": ["from", "to", "kind"],
|
|
142
|
+
"properties": {
|
|
143
|
+
"from": { "type": "string", "minLength": 1 },
|
|
144
|
+
"to": { "type": "string", "minLength": 1 },
|
|
145
|
+
"kind": { "type": "string", "minLength": 1 }
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
},
|
|
149
|
+
"sync": {
|
|
150
|
+
"type": "object",
|
|
151
|
+
"additionalProperties": true,
|
|
152
|
+
"required": ["mode"],
|
|
153
|
+
"properties": {
|
|
154
|
+
"mode": {
|
|
155
|
+
"type": "string",
|
|
156
|
+
"enum": ["incremental"],
|
|
157
|
+
"description": "Incremental sync is mandatory for CRM profile datasources."
|
|
158
|
+
},
|
|
159
|
+
"datasources": {
|
|
160
|
+
"type": "array",
|
|
161
|
+
"items": { "type": "string", "pattern": "^[a-z0-9-]+$" }
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
},
|
|
165
|
+
"capabilities": {
|
|
166
|
+
"type": "object",
|
|
167
|
+
"additionalProperties": {
|
|
168
|
+
"type": "array",
|
|
169
|
+
"items": { "type": "string", "pattern": "^[a-z][a-zA-Z0-9]*$" }
|
|
170
|
+
},
|
|
171
|
+
"description": "Map of datasource key → capability keys (must include named write for company DS)."
|
|
172
|
+
},
|
|
173
|
+
"authority": {
|
|
174
|
+
"type": "object",
|
|
175
|
+
"additionalProperties": true,
|
|
176
|
+
"required": ["writeRequiresUserAuthority"],
|
|
177
|
+
"properties": {
|
|
178
|
+
"writeRequiresUserAuthority": { "type": "boolean" },
|
|
179
|
+
"notes": { "type": "string" }
|
|
180
|
+
}
|
|
181
|
+
},
|
|
182
|
+
"acceptance": {
|
|
183
|
+
"type": "object",
|
|
184
|
+
"additionalProperties": true,
|
|
185
|
+
"required": ["gates"],
|
|
186
|
+
"properties": {
|
|
187
|
+
"gates": {
|
|
188
|
+
"type": "array",
|
|
189
|
+
"minItems": 1,
|
|
190
|
+
"items": { "type": "string", "minLength": 1 }
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
}
|
|
@@ -110,6 +110,9 @@ function buildAuthenticationFromMethod(systemKey, authType) {
|
|
|
110
110
|
if (method === 'none') {
|
|
111
111
|
return { authType: 'none' };
|
|
112
112
|
}
|
|
113
|
+
if (method === 'system') {
|
|
114
|
+
return { authType: 'system' };
|
|
115
|
+
}
|
|
113
116
|
const flat = buildFlatCredentialTemplate(systemKey, method);
|
|
114
117
|
return {
|
|
115
118
|
authType: 'credential',
|
|
@@ -174,6 +177,9 @@ function migrateAuthenticationToNestedSchema(authentication, systemKey) {
|
|
|
174
177
|
if (authentication.authType === 'none') {
|
|
175
178
|
return { authentication: { authType: 'none' }, changed: false };
|
|
176
179
|
}
|
|
180
|
+
if (authentication.authType === 'system') {
|
|
181
|
+
return { authentication: { authType: 'system' }, changed: false };
|
|
182
|
+
}
|
|
177
183
|
if (authentication.authType === 'apiProvider' && authentication.apiProvider) {
|
|
178
184
|
return { authentication, changed: false };
|
|
179
185
|
}
|
|
@@ -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
|
-
//
|
|
257
|
-
if (options && options.tag && options.tag !== 'latest') {
|
|
256
|
+
// Also apply floating `main` when building a version tag (matches dev-promote dual tags).
|
|
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}:
|
|
261
|
+
await execFileWithDockerEnv('docker', ['tag', `${imageName}:${tag}`, `${imageName}:main`], { env });
|
|
262
262
|
}
|
|
263
263
|
}
|
|
264
264
|
|
|
@@ -111,6 +111,7 @@ const CATEGORIES = [
|
|
|
111
111
|
{ name: 'delete', term: 'delete <systemKey>' },
|
|
112
112
|
{ name: 'repair', term: 'repair <systemKey>' },
|
|
113
113
|
{ name: 'manifest', term: 'manifest get-section <systemKey> <sectionKey>' },
|
|
114
|
+
{ name: 'integration-definition', term: 'integration-definition apply <file>' },
|
|
114
115
|
{ name: 'channel', term: 'channel scaffold <channelTarget>' },
|
|
115
116
|
{ name: 'datasource' },
|
|
116
117
|
{ name: 'datasource load', term: 'datasource load <datasourceKey>' },
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Promote a developer-scoped local Docker image to the manifest base repository (tag only).
|
|
3
3
|
*
|
|
4
|
-
* @fileoverview Used by `aifabrix build --base --from-dev` and dev-promote workflows
|
|
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
6
|
* @author AI Fabrix Team
|
|
6
|
-
* @version 1.
|
|
7
|
+
* @version 1.1.0
|
|
7
8
|
*/
|
|
8
9
|
|
|
9
10
|
'use strict';
|
|
@@ -15,6 +16,9 @@ const { execWithDockerEnv } = require('./docker-exec');
|
|
|
15
16
|
const logger = require('./logger');
|
|
16
17
|
const { formatSuccessLine } = require('./cli-test-layout-chalk');
|
|
17
18
|
|
|
19
|
+
/** Floating base tag applied on every promote alongside the version tag. */
|
|
20
|
+
const PROMOTE_MAIN_TAG = 'main';
|
|
21
|
+
|
|
18
22
|
/**
|
|
19
23
|
* @param {string} value
|
|
20
24
|
* @returns {string}
|
|
@@ -61,6 +65,33 @@ async function dockerTagImageRef(sourceRef, targetRef) {
|
|
|
61
65
|
await execWithDockerEnv(`docker tag ${shellQuote(sourceRef)} ${shellQuote(targetRef)}`);
|
|
62
66
|
}
|
|
63
67
|
|
|
68
|
+
/**
|
|
69
|
+
* Version tag for promote (not floating aliases).
|
|
70
|
+
* @param {Object} options
|
|
71
|
+
* @returns {string}
|
|
72
|
+
*/
|
|
73
|
+
function resolvePromoteVersionTag(options = {}) {
|
|
74
|
+
const raw = String(options.tag || '').trim();
|
|
75
|
+
if (!raw || raw === 'latest' || raw === PROMOTE_MAIN_TAG) {
|
|
76
|
+
return 'latest';
|
|
77
|
+
}
|
|
78
|
+
return raw;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Base tags to apply on promote: version first, then `main` when distinct.
|
|
83
|
+
* @param {Object} options
|
|
84
|
+
* @returns {string[]}
|
|
85
|
+
*/
|
|
86
|
+
function resolvePromoteBaseTags(options = {}) {
|
|
87
|
+
const versionTag = resolvePromoteVersionTag(options);
|
|
88
|
+
const tags = [versionTag];
|
|
89
|
+
if (versionTag !== PROMOTE_MAIN_TAG) {
|
|
90
|
+
tags.push(PROMOTE_MAIN_TAG);
|
|
91
|
+
}
|
|
92
|
+
return tags;
|
|
93
|
+
}
|
|
94
|
+
|
|
64
95
|
/**
|
|
65
96
|
* @param {string} appName
|
|
66
97
|
* @param {Object} variables
|
|
@@ -69,7 +100,7 @@ async function dockerTagImageRef(sourceRef, targetRef) {
|
|
|
69
100
|
* @returns {Promise<{ devRef: string, baseRef: string, devTag: string, baseTag: string }>}
|
|
70
101
|
*/
|
|
71
102
|
async function resolveDevAndBaseImageRefs(appName, variables, developerId, options) {
|
|
72
|
-
const baseTag = options
|
|
103
|
+
const baseTag = resolvePromoteVersionTag(options);
|
|
73
104
|
const devTag = options.fromDevTag || 'latest';
|
|
74
105
|
const { buildImageName: baseName } = await resolveBuildDockerTagNames(appName, variables, developerId, {
|
|
75
106
|
...options,
|
|
@@ -93,31 +124,68 @@ async function resolveDevAndBaseImageRefs(appName, variables, developerId, optio
|
|
|
93
124
|
* @param {Object} appConfig
|
|
94
125
|
* @param {string|number} developerId
|
|
95
126
|
* @param {Object} options
|
|
96
|
-
* @returns {Promise<{
|
|
127
|
+
* @returns {Promise<{
|
|
128
|
+
* devRef: string,
|
|
129
|
+
* baseRef: string,
|
|
130
|
+
* baseName: string,
|
|
131
|
+
* baseTags: string[]
|
|
132
|
+
* }|null>}
|
|
97
133
|
*/
|
|
98
134
|
async function resolvePromoteSourceAndTargetRefs(appName, appConfig, developerId, options) {
|
|
99
|
-
const
|
|
135
|
+
const baseTags = resolvePromoteBaseTags(options);
|
|
136
|
+
const versionTag = baseTags[0];
|
|
100
137
|
const devTagCandidates = ['latest'];
|
|
101
|
-
if (
|
|
102
|
-
devTagCandidates.push(
|
|
138
|
+
if (versionTag !== 'latest' && !devTagCandidates.includes(versionTag)) {
|
|
139
|
+
devTagCandidates.push(versionTag);
|
|
103
140
|
}
|
|
104
141
|
for (const fromDevTag of devTagCandidates) {
|
|
105
142
|
const refs = await resolveDevAndBaseImageRefs(appName, appConfig, developerId, {
|
|
106
143
|
...options,
|
|
144
|
+
tag: versionTag,
|
|
107
145
|
fromDevTag
|
|
108
146
|
});
|
|
109
147
|
if (await dockerImageRefExists(refs.devRef)) {
|
|
110
|
-
|
|
148
|
+
const baseName = refs.baseRef.slice(0, refs.baseRef.lastIndexOf(':'));
|
|
149
|
+
return {
|
|
150
|
+
devRef: refs.devRef,
|
|
151
|
+
baseRef: refs.baseRef,
|
|
152
|
+
baseName,
|
|
153
|
+
baseTags
|
|
154
|
+
};
|
|
111
155
|
}
|
|
112
156
|
}
|
|
113
157
|
return null;
|
|
114
158
|
}
|
|
115
159
|
|
|
160
|
+
/**
|
|
161
|
+
* @param {string} baseName
|
|
162
|
+
* @param {string[]} baseTags
|
|
163
|
+
* @param {string|null} devId
|
|
164
|
+
* @returns {Promise<boolean>} true when every target already matches dev image id
|
|
165
|
+
*/
|
|
166
|
+
async function allBaseTagsMatchDev(baseName, baseTags, devId) {
|
|
167
|
+
if (!devId) {
|
|
168
|
+
return false;
|
|
169
|
+
}
|
|
170
|
+
for (const tag of baseTags) {
|
|
171
|
+
const ref = `${baseName}:${tag}`;
|
|
172
|
+
if (!(await dockerImageRefExists(ref))) {
|
|
173
|
+
return false;
|
|
174
|
+
}
|
|
175
|
+
const baseId = await readDockerImageId(ref);
|
|
176
|
+
if (!baseId || baseId !== devId) {
|
|
177
|
+
return false;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
return true;
|
|
181
|
+
}
|
|
182
|
+
|
|
116
183
|
/**
|
|
117
184
|
* Tag developer-scoped image as manifest base when present locally.
|
|
185
|
+
* Always applies the version tag and `main`.
|
|
118
186
|
* @param {string} appName
|
|
119
187
|
* @param {Object} options - build options (`base`, `tag`, `registry`, `fromDev`)
|
|
120
|
-
* @returns {Promise<string|null>} base image ref when promoted
|
|
188
|
+
* @returns {Promise<string|null>} primary base image ref (version) when promoted; null when missing
|
|
121
189
|
*/
|
|
122
190
|
async function tryPromoteDevImageToBase(appName, options = {}) {
|
|
123
191
|
if (options.base !== true) {
|
|
@@ -135,19 +203,27 @@ async function tryPromoteDevImageToBase(appName, options = {}) {
|
|
|
135
203
|
if (!resolved) {
|
|
136
204
|
return null;
|
|
137
205
|
}
|
|
138
|
-
const { devRef, baseRef } = resolved;
|
|
206
|
+
const { devRef, baseRef, baseName, baseTags } = resolved;
|
|
139
207
|
|
|
140
208
|
const devId = await readDockerImageId(devRef);
|
|
141
|
-
if (await
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
209
|
+
if (await allBaseTagsMatchDev(baseName, baseTags, devId)) {
|
|
210
|
+
logger.log(
|
|
211
|
+
formatSuccessLine(
|
|
212
|
+
`Base image already matches dev build: ${baseTags.map((t) => `${baseName}:${t}`).join(', ')}`
|
|
213
|
+
)
|
|
214
|
+
);
|
|
215
|
+
return baseRef;
|
|
147
216
|
}
|
|
148
217
|
|
|
149
|
-
|
|
150
|
-
|
|
218
|
+
for (const tag of baseTags) {
|
|
219
|
+
const targetRef = `${baseName}:${tag}`;
|
|
220
|
+
await dockerTagImageRef(devRef, targetRef);
|
|
221
|
+
}
|
|
222
|
+
logger.log(
|
|
223
|
+
formatSuccessLine(
|
|
224
|
+
`Promoted dev image to base (tag only): ${baseTags.map((t) => `${baseName}:${t}`).join(', ')}`
|
|
225
|
+
)
|
|
226
|
+
);
|
|
151
227
|
return baseRef;
|
|
152
228
|
}
|
|
153
229
|
|
|
@@ -160,10 +236,13 @@ function shouldAttemptDevPromote(options) {
|
|
|
160
236
|
}
|
|
161
237
|
|
|
162
238
|
module.exports = {
|
|
239
|
+
PROMOTE_MAIN_TAG,
|
|
163
240
|
tryPromoteDevImageToBase,
|
|
164
241
|
shouldAttemptDevPromote,
|
|
165
242
|
resolveDevAndBaseImageRefs,
|
|
166
243
|
resolvePromoteSourceAndTargetRefs,
|
|
244
|
+
resolvePromoteBaseTags,
|
|
245
|
+
resolvePromoteVersionTag,
|
|
167
246
|
dockerImageRefExists,
|
|
168
247
|
readDockerImageId,
|
|
169
248
|
dockerTagImageRef
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Transient developer-id override for CLI commands (does not persist config).
|
|
3
|
+
*
|
|
4
|
+
* @fileoverview Resolve optional --developer-id / --developer without setDeveloperId
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
'use strict';
|
|
8
|
+
|
|
9
|
+
const { validateAndNormalizeDeveloperId } = require('../core/config-normalize');
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Resolve optional CLI developer override (transient — does not persist).
|
|
13
|
+
* Prefers developerId (--developer-id) over developer (-d/--developer).
|
|
14
|
+
*
|
|
15
|
+
* @param {Object} [options] - Commander options
|
|
16
|
+
* @returns {string|undefined} Normalized digit string, or undefined to use config
|
|
17
|
+
*/
|
|
18
|
+
function resolveTransientDeveloperIdFromOptions(options) {
|
|
19
|
+
const raw = options && (options.developerId ?? options.developer);
|
|
20
|
+
if (raw === undefined || raw === null || raw === '') {
|
|
21
|
+
return undefined;
|
|
22
|
+
}
|
|
23
|
+
return validateAndNormalizeDeveloperId(raw);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
module.exports = {
|
|
27
|
+
resolveTransientDeveloperIdFromOptions
|
|
28
|
+
};
|
package/package.json
CHANGED
package/scripts/pnpm/af.mjs
CHANGED
|
@@ -4,9 +4,13 @@
|
|
|
4
4
|
* Usage: pnpm run af <subcommand> | npm run af -- <subcommand>
|
|
5
5
|
*/
|
|
6
6
|
import { spawn } from 'node:child_process';
|
|
7
|
+
import { readFileSync } from 'node:fs';
|
|
8
|
+
import { dirname, join } from 'node:path';
|
|
9
|
+
import { fileURLToPath } from 'node:url';
|
|
7
10
|
import { resolveAifabrixBin } from './lib/resolve-aifabrix-bin.mjs';
|
|
8
11
|
|
|
9
12
|
const AIFABRIX_BIN = resolveAifabrixBin();
|
|
13
|
+
const REPO_ROOT = join(dirname(fileURLToPath(import.meta.url)), '../..');
|
|
10
14
|
|
|
11
15
|
const USAGE = `Usage: pnpm run af <subcommand>
|
|
12
16
|
|
|
@@ -19,7 +23,7 @@ Subcommands:
|
|
|
19
23
|
ba-build aifabrix build builder-api
|
|
20
24
|
dev-build ba-build then ba-run (local Docker image)
|
|
21
25
|
dev-reload Docker live reload (spinner + URL + .temp/dev-reload.log)
|
|
22
|
-
dev-promote build --base --from-dev + run --base (
|
|
26
|
+
dev-promote build --base --from-dev + run --base (tags main + version; TAG=2.52.0 or package.json)
|
|
23
27
|
`;
|
|
24
28
|
|
|
25
29
|
const COMMANDS = {
|
|
@@ -36,10 +40,20 @@ async function runDevBuild() {
|
|
|
36
40
|
await runCommand(COMMANDS['ba-run']);
|
|
37
41
|
}
|
|
38
42
|
|
|
43
|
+
function readPackageVersion() {
|
|
44
|
+
try {
|
|
45
|
+
const pkg = JSON.parse(readFileSync(join(REPO_ROOT, 'package.json'), 'utf8'));
|
|
46
|
+
const version = String(pkg.version || '').trim();
|
|
47
|
+
return version || null;
|
|
48
|
+
} catch {
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
39
53
|
function resolvePromoteTag() {
|
|
40
54
|
const fromArg = process.argv[3];
|
|
41
|
-
const tag = (process.env.TAG || fromArg || '').trim();
|
|
42
|
-
if (!tag || tag === 'latest') {
|
|
55
|
+
const tag = (process.env.TAG || fromArg || readPackageVersion() || '').trim();
|
|
56
|
+
if (!tag || tag === 'latest' || tag === 'main') {
|
|
43
57
|
return null;
|
|
44
58
|
}
|
|
45
59
|
return tag;
|
|
@@ -47,13 +61,14 @@ function resolvePromoteTag() {
|
|
|
47
61
|
|
|
48
62
|
async function runDevPromote() {
|
|
49
63
|
const tag = resolvePromoteTag();
|
|
50
|
-
if (tag) {
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
64
|
+
if (!tag) {
|
|
65
|
+
throw new Error(
|
|
66
|
+
'dev-promote requires a version tag. Set TAG=x.y.z or ensure package.json has "version".'
|
|
67
|
+
);
|
|
54
68
|
}
|
|
55
|
-
|
|
56
|
-
await runCommand([AIFABRIX_BIN, '
|
|
69
|
+
// Promote applies both :main and :<version>; run uses the version tag.
|
|
70
|
+
await runCommand([AIFABRIX_BIN, 'build', 'builder-api', '--base', '--from-dev', '--tag', tag]);
|
|
71
|
+
await runCommand([AIFABRIX_BIN, 'run', 'builder-api', '--base', '--tag', tag]);
|
|
57
72
|
}
|
|
58
73
|
|
|
59
74
|
function runCommand(args) {
|
package/scripts/pnpm/help.mjs
CHANGED
|
@@ -25,7 +25,7 @@ const publicSections = [
|
|
|
25
25
|
|
|
26
26
|
const advancedLines = [
|
|
27
27
|
'--- Advanced (help -- --all) ---',
|
|
28
|
-
'dev-build / dev-promote / af ba-*',
|
|
28
|
+
'dev-build / dev-promote (tags main + version) / af ba-*',
|
|
29
29
|
'build:ci → lint + schema-sync + flags + test:ci (same as GitHub)',
|
|
30
30
|
'sync-json → publish builder/builder-api → templates/',
|
|
31
31
|
'test:154 → builder-api scoped Jest + arch grep',
|
|
@@ -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.
|
|
8
|
+
version: 2.0.26
|
|
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).
|
|
@@ -88,7 +88,8 @@ build:
|
|
|
88
88
|
# - MISO_*, KEYCLOAK_* → auto-generated during Azure install
|
|
89
89
|
# - AI params / provider secrets → LLM external systems + secret store
|
|
90
90
|
# (never product OPENAI_* / AZURE_OPENAI_*). ≥2 providers →
|
|
91
|
-
# ENTERPRISE_LLM_DEFAULT_SELECTOR (portal
|
|
91
|
+
# ENTERPRISE_LLM_DEFAULT_SELECTOR (portal/env deploy default); Environment
|
|
92
|
+
# Settings ai.llmProvider overrides without redeploy.
|
|
92
93
|
# - ENVIRONMENT → set by deployment target
|
|
93
94
|
|
|
94
95
|
configuration:
|
|
@@ -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.
|
|
47
|
+
VERSION=2.0.26
|
|
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
|
|
@@ -155,11 +155,10 @@ MISO_PIPELINE_ENV_KEY=
|
|
|
155
155
|
|
|
156
156
|
# LLM: chat/embed route through governed enterprise-llm only. Provider credentials
|
|
157
157
|
# live on LLM provider external systems + dataplane secret store (not product .env).
|
|
158
|
-
#
|
|
159
|
-
#
|
|
160
|
-
#
|
|
161
|
-
#
|
|
162
|
-
# enterprise-llm (fail-closed if empty). Portal category LLM can override.
|
|
158
|
+
# Deploy-time default provider (systemKey) when Environment Settings ai.llmProvider
|
|
159
|
+
# is empty. Operators can override in Environment Settings without redeploying.
|
|
160
|
+
# When ≥2 LLM providers are published and both settings and this env are empty,
|
|
161
|
+
# resolve fails closed. Ignored when only one provider is published.
|
|
163
162
|
# Lab default: openai (catalog also ships azure-openai).
|
|
164
163
|
ENTERPRISE_LLM_DEFAULT_SELECTOR=openai
|
|
165
164
|
|
|
@@ -180,6 +179,8 @@ ABAC_EXPLAIN_MODE_ENABLED=false
|
|
|
180
179
|
ABAC_PERFORMANCE_THRESHOLD_MS=1000
|
|
181
180
|
|
|
182
181
|
# RBAC Audit Configuration
|
|
182
|
+
# When true, audit denials (rbac.permission.check.failure) and decision traces.
|
|
183
|
+
# Successful allow checks are not audited (no forensic value; floods every request).
|
|
183
184
|
RBAC_AUDIT_ENABLED=true
|
|
184
185
|
# RBAC audit detail: summary | detailed | explain
|
|
185
186
|
RBAC_AUDIT_DETAIL_LEVEL=summary
|