@aifabrix/builder 2.54.1 → 2.55.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -0
- package/lib/agent-kit/apply-ops.js +82 -0
- package/lib/agent-kit/checksums.js +28 -0
- package/lib/agent-kit/classify.js +113 -0
- package/lib/agent-kit/claude-shim.js +61 -0
- package/lib/agent-kit/constants.js +55 -0
- package/lib/agent-kit/file-list.js +103 -0
- package/lib/agent-kit/host-approve.js +130 -0
- package/lib/agent-kit/hosting/github.js +63 -0
- package/lib/agent-kit/hosts.js +71 -0
- package/lib/agent-kit/init.js +319 -0
- package/lib/agent-kit/kit-root.js +17 -0
- package/lib/agent-kit/managed-section.js +79 -0
- package/lib/agent-kit/package-load.js +45 -0
- package/lib/agent-kit/plan-ops.js +69 -0
- package/lib/agent-kit/remote-prompts.js +239 -0
- package/lib/agent-kit/render-host-markdown.js +40 -0
- package/lib/agent-kit/repo-guard.js +47 -0
- package/lib/agent-kit/report.js +107 -0
- package/lib/agent-kit/run.js +82 -0
- package/lib/agent-kit/scm/git.js +122 -0
- package/lib/agent-kit/state-file.js +83 -0
- package/lib/agent-kit/status.js +50 -0
- package/lib/agent-kit/v1-detect.js +81 -0
- package/lib/agent-kit/validate-kit.js +87 -0
- package/lib/agent-kit/workspace-name.js +40 -0
- package/lib/agent-kit/workspace-skeleton.js +103 -0
- package/lib/api/role-assistant-test-pack.api.js +86 -0
- package/lib/api/work-search.api.js +31 -4
- package/lib/api/work.api.js +19 -12
- package/lib/app/list-external-systems.js +2 -1
- package/lib/app/show-display.js +33 -6
- package/lib/app/show.js +25 -2
- package/lib/channels/add-channel.js +1 -0
- package/lib/channels/approval-guides/claude.js +2 -2
- package/lib/channels/channel-add-failure.js +1 -0
- package/lib/channels/channel-add-remaining.js +15 -9
- package/lib/channels/channel-definition-status.js +115 -0
- package/lib/channels/channel-description.js +2 -1
- package/lib/channels/channel-install-envelope.js +4 -2
- package/lib/channels/channel-integration-generator.js +2 -0
- package/lib/channels/channel-runtime-resolver.js +14 -0
- package/lib/channels/channel-targets.js +31 -5
- package/lib/channels/claude-setup-values.js +4 -1
- package/lib/channels/slack-channel-add.js +59 -6
- package/lib/channels/slack-mcp-callback.js +51 -0
- package/lib/channels/slack-mcp-runtime.js +156 -0
- package/lib/channels/slack-provisioning/install-cli.js +14 -2
- package/lib/channels/slack-provisioning/with-curl-on-path.js +6 -1
- package/lib/cli/index.js +4 -0
- package/lib/cli/setup-certification-commands.js +2 -2
- package/lib/cli/setup-certification.help.js +3 -1
- package/lib/commands/agent-kit-help.js +79 -0
- package/lib/commands/agent-kit.js +346 -0
- package/lib/commands/channel-add-help.js +5 -5
- package/lib/commands/datasource-capability-relate-cli.js +5 -4
- package/lib/commands/repair-auth-apply.js +1 -1
- package/lib/commands/repair-datasource-key-skip.js +39 -0
- package/lib/commands/repair-datasource-keys.js +24 -12
- package/lib/commands/repair-rbac.js +16 -10
- package/lib/commands/role-assistant-copy-output.js +61 -0
- package/lib/commands/role-assistant-copy.js +155 -0
- package/lib/commands/role-assistant-test-output.js +8 -9
- package/lib/commands/role-assistant-test-result-rows.js +42 -0
- package/lib/commands/role-assistant.js +117 -48
- package/lib/commands/search.js +143 -0
- package/lib/commands/test-trust-external.js +7 -5
- package/lib/commands/upload-sidecar-followups.js +4 -1
- package/lib/commands/verify-operations-skip-e2e.js +131 -0
- package/lib/commands/verify-operations-step-plan.js +6 -33
- package/lib/commands/verify-operations-steps.js +3 -1
- package/lib/datasource/capability/relate-operations.js +28 -10
- package/lib/datasource/capability/run-capability-relate.js +48 -25
- package/lib/datasource/datasource-identifier.js +13 -5
- package/lib/deployment/deployer-poll-run.js +117 -35
- package/lib/external-system/deploy-dataplane-published.js +70 -0
- package/lib/external-system/deploy.js +12 -2
- package/lib/external-system/download-role-assistant.js +41 -15
- package/lib/external-system/download.js +7 -1
- package/lib/generator/wizard-datasource-key.js +54 -0
- package/lib/generator/wizard.js +55 -54
- package/lib/integration-definition/apply-datasource-keys.js +75 -0
- package/lib/integration-definition/apply-relationships.js +195 -0
- package/lib/integration-definition/apply-static-enterprise.js +229 -0
- package/lib/integration-definition/apply-system-file.js +71 -0
- package/lib/integration-definition/apply-test-endpoint.js +89 -0
- package/lib/integration-definition/apply-viewpoint-defaults.js +157 -0
- package/lib/integration-definition/apply.js +78 -59
- package/lib/integration-definition/enrich-openapi-operations.js +36 -10
- package/lib/integration-definition/enrich-openapi-pick.js +87 -0
- package/lib/lifecycle/role-assistant-governance.js +56 -21
- package/lib/programmatic/capability-registry.js +1 -0
- package/lib/programmatic/index.js +2 -0
- package/lib/programmatic/openapi-apply-result-schemas.js +8 -0
- package/lib/programmatic/openapi-channel-install-schemas.js +4 -1
- package/lib/programmatic/openapi-component-schemas.js +1 -0
- package/lib/programmatic/openapi-descriptions.js +9 -3
- package/lib/programmatic/openapi-envelope-examples.js +7 -0
- package/lib/programmatic/openapi-operation-bindings.js +5 -0
- package/lib/programmatic/openapi-request-schemas.js +5 -0
- package/lib/programmatic/openapi-schema-descriptions-channel.js +8 -5
- package/lib/programmatic/openapi-schema-descriptions-operations.js +19 -0
- package/lib/programmatic/openapi-spec.js +1 -0
- package/lib/programmatic/request-timeout.js +3 -0
- package/lib/programmatic/route-handler-map.js +7 -0
- package/lib/programmatic/run-channel-catalog.js +50 -8
- package/lib/programmatic/run-channel-install-options.js +5 -1
- package/lib/programmatic/run-download.js +41 -0
- package/lib/protection/auth-context.js +3 -1
- package/lib/protection/protection-display.js +3 -2
- package/lib/protection/report-exit.js +50 -0
- package/lib/protection/run-commands.js +2 -0
- package/lib/role-assistant/package-validate.js +175 -0
- package/lib/role-assistant/test-case-assertion-row.js +36 -0
- package/lib/role-assistant/test-case-copy-bind.js +77 -0
- package/lib/role-assistant/test-case-copy-expected.js +127 -0
- package/lib/role-assistant/test-case-copy-from-work.js +90 -0
- package/lib/role-assistant/test-case-copy-interactions.js +67 -0
- package/lib/role-assistant/test-case-copy.js +119 -0
- package/lib/role-assistant/test-case-expected-catalog.js +135 -0
- package/lib/role-assistant/test-case-validate.js +88 -0
- package/lib/role-assistant/test-cases-bid.js +117 -4
- package/lib/role-assistant/test-cases-evaluate.js +137 -30
- package/lib/role-assistant/test-cases-honesty.js +155 -6
- package/lib/role-assistant/test-cases-lifecycle.js +291 -0
- package/lib/role-assistant/test-cases-search-graph.js +122 -0
- package/lib/role-assistant/test-cases-search.js +20 -2
- package/lib/role-assistant/test-cases.js +34 -168
- package/lib/role-assistant/test-pack-map.js +180 -0
- package/lib/role-assistant/test-pack-sync.js +187 -0
- package/lib/role-assistant/test-runner-debug.js +54 -0
- package/lib/role-assistant/test-runner-respond-body.js +36 -0
- package/lib/role-assistant/test-runner-search.js +20 -5
- package/lib/role-assistant/test-runner-start-payload.js +63 -10
- package/lib/role-assistant/test-runner-workhub-answers.js +45 -16
- package/lib/role-assistant/test-runner-workhub.js +45 -21
- package/lib/role-assistant/test-runner.js +50 -10
- package/lib/schema/external-datasource.schema.json +36 -5
- package/lib/schema/external-system.schema.json +20 -2
- package/lib/schema/role-assistant-test-case.schema.json +408 -0
- package/lib/search/dataplane-context.js +23 -0
- package/lib/search/search-args.js +211 -0
- package/lib/search/search-display.js +238 -0
- package/lib/search/search-errors.js +50 -0
- package/lib/search/search-prompts.js +119 -0
- package/lib/search/search-roles.js +85 -0
- package/lib/search/search-run.js +241 -0
- package/lib/search/search-skills.js +198 -0
- package/lib/utils/authentication-slot-show.js +153 -0
- package/lib/utils/credential-display.js +43 -1
- package/lib/utils/external-system-readiness-compact-blocks.js +5 -1
- package/lib/utils/external-system-readiness-compact-display.js +30 -1
- package/lib/utils/external-system-validators.js +2 -1
- package/lib/utils/help-builder.js +3 -1
- package/lib/utils/schema-loader.js +37 -0
- package/lib/validation/validate-external-file.js +5 -1
- package/lib/validation/validate.js +3 -2
- package/package.json +1 -1
- package/templates/agent-kit/adapters/claude/nested.CLAUDE.md +1 -0
- package/templates/agent-kit/adapters/claude/root.CLAUDE.md +5 -0
- package/templates/agent-kit/adapters/cursor/commands/cust-0-plan.md +5 -0
- package/templates/agent-kit/adapters/cursor/commands/cust-1-validate.md +5 -0
- package/templates/agent-kit/adapters/cursor/commands/cust-2-build.md +5 -0
- package/templates/agent-kit/adapters/cursor/commands/cust-3-push.md +5 -0
- package/templates/agent-kit/adapters/cursor/commands/cust-biz-1-validate.md +5 -0
- package/templates/agent-kit/adapters/cursor/commands/cust-biz-2-build.md +5 -0
- package/templates/agent-kit/adapters/cursor/commands/cust-biz-3-prove.md +5 -0
- package/templates/agent-kit/agent-kit.yaml +45 -0
- package/templates/agent-kit/instructions/AGENTKIT.md +83 -0
- package/templates/agent-kit/instructions/integration.AGENTS.md +25 -0
- package/templates/agent-kit/instructions/role-assistant.AGENTS.md +22 -0
- package/templates/agent-kit/instructions/root.AGENTS.md +37 -0
- package/templates/agent-kit/parity-inventory.yaml +188 -0
- package/templates/agent-kit/skills/aifabrix-connected-system/SKILL.md +58 -0
- package/templates/agent-kit/skills/aifabrix-connected-system/references/contract-checklist.md +40 -0
- package/templates/agent-kit/skills/aifabrix-connected-system/references/delivery-gates.md +54 -0
- package/templates/agent-kit/skills/aifabrix-connected-system/references/production-safety.md +35 -0
- package/templates/agent-kit/skills/aifabrix-connected-system/scripts/reserved-system-key.js +19 -0
- package/templates/agent-kit/skills/aifabrix-plan/SKILL.md +51 -0
- package/templates/agent-kit/skills/aifabrix-plan/references/challenge-checklist.md +108 -0
- package/templates/agent-kit/skills/aifabrix-plan/references/interaction.md +34 -0
- package/templates/agent-kit/skills/aifabrix-plan/references/plan-template.md +182 -0
- package/templates/agent-kit/skills/aifabrix-prove/SKILL.md +50 -0
- package/templates/agent-kit/skills/aifabrix-prove/references/demo-template.md +65 -0
- package/templates/agent-kit/skills/aifabrix-prove/references/evidence-lifecycle.md +68 -0
- package/templates/agent-kit/skills/aifabrix-prove/references/learning-template.md +66 -0
- package/templates/agent-kit/skills/aifabrix-role-assistant/SKILL.md +42 -0
- package/templates/agent-kit/skills/aifabrix-role-assistant/references/package-boundary.md +21 -0
- package/templates/agent-kit/skills/shared/hosts.md +78 -0
- package/templates/agent-kit/workspace/.cursor/plans/README.md +22 -0
- package/templates/agent-kit/workspace/LICENSE.mit +21 -0
- package/templates/agent-kit/workspace/README.md +89 -0
- package/templates/applications/builder-api/Dockerfile +3 -1
- package/templates/applications/dataplane/application.yaml +2 -2
- package/templates/applications/dataplane/env.template +1 -1
- package/templates/applications/dataplane/rbac.yaml +28 -0
- package/templates/applications/keycloak/env.template +3 -3
- package/templates/applications/miso-controller/application.yaml +1 -1
- package/templates/applications/miso-controller/env.template +8 -9
- package/templates/channels/claude/TROUBLESHOOTING.md +3 -0
- package/templates/channels/claude/channel.json +1 -1
- package/templates/channels/claude/install.template.md.hbs +1 -0
- package/templates/channels/claude/integration/claude-system.json +1 -1
- package/templates/channels/slack/channel.json +11 -0
- package/templates/channels/slack/install.template.md.hbs +9 -0
- package/templates/channels/slack/integration/slack-system.json +11 -0
- package/templates/channels/slack/manifest.template.json.hbs +6 -3
- package/templates/channels/teams-bot/channel.json +1 -0
- package/templates/python/docker-compose.hbs +14 -0
- package/templates/typescript/docker-compose.hbs +14 -0
|
@@ -117,7 +117,8 @@ async function runUploadSidecarFollowups(ctx) {
|
|
|
117
117
|
governance: null,
|
|
118
118
|
evidence: null,
|
|
119
119
|
knowledge: null,
|
|
120
|
-
documentStorageSettings: null
|
|
120
|
+
documentStorageSettings: null,
|
|
121
|
+
tests: null
|
|
121
122
|
};
|
|
122
123
|
const quietSidecar = isQuietMinimalSync(ctx.options);
|
|
123
124
|
const model = buildRoleAssistantModel(ctx.payload);
|
|
@@ -131,6 +132,8 @@ async function runUploadSidecarFollowups(ctx) {
|
|
|
131
132
|
throw err;
|
|
132
133
|
}
|
|
133
134
|
await runKnowledgeSidecar(ctx, model, sidecar, quietSidecar);
|
|
135
|
+
const { runTestsSidecar } = require('../role-assistant/test-pack-sync');
|
|
136
|
+
await runTestsSidecar(ctx, sidecar, quietSidecar);
|
|
134
137
|
return sidecar;
|
|
135
138
|
}
|
|
136
139
|
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* When verify-operations should skip live E2E (Role Assistant or static custom).
|
|
3
|
+
*
|
|
4
|
+
* @fileoverview Skip E2E for packages that have no live vendor pull
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
'use strict';
|
|
8
|
+
|
|
9
|
+
const path = require('path');
|
|
10
|
+
const { loadConfigFile } = require('../utils/config-format');
|
|
11
|
+
const { discoverIntegrationFiles } = require('./repair-internal');
|
|
12
|
+
|
|
13
|
+
const STORAGE_ENTITY_TYPES = new Set(['recordStorage', 'documentStorage']);
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Role Assistant packages use authType none + nested builder-api CIP.
|
|
17
|
+
* Live capacity E2E expects a system baseUrl credential and is not the RA certify path.
|
|
18
|
+
* @param {string} systemKey
|
|
19
|
+
* @param {Object} options
|
|
20
|
+
* @returns {boolean}
|
|
21
|
+
*/
|
|
22
|
+
function shouldSkipE2eForRoleAssistant(systemKey, options) {
|
|
23
|
+
if (options.forceE2e === true) {
|
|
24
|
+
return false;
|
|
25
|
+
}
|
|
26
|
+
try {
|
|
27
|
+
const { resolveAppWorkspace } = require('../utils/app-path-resolve');
|
|
28
|
+
const { baseDir } = resolveAppWorkspace(systemKey, {});
|
|
29
|
+
return baseDir === 'role-assistant';
|
|
30
|
+
} catch {
|
|
31
|
+
return false;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* @param {string} filePath
|
|
37
|
+
* @returns {Object|null}
|
|
38
|
+
*/
|
|
39
|
+
function readManifestObject(filePath) {
|
|
40
|
+
try {
|
|
41
|
+
const parsed = loadConfigFile(filePath);
|
|
42
|
+
return parsed && typeof parsed === 'object' ? parsed : null;
|
|
43
|
+
} catch {
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* @param {string} appPath
|
|
50
|
+
* @param {string} systemKey
|
|
51
|
+
* @returns {string|null}
|
|
52
|
+
*/
|
|
53
|
+
function readLocalSystemType(appPath, systemKey) {
|
|
54
|
+
const candidates = [
|
|
55
|
+
path.join(appPath, `${systemKey}-system.json`),
|
|
56
|
+
path.join(appPath, `${systemKey}-system.yaml`),
|
|
57
|
+
path.join(appPath, `${systemKey}-system.yml`)
|
|
58
|
+
];
|
|
59
|
+
for (const filePath of candidates) {
|
|
60
|
+
const doc = readManifestObject(filePath);
|
|
61
|
+
if (doc && typeof doc.type === 'string' && doc.type.trim()) {
|
|
62
|
+
return doc.type.trim().toLowerCase();
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* @param {string} appPath
|
|
70
|
+
* @returns {Object[]}
|
|
71
|
+
*/
|
|
72
|
+
function loadStorageDatasources(appPath) {
|
|
73
|
+
const { datasourceFiles } = discoverIntegrationFiles(appPath);
|
|
74
|
+
const storage = [];
|
|
75
|
+
for (const name of datasourceFiles) {
|
|
76
|
+
const doc = readManifestObject(path.join(appPath, name));
|
|
77
|
+
if (!doc || !STORAGE_ENTITY_TYPES.has(String(doc.entityType || ''))) {
|
|
78
|
+
continue;
|
|
79
|
+
}
|
|
80
|
+
storage.push(doc);
|
|
81
|
+
}
|
|
82
|
+
return storage;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Static custom packages (syncBridge disabled) have no live vendor pull.
|
|
87
|
+
* @param {string} systemKey
|
|
88
|
+
* @param {Object} options
|
|
89
|
+
* @returns {boolean}
|
|
90
|
+
*/
|
|
91
|
+
function shouldSkipE2eForStaticPackage(systemKey, options) {
|
|
92
|
+
if (options.forceE2e === true) {
|
|
93
|
+
return false;
|
|
94
|
+
}
|
|
95
|
+
try {
|
|
96
|
+
const { resolveAppWorkspace } = require('../utils/app-path-resolve');
|
|
97
|
+
const { baseDir, appPath } = resolveAppWorkspace(systemKey, options);
|
|
98
|
+
if (baseDir !== 'integration' || !appPath) {
|
|
99
|
+
return false;
|
|
100
|
+
}
|
|
101
|
+
if (readLocalSystemType(appPath, systemKey) !== 'custom') {
|
|
102
|
+
return false;
|
|
103
|
+
}
|
|
104
|
+
const storage = loadStorageDatasources(appPath);
|
|
105
|
+
if (storage.length === 0) {
|
|
106
|
+
return false;
|
|
107
|
+
}
|
|
108
|
+
return storage.every((ds) => String(ds.syncBridge || 'disabled').toLowerCase() === 'disabled');
|
|
109
|
+
} catch {
|
|
110
|
+
return false;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* @param {string} systemKey
|
|
116
|
+
* @param {Object} options
|
|
117
|
+
* @returns {boolean}
|
|
118
|
+
*/
|
|
119
|
+
function shouldSkipOperationsE2e(systemKey, options) {
|
|
120
|
+
if (options.skipE2e === true || options.minimal === true) {
|
|
121
|
+
return true;
|
|
122
|
+
}
|
|
123
|
+
return shouldSkipE2eForRoleAssistant(systemKey, options)
|
|
124
|
+
|| shouldSkipE2eForStaticPackage(systemKey, options);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
module.exports = {
|
|
128
|
+
shouldSkipOperationsE2e,
|
|
129
|
+
shouldSkipE2eForRoleAssistant,
|
|
130
|
+
shouldSkipE2eForStaticPackage
|
|
131
|
+
};
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
const { validateExternalSystemComplete } = require('../validation/validate');
|
|
8
8
|
const { testExternalSystem, testExternalSystemIntegration } = require('../external-system/test');
|
|
9
9
|
const { runTestE2EForExternalSystem } = require('./test-e2e-external');
|
|
10
|
+
const { shouldSkipOperationsE2e } = require('./verify-operations-skip-e2e');
|
|
10
11
|
|
|
11
12
|
/**
|
|
12
13
|
* @param {string} systemKey
|
|
@@ -59,21 +60,13 @@ function buildAfterSyncSteps(systemKey, options) {
|
|
|
59
60
|
* @returns {Object}
|
|
60
61
|
*/
|
|
61
62
|
function buildE2eStep(systemKey, options) {
|
|
62
|
-
if (
|
|
63
|
+
if (shouldSkipOperationsE2e(systemKey, options)) {
|
|
64
|
+
const reason = options.skipE2e === true || options.minimal === true
|
|
65
|
+
? 'minimal'
|
|
66
|
+
: 'static-or-role-assistant';
|
|
63
67
|
return {
|
|
64
68
|
key: 'e2e',
|
|
65
|
-
run: async() => ({ success: true, skipped: true, reason
|
|
66
|
-
ok: () => true
|
|
67
|
-
};
|
|
68
|
-
}
|
|
69
|
-
if (shouldSkipE2eForRoleAssistant(systemKey, options)) {
|
|
70
|
-
return {
|
|
71
|
-
key: 'e2e',
|
|
72
|
-
run: async() => ({
|
|
73
|
-
success: true,
|
|
74
|
-
skipped: true,
|
|
75
|
-
reason: 'role-assistant-auth-none'
|
|
76
|
-
}),
|
|
69
|
+
run: async() => ({ success: true, skipped: true, reason }),
|
|
77
70
|
ok: () => true
|
|
78
71
|
};
|
|
79
72
|
}
|
|
@@ -99,26 +92,6 @@ function buildE2eStep(systemKey, options) {
|
|
|
99
92
|
};
|
|
100
93
|
}
|
|
101
94
|
|
|
102
|
-
/**
|
|
103
|
-
* Role Assistant packages use authType none + nested builder-api CIP.
|
|
104
|
-
* Live capacity E2E expects a system baseUrl credential and is not the RA certify path.
|
|
105
|
-
* @param {string} systemKey
|
|
106
|
-
* @param {Object} options
|
|
107
|
-
* @returns {boolean}
|
|
108
|
-
*/
|
|
109
|
-
function shouldSkipE2eForRoleAssistant(systemKey, options) {
|
|
110
|
-
if (options.forceE2e === true) {
|
|
111
|
-
return false;
|
|
112
|
-
}
|
|
113
|
-
try {
|
|
114
|
-
const { resolveAppWorkspace } = require('../utils/app-path-resolve');
|
|
115
|
-
const { baseDir } = resolveAppWorkspace(systemKey, {});
|
|
116
|
-
return baseDir === 'role-assistant';
|
|
117
|
-
} catch {
|
|
118
|
-
return false;
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
|
|
122
95
|
/**
|
|
123
96
|
* @param {string} systemKey
|
|
124
97
|
* @param {Object} options
|
|
@@ -86,7 +86,9 @@ async function runOperationsVerifySteps(systemKey, options) {
|
|
|
86
86
|
}
|
|
87
87
|
}
|
|
88
88
|
|
|
89
|
-
|
|
89
|
+
// Upload is not publish. Verify against the already-deployed dataplane unless
|
|
90
|
+
// the caller opts in with upload: true (and did not pass --no-publish).
|
|
91
|
+
if (options.upload === true && !cliOptsSkipPublish(options)) {
|
|
90
92
|
const uploadCtx = await syncLocalIfRequested(systemKey, options);
|
|
91
93
|
attachUploadAuthToOptions(options, uploadCtx);
|
|
92
94
|
}
|
|
@@ -78,23 +78,41 @@ function _buildRelationMetadataSchema({ relationName, targetDoc, resolvedTargetF
|
|
|
78
78
|
return out;
|
|
79
79
|
}
|
|
80
80
|
|
|
81
|
+
function relationNameFromCarrier(fields) {
|
|
82
|
+
const raw = String((Array.isArray(fields) && fields[0]) || '').trim();
|
|
83
|
+
if (!raw) {
|
|
84
|
+
return '';
|
|
85
|
+
}
|
|
86
|
+
if (/^[a-z][a-zA-Z0-9]*$/.test(raw)) {
|
|
87
|
+
return raw;
|
|
88
|
+
}
|
|
89
|
+
const camel = raw.replace(/[-_]([a-zA-Z0-9])/g, (_, ch) => String(ch).toUpperCase());
|
|
90
|
+
return /^[a-z][a-zA-Z0-9]*$/.test(camel) ? camel : '';
|
|
91
|
+
}
|
|
92
|
+
|
|
81
93
|
/**
|
|
82
94
|
* FK `name` must match schema: ^[a-z][a-zA-Z0-9]*$
|
|
95
|
+
* Defaults to the published carrier (fields[0]) when --relation-name is omitted.
|
|
83
96
|
*
|
|
84
97
|
* @param {string} raw
|
|
98
|
+
* @param {string[]} [fields]
|
|
85
99
|
* @returns {string}
|
|
86
100
|
*/
|
|
87
|
-
function normalizeRelationName(raw) {
|
|
101
|
+
function normalizeRelationName(raw, fields) {
|
|
88
102
|
const s = String(raw || '').trim();
|
|
89
|
-
if (
|
|
90
|
-
|
|
103
|
+
if (s) {
|
|
104
|
+
if (!/^[a-z][a-zA-Z0-9]*$/.test(s)) {
|
|
105
|
+
throw new Error(
|
|
106
|
+
`--relation-name "${s}" must match ^[a-z][a-zA-Z0-9]*$ (camelCase)`
|
|
107
|
+
);
|
|
108
|
+
}
|
|
109
|
+
return s;
|
|
91
110
|
}
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
);
|
|
111
|
+
const fromField = relationNameFromCarrier(fields);
|
|
112
|
+
if (fromField) {
|
|
113
|
+
return fromField;
|
|
96
114
|
}
|
|
97
|
-
|
|
115
|
+
throw new Error('--relation-name is required when --field is not a camelCase carrier');
|
|
98
116
|
}
|
|
99
117
|
|
|
100
118
|
/**
|
|
@@ -287,9 +305,9 @@ function buildForeignKeyRow(name, fields, targetDatasource, targetFields, option
|
|
|
287
305
|
* }}
|
|
288
306
|
*/
|
|
289
307
|
function applyCapabilityRelate(doc, opts) {
|
|
290
|
-
const name = normalizeRelationName(opts.relationName);
|
|
291
|
-
const targetDatasource = normalizeTargetDatasource(opts.targetDatasource);
|
|
292
308
|
const { fields, targetFields } = parseRelateFields(opts);
|
|
309
|
+
const name = normalizeRelationName(opts.relationName, fields);
|
|
310
|
+
const targetDatasource = normalizeTargetDatasource(opts.targetDatasource);
|
|
293
311
|
|
|
294
312
|
const d = deepClone(doc);
|
|
295
313
|
if (!Array.isArray(d.foreignKeys)) {
|
|
@@ -25,13 +25,18 @@ function loadTargetDocLocal(targetDatasourceKey) {
|
|
|
25
25
|
return localTarget.ok ? readJsonFile(localTarget.path) : null;
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
-
async function maybeFetchRemoteTargetConfig({
|
|
28
|
+
async function maybeFetchRemoteTargetConfig({
|
|
29
|
+
systemKeyForAuth,
|
|
30
|
+
targetSystemKey,
|
|
31
|
+
targetDatasourceKey
|
|
32
|
+
}) {
|
|
29
33
|
const remoteFetchMeta = { attempted: false, ok: false, notAuthenticated: false };
|
|
30
|
-
|
|
34
|
+
const authKey = String(targetSystemKey || systemKeyForAuth || '').trim();
|
|
35
|
+
if (!authKey) {
|
|
31
36
|
return { remoteTargetConfig: null, remoteFetchMeta };
|
|
32
37
|
}
|
|
33
38
|
remoteFetchMeta.attempted = true;
|
|
34
|
-
const remote = await tryFetchDatasourceConfig(
|
|
39
|
+
const remote = await tryFetchDatasourceConfig(authKey, targetDatasourceKey, { silent: true });
|
|
35
40
|
if (remote.ok) {
|
|
36
41
|
return {
|
|
37
42
|
remoteTargetConfig: remote.datasourceConfig,
|
|
@@ -105,6 +110,38 @@ function applyRelateAndValidateSchema(parsed, opts, semanticWarnings, remoteVali
|
|
|
105
110
|
return { result, validation };
|
|
106
111
|
}
|
|
107
112
|
|
|
113
|
+
function persistOrDryRunRelate({
|
|
114
|
+
opts,
|
|
115
|
+
resolvedPath,
|
|
116
|
+
result,
|
|
117
|
+
validation,
|
|
118
|
+
semantic,
|
|
119
|
+
remoteFetchMeta
|
|
120
|
+
}) {
|
|
121
|
+
if (opts.dryRun) {
|
|
122
|
+
return buildRelateResultEnvelope({
|
|
123
|
+
dryRun: true,
|
|
124
|
+
resolvedPath,
|
|
125
|
+
result,
|
|
126
|
+
backupPath: null,
|
|
127
|
+
validation,
|
|
128
|
+
semanticWarnings: semantic.warnings,
|
|
129
|
+
remoteValidation: remoteFetchMeta
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
const backupPath = writeBackup(resolvedPath, Boolean(opts.noBackup));
|
|
133
|
+
atomicWriteJson(resolvedPath, result.doc);
|
|
134
|
+
return buildRelateResultEnvelope({
|
|
135
|
+
dryRun: false,
|
|
136
|
+
resolvedPath,
|
|
137
|
+
result,
|
|
138
|
+
backupPath,
|
|
139
|
+
validation,
|
|
140
|
+
semanticWarnings: semantic.warnings,
|
|
141
|
+
remoteValidation: remoteFetchMeta
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
|
|
108
145
|
/**
|
|
109
146
|
* @typedef {object} RunCapabilityRelateOpts
|
|
110
147
|
* @property {string} fileOrKey
|
|
@@ -137,8 +174,10 @@ async function runCapabilityRelate(opts) {
|
|
|
137
174
|
|
|
138
175
|
const targetDocLocal = loadTargetDocLocal(opts.targetDatasource);
|
|
139
176
|
const systemKeyForAuth = resolveSystemKeyForAuth(parsed);
|
|
177
|
+
const targetSystemKey = resolveSystemKeyForAuth(targetDocLocal) || systemKeyForAuth;
|
|
140
178
|
const { remoteTargetConfig, remoteFetchMeta } = await maybeFetchRemoteTargetConfig({
|
|
141
179
|
systemKeyForAuth,
|
|
180
|
+
targetSystemKey,
|
|
142
181
|
targetDatasourceKey: opts.targetDatasource
|
|
143
182
|
});
|
|
144
183
|
const semantic = runSemanticValidation({
|
|
@@ -159,33 +198,17 @@ async function runCapabilityRelate(opts) {
|
|
|
159
198
|
semantic.warnings,
|
|
160
199
|
remoteFetchMeta
|
|
161
200
|
);
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
return buildRelateResultEnvelope({
|
|
165
|
-
dryRun: true,
|
|
166
|
-
resolvedPath,
|
|
167
|
-
result,
|
|
168
|
-
backupPath: null,
|
|
169
|
-
validation,
|
|
170
|
-
semanticWarnings: semantic.warnings,
|
|
171
|
-
remoteValidation: remoteFetchMeta
|
|
172
|
-
});
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
const backupPath = writeBackup(resolvedPath, Boolean(opts.noBackup));
|
|
176
|
-
atomicWriteJson(resolvedPath, result.doc);
|
|
177
|
-
|
|
178
|
-
return buildRelateResultEnvelope({
|
|
179
|
-
dryRun: false,
|
|
201
|
+
return persistOrDryRunRelate({
|
|
202
|
+
opts,
|
|
180
203
|
resolvedPath,
|
|
181
204
|
result,
|
|
182
|
-
backupPath,
|
|
183
205
|
validation,
|
|
184
|
-
|
|
185
|
-
|
|
206
|
+
semantic,
|
|
207
|
+
remoteFetchMeta
|
|
186
208
|
});
|
|
187
209
|
}
|
|
188
210
|
|
|
189
211
|
module.exports = {
|
|
190
|
-
runCapabilityRelate
|
|
212
|
+
runCapabilityRelate,
|
|
213
|
+
resolveSystemKeyForAuth
|
|
191
214
|
};
|
|
@@ -56,12 +56,20 @@ function pickIntegrationAppForDatasourceIdentifier(identifier, appNames) {
|
|
|
56
56
|
if (!id) {
|
|
57
57
|
return null;
|
|
58
58
|
}
|
|
59
|
-
const
|
|
60
|
-
if (
|
|
61
|
-
|
|
59
|
+
const prefixMatches = (appNames || []).filter((app) => id === app || id.startsWith(`${app}-`));
|
|
60
|
+
if (prefixMatches.length > 0) {
|
|
61
|
+
prefixMatches.sort((a, b) => b.length - a.length);
|
|
62
|
+
return prefixMatches[0];
|
|
63
|
+
}
|
|
64
|
+
const keyMatches = (appNames || []).filter((app) => appHasDatasourceIdentifier(app, id));
|
|
65
|
+
if (keyMatches.length === 1) {
|
|
66
|
+
return keyMatches[0];
|
|
67
|
+
}
|
|
68
|
+
if (keyMatches.length > 1) {
|
|
69
|
+
keyMatches.sort((a, b) => b.length - a.length);
|
|
70
|
+
return keyMatches[0];
|
|
62
71
|
}
|
|
63
|
-
|
|
64
|
-
return matches[0];
|
|
72
|
+
return null;
|
|
65
73
|
}
|
|
66
74
|
|
|
67
75
|
/**
|
|
@@ -18,52 +18,133 @@ const {
|
|
|
18
18
|
const { resolvePollIntervalFromController } = require('./poll-interval');
|
|
19
19
|
const { createDeployPollHandlers } = require('./deploy-poll-ui');
|
|
20
20
|
|
|
21
|
+
/** Same progress for this many polls + dataplane published → treat deploy as complete. */
|
|
22
|
+
const STUCK_PROGRESS_POLLS = 10;
|
|
23
|
+
|
|
24
|
+
function recordStuckProgress(state, snapshot) {
|
|
25
|
+
const progress = Number(snapshot && snapshot.progress);
|
|
26
|
+
const value = Number.isFinite(progress) ? progress : null;
|
|
27
|
+
if (value === state.lastProgress) {
|
|
28
|
+
state.stuckCount += 1;
|
|
29
|
+
} else {
|
|
30
|
+
state.lastProgress = value;
|
|
31
|
+
state.stuckCount = 1;
|
|
32
|
+
}
|
|
33
|
+
return state;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
async function completeIfDataplanePublished(lastSeen, options) {
|
|
37
|
+
if (!lastSeen || typeof options.isDataplanePublished !== 'function') {
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
try {
|
|
41
|
+
if (await options.isDataplanePublished()) {
|
|
42
|
+
return {
|
|
43
|
+
...lastSeen,
|
|
44
|
+
status: 'completed',
|
|
45
|
+
progress: 100,
|
|
46
|
+
completedViaDataplane: true
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
} catch {
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
|
|
21
55
|
/**
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
* @
|
|
25
|
-
* @param {string} deploymentId
|
|
26
|
-
* @param {string} controllerUrl
|
|
27
|
-
* @param {string} envKey
|
|
28
|
-
* @param {Object} authConfig
|
|
29
|
-
* @param {Object} [options]
|
|
30
|
-
* @returns {Promise<Object>}
|
|
56
|
+
* Poll until the controller job is terminal, or dataplane is already published.
|
|
57
|
+
* @param {Object} ctx
|
|
58
|
+
* @returns {Promise<Object|null>}
|
|
31
59
|
*/
|
|
60
|
+
async function pollUntilTerminal(ctx) {
|
|
61
|
+
const {
|
|
62
|
+
controllerUrl,
|
|
63
|
+
validatedEnvKey,
|
|
64
|
+
deploymentId,
|
|
65
|
+
pipelineAuthConfig,
|
|
66
|
+
interval,
|
|
67
|
+
maxAttempts,
|
|
68
|
+
onPollWait,
|
|
69
|
+
wrappedProgress,
|
|
70
|
+
stuckState,
|
|
71
|
+
options,
|
|
72
|
+
getLastSeen
|
|
73
|
+
} = ctx;
|
|
74
|
+
for (let attempt = 0; attempt < maxAttempts; attempt++) {
|
|
75
|
+
if (onPollWait) {
|
|
76
|
+
onPollWait(attempt, maxAttempts);
|
|
77
|
+
}
|
|
78
|
+
const response = await getPipelineDeployment(
|
|
79
|
+
controllerUrl,
|
|
80
|
+
validatedEnvKey,
|
|
81
|
+
deploymentId,
|
|
82
|
+
pipelineAuthConfig
|
|
83
|
+
);
|
|
84
|
+
const deploymentData = await processDeploymentStatusResponse(
|
|
85
|
+
response,
|
|
86
|
+
attempt,
|
|
87
|
+
maxAttempts,
|
|
88
|
+
interval,
|
|
89
|
+
deploymentId,
|
|
90
|
+
wrappedProgress
|
|
91
|
+
);
|
|
92
|
+
if (deploymentData) {
|
|
93
|
+
return deploymentData;
|
|
94
|
+
}
|
|
95
|
+
if (stuckState.stuckCount >= STUCK_PROGRESS_POLLS) {
|
|
96
|
+
const viaDataplane = await completeIfDataplanePublished(getLastSeen(), options);
|
|
97
|
+
if (viaDataplane) {
|
|
98
|
+
return viaDataplane;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
return null;
|
|
103
|
+
}
|
|
104
|
+
|
|
32
105
|
async function pollDeploymentStatus(deploymentId, controllerUrl, envKey, authConfig, options = {}) {
|
|
33
106
|
const interval = options.interval || 5000;
|
|
34
107
|
const maxAttempts = options.maxAttempts || 300;
|
|
35
|
-
|
|
36
108
|
const validatedEnvKey = validateEnvironmentKey(envKey);
|
|
37
109
|
const pipelineAuthConfig = convertToPipelineAuthConfig(authConfig);
|
|
38
|
-
|
|
39
110
|
const onPollProgress = typeof options.onPollProgress === 'function' ? options.onPollProgress : null;
|
|
40
111
|
const onPollWait = typeof options.onPollWait === 'function' ? options.onPollWait : null;
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
112
|
+
const stuckState = { lastProgress: undefined, stuckCount: 0 };
|
|
113
|
+
let lastSeen = null;
|
|
114
|
+
const wrappedProgress = (data, attempt, max) => {
|
|
115
|
+
lastSeen = data;
|
|
116
|
+
recordStuckProgress(stuckState, data);
|
|
117
|
+
if (onPollProgress) {
|
|
118
|
+
onPollProgress(data, attempt, max);
|
|
119
|
+
}
|
|
120
|
+
};
|
|
121
|
+
try {
|
|
122
|
+
const terminal = await pollUntilTerminal({
|
|
123
|
+
controllerUrl,
|
|
124
|
+
validatedEnvKey,
|
|
125
|
+
deploymentId,
|
|
126
|
+
pipelineAuthConfig,
|
|
127
|
+
interval,
|
|
128
|
+
maxAttempts,
|
|
129
|
+
onPollWait,
|
|
130
|
+
wrappedProgress,
|
|
131
|
+
stuckState,
|
|
132
|
+
options,
|
|
133
|
+
getLastSeen: () => lastSeen
|
|
134
|
+
});
|
|
135
|
+
if (terminal) {
|
|
136
|
+
return terminal;
|
|
137
|
+
}
|
|
138
|
+
} catch (error) {
|
|
139
|
+
if (error.message && error.message.includes('not found')) {
|
|
63
140
|
throw error;
|
|
64
141
|
}
|
|
142
|
+
throw error;
|
|
143
|
+
}
|
|
144
|
+
const timedOutComplete = await completeIfDataplanePublished(lastSeen, options);
|
|
145
|
+
if (timedOutComplete) {
|
|
146
|
+
return timedOutComplete;
|
|
65
147
|
}
|
|
66
|
-
|
|
67
148
|
throw new Error('Deployment timeout: Maximum polling attempts reached');
|
|
68
149
|
}
|
|
69
150
|
|
|
@@ -94,7 +175,8 @@ async function pollDeployment(result, url, validatedEnvKey, authConfig, options)
|
|
|
94
175
|
interval: resolvedInterval,
|
|
95
176
|
maxAttempts,
|
|
96
177
|
onPollProgress: pollUi.onPollProgress,
|
|
97
|
-
onPollWait: pollUi.onPollWait
|
|
178
|
+
onPollWait: pollUi.onPollWait,
|
|
179
|
+
isDataplanePublished: options.isDataplanePublished
|
|
98
180
|
}
|
|
99
181
|
);
|
|
100
182
|
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Treat a stuck controller deploy poll as complete when dataplane is already published.
|
|
3
|
+
*
|
|
4
|
+
* @fileoverview Dataplane published check for external deploy poll
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
'use strict';
|
|
8
|
+
|
|
9
|
+
const { getExternalSystem } = require('../api/external-systems.api');
|
|
10
|
+
const { unwrapApiData } = require('../utils/external-system-readiness-core');
|
|
11
|
+
const { resolveDataplaneUrl } = require('../utils/dataplane-resolver');
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* @param {Object|null|undefined} system
|
|
15
|
+
* @returns {boolean}
|
|
16
|
+
*/
|
|
17
|
+
function isDataplaneSystemPublished(system) {
|
|
18
|
+
const status = String((system && system.status) || '').toLowerCase();
|
|
19
|
+
return status === 'published' || status === 'deployed';
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* @param {string} controllerUrl
|
|
24
|
+
* @param {string} environment
|
|
25
|
+
* @param {Object} authConfig
|
|
26
|
+
* @param {string} systemKey
|
|
27
|
+
* @param {string} [dataplaneUrl]
|
|
28
|
+
* @returns {Promise<boolean>}
|
|
29
|
+
*/
|
|
30
|
+
async function checkDataplaneSystemPublished(
|
|
31
|
+
controllerUrl,
|
|
32
|
+
environment,
|
|
33
|
+
authConfig,
|
|
34
|
+
systemKey,
|
|
35
|
+
dataplaneUrl
|
|
36
|
+
) {
|
|
37
|
+
let url = dataplaneUrl;
|
|
38
|
+
if (!url) {
|
|
39
|
+
url = await resolveDataplaneUrl(controllerUrl, environment, authConfig, { silent: true });
|
|
40
|
+
}
|
|
41
|
+
const getRes = await getExternalSystem(url, systemKey, authConfig);
|
|
42
|
+
return isDataplaneSystemPublished(unwrapApiData(getRes));
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* @param {Object} params
|
|
47
|
+
* @returns {() => Promise<boolean>}
|
|
48
|
+
*/
|
|
49
|
+
function createDataplanePublishedCheck(params) {
|
|
50
|
+
const { controllerUrl, environment, authConfig, systemKey, dataplaneUrl } = params;
|
|
51
|
+
return async function isDataplanePublished() {
|
|
52
|
+
try {
|
|
53
|
+
return await checkDataplaneSystemPublished(
|
|
54
|
+
controllerUrl,
|
|
55
|
+
environment,
|
|
56
|
+
authConfig,
|
|
57
|
+
systemKey,
|
|
58
|
+
dataplaneUrl
|
|
59
|
+
);
|
|
60
|
+
} catch {
|
|
61
|
+
return false;
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
module.exports = {
|
|
67
|
+
isDataplaneSystemPublished,
|
|
68
|
+
checkDataplaneSystemPublished,
|
|
69
|
+
createDataplanePublishedCheck
|
|
70
|
+
};
|