@aifabrix/builder 2.50.0 → 2.51.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/.cursor/plans/Done/157.1-security-quality-violations-by-type.csv +374 -0
- package/.cursor/rules/assistant-driven-integration-build.mdc +40 -0
- package/integration/hubspot-test/create-hubspot.js +1 -16
- package/jest.isolated-projects.js +77 -1
- package/jest.projects.js +5 -426
- package/lib/api/assistants.api.js +67 -0
- package/lib/api/datasources-extended.api.js +6 -6
- package/lib/api/evidence-authoring-helpers.js +267 -0
- package/lib/api/evidence-authoring.api.js +337 -0
- package/lib/api/evidence-kinds.api.js +128 -0
- package/lib/api/evidence.api.js +57 -1
- package/lib/api/index.js +24 -7
- package/lib/api/pipeline.api.js +47 -3
- package/lib/api/records-bulk.api.js +28 -3
- package/lib/api/resource-types.api.js +4 -4
- package/lib/api/roles.api.js +121 -0
- package/lib/api/types/evidence-kinds.types.js +42 -0
- package/lib/api/types/evidence.types.js +156 -8
- package/lib/api/types/roles.types.js +36 -0
- package/lib/api/work.api.js +283 -0
- package/lib/app/certification-verify-rows.js +1 -1
- package/lib/app/run-deploy-job.js +108 -0
- package/lib/app/run-helpers.js +10 -3
- package/lib/app/run.js +8 -0
- package/lib/app/show.js +2 -2
- package/lib/certification/merge-certification-from-artifact.js +1 -1
- package/lib/channels/add-channel.js +54 -23
- package/lib/channels/approval-guides/chatgpt.js +8 -7
- package/lib/channels/approval-guides/microsoft-copilot.js +7 -8
- package/lib/channels/channel-artifact-generator.js +16 -3
- package/lib/channels/channel-credential.js +3 -1
- package/lib/channels/channel-probe-evidence.js +79 -0
- package/lib/channels/channel-probe.js +265 -13
- package/lib/channels/channel-runtime-resolver.js +120 -29
- package/lib/channels/channel-status-display.js +44 -0
- package/lib/channels/channel-targets.js +7 -0
- package/lib/channels/chatgpt-runtime-contract.js +29 -0
- package/lib/channels/microsoft-copilot-package.js +280 -0
- package/lib/channels/runtime-tool-catalog.js +165 -0
- package/lib/cli/index.js +6 -0
- package/lib/cli/setup-app.js +26 -5
- package/lib/cli/setup-dev.js +23 -7
- package/lib/cli/setup-external-system-help.js +8 -2
- package/lib/cli/setup-external-system.js +12 -0
- package/lib/cli/setup-utility-help-after.js +12 -0
- package/lib/cli/setup-utility-validate.js +41 -2
- package/lib/commands/app-test.js +2 -14
- package/lib/commands/datasource-load-export-cli.js +6 -1
- package/lib/commands/deploy-gate-failure-details.js +23 -0
- package/lib/commands/deploy-gate-orchestrator.js +64 -18
- package/lib/commands/dev-cli-handlers.js +6 -3
- package/lib/commands/dev-down.js +2 -6
- package/lib/commands/dev-init.js +7 -109
- package/lib/commands/dev-refresh.js +197 -0
- package/lib/commands/evidence-kind.js +478 -0
- package/lib/commands/governance-command-action.js +4 -2
- package/lib/commands/governance-generate-external.js +56 -21
- package/lib/commands/lifecycle-external.js +10 -4
- package/lib/commands/repair-datasource-apply.js +10 -1
- package/lib/commands/repair-datasource-run.js +4 -2
- package/lib/commands/repair-datasource-test-payload.js +80 -19
- package/lib/commands/repair-datasource-webhooks-sync-bridge.js +42 -3
- package/lib/commands/repair-internal.js +9 -1
- package/lib/commands/repair-openapi-export.js +1 -1
- package/lib/commands/repair-openapi-sync.js +43 -18
- package/lib/commands/resource-type.js +101 -18
- package/lib/commands/role-assistant.js +313 -0
- package/lib/commands/role.js +452 -0
- package/lib/commands/setup.js +1 -1
- package/lib/commands/upload-data-sync-finalize.js +5 -4
- package/lib/commands/upload-sidecar-followups.js +144 -47
- package/lib/commands/upload.js +43 -6
- package/lib/commands/validate-json.js +230 -0
- package/lib/commands/verify-governance-combined-external.js +73 -9
- package/lib/commands/verify-governance-db.js +80 -25
- package/lib/commands/verify-governance-external.js +29 -5
- package/lib/commands/verify-operations-step-plan.js +78 -40
- package/lib/commands/wizard-core-helpers.js +7 -3
- package/lib/commands/wizard-core.js +78 -9
- package/lib/core/audit-logger.js +1 -1
- package/lib/core/secrets-load.js +1 -3
- package/lib/datasource/bulk-loader-run-batches.js +4 -2
- package/lib/datasource/bulk-loader-service.js +5 -1
- package/lib/datasource/capability/relate-operations.js +1 -1
- package/lib/datasource/datasource-validate-summary.js +2 -2
- package/lib/datasource/validate.js +13 -6
- package/lib/external-system/deploy-helpers.js +35 -1
- package/lib/external-system/deploy.js +23 -10
- package/lib/external-system/download-helpers.js +111 -2
- package/lib/external-system/download-role-assistant.js +55 -0
- package/lib/external-system/download.js +45 -26
- package/lib/external-system/test-helpers.js +1 -1
- package/lib/external-system/test.js +39 -3
- package/lib/generator/builders.js +3 -0
- package/lib/generator/helpers.js +13 -5
- package/lib/generator/split-variables.js +9 -3
- package/lib/generator/wizard.js +51 -4
- package/lib/infrastructure/helpers-docker-check.js +6 -0
- package/lib/infrastructure/helpers.js +1 -1
- package/lib/lifecycle/access-control-abac.js +121 -0
- package/lib/lifecycle/lifecycle-governance-options.js +4 -1
- package/lib/lifecycle/product-model.js +3 -2
- package/lib/lifecycle/report-display.js +14 -2
- package/lib/programmatic/auth-config.js +3 -2
- package/lib/programmatic/bearer-auth.js +1 -1
- package/lib/programmatic/capability-registry.js +20 -1
- package/lib/programmatic/command-options.js +1 -0
- package/lib/programmatic/enterprise-mcp-client.js +18 -2
- package/lib/programmatic/index.js +4 -0
- package/lib/programmatic/manifest-patch-section.js +48 -9
- package/lib/programmatic/manifest-section-registry-data.js +3 -2
- package/lib/programmatic/manifest-section-registry.js +18 -1
- package/lib/programmatic/openapi-apply-result-schemas.js +44 -0
- package/lib/programmatic/openapi-component-schemas.js +16 -1
- package/lib/programmatic/openapi-description-enricher.js +3 -1
- package/lib/programmatic/openapi-descriptions.js +55 -5
- package/lib/programmatic/openapi-evidence-schemas.js +161 -0
- package/lib/programmatic/openapi-manifest-schemas.js +6 -2
- package/lib/programmatic/openapi-operation-bindings.js +99 -0
- package/lib/programmatic/openapi-request-schemas.js +36 -1
- package/lib/programmatic/openapi-result-schemas.js +29 -12
- package/lib/programmatic/openapi-schema-descriptions-certify.js +5 -1
- package/lib/programmatic/openapi-schema-descriptions-core.js +6 -3
- package/lib/programmatic/openapi-schema-descriptions-evidence.js +156 -0
- package/lib/programmatic/openapi-schema-descriptions-manifest.js +4 -3
- package/lib/programmatic/openapi-schema-descriptions-operations.js +70 -0
- package/lib/programmatic/openapi-schema-helpers.js +16 -1
- package/lib/programmatic/openapi-spec.js +48 -29
- package/lib/programmatic/orchestration-openapi-meta.js +5 -0
- package/lib/programmatic/request-timeout.js +3 -0
- package/lib/programmatic/route-handler-map.js +89 -2
- package/lib/programmatic/run-certify.js +15 -2
- package/lib/programmatic/run-deploy.js +74 -0
- package/lib/programmatic/run-evidence-authoring.js +80 -0
- package/lib/programmatic/run-protection.js +124 -0
- package/lib/programmatic/run-repository-emcp.js +11 -3
- package/lib/programmatic/run-template-workspace.js +42 -2
- package/lib/programmatic/workspace-context.js +6 -0
- package/lib/protection/auth-context.js +48 -6
- package/lib/protection/paths.js +22 -0
- package/lib/protection/run-protection-create-helpers.js +22 -1
- package/lib/protection/run-protection-create.js +1 -1
- package/lib/protection/sync-after-upload.js +1 -1
- package/lib/resolvers/evidence-kind-file.js +230 -0
- package/lib/resolvers/resource-type-file.js +36 -1
- package/lib/resolvers/role-file.js +227 -0
- package/lib/role-assistant/evidence-sync.js +450 -0
- package/lib/role-assistant/github-workflow.js +96 -0
- package/lib/role-assistant/knowledge-sync.js +465 -0
- package/lib/role-assistant/settings.js +126 -0
- package/lib/role-assistant/test-cases-evaluate.js +170 -0
- package/lib/role-assistant/test-cases-honesty.js +184 -0
- package/lib/role-assistant/test-cases.js +364 -0
- package/lib/role-assistant/test-runner-workhub-answers.js +203 -0
- package/lib/role-assistant/test-runner-workhub.js +385 -0
- package/lib/role-assistant/test-runner.js +500 -0
- package/lib/schema/application-schema.json +36 -2
- package/lib/schema/deployment-rules.yaml +1 -0
- package/lib/schema/external-datasource.schema.json +121 -4
- package/lib/schema/external-system.schema.json +20 -6
- package/lib/schema/infra.parameter.yaml +45 -1
- package/lib/schema/role-assistant-settings.schema.json +108 -0
- package/lib/schema/type/credential-field-registry.json +19 -3
- package/lib/utils/api.js +52 -21
- package/lib/utils/app-path-resolve.js +72 -24
- package/lib/utils/app-register-config.js +4 -3
- package/lib/utils/authentication-shape.js +3 -1
- package/lib/utils/cli-sync-options.js +59 -3
- package/lib/utils/cli-utils.js +11 -3
- package/lib/utils/deployment-validation.js +28 -3
- package/lib/utils/dev-ca-install.js +5 -6
- package/lib/utils/dev-config.js +2 -2
- package/lib/utils/dev-list-sort.js +145 -0
- package/lib/utils/dev-platform-tls.js +0 -63
- package/lib/utils/docker-build.js +46 -27
- package/lib/utils/docker-daemon-tls-ca.js +1 -1
- package/lib/utils/docker-exec.js +91 -6
- package/lib/utils/docker-run-ephemeral.js +55 -0
- package/lib/utils/enterprise-knowledge-paths.js +29 -12
- package/lib/utils/env-copy-keycloak.js +196 -0
- package/lib/utils/env-copy.js +35 -4
- package/lib/utils/error-formatters/error-parser.js +0 -3
- package/lib/utils/external-env-template.js +1 -1
- package/lib/utils/external-system-readiness-compact-blocks.js +28 -1
- package/lib/utils/external-system-readiness-compact-display.js +3 -0
- package/lib/utils/health-check-request.js +2 -3
- package/lib/utils/help-builder.js +4 -0
- package/lib/utils/image-name.js +22 -4
- package/lib/utils/logger.js +37 -4
- package/lib/utils/manifest-source-emit.js +1 -1
- package/lib/utils/paths.js +98 -26
- package/lib/utils/pipeline-upload-auth-retry.js +19 -3
- package/lib/utils/postgres-platform-bootstrap.js +24 -17
- package/lib/utils/postgres-wipe.js +5 -2
- package/lib/utils/remote-dev-auth.js +39 -4
- package/lib/utils/resolve-docker-image-ref.js +36 -6
- package/lib/utils/role-assistant-paths.js +98 -0
- package/lib/utils/schema-loader.js +41 -0
- package/lib/utils/schema-resolver.js +36 -4
- package/lib/utils/token-manager.js +47 -22
- package/lib/utils/upload-sync-options.js +17 -3
- package/lib/utils/url-declarative-resolve-load-doc.js +38 -1
- package/lib/validation/external-manifest-validator.js +31 -8
- package/lib/validation/openapi-contract-surface-validator.js +4 -0
- package/lib/validation/validate-external-file.js +8 -1
- package/lib/validation/validator.js +6 -5
- package/package.json +35 -12
- package/scripts/export-builder-api-evidence.js +3 -3
- package/scripts/lib/plan-actor.js +2 -2
- package/scripts/lib/plan-meta.js +1 -1
- package/scripts/lib/plan-parse.js +1 -1
- package/scripts/lib/plan-prevalidate-gates.js +12 -1
- package/scripts/lib/plan-run-report.js +13 -2
- package/scripts/pnpm/lib/resolve-aifabrix-bin.mjs +10 -10
- package/templates/applications/dataplane/application.yaml +47 -17
- package/templates/applications/dataplane/env.template +28 -9
- package/templates/applications/dataplane/rbac.yaml +78 -2
- package/templates/applications/miso-controller/application.yaml +10 -1
- package/templates/applications/miso-controller/env.template +13 -8
- package/templates/applications/miso-controller/rbac.yaml +72 -0
- package/templates/channels/chatgpt/install.template.md.hbs +42 -24
- package/templates/channels/chatgpt/integration/README.md +10 -7
- package/templates/channels/chatgpt/integration/application.json +2 -2
- package/templates/channels/chatgpt/integration/chatgpt-system.json +3 -3
- package/templates/channels/chatgpt/integration/env.template +1 -1
- package/templates/channels/chatgpt/manifest.template.json.hbs +36 -5
- package/templates/channels/microsoft-copilot/ai-plugin.template.json.hbs +28 -0
- package/templates/channels/microsoft-copilot/declarative-agent.template.json.hbs +22 -0
- package/templates/channels/microsoft-copilot/install.template.md.hbs +32 -22
- package/templates/channels/microsoft-copilot/integration/README.md +9 -7
- package/templates/channels/microsoft-copilot/integration/application.json +2 -2
- package/templates/channels/microsoft-copilot/integration/env.template +1 -1
- package/templates/channels/microsoft-copilot/integration/microsoft-copilot-system.json +7 -6
- package/templates/channels/microsoft-copilot/manifest.template.json.hbs +34 -6
- package/templates/channels/slack/integration/slack-system.json +60 -2
- package/templates/python/requirements.txt +1 -1
|
@@ -57,30 +57,51 @@ function getSchemaNodeAtPath(rootSchema, pathParts) {
|
|
|
57
57
|
return node;
|
|
58
58
|
}
|
|
59
59
|
|
|
60
|
+
/**
|
|
61
|
+
* Format / pattern samples for string-like schema nodes.
|
|
62
|
+
* @param {Object} node - JSON Schema node
|
|
63
|
+
* @returns {*|undefined}
|
|
64
|
+
*/
|
|
65
|
+
function placeholderFromFormatOrPattern(node) {
|
|
66
|
+
const samples = {
|
|
67
|
+
'date-time': '2025-01-15T10:30:00Z',
|
|
68
|
+
date: '2025-01-15',
|
|
69
|
+
uuid: '00000000-0000-4000-8000-000000000001',
|
|
70
|
+
uri: 'https://example.com/objects/1',
|
|
71
|
+
'uri-reference': 'https://example.com/objects/1',
|
|
72
|
+
email: 'sample@example.com'
|
|
73
|
+
};
|
|
74
|
+
if (node.format && Object.prototype.hasOwnProperty.call(samples, node.format)) {
|
|
75
|
+
return samples[node.format];
|
|
76
|
+
}
|
|
77
|
+
if (typeof node.pattern === 'string' && /\\d/.test(node.pattern)) {
|
|
78
|
+
return '+1 555 0100';
|
|
79
|
+
}
|
|
80
|
+
return undefined;
|
|
81
|
+
}
|
|
82
|
+
|
|
60
83
|
/**
|
|
61
84
|
* @param {Object|null|undefined} node - JSON Schema node
|
|
62
85
|
* @returns {*}
|
|
63
86
|
*/
|
|
64
87
|
function placeholderFromSchemaNode(node) {
|
|
65
|
-
if (!node || typeof node !== 'object') return '';
|
|
88
|
+
if (!node || typeof node !== 'object') return 'sample';
|
|
89
|
+
const fromFormat = placeholderFromFormatOrPattern(node);
|
|
90
|
+
if (fromFormat !== undefined) return fromFormat;
|
|
66
91
|
const type = primarySchemaType(node.type);
|
|
67
|
-
if (node.format === 'date-time') return '2025-01-15T10:30:00Z';
|
|
68
|
-
if (node.format === 'date') return '2025-01-15';
|
|
69
|
-
if (node.format === 'uuid') return '00000000-0000-4000-8000-000000000001';
|
|
70
92
|
if (type === 'boolean') return false;
|
|
71
93
|
if (type === 'integer' || type === 'number') return 0;
|
|
72
94
|
if (type === 'array') return [];
|
|
73
95
|
if (type === 'object') {
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
return out;
|
|
96
|
+
const props = node.properties;
|
|
97
|
+
if (!props || typeof props !== 'object') return {};
|
|
98
|
+
const out = {};
|
|
99
|
+
for (const [k, sub] of Object.entries(props)) {
|
|
100
|
+
out[k] = placeholderFromSchemaNode(sub);
|
|
80
101
|
}
|
|
81
|
-
return
|
|
102
|
+
return out;
|
|
82
103
|
}
|
|
83
|
-
return '';
|
|
104
|
+
return 'sample';
|
|
84
105
|
}
|
|
85
106
|
|
|
86
107
|
/**
|
|
@@ -92,18 +113,35 @@ function placeholderForType(type) {
|
|
|
92
113
|
if (type === 'boolean') return false;
|
|
93
114
|
if (type === 'array') return [];
|
|
94
115
|
if (type === 'object') return {};
|
|
95
|
-
return '';
|
|
116
|
+
return 'sample';
|
|
96
117
|
}
|
|
97
118
|
|
|
98
119
|
/**
|
|
99
|
-
*
|
|
100
|
-
* @param {
|
|
101
|
-
* @param {Object}
|
|
102
|
-
* @returns {
|
|
120
|
+
* Prefer schema nodes that carry format/pattern over bare type-only nodes.
|
|
121
|
+
* @param {Object|null|undefined} a
|
|
122
|
+
* @param {Object|null|undefined} b
|
|
123
|
+
* @returns {Object|null}
|
|
103
124
|
*/
|
|
125
|
+
function pickRicherSchemaNode(a, b) {
|
|
126
|
+
const score = node => {
|
|
127
|
+
if (!node || typeof node !== 'object') return -1;
|
|
128
|
+
let s = 0;
|
|
129
|
+
if (node.format) s += 2;
|
|
130
|
+
if (node.pattern) s += 2;
|
|
131
|
+
if (node.type) s += 1;
|
|
132
|
+
return s;
|
|
133
|
+
};
|
|
134
|
+
if (score(a) < 0 && score(b) < 0) return null;
|
|
135
|
+
return score(a) >= score(b) ? a : b;
|
|
136
|
+
}
|
|
137
|
+
|
|
104
138
|
function resolvePlaceholderForPath(parsed, pathParts, attrConfig) {
|
|
105
139
|
const node = getSchemaNodeAtPath(parsed?.metadataSchema, pathParts);
|
|
106
|
-
|
|
140
|
+
// HubSpot-style: materialized root prop (phone/email/url) may be richer than nested.
|
|
141
|
+
const leaf = pathParts[pathParts.length - 1];
|
|
142
|
+
const rootNode = parsed?.metadataSchema?.properties?.[leaf];
|
|
143
|
+
const picked = pickRicherSchemaNode(node, rootNode);
|
|
144
|
+
if (picked) return placeholderFromSchemaNode(picked);
|
|
107
145
|
return placeholderForType(attrConfig?.type || 'string');
|
|
108
146
|
}
|
|
109
147
|
|
|
@@ -154,7 +192,10 @@ function enrichObjectLeavesFromNestedPaths(parsed, pathEntries, payloadTemplate)
|
|
|
154
192
|
payloadTemplate.properties = placeholderFromSchemaNode(propertiesNode);
|
|
155
193
|
}
|
|
156
194
|
if (payloadTemplate.properties[leaf] === undefined || payloadTemplate.properties[leaf] === '') {
|
|
157
|
-
const leafNode =
|
|
195
|
+
const leafNode = pickRicherSchemaNode(
|
|
196
|
+
getSchemaNodeAtPath(parsed?.metadataSchema, ['properties', leaf]),
|
|
197
|
+
parsed?.metadataSchema?.properties?.[leaf]
|
|
198
|
+
);
|
|
158
199
|
payloadTemplate.properties[leaf] = leafNode
|
|
159
200
|
? placeholderFromSchemaNode(leafNode)
|
|
160
201
|
: 'sample';
|
|
@@ -209,6 +250,24 @@ function buildPathEntriesFromAttributes(parsed, attrs) {
|
|
|
209
250
|
* @param {Array} pathEntries - From buildPathEntriesFromAttributes
|
|
210
251
|
* @returns {{ payloadTemplate: Object, expectedResult: Object }}
|
|
211
252
|
*/
|
|
253
|
+
/**
|
|
254
|
+
* Ensure metadataSchema.required root keys exist on payloadTemplate.
|
|
255
|
+
* @param {Object} parsed
|
|
256
|
+
* @param {Object} payloadTemplate
|
|
257
|
+
*/
|
|
258
|
+
function ensureRequiredRootPlaceholders(parsed, payloadTemplate) {
|
|
259
|
+
const required = parsed?.metadataSchema?.required;
|
|
260
|
+
const props = parsed?.metadataSchema?.properties;
|
|
261
|
+
if (!Array.isArray(required) || !props || typeof props !== 'object') return;
|
|
262
|
+
for (const key of required) {
|
|
263
|
+
if (!key || typeof key !== 'string') continue;
|
|
264
|
+
const existing = payloadTemplate[key];
|
|
265
|
+
if (existing !== undefined && existing !== '') continue;
|
|
266
|
+
const node = props[key];
|
|
267
|
+
payloadTemplate[key] = node ? placeholderFromSchemaNode(node) : 'sample';
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
|
|
212
271
|
function buildTemplatesFromPathEntries(parsed, pathEntries) {
|
|
213
272
|
const payloadTemplate = {};
|
|
214
273
|
const expectedResult = {};
|
|
@@ -223,6 +282,7 @@ function buildTemplatesFromPathEntries(parsed, pathEntries) {
|
|
|
223
282
|
setNestedValue(payloadTemplate, parts, resolvePlaceholderForPath(parsed, parts, config));
|
|
224
283
|
}
|
|
225
284
|
enrichObjectLeavesFromNestedPaths(parsed, pathEntries, payloadTemplate);
|
|
285
|
+
ensureRequiredRootPlaceholders(parsed, payloadTemplate);
|
|
226
286
|
return { payloadTemplate, expectedResult };
|
|
227
287
|
}
|
|
228
288
|
|
|
@@ -255,5 +315,6 @@ module.exports = {
|
|
|
255
315
|
placeholderFromSchemaNode,
|
|
256
316
|
placeholderForType,
|
|
257
317
|
resolvePlaceholderForPath,
|
|
318
|
+
ensureRequiredRootPlaceholders,
|
|
258
319
|
repairTestPayload
|
|
259
320
|
};
|
|
@@ -88,21 +88,59 @@ function ensureSyncBridgeExternalIdMetadata(parsed, changes) {
|
|
|
88
88
|
return true;
|
|
89
89
|
}
|
|
90
90
|
|
|
91
|
+
/**
|
|
92
|
+
* Default syncBridge when --sync repair runs and the field is absent.
|
|
93
|
+
* Explicit values (including disabled) are never overwritten.
|
|
94
|
+
*
|
|
95
|
+
* @param {object} parsed
|
|
96
|
+
* @param {Object} [options]
|
|
97
|
+
* @returns {'master' | 'disabled' | null}
|
|
98
|
+
*/
|
|
99
|
+
function defaultSyncBridgeForSyncRepair(parsed, options = {}) {
|
|
100
|
+
if (options.sync !== true) {
|
|
101
|
+
return null;
|
|
102
|
+
}
|
|
103
|
+
if (Object.prototype.hasOwnProperty.call(parsed || {}, 'syncBridge')) {
|
|
104
|
+
return null;
|
|
105
|
+
}
|
|
106
|
+
const entityType = String(parsed?.entityType || '').trim();
|
|
107
|
+
if (entityType === 'none') {
|
|
108
|
+
return null;
|
|
109
|
+
}
|
|
110
|
+
const resourceType = String(parsed?.resourceType || '').trim();
|
|
111
|
+
// Owner/employee catalogs typically do not own sync propagation.
|
|
112
|
+
if (resourceType === 'employee') {
|
|
113
|
+
return 'disabled';
|
|
114
|
+
}
|
|
115
|
+
if (entityType === 'recordStorage' || entityType === '') {
|
|
116
|
+
return 'master';
|
|
117
|
+
}
|
|
118
|
+
return null;
|
|
119
|
+
}
|
|
120
|
+
|
|
91
121
|
/**
|
|
92
122
|
* @param {object} parsed
|
|
93
123
|
* @param {string[]} changes
|
|
94
124
|
* @param {string[]} warnings
|
|
125
|
+
* @param {Object} [options]
|
|
95
126
|
* @returns {boolean}
|
|
96
127
|
*/
|
|
97
|
-
function repairSyncBridgeSection(parsed, changes, warnings) {
|
|
128
|
+
function repairSyncBridgeSection(parsed, changes, warnings, options = {}) {
|
|
129
|
+
let updated = false;
|
|
130
|
+
const seeded = defaultSyncBridgeForSyncRepair(parsed, options);
|
|
131
|
+
if (seeded) {
|
|
132
|
+
parsed.syncBridge = seeded;
|
|
133
|
+
changes.push(`Set syncBridge default ${seeded} (--sync repair; field was absent)`);
|
|
134
|
+
updated = true;
|
|
135
|
+
}
|
|
98
136
|
const mode = parsed?.syncBridge || 'disabled';
|
|
99
137
|
if (mode === 'disabled') {
|
|
100
|
-
return
|
|
138
|
+
return updated;
|
|
101
139
|
}
|
|
102
140
|
if (mode === 'master' && !parsed.resourceType) {
|
|
103
141
|
warnings.push('syncBridge=master requires resourceType for propagation grouping.');
|
|
104
142
|
}
|
|
105
|
-
return ensureSyncBridgeExternalIdMetadata(parsed, changes);
|
|
143
|
+
return ensureSyncBridgeExternalIdMetadata(parsed, changes) || updated;
|
|
106
144
|
}
|
|
107
145
|
|
|
108
146
|
/**
|
|
@@ -128,6 +166,7 @@ function collectWebhookSyncBridgeSystemWarnings(parsed, systemParsed, warnings)
|
|
|
128
166
|
module.exports = {
|
|
129
167
|
repairWebhookSection,
|
|
130
168
|
repairSyncBridgeSection,
|
|
169
|
+
defaultSyncBridgeForSyncRepair,
|
|
131
170
|
collectWebhookSyncBridgeSystemWarnings,
|
|
132
171
|
systemHasWebhookInboundAuth
|
|
133
172
|
};
|
|
@@ -28,7 +28,15 @@ function discoverIntegrationFiles(appPath) {
|
|
|
28
28
|
if (!fs.existsSync(appPath)) {
|
|
29
29
|
return { systemFiles: [], datasourceFiles: [] };
|
|
30
30
|
}
|
|
31
|
-
|
|
31
|
+
let entries = [];
|
|
32
|
+
try {
|
|
33
|
+
entries = fs.readdirSync(appPath);
|
|
34
|
+
} catch (error) {
|
|
35
|
+
if (error && error.code === 'ENOENT') {
|
|
36
|
+
return { systemFiles: [], datasourceFiles: [] };
|
|
37
|
+
}
|
|
38
|
+
throw error;
|
|
39
|
+
}
|
|
32
40
|
const systemFiles = [];
|
|
33
41
|
const datasourceFiles = [];
|
|
34
42
|
for (const name of entries) {
|
|
@@ -30,7 +30,7 @@ function documentKeyToLocalOpenApiPath(appPath, systemKey, documentKey) {
|
|
|
30
30
|
}
|
|
31
31
|
|
|
32
32
|
/** @type {Set<string>} */
|
|
33
|
-
const PROGRAMMATIC_ORCHESTRATION_SYSTEM_KEYS = new Set(['
|
|
33
|
+
const PROGRAMMATIC_ORCHESTRATION_SYSTEM_KEYS = new Set(['builder-api']);
|
|
34
34
|
|
|
35
35
|
/**
|
|
36
36
|
* @param {string} systemKey
|
|
@@ -97,7 +97,7 @@ function extractOpenApiFileListItems(res) {
|
|
|
97
97
|
const data = res && res.data;
|
|
98
98
|
if (data && Array.isArray(data.data)) return data.data;
|
|
99
99
|
if (data && Array.isArray(data.items)) return data.items;
|
|
100
|
-
if (
|
|
100
|
+
if (Array.isArray(res.items)) return res.items;
|
|
101
101
|
return [];
|
|
102
102
|
}
|
|
103
103
|
|
|
@@ -123,6 +123,28 @@ function readDatasourceDocumentKey(appPath, datasourceFileName) {
|
|
|
123
123
|
return openapi && typeof openapi.documentKey === 'string' ? openapi.documentKey : null;
|
|
124
124
|
}
|
|
125
125
|
|
|
126
|
+
/**
|
|
127
|
+
* @returns {Promise<'uploaded'|'skipped'|'skip'>}
|
|
128
|
+
*/
|
|
129
|
+
async function syncOneDatasourceOpenApiFile(ctx, fileName) {
|
|
130
|
+
const documentKey = readDatasourceDocumentKey(ctx.appPath, fileName);
|
|
131
|
+
if (!documentKey || ctx.uploadedThisRun.has(documentKey)) return 'skip';
|
|
132
|
+
if (!ctx.forceUpload && ctx.existingKeys.has(documentKey)) return 'skip';
|
|
133
|
+
|
|
134
|
+
const localPath = documentKeyToLocalOpenApiPath(ctx.appPath, ctx.systemKey, documentKey);
|
|
135
|
+
if (!(await fileExistsAsync(localPath))) return 'skipped';
|
|
136
|
+
|
|
137
|
+
await uploadOneOpenApiFile(
|
|
138
|
+
ctx.dataplaneUrl,
|
|
139
|
+
ctx.authConfig,
|
|
140
|
+
ctx.systemKey,
|
|
141
|
+
localPath,
|
|
142
|
+
documentKey
|
|
143
|
+
);
|
|
144
|
+
ctx.uploadedThisRun.add(documentKey);
|
|
145
|
+
return 'uploaded';
|
|
146
|
+
}
|
|
147
|
+
|
|
126
148
|
/**
|
|
127
149
|
* @param {Object} opts
|
|
128
150
|
* @param {string} opts.appPath
|
|
@@ -139,24 +161,21 @@ async function syncOpenApiFilesForMcp(opts) {
|
|
|
139
161
|
dataplaneUrl: opts.dataplaneUrl,
|
|
140
162
|
authConfig: opts.authConfig
|
|
141
163
|
});
|
|
142
|
-
const
|
|
143
|
-
|
|
164
|
+
const ctx = {
|
|
165
|
+
appPath,
|
|
166
|
+
systemKey,
|
|
167
|
+
dataplaneUrl,
|
|
168
|
+
authConfig,
|
|
169
|
+
existingKeys: await getExistingOpenApiKeys(dataplaneUrl, systemKey, authConfig),
|
|
170
|
+
uploadedThisRun: new Set(),
|
|
171
|
+
forceUpload: opts.force === true
|
|
172
|
+
};
|
|
144
173
|
let uploaded = 0;
|
|
145
174
|
let skipped = 0;
|
|
146
|
-
|
|
147
175
|
for (const fileName of datasourceFiles || []) {
|
|
148
|
-
const
|
|
149
|
-
if (
|
|
150
|
-
|
|
151
|
-
const localPath = documentKeyToLocalOpenApiPath(appPath, systemKey, documentKey);
|
|
152
|
-
if (!(await fileExistsAsync(localPath))) {
|
|
153
|
-
skipped += 1;
|
|
154
|
-
continue;
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
await uploadOneOpenApiFile(dataplaneUrl, authConfig, systemKey, localPath, documentKey);
|
|
158
|
-
uploadedKeys.add(documentKey);
|
|
159
|
-
uploaded += 1;
|
|
176
|
+
const outcome = await syncOneDatasourceOpenApiFile(ctx, fileName);
|
|
177
|
+
if (outcome === 'uploaded') uploaded += 1;
|
|
178
|
+
if (outcome === 'skipped') skipped += 1;
|
|
160
179
|
}
|
|
161
180
|
|
|
162
181
|
return { uploaded, skipped };
|
|
@@ -177,7 +196,11 @@ async function maybeSyncOpenApiFilesForMcp(opts) {
|
|
|
177
196
|
const r = await syncOpenApiFilesForMcp(opts);
|
|
178
197
|
const lines = [];
|
|
179
198
|
if (r.uploaded > 0) {
|
|
180
|
-
lines.push(
|
|
199
|
+
lines.push(
|
|
200
|
+
opts.force
|
|
201
|
+
? `Uploaded ${r.uploaded} OpenAPI file(s) for MCP (force replace by openapi.documentKey)`
|
|
202
|
+
: `Uploaded ${r.uploaded} OpenAPI file(s) for MCP (keyed by openapi.documentKey)`
|
|
203
|
+
);
|
|
181
204
|
}
|
|
182
205
|
if (r.skipped > 0) {
|
|
183
206
|
lines.push(
|
|
@@ -215,7 +238,9 @@ async function maybeRunOpenApiSyncForMcp({
|
|
|
215
238
|
dryRun,
|
|
216
239
|
appPath,
|
|
217
240
|
systemKey,
|
|
218
|
-
datasourceFiles
|
|
241
|
+
datasourceFiles,
|
|
242
|
+
// --api re-exports local OpenAPI; always replace SpecFile rows that already exist.
|
|
243
|
+
force: true
|
|
219
244
|
});
|
|
220
245
|
openapiLines.forEach((l) => changes.push(l));
|
|
221
246
|
return exportLines.length > 0 || openapiLines.length > 0;
|
|
@@ -2,10 +2,7 @@
|
|
|
2
2
|
* Resource type catalog commands (Controller).
|
|
3
3
|
*
|
|
4
4
|
* Commands:
|
|
5
|
-
* - aifabrix resource-type create
|
|
6
|
-
* - aifabrix resource-type import
|
|
7
|
-
* - aifabrix resource-type get
|
|
8
|
-
* - aifabrix resource-type list
|
|
5
|
+
* - aifabrix resource-type create|import|export|get|list
|
|
9
6
|
*
|
|
10
7
|
* @fileoverview Resource type CLI commands
|
|
11
8
|
* @author AI Fabrix Team
|
|
@@ -21,7 +18,9 @@ const { normalizeControllerUrl, resolveEnvironment } = require('../core/config')
|
|
|
21
18
|
const { getOrRefreshDeviceToken } = require('../utils/token-manager');
|
|
22
19
|
const {
|
|
23
20
|
readResourceTypeCreateFile,
|
|
24
|
-
readResourceTypeCatalogFile
|
|
21
|
+
readResourceTypeCatalogFile,
|
|
22
|
+
mapResourceTypeApiToFileRow,
|
|
23
|
+
writeResourceTypeCatalogFile
|
|
25
24
|
} = require('../resolvers/resource-type-file');
|
|
26
25
|
const {
|
|
27
26
|
formatBlockingError,
|
|
@@ -41,45 +40,43 @@ const RESOURCE_TYPE_KEY_PATTERN = /^[a-z][a-z0-9-]*$/;
|
|
|
41
40
|
const RESOURCE_TYPE_HELP_AFTER = `
|
|
42
41
|
|
|
43
42
|
Examples:
|
|
44
|
-
$ aifabrix resource-type create --key customer --display-name "Customer"
|
|
43
|
+
$ aifabrix resource-type create --key customer --display-name "Customer" --domain sales
|
|
45
44
|
$ aifabrix resource-type get customer
|
|
46
45
|
$ aifabrix resource-type list --search customer
|
|
46
|
+
$ aifabrix resource-type export --file ./resource-types-export.json
|
|
47
47
|
|
|
48
48
|
Per-command help: aifabrix resource-type <command> --help
|
|
49
49
|
`;
|
|
50
50
|
|
|
51
51
|
const RESOURCE_TYPE_IMPORT_HELP_AFTER = `
|
|
52
|
-
|
|
53
52
|
Examples:
|
|
54
53
|
$ aifabrix resource-type import --file ./baseline-resourcetypes.json
|
|
55
54
|
$ aifabrix resource-type import --file /path/to/catalog.json
|
|
55
|
+
`;
|
|
56
56
|
|
|
57
|
+
const RESOURCE_TYPE_EXPORT_HELP_AFTER = `
|
|
58
|
+
Examples:
|
|
59
|
+
$ aifabrix resource-type export --file ./resource-types-export.json
|
|
57
60
|
`;
|
|
58
61
|
|
|
59
62
|
const RESOURCE_TYPE_CREATE_HELP_AFTER = `
|
|
60
|
-
|
|
61
63
|
Examples:
|
|
62
|
-
$ aifabrix resource-type create --key customer --display-name "Customer"
|
|
64
|
+
$ aifabrix resource-type create --key customer --display-name "Customer" --domain sales
|
|
63
65
|
$ aifabrix resource-type create --key deal --display-name "Deal" --category business --default-risk-level medium --domain sales
|
|
64
66
|
$ aifabrix resource-type create --file ./customer.json
|
|
65
|
-
|
|
66
67
|
`;
|
|
67
68
|
|
|
68
69
|
const RESOURCE_TYPE_GET_HELP_AFTER = `
|
|
69
|
-
|
|
70
70
|
Examples:
|
|
71
71
|
$ aifabrix resource-type get customer
|
|
72
72
|
$ aifabrix resource-type get clx1234567890abcdef
|
|
73
|
-
|
|
74
73
|
`;
|
|
75
74
|
|
|
76
75
|
const RESOURCE_TYPE_LIST_HELP_AFTER = `
|
|
77
|
-
|
|
78
76
|
Examples:
|
|
79
77
|
$ aifabrix resource-type list
|
|
80
78
|
$ aifabrix resource-type list --page 1 --page-size 50
|
|
81
79
|
$ aifabrix resource-type list --search customer
|
|
82
|
-
|
|
83
80
|
`;
|
|
84
81
|
|
|
85
82
|
/**
|
|
@@ -165,8 +162,62 @@ function unwrapControllerData(response) {
|
|
|
165
162
|
}
|
|
166
163
|
|
|
167
164
|
/**
|
|
168
|
-
* @param {
|
|
165
|
+
* @param {Object|null|undefined} response
|
|
166
|
+
* @param {string} label
|
|
167
|
+
* @returns {void}
|
|
168
|
+
*/
|
|
169
|
+
function assertControllerListSuccess(response, label) {
|
|
170
|
+
if (response && response.success !== false) {
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
const message =
|
|
174
|
+
response?.formattedError || response?.error || `Failed to list ${label}`;
|
|
175
|
+
throw new Error(message);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* @param {Object} response
|
|
180
|
+
* @returns {Object[]}
|
|
169
181
|
*/
|
|
182
|
+
function extractControllerListItems(response) {
|
|
183
|
+
if (response?.data && Array.isArray(response.data.data)) {
|
|
184
|
+
return response.data.data;
|
|
185
|
+
}
|
|
186
|
+
const unwrapped = unwrapControllerData(response);
|
|
187
|
+
if (Array.isArray(unwrapped)) {
|
|
188
|
+
return unwrapped;
|
|
189
|
+
}
|
|
190
|
+
if (unwrapped && Array.isArray(unwrapped.data)) {
|
|
191
|
+
return unwrapped.data;
|
|
192
|
+
}
|
|
193
|
+
throw new Error('Unexpected list response from controller');
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* @param {string} controllerUrl
|
|
198
|
+
* @param {Object} authConfig
|
|
199
|
+
* @returns {Promise<Object[]>}
|
|
200
|
+
*/
|
|
201
|
+
async function fetchAllResourceTypes(controllerUrl, authConfig) {
|
|
202
|
+
const all = [];
|
|
203
|
+
let page = 1;
|
|
204
|
+
const pageSize = 100;
|
|
205
|
+
for (;;) {
|
|
206
|
+
const res = await listResourceTypes(controllerUrl, authConfig, { page, pageSize });
|
|
207
|
+
assertControllerListSuccess(res, 'resource types');
|
|
208
|
+
const items = extractControllerListItems(res);
|
|
209
|
+
if (items.length === 0) {
|
|
210
|
+
break;
|
|
211
|
+
}
|
|
212
|
+
all.push(...items);
|
|
213
|
+
if (items.length < pageSize) {
|
|
214
|
+
break;
|
|
215
|
+
}
|
|
216
|
+
page += 1;
|
|
217
|
+
}
|
|
218
|
+
return all;
|
|
219
|
+
}
|
|
220
|
+
|
|
170
221
|
/**
|
|
171
222
|
* @param {Object[]} rows
|
|
172
223
|
* @param {string} controllerUrl
|
|
@@ -213,6 +264,35 @@ function setupResourceTypeImportCommand(rt) {
|
|
|
213
264
|
});
|
|
214
265
|
}
|
|
215
266
|
|
|
267
|
+
function setupResourceTypeExportCommand(rt) {
|
|
268
|
+
rt
|
|
269
|
+
.command('export')
|
|
270
|
+
.description('Export resource types to baseline-shaped JSON')
|
|
271
|
+
.addHelpText('after', RESOURCE_TYPE_EXPORT_HELP_AFTER)
|
|
272
|
+
.requiredOption('--file <path>', 'Output path for baseline catalog JSON')
|
|
273
|
+
.action(async(options) => {
|
|
274
|
+
try {
|
|
275
|
+
const { controllerUrl, authConfig } = await resolveControllerAndAuth(options);
|
|
276
|
+
const records = await fetchAllResourceTypes(controllerUrl, authConfig);
|
|
277
|
+
const rows = records.map(mapResourceTypeApiToFileRow);
|
|
278
|
+
writeResourceTypeCatalogFile(options.file, rows);
|
|
279
|
+
if (rows.length === 0) {
|
|
280
|
+
logger.log(
|
|
281
|
+
infoLine(
|
|
282
|
+
'No resource types on the controller. Create or import types, then re-run export.'
|
|
283
|
+
)
|
|
284
|
+
);
|
|
285
|
+
}
|
|
286
|
+
logger.log(
|
|
287
|
+
formatSuccessLine(`Resource types exported: ${rows.length} written to ${options.file}`)
|
|
288
|
+
);
|
|
289
|
+
} catch (e) {
|
|
290
|
+
logger.error(formatBlockingError(e.message));
|
|
291
|
+
process.exit(1);
|
|
292
|
+
}
|
|
293
|
+
});
|
|
294
|
+
}
|
|
295
|
+
|
|
216
296
|
function setupResourceTypeCreateCommand(rt) {
|
|
217
297
|
rt
|
|
218
298
|
.command('create')
|
|
@@ -377,8 +457,8 @@ function setupResourceTypeListCommand(rt) {
|
|
|
377
457
|
category: options.category,
|
|
378
458
|
status: options.status
|
|
379
459
|
});
|
|
380
|
-
|
|
381
|
-
const items =
|
|
460
|
+
assertControllerListSuccess(res, 'resource types');
|
|
461
|
+
const items = extractControllerListItems(res);
|
|
382
462
|
displayResourceTypeList(items, environment, headerControllerUrl);
|
|
383
463
|
} catch (e) {
|
|
384
464
|
logger.error(formatBlockingError(e.message));
|
|
@@ -397,6 +477,7 @@ function setupResourceTypeCommands(program) {
|
|
|
397
477
|
.addHelpText('after', RESOURCE_TYPE_HELP_AFTER);
|
|
398
478
|
setupResourceTypeCreateCommand(rt);
|
|
399
479
|
setupResourceTypeImportCommand(rt);
|
|
480
|
+
setupResourceTypeExportCommand(rt);
|
|
400
481
|
setupResourceTypeGetCommand(rt);
|
|
401
482
|
setupResourceTypeListCommand(rt);
|
|
402
483
|
}
|
|
@@ -406,5 +487,7 @@ module.exports = {
|
|
|
406
487
|
requireResourceTypeKey,
|
|
407
488
|
buildCreatePayload,
|
|
408
489
|
unwrapControllerData,
|
|
409
|
-
importResourceTypeRows
|
|
490
|
+
importResourceTypeRows,
|
|
491
|
+
fetchAllResourceTypes,
|
|
492
|
+
mapResourceTypeApiToFileRow
|
|
410
493
|
};
|