@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.
Files changed (134) hide show
  1. package/jest.isolated-projects.js +25 -2
  2. package/jest.projects.js +4 -1
  3. package/lib/api/datasources-extended.api.js +8 -8
  4. package/lib/api/keycloak.api.js +92 -0
  5. package/lib/api/token-providers.api.js +77 -0
  6. package/lib/api/types/keycloak.types.js +23 -0
  7. package/lib/api/types/token-providers.types.js +65 -0
  8. package/lib/app/down.js +16 -1
  9. package/lib/channels/add-channel.js +126 -19
  10. package/lib/channels/approval-guides/chatgpt.js +1 -1
  11. package/lib/channels/approval-guides/microsoft-copilot.js +16 -12
  12. package/lib/channels/channel-add-ensure.js +128 -0
  13. package/lib/channels/channel-artifact-generator.js +11 -1
  14. package/lib/channels/channel-branding.js +94 -0
  15. package/lib/channels/channel-credential.js +8 -0
  16. package/lib/channels/channel-discovery.js +330 -0
  17. package/lib/channels/channel-external-system.js +1 -1
  18. package/lib/channels/channel-runtime-resolver.js +103 -14
  19. package/lib/channels/channel-status-display.js +7 -1
  20. package/lib/channels/microsoft-copilot-channel-add.js +138 -0
  21. package/lib/channels/microsoft-copilot-entra-sso-form.js +180 -0
  22. package/lib/channels/microsoft-copilot-entra-sso.js +264 -0
  23. package/lib/channels/microsoft-copilot-icons.js +74 -0
  24. package/lib/channels/microsoft-copilot-limits.js +84 -0
  25. package/lib/channels/microsoft-copilot-package.js +130 -49
  26. package/lib/channels/microsoft-copilot-validate.js +197 -0
  27. package/lib/cli/index.js +2 -0
  28. package/lib/cli/infra-guided.js +13 -6
  29. package/lib/cli/setup-infra.js +31 -4
  30. package/lib/commands/channel-add-setup.js +28 -9
  31. package/lib/commands/channel.js +14 -10
  32. package/lib/commands/integration-definition.js +158 -0
  33. package/lib/commands/repair-auth-apply.js +19 -5
  34. package/lib/commands/repair-datasource-sync-operations.js +4 -1
  35. package/lib/commands/role-assistant-test-output.js +200 -0
  36. package/lib/commands/role-assistant.js +125 -166
  37. package/lib/datasource/capability/capability-resolve.js +15 -5
  38. package/lib/datasource/capability/copy-operations.js +14 -4
  39. package/lib/infrastructure/index.js +19 -4
  40. package/lib/integration-definition/apply.js +478 -0
  41. package/lib/integration-definition/canonical-json.js +51 -0
  42. package/lib/integration-definition/emit-suite-bid.js +94 -0
  43. package/lib/integration-definition/enrich-crm-field-mappings.js +240 -0
  44. package/lib/integration-definition/enrich-openapi-operations.js +434 -0
  45. package/lib/integration-definition/from-discovery.js +339 -0
  46. package/lib/integration-definition/hash.js +37 -0
  47. package/lib/integration-definition/index.js +37 -0
  48. package/lib/integration-definition/openapi-enrichment/hubspot-crm-companies.json +205 -0
  49. package/lib/integration-definition/resource-type-catalog.js +59 -0
  50. package/lib/integration-definition/system-kind-enum.js +40 -0
  51. package/lib/integration-definition/validate.js +148 -0
  52. package/lib/programmatic/builder-api-request.js +5 -1
  53. package/lib/programmatic/builder-help-governance.js +3 -2
  54. package/lib/programmatic/capability-registry.js +19 -0
  55. package/lib/programmatic/openapi-component-schemas.js +6 -1
  56. package/lib/programmatic/openapi-descriptions.js +40 -0
  57. package/lib/programmatic/openapi-envelope-examples.js +269 -0
  58. package/lib/programmatic/openapi-operation-bindings.js +45 -0
  59. package/lib/programmatic/openapi-request-schemas.js +14 -0
  60. package/lib/programmatic/openapi-result-schemas-overflow.js +101 -0
  61. package/lib/programmatic/openapi-result-schemas.js +46 -99
  62. package/lib/programmatic/openapi-schema-descriptions-core.js +7 -4
  63. package/lib/programmatic/openapi-schema-descriptions-help.js +1 -1
  64. package/lib/programmatic/openapi-schema-descriptions-operations.js +24 -0
  65. package/lib/programmatic/openapi-schema-helpers.js +9 -9
  66. package/lib/programmatic/openapi-spec.js +11 -1
  67. package/lib/programmatic/orchestration-openapi-meta.js +10 -0
  68. package/lib/programmatic/route-handler-map.js +29 -1
  69. package/lib/programmatic/run-channel-package.js +236 -0
  70. package/lib/programmatic/run-integration-definition.js +69 -0
  71. package/lib/role-assistant/knowledge-sync-logging.js +58 -0
  72. package/lib/role-assistant/knowledge-sync.js +88 -93
  73. package/lib/role-assistant/test-cases-bid.js +155 -0
  74. package/lib/role-assistant/test-cases-evaluate.js +9 -1
  75. package/lib/role-assistant/test-cases-honesty.js +24 -10
  76. package/lib/role-assistant/test-cases.js +28 -0
  77. package/lib/role-assistant/test-runner-bid-apply.js +122 -0
  78. package/lib/role-assistant/test-runner-bid-document.js +278 -0
  79. package/lib/role-assistant/test-runner-workhub-answers.js +245 -20
  80. package/lib/role-assistant/test-runner-workhub.js +100 -18
  81. package/lib/role-assistant/test-suite.js +119 -0
  82. package/lib/schema/application-schema.json +56 -6
  83. package/lib/schema/external-datasource.schema.json +103 -17
  84. package/lib/schema/external-system.schema.json +54 -3
  85. package/lib/schema/integration-definition.schema.json +195 -0
  86. package/lib/utils/authentication-shape.js +6 -0
  87. package/lib/utils/docker-build.js +3 -3
  88. package/lib/utils/help-builder.js +1 -0
  89. package/lib/utils/promote-dev-docker-image.js +97 -18
  90. package/lib/utils/transient-developer-id.js +28 -0
  91. package/package.json +1 -1
  92. package/scripts/pnpm/af.mjs +24 -9
  93. package/scripts/pnpm/help.mjs +1 -1
  94. package/templates/applications/dataplane/application.yaml +3 -2
  95. package/templates/applications/dataplane/env.template +7 -6
  96. package/templates/applications/miso-controller/application.yaml +23 -9
  97. package/templates/applications/miso-controller/env.template +8 -2
  98. package/templates/channels/README.md +11 -1
  99. package/templates/channels/brand/color-192.png +0 -0
  100. package/templates/channels/brand/dark/128.png +0 -0
  101. package/templates/channels/brand/dark/192.png +0 -0
  102. package/templates/channels/brand/dark/256.png +0 -0
  103. package/templates/channels/brand/dark/32.png +0 -0
  104. package/templates/channels/brand/dark/512.png +0 -0
  105. package/templates/channels/brand/dark/64.png +0 -0
  106. package/templates/channels/brand/light/128.png +0 -0
  107. package/templates/channels/brand/light/192.png +0 -0
  108. package/templates/channels/brand/light/256.png +0 -0
  109. package/templates/channels/brand/light/32.png +0 -0
  110. package/templates/channels/brand/light/512.png +0 -0
  111. package/templates/channels/brand/light/64.png +0 -0
  112. package/templates/channels/brand/mono-white/128.png +0 -0
  113. package/templates/channels/brand/mono-white/192.png +0 -0
  114. package/templates/channels/brand/mono-white/256.png +0 -0
  115. package/templates/channels/brand/mono-white/32.png +0 -0
  116. package/templates/channels/brand/mono-white/512.png +0 -0
  117. package/templates/channels/brand/mono-white/64.png +0 -0
  118. package/templates/channels/brand/outline-32.png +0 -0
  119. package/templates/channels/chatgpt/install.template.md.hbs +6 -4
  120. package/templates/channels/chatgpt/integration/README.md +4 -5
  121. package/templates/channels/chatgpt/integration/chatgpt-system.json +1 -16
  122. package/templates/channels/microsoft-copilot/ai-plugin.template.json.hbs +10 -16
  123. package/templates/channels/microsoft-copilot/declarative-agent.template.json.hbs +5 -3
  124. package/templates/channels/microsoft-copilot/entra-sso-registration.template.md.hbs +27 -0
  125. package/templates/channels/microsoft-copilot/install.template.md.hbs +39 -20
  126. package/templates/channels/microsoft-copilot/integration/README.md +2 -3
  127. package/templates/channels/microsoft-copilot/integration/microsoft-copilot-system.json +1 -22
  128. package/templates/channels/microsoft-copilot/manifest.template.json.hbs +7 -7
  129. package/templates/channels/microsoft-copilot/oauth-plugin-vault-required.template.md.hbs +11 -0
  130. package/templates/channels/slack/integration/README.md +2 -4
  131. package/templates/channels/teams-bot/install.template.md.hbs +4 -0
  132. package/templates/channels/teams-bot/integration/README.md +3 -5
  133. package/templates/channels/web-embed/integration/web-embed-system.json +1 -16
  134. /package/.cursor/plans/{Done → Archive}/157.1-security-quality-violations-by-type.csv +0 -0
@@ -0,0 +1,155 @@
1
+ /**
2
+ * Evaluate expected.bid assertions for suite 002 hard GP.
3
+ * @fileoverview BID emission / apply expectation checks
4
+ */
5
+
6
+ 'use strict';
7
+
8
+ function asArray(value) {
9
+ return Array.isArray(value) ? value : [];
10
+ }
11
+
12
+ function softWrap(assertion, soft) {
13
+ if (!assertion || assertion.passed || !soft) {
14
+ return assertion;
15
+ }
16
+ return {
17
+ ...assertion,
18
+ passed: true,
19
+ soft: true,
20
+ warn: true,
21
+ message: `${assertion.message} (soft — warning only)`
22
+ };
23
+ }
24
+
25
+ function findInteraction(interactions, type) {
26
+ return asArray(interactions).find((row) => row && row.type === type) || null;
27
+ }
28
+
29
+ function pushBidAssertion(assertions, soft, id, passed, okMsg, failMsg) {
30
+ assertions.push(
31
+ softWrap(
32
+ {
33
+ id,
34
+ passed,
35
+ message: passed ? okMsg : failMsg
36
+ },
37
+ soft
38
+ )
39
+ );
40
+ }
41
+
42
+ function hasEmitOrApply(observed) {
43
+ return Boolean(observed.emitRow || observed.applyRow || observed.emitted);
44
+ }
45
+
46
+ function collectBidSchemaAssertions(expectedBid, observed, assertions, soft) {
47
+ const { emitRow, applyRow, emitted } = observed;
48
+ if (expectedBid.schemaName) {
49
+ const actualSchema = (emitted && emitted.schemaName) || expectedBid.schemaName;
50
+ pushBidAssertion(
51
+ assertions,
52
+ soft,
53
+ 'expected.bid.schemaName',
54
+ hasEmitOrApply(observed) && actualSchema === expectedBid.schemaName,
55
+ `BID schemaName observed (${expectedBid.schemaName}).`,
56
+ `BID schemaName "${expectedBid.schemaName}" not observed in suite emit/apply.`
57
+ );
58
+ }
59
+ if (expectedBid.schemaVersion) {
60
+ const version = emitted && emitted.schemaVersion;
61
+ const versionOk =
62
+ version === null || version === undefined || version === expectedBid.schemaVersion;
63
+ pushBidAssertion(
64
+ assertions,
65
+ soft,
66
+ 'expected.bid.schemaVersion',
67
+ hasEmitOrApply({ emitRow, applyRow, emitted }) && versionOk,
68
+ `BID schemaVersion "${expectedBid.schemaVersion}" accepted.`,
69
+ `BID schemaVersion "${expectedBid.schemaVersion}" not observed.`
70
+ );
71
+ }
72
+ }
73
+
74
+ function assertDefinitionHash(expectedBid, observed, assertions, soft) {
75
+ if (expectedBid.requireDefinitionHash !== true) {
76
+ return;
77
+ }
78
+ const { emitRow, applyRow, emitted } = observed;
79
+ const hash =
80
+ (emitted && emitted.definitionHash) ||
81
+ (emitRow && emitRow.definitionHash) ||
82
+ (applyRow && applyRow.definitionHash) ||
83
+ '';
84
+ pushBidAssertion(
85
+ assertions,
86
+ soft,
87
+ 'expected.bid.requireDefinitionHash',
88
+ typeof hash === 'string' && hash.startsWith('sha256:'),
89
+ `BID definitionHash present (${String(hash).slice(0, 18)}…).`,
90
+ 'BID definitionHash (sha256:…) not observed on emit/apply.'
91
+ );
92
+ }
93
+
94
+ function assertSuiteRunId(expectedBid, observed, assertions, soft) {
95
+ if (expectedBid.requireSuiteRunId !== true) {
96
+ return;
97
+ }
98
+ const { emitRow, applyRow, emitted } = observed;
99
+ const suiteRunId =
100
+ (emitted && emitted.meta && emitted.meta.suiteRunId) ||
101
+ (emitRow && emitRow.suiteRunId) ||
102
+ (applyRow && applyRow.suiteRunId) ||
103
+ '';
104
+ pushBidAssertion(
105
+ assertions,
106
+ soft,
107
+ 'expected.bid.requireSuiteRunId',
108
+ Boolean(String(suiteRunId).trim()),
109
+ `BID suiteRunId present (${suiteRunId}).`,
110
+ 'BID meta.suiteRunId not observed on emit/apply.'
111
+ );
112
+ }
113
+
114
+ function collectBidProvenanceAssertions(expectedBid, observed, assertions, soft) {
115
+ assertDefinitionHash(expectedBid, observed, assertions, soft);
116
+ assertSuiteRunId(expectedBid, observed, assertions, soft);
117
+ }
118
+
119
+ function collectBidPathAssertions(expectedBid, observed, assertions, soft) {
120
+ if (expectedBid.forbidGoldPaths === true) {
121
+ const { emitRow, applyRow, emitted } = observed;
122
+ const blob = JSON.stringify(emitted || emitRow || applyRow || {});
123
+ const banned = /test-e2e-hubspot|knownPlatform|gold-package/i.test(blob);
124
+ pushBidAssertion(
125
+ assertions,
126
+ soft,
127
+ 'expected.bid.forbidGoldPaths',
128
+ !banned,
129
+ 'BID trail has no gold/knownPlatform generation paths.',
130
+ 'BID trail references forbidden gold/knownPlatform generation paths.'
131
+ );
132
+ }
133
+ }
134
+
135
+ function collectBidAssertions(expectedBid, observation, options = {}) {
136
+ if (!expectedBid || typeof expectedBid !== 'object') {
137
+ return [];
138
+ }
139
+ const interactions = asArray(observation.interactions || observation.workInteractions);
140
+ const observed = {
141
+ emitRow: findInteraction(interactions, 'bid-emit'),
142
+ applyRow: findInteraction(interactions, 'bid-apply'),
143
+ emitted: observation.emittedBid || null
144
+ };
145
+ const assertions = [];
146
+ const soft = options.soft === true;
147
+ collectBidSchemaAssertions(expectedBid, observed, assertions, soft);
148
+ collectBidProvenanceAssertions(expectedBid, observed, assertions, soft);
149
+ collectBidPathAssertions(expectedBid, observed, assertions, soft);
150
+ return assertions;
151
+ }
152
+
153
+ module.exports = {
154
+ collectBidAssertions
155
+ };
@@ -9,6 +9,7 @@ const {
9
9
  buildHonestyObservation,
10
10
  evaluateHonestyExpectations
11
11
  } = require('./test-cases-honesty');
12
+ const { collectBidAssertions } = require('./test-cases-bid');
12
13
 
13
14
  function getValueAtPath(obj, dottedPath) {
14
15
  const parts = String(dottedPath || '').split('.').filter(Boolean);
@@ -109,7 +110,13 @@ function buildCaseObservation(result, lifecycleVerdict, portalUrl) {
109
110
  processExecutionId: result?.processExecutionId || null,
110
111
  workResult,
111
112
  workResultError: result?.workResultError || null,
112
- honesty: buildHonestyObservation(workResult)
113
+ honesty: buildHonestyObservation(workResult),
114
+ interactions: Array.isArray(result?.interactions)
115
+ ? result.interactions
116
+ : Array.isArray(result?.workInteractions)
117
+ ? result.workInteractions
118
+ : [],
119
+ emittedBid: result?.emittedBid || null
113
120
  };
114
121
  }
115
122
 
@@ -146,6 +153,7 @@ function collectCaseAssertions(expected, observation) {
146
153
  assertions.push(...evaluateFactExpectations(expected.facts, observation));
147
154
  assertions.push(...evaluateProhibitedExpectations(expected.prohibited, observation));
148
155
  assertions.push(...evaluateHonestyExpectations(expected.honesty, observation, { soft }));
156
+ assertions.push(...collectBidAssertions(expected.bid, observation, { soft }));
149
157
  return assertions;
150
158
  }
151
159
 
@@ -68,7 +68,18 @@ function buildHonestyObservation(workResult) {
68
68
  };
69
69
  }
70
70
 
71
- function pushHonestyAssertion(assertions, id, passed, message) {
71
+ function pushHonestyAssertion(assertions, id, passed, message, options = {}) {
72
+ const soft = options.soft === true;
73
+ if (soft && !passed) {
74
+ assertions.push({
75
+ id,
76
+ passed: true,
77
+ soft: true,
78
+ warn: true,
79
+ message: `${message} (soft — warning only)`
80
+ });
81
+ return;
82
+ }
72
83
  assertions.push({ id, passed, message });
73
84
  }
74
85
 
@@ -87,7 +98,7 @@ function assertWorkResultPresent(assertions, honesty, observation) {
87
98
  );
88
99
  }
89
100
 
90
- function assertSchemaVersion(assertions, honestySpec, honesty) {
101
+ function assertSchemaVersion(assertions, honestySpec, honesty, soft) {
91
102
  if (!honestySpec.schemaVersion) {
92
103
  return;
93
104
  }
@@ -99,11 +110,12 @@ function assertSchemaVersion(assertions, honestySpec, honesty) {
99
110
  passed,
100
111
  passed
101
112
  ? `schemaVersion matched "${expected}".`
102
- : `schemaVersion expected "${expected}" but got ${JSON.stringify(honesty.schemaVersion)}.`
113
+ : `schemaVersion expected "${expected}" but got ${JSON.stringify(honesty.schemaVersion)}.`,
114
+ { soft }
103
115
  );
104
116
  }
105
117
 
106
- function assertCompletion(assertions, honestySpec, honesty) {
118
+ function assertCompletion(assertions, honestySpec, honesty, soft) {
107
119
  if (honestySpec.requireCompletion === false) {
108
120
  return;
109
121
  }
@@ -115,11 +127,12 @@ function assertCompletion(assertions, honestySpec, honesty) {
115
127
  stateOk && goalOk,
116
128
  stateOk && goalOk
117
129
  ? `completion.state/goalStatus honest (${honesty.completionState}/${honesty.goalStatus}).`
118
- : `completion missing or invalid: state=${JSON.stringify(honesty.completionState)} goalStatus=${JSON.stringify(honesty.goalStatus)}.`
130
+ : `completion missing or invalid: state=${JSON.stringify(honesty.completionState)} goalStatus=${JSON.stringify(honesty.goalStatus)}.`,
131
+ { soft }
119
132
  );
120
133
  }
121
134
 
122
- function assertContribution(assertions, honestySpec, honesty) {
135
+ function assertContribution(assertions, honestySpec, honesty, soft) {
123
136
  if (honestySpec.requireContributionObservation === false) {
124
137
  return;
125
138
  }
@@ -130,7 +143,8 @@ function assertContribution(assertions, honestySpec, honesty) {
130
143
  ok,
131
144
  ok
132
145
  ? `contributionObservation.status honest (${honesty.contributionStatus}).`
133
- : `contributionObservation.status missing or invalid: ${JSON.stringify(honesty.contributionStatus)}.`
146
+ : `contributionObservation.status missing or invalid: ${JSON.stringify(honesty.contributionStatus)}.`,
147
+ { soft }
134
148
  );
135
149
  }
136
150
 
@@ -171,9 +185,9 @@ function evaluateHonestyExpectations(honestySpec, observation, options = {}) {
171
185
  // Missing Output is always a hard failure when required — soft does not waive it.
172
186
  return assertions;
173
187
  }
174
- assertSchemaVersion(assertions, honestySpec, honesty);
175
- assertCompletion(assertions, honestySpec, honesty);
176
- assertContribution(assertions, honestySpec, honesty);
188
+ assertSchemaVersion(assertions, honestySpec, honesty, soft);
189
+ assertCompletion(assertions, honestySpec, honesty, soft);
190
+ assertContribution(assertions, honestySpec, honesty, soft);
177
191
  assertMinResourceChanges(assertions, honestySpec, honesty, soft);
178
192
  return assertions;
179
193
  }
@@ -331,6 +331,32 @@ function collectCapabilitiesFromWorkSteps(out, steps) {
331
331
  }
332
332
  }
333
333
 
334
+ /**
335
+ * Work-result artifactIndex often carries CIP fingerprints like
336
+ * `capabilityKey=customer.createBasic|operationId=createbasic` when steps[] is empty.
337
+ * @param {Set<string>} out
338
+ * @param {unknown} artifacts
339
+ */
340
+ function collectCapabilitiesFromArtifactIndex(out, artifacts) {
341
+ if (!Array.isArray(artifacts)) return;
342
+ for (const row of artifacts) {
343
+ if (!row || typeof row !== 'object') continue;
344
+ addCapabilityToken(out, row.capabilityKey);
345
+ addCapabilityToken(out, row.capability);
346
+ const fingerprint = typeof row.fingerprint === 'string' ? row.fingerprint : '';
347
+ const match = fingerprint.match(/(?:^|[|,])capabilityKey=([^|,]+)/);
348
+ if (match) {
349
+ addCapabilityToken(out, match[1]);
350
+ }
351
+ const artifactRef = typeof row.artifactRef === 'string' ? row.artifactRef : '';
352
+ // cip-log:fallback:customer.create:create:<id>
353
+ const refMatch = artifactRef.match(/^cip-log:[^:]+:([^:]+):/);
354
+ if (refMatch) {
355
+ addCapabilityToken(out, refMatch[1]);
356
+ }
357
+ }
358
+ }
359
+
334
360
  function collectObservedCapabilities(result) {
335
361
  const out = new Set();
336
362
  const steps = Array.isArray(result?.stepsRun) ? result.stepsRun : [];
@@ -341,6 +367,8 @@ function collectObservedCapabilities(result) {
341
367
  }
342
368
  collectCapabilitiesFromWorkSteps(out, result?.steps);
343
369
  collectCapabilitiesFromWorkSteps(out, result?.workResult?.steps);
370
+ collectCapabilitiesFromArtifactIndex(out, result?.workResult?.artifactIndex);
371
+ collectCapabilitiesFromArtifactIndex(out, result?.artifactIndex);
344
372
  addCapabilityToken(out, result?.task?.capabilityKey);
345
373
  addCapabilityToken(out, result?.task?.capability);
346
374
  if (result?.task?.approvalContext && typeof result.task.approvalContext === 'object') {
@@ -0,0 +1,122 @@
1
+ /**
2
+ * Soft-loop BID apply after workspace.createsystem (suite 002 / plan 512.4).
3
+ * @fileoverview Apply input.bidPath into builder-api workspace when Runtime COM
4
+ * cannot yet invoke template.applyintegrationdefinition (RA facade not refreshed).
5
+ */
6
+
7
+ 'use strict';
8
+
9
+ const fs = require('../internal/fs-real-sync');
10
+ const path = require('path');
11
+ const {
12
+ loadCaseBidDocument,
13
+ resolveTargetSystemKey
14
+ } = require('./test-runner-bid-document');
15
+
16
+ /**
17
+ * @returns {string}
18
+ */
19
+ function resolveBuilderApiBaseUrl() {
20
+ const fromEnv = String(process.env.BUILDER_API_URL || process.env.BUILDER_URL || '').trim();
21
+ if (fromEnv) {
22
+ return fromEnv.replace(/\/$/, '');
23
+ }
24
+ let developerId = 0;
25
+ try {
26
+ const yaml = require('js-yaml');
27
+ const cfgPath = path.join(
28
+ process.env.HOME || '',
29
+ '.aifabrix',
30
+ 'config.yaml'
31
+ );
32
+ if (fs.existsSync(cfgPath)) {
33
+ const cfg = yaml.load(fs.readFileSync(cfgPath, 'utf8')) || {};
34
+ developerId = Number(cfg['developer-id'] || cfg.developerId || 0) || 0;
35
+ }
36
+ } catch (_err) {
37
+ developerId = 0;
38
+ }
39
+ return `http://localhost:${3005 + developerId * 100}`;
40
+ }
41
+
42
+ /**
43
+ * @param {Object} authConfig
44
+ * @returns {string}
45
+ */
46
+ function extractBearer(authConfig) {
47
+ if (!authConfig || typeof authConfig !== 'object') {
48
+ return '';
49
+ }
50
+ if (typeof authConfig.token === 'string' && authConfig.token.trim()) {
51
+ return authConfig.token.trim();
52
+ }
53
+ if (typeof authConfig.bearerToken === 'string' && authConfig.bearerToken.trim()) {
54
+ return authConfig.bearerToken.trim();
55
+ }
56
+ const header = authConfig.headers && authConfig.headers.Authorization;
57
+ const m = /^Bearer\s+(.+)$/i.exec(String(header || ''));
58
+ return m ? m[1].trim() : '';
59
+ }
60
+
61
+ /**
62
+ * @param {Object} testCase
63
+ * @param {Object} wait
64
+ * @returns {boolean}
65
+ */
66
+ function shouldApplyBidAfterWait(testCase, wait) {
67
+ const bid = loadCaseBidDocument(testCase);
68
+ if (!bid) {
69
+ return false;
70
+ }
71
+ const capability = String(
72
+ wait.questionKey || wait.objectRef?.capabilityKey || wait.prompt || ''
73
+ ).toLowerCase();
74
+ return (
75
+ capability.includes('createsystem') ||
76
+ capability.includes('applyintegrationdefinition')
77
+ );
78
+ }
79
+
80
+ /**
81
+ * @param {Object} authConfig
82
+ * @param {Object} testCase
83
+ * @returns {Promise<Object|null>}
84
+ */
85
+ async function applyCaseBidViaBuilderApi(authConfig, testCase) {
86
+ const bid = loadCaseBidDocument(testCase);
87
+ const systemKey = resolveTargetSystemKey(testCase);
88
+ const token = extractBearer(authConfig);
89
+ if (!bid || !systemKey || !token) {
90
+ return null;
91
+ }
92
+ const url = `${resolveBuilderApiBaseUrl()}/api/v1/builder/systems/${encodeURIComponent(systemKey)}/integration-definition/apply`;
93
+ const res = await fetch(url, {
94
+ method: 'POST',
95
+ headers: {
96
+ Authorization: `Bearer ${token}`,
97
+ 'Content-Type': 'application/json'
98
+ },
99
+ body: JSON.stringify({ integrationDefinition: bid, force: true })
100
+ });
101
+ const text = await res.text();
102
+ let body = null;
103
+ try {
104
+ body = text ? JSON.parse(text) : null;
105
+ } catch (_err) {
106
+ body = { raw: text };
107
+ }
108
+ if (!res.ok) {
109
+ const err = new Error(
110
+ `BID apply failed HTTP ${res.status}: ${typeof body === 'object' ? JSON.stringify(body).slice(0, 300) : text}`
111
+ );
112
+ err.status = res.status;
113
+ throw err;
114
+ }
115
+ return body && body.data ? body.data : body;
116
+ }
117
+
118
+ module.exports = {
119
+ resolveBuilderApiBaseUrl,
120
+ shouldApplyBidAfterWait,
121
+ applyCaseBidViaBuilderApi
122
+ };