@aifabrix/builder 2.51.1 → 2.52.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/jest.isolated-projects.js +25 -2
- package/jest.projects.js +4 -1
- package/lib/api/datasources-extended.api.js +8 -8
- package/lib/api/keycloak.api.js +92 -0
- package/lib/api/token-providers.api.js +77 -0
- package/lib/api/types/keycloak.types.js +23 -0
- package/lib/api/types/token-providers.types.js +65 -0
- package/lib/app/down.js +16 -1
- package/lib/app/run-container-start.js +4 -0
- package/lib/app/run-deploy-job.js +135 -27
- package/lib/app/run-env-compose.js +32 -0
- package/lib/app/run-env-recovery.js +35 -0
- package/lib/app/run-helpers.js +5 -6
- package/lib/app/run.js +11 -3
- package/lib/channels/add-channel.js +126 -19
- package/lib/channels/approval-guides/chatgpt.js +12 -9
- package/lib/channels/approval-guides/microsoft-copilot.js +16 -12
- package/lib/channels/channel-add-ensure.js +128 -0
- package/lib/channels/channel-artifact-generator.js +11 -1
- package/lib/channels/channel-branding.js +94 -0
- package/lib/channels/channel-credential.js +8 -0
- package/lib/channels/channel-discovery.js +330 -0
- package/lib/channels/channel-external-system.js +1 -1
- package/lib/channels/channel-runtime-resolver.js +103 -14
- package/lib/channels/channel-status-display.js +7 -1
- package/lib/channels/channel-targets.js +18 -1
- package/lib/channels/chatgpt-runtime-contract.js +1 -1
- package/lib/channels/microsoft-copilot-channel-add.js +138 -0
- package/lib/channels/microsoft-copilot-entra-sso-form.js +180 -0
- package/lib/channels/microsoft-copilot-entra-sso.js +264 -0
- package/lib/channels/microsoft-copilot-icons.js +74 -0
- package/lib/channels/microsoft-copilot-limits.js +84 -0
- package/lib/channels/microsoft-copilot-package.js +130 -49
- package/lib/channels/microsoft-copilot-validate.js +197 -0
- package/lib/cli/index.js +2 -0
- package/lib/cli/infra-guided-footers.js +1 -1
- package/lib/cli/infra-guided-public-url-warn.js +55 -0
- package/lib/cli/infra-guided.js +78 -62
- package/lib/cli/setup-app.help.js +4 -0
- package/lib/cli/setup-app.test-commands.js +59 -28
- package/lib/cli/setup-external-system.js +50 -12
- package/lib/cli/setup-infra.js +31 -4
- package/lib/commands/channel-add-setup.js +35 -10
- package/lib/commands/channel.js +27 -12
- package/lib/commands/deploy-gate-orchestrator.js +3 -2
- package/lib/commands/governance-verify-external.js +2 -5
- package/lib/commands/integration-definition.js +158 -0
- package/lib/commands/lifecycle-external.js +14 -7
- package/lib/commands/repair-auth-apply.js +19 -5
- package/lib/commands/repair-datasource-sync-operations.js +4 -1
- package/lib/commands/role-assistant-test-output.js +200 -0
- package/lib/commands/role-assistant.js +131 -114
- package/lib/commands/test-governance-external.js +2 -5
- package/lib/commands/up-miso.js +61 -24
- package/lib/commands/upload-data-sync-finalize.js +20 -3
- package/lib/commands/verify-governance-external.js +2 -5
- package/lib/commands/verify-operations-external.js +2 -5
- package/lib/commands/verify-operations-step-plan.js +31 -0
- package/lib/commands/verify-trust-command-action.js +2 -5
- package/lib/datasource/capability/capability-resolve.js +15 -5
- package/lib/datasource/capability/copy-operations.js +14 -4
- package/lib/datasource/datasource-identifier.js +4 -3
- package/lib/datasource/integration-context.js +3 -3
- package/lib/datasource/resolve-app.js +90 -8
- package/lib/datasource/test-e2e.js +2 -2
- package/lib/datasource/unified-validation-run-body.js +15 -1
- package/lib/datasource/unified-validation-run-resolve.js +2 -2
- package/lib/external-system/deploy-helpers.js +9 -5
- package/lib/external-system/deploy.js +2 -1
- package/lib/external-system/test-system-level.js +21 -3
- package/lib/external-system/test.js +4 -4
- package/lib/generator/builders.js +9 -15
- package/lib/infrastructure/index.js +19 -4
- package/lib/integration-definition/apply.js +478 -0
- package/lib/integration-definition/canonical-json.js +51 -0
- package/lib/integration-definition/emit-suite-bid.js +94 -0
- package/lib/integration-definition/enrich-crm-field-mappings.js +240 -0
- package/lib/integration-definition/enrich-openapi-operations.js +434 -0
- package/lib/integration-definition/from-discovery.js +339 -0
- package/lib/integration-definition/hash.js +37 -0
- package/lib/integration-definition/index.js +37 -0
- package/lib/integration-definition/openapi-enrichment/hubspot-crm-companies.json +205 -0
- package/lib/integration-definition/resource-type-catalog.js +59 -0
- package/lib/integration-definition/system-kind-enum.js +40 -0
- package/lib/integration-definition/validate.js +148 -0
- package/lib/programmatic/builder-api-request.js +5 -1
- package/lib/programmatic/builder-help-governance.js +3 -2
- package/lib/programmatic/capability-registry.js +19 -0
- package/lib/programmatic/openapi-component-schemas.js +6 -1
- package/lib/programmatic/openapi-descriptions.js +40 -0
- package/lib/programmatic/openapi-envelope-examples.js +269 -0
- package/lib/programmatic/openapi-operation-bindings.js +45 -0
- package/lib/programmatic/openapi-request-schemas.js +14 -0
- package/lib/programmatic/openapi-result-schemas-overflow.js +101 -0
- package/lib/programmatic/openapi-result-schemas.js +46 -99
- package/lib/programmatic/openapi-schema-descriptions-core.js +7 -4
- package/lib/programmatic/openapi-schema-descriptions-help.js +1 -1
- package/lib/programmatic/openapi-schema-descriptions-operations.js +24 -0
- package/lib/programmatic/openapi-schema-helpers.js +9 -9
- package/lib/programmatic/openapi-spec.js +11 -1
- package/lib/programmatic/orchestration-openapi-meta.js +10 -0
- package/lib/programmatic/route-handler-map.js +29 -1
- package/lib/programmatic/run-channel-package.js +236 -0
- package/lib/programmatic/run-integration-definition.js +69 -0
- package/lib/role-assistant/knowledge-markdown-frontmatter.js +75 -0
- package/lib/role-assistant/knowledge-sync-logging.js +58 -0
- package/lib/role-assistant/knowledge-sync.js +104 -89
- package/lib/role-assistant/test-cases-bid.js +155 -0
- package/lib/role-assistant/test-cases-evaluate.js +9 -1
- package/lib/role-assistant/test-cases-honesty.js +24 -10
- package/lib/role-assistant/test-cases.js +28 -0
- package/lib/role-assistant/test-runner-bid-apply.js +122 -0
- package/lib/role-assistant/test-runner-bid-document.js +278 -0
- package/lib/role-assistant/test-runner-workhub-answers.js +289 -46
- package/lib/role-assistant/test-runner-workhub.js +121 -25
- package/lib/role-assistant/test-suite.js +119 -0
- package/lib/schema/application-schema.json +64 -32
- package/lib/schema/external-datasource.schema.json +103 -17
- package/lib/schema/external-system.schema.json +54 -3
- package/lib/schema/infra.parameter.yaml +0 -20
- package/lib/schema/integration-definition.schema.json +195 -0
- package/lib/utils/authentication-shape.js +6 -0
- package/lib/utils/certify-external-workspace.js +40 -0
- package/lib/utils/docker-build.js +3 -3
- package/lib/utils/docker-run-ephemeral.js +77 -17
- package/lib/utils/health-check-public-warn.js +31 -13
- package/lib/utils/health-check.js +11 -1
- package/lib/utils/help-builder.js +1 -0
- package/lib/utils/paths.js +13 -15
- package/lib/utils/promote-dev-docker-image.js +97 -18
- package/lib/utils/role-assistant-paths.js +76 -14
- package/lib/utils/transient-developer-id.js +28 -0
- package/lib/utils/upload-sync-options.js +7 -4
- package/lib/validation/reserved-system-key-prefixes.js +56 -0
- package/lib/validation/validate.js +54 -19
- package/package.json +1 -1
- package/scripts/pnpm/af.mjs +24 -9
- package/scripts/pnpm/help.mjs +1 -1
- package/templates/applications/builder-api/application.yaml +3 -3
- package/templates/applications/dataplane/application.yaml +5 -5
- package/templates/applications/dataplane/env.template +8 -7
- package/templates/applications/dataplane/rbac.yaml +10 -10
- package/templates/applications/keycloak/application.yaml +2 -2
- package/templates/applications/keycloak/env.template +5 -2
- package/templates/applications/miso-controller/application.yaml +25 -11
- package/templates/applications/miso-controller/env.template +24 -5
- package/templates/channels/README.md +11 -1
- package/templates/channels/brand/color-192.png +0 -0
- package/templates/channels/brand/dark/128.png +0 -0
- package/templates/channels/brand/dark/192.png +0 -0
- package/templates/channels/brand/dark/256.png +0 -0
- package/templates/channels/brand/dark/32.png +0 -0
- package/templates/channels/brand/dark/512.png +0 -0
- package/templates/channels/brand/dark/64.png +0 -0
- package/templates/channels/brand/light/128.png +0 -0
- package/templates/channels/brand/light/192.png +0 -0
- package/templates/channels/brand/light/256.png +0 -0
- package/templates/channels/brand/light/32.png +0 -0
- package/templates/channels/brand/light/512.png +0 -0
- package/templates/channels/brand/light/64.png +0 -0
- package/templates/channels/brand/mono-white/128.png +0 -0
- package/templates/channels/brand/mono-white/192.png +0 -0
- package/templates/channels/brand/mono-white/256.png +0 -0
- package/templates/channels/brand/mono-white/32.png +0 -0
- package/templates/channels/brand/mono-white/512.png +0 -0
- package/templates/channels/brand/mono-white/64.png +0 -0
- package/templates/channels/brand/outline-32.png +0 -0
- package/templates/channels/chatgpt/install.template.md.hbs +76 -36
- package/templates/channels/chatgpt/integration/README.md +4 -4
- package/templates/channels/chatgpt/integration/chatgpt-system.json +1 -16
- package/templates/channels/chatgpt/manifest.template.json.hbs +3 -3
- package/templates/channels/microsoft-copilot/ai-plugin.template.json.hbs +10 -16
- package/templates/channels/microsoft-copilot/declarative-agent.template.json.hbs +5 -3
- package/templates/channels/microsoft-copilot/entra-sso-registration.template.md.hbs +27 -0
- package/templates/channels/microsoft-copilot/install.template.md.hbs +39 -20
- package/templates/channels/microsoft-copilot/integration/README.md +2 -3
- package/templates/channels/microsoft-copilot/integration/microsoft-copilot-system.json +1 -22
- package/templates/channels/microsoft-copilot/manifest.template.json.hbs +7 -7
- package/templates/channels/microsoft-copilot/oauth-plugin-vault-required.template.md.hbs +11 -0
- package/templates/channels/slack/install.template.md.hbs +6 -1
- package/templates/channels/slack/integration/README.md +2 -4
- package/templates/channels/teams-bot/install.template.md.hbs +4 -0
- package/templates/channels/teams-bot/integration/README.md +3 -5
- package/templates/channels/web-embed/integration/web-embed-system.json +1 -16
- package/templates/python/docker-compose.hbs +10 -0
- package/templates/typescript/docker-compose.hbs +10 -0
- /package/.cursor/plans/{Done → Archive}/157.1-security-quality-violations-by-type.csv +0 -0
|
@@ -23,6 +23,10 @@ const {
|
|
|
23
23
|
alreadyResponded,
|
|
24
24
|
buildRespondPayload
|
|
25
25
|
} = require('./test-runner-workhub-answers');
|
|
26
|
+
const {
|
|
27
|
+
shouldApplyBidAfterWait,
|
|
28
|
+
applyCaseBidViaBuilderApi
|
|
29
|
+
} = require('./test-runner-bid-apply');
|
|
26
30
|
|
|
27
31
|
const TERMINAL_TASK = new Set(['completed', 'failed', 'cancelled', 'canceled']);
|
|
28
32
|
const PLAN_LOST_RE =
|
|
@@ -180,23 +184,34 @@ async function createActivateAcceptPlan(dataplaneUrl, authConfig, planBody, assi
|
|
|
180
184
|
throw lastError || new Error('Work Hub plan create/activate/accept failed.');
|
|
181
185
|
}
|
|
182
186
|
|
|
183
|
-
|
|
187
|
+
function _approveGateDecision(ctx) {
|
|
184
188
|
if (ctx.approveStopped) {
|
|
185
|
-
return;
|
|
189
|
+
return 'skip';
|
|
186
190
|
}
|
|
187
191
|
const status = String(ctx.status || '').trim().toLowerCase();
|
|
188
192
|
if (status !== 'pendingapproval' && status !== 'pending_approval') {
|
|
189
|
-
return;
|
|
193
|
+
return 'skip';
|
|
190
194
|
}
|
|
191
195
|
const approvalState = String(ctx.taskRow?.approvalState || '').trim().toLowerCase();
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
196
|
+
// Treat empty / pending* as still actionable. Stop only after a terminal decision.
|
|
197
|
+
const terminalDecision = new Set([
|
|
198
|
+
'approved',
|
|
199
|
+
'rejected',
|
|
200
|
+
'cancelled',
|
|
201
|
+
'canceled',
|
|
202
|
+
'notrequired',
|
|
203
|
+
'not_required'
|
|
204
|
+
]);
|
|
205
|
+
if (approvalState && terminalDecision.has(approvalState)) {
|
|
206
|
+
return 'stop';
|
|
195
207
|
}
|
|
196
208
|
if ((Number(ctx.approveAttempts) || 0) >= 8) {
|
|
197
|
-
|
|
198
|
-
return;
|
|
209
|
+
return 'stop';
|
|
199
210
|
}
|
|
211
|
+
return 'run';
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
async function _attemptApproveTask(dataplaneUrl, authConfig, taskId, ctx) {
|
|
200
215
|
try {
|
|
201
216
|
await approveWorkTask(dataplaneUrl, authConfig, taskId, {});
|
|
202
217
|
ctx.interactions.push({ type: 'approve', taskId, attempt: ctx.approveAttempts });
|
|
@@ -211,46 +226,126 @@ async function maybeApproveTask(dataplaneUrl, authConfig, taskId, ctx) {
|
|
|
211
226
|
}
|
|
212
227
|
}
|
|
213
228
|
|
|
229
|
+
async function maybeApproveTask(dataplaneUrl, authConfig, taskId, ctx) {
|
|
230
|
+
const gate = _approveGateDecision(ctx);
|
|
231
|
+
if (gate === 'skip') {
|
|
232
|
+
return;
|
|
233
|
+
}
|
|
234
|
+
if (gate === 'stop') {
|
|
235
|
+
ctx.approveStopped = true;
|
|
236
|
+
return;
|
|
237
|
+
}
|
|
238
|
+
await _attemptApproveTask(dataplaneUrl, authConfig, taskId, ctx);
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
function recordSkippedResponse(interactions, peId, fingerprint, wait) {
|
|
242
|
+
interactions.push({
|
|
243
|
+
type: 'respond-skipped',
|
|
244
|
+
processExecutionId: peId,
|
|
245
|
+
fingerprint,
|
|
246
|
+
questionKey: wait.questionKey || wait.requestKey || null,
|
|
247
|
+
objectRefKind: wait.objectRef?.kind || null,
|
|
248
|
+
reason: 'no-matching-answer-or-synthesis'
|
|
249
|
+
});
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
async function applyBidAfterWait(authConfig, testCase, interactions, peId) {
|
|
253
|
+
try {
|
|
254
|
+
const { loadCaseBidDocument } = require('./test-runner-workhub-answers');
|
|
255
|
+
const emittedBid = loadCaseBidDocument(testCase);
|
|
256
|
+
if (emittedBid && emittedBid.meta && emittedBid.meta.suiteRunId) {
|
|
257
|
+
interactions.push({
|
|
258
|
+
type: 'bid-emit',
|
|
259
|
+
processExecutionId: peId,
|
|
260
|
+
suiteRunId: emittedBid.meta.suiteRunId,
|
|
261
|
+
definitionId: emittedBid.definitionId,
|
|
262
|
+
definitionHash: emittedBid.definitionHash
|
|
263
|
+
});
|
|
264
|
+
}
|
|
265
|
+
const applied = await applyCaseBidViaBuilderApi(authConfig, testCase);
|
|
266
|
+
interactions.push({
|
|
267
|
+
type: 'bid-apply',
|
|
268
|
+
processExecutionId: peId,
|
|
269
|
+
systemKey: applied && applied.systemKey,
|
|
270
|
+
definitionHash: applied && applied.definitionHash,
|
|
271
|
+
suiteRunId: applied && applied.suiteRunId,
|
|
272
|
+
status: applied && applied.status
|
|
273
|
+
});
|
|
274
|
+
} catch (applyErr) {
|
|
275
|
+
interactions.push({
|
|
276
|
+
type: 'bid-apply-error',
|
|
277
|
+
processExecutionId: peId,
|
|
278
|
+
error: applyErr.message || String(applyErr)
|
|
279
|
+
});
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
async function respondToWaitingExecution(
|
|
284
|
+
dataplaneUrl,
|
|
285
|
+
authConfig,
|
|
286
|
+
peId,
|
|
287
|
+
testCase,
|
|
288
|
+
interactions,
|
|
289
|
+
statusRow
|
|
290
|
+
) {
|
|
291
|
+
const wait = statusRow.wait && typeof statusRow.wait === 'object' ? statusRow.wait : {};
|
|
292
|
+
const fingerprint = waitRespondFingerprint(wait);
|
|
293
|
+
if (alreadyResponded(interactions, fingerprint)) {
|
|
294
|
+
return;
|
|
295
|
+
}
|
|
296
|
+
const payload = buildRespondPayload(
|
|
297
|
+
peId,
|
|
298
|
+
wait,
|
|
299
|
+
testCase,
|
|
300
|
+
statusRow,
|
|
301
|
+
interactions,
|
|
302
|
+
resolveObjective(testCase)
|
|
303
|
+
);
|
|
304
|
+
if (!payload) {
|
|
305
|
+
recordSkippedResponse(interactions, peId, fingerprint, wait);
|
|
306
|
+
return;
|
|
307
|
+
}
|
|
308
|
+
await respondWorkExecution(dataplaneUrl, authConfig, peId, payload);
|
|
309
|
+
await continueWorkExecution(dataplaneUrl, authConfig, peId, {});
|
|
310
|
+
interactions.push({ type: 'respond', processExecutionId: peId, fingerprint });
|
|
311
|
+
if (shouldApplyBidAfterWait(testCase, wait)) {
|
|
312
|
+
await applyBidAfterWait(authConfig, testCase, interactions, peId);
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
|
|
214
316
|
async function maybeRespondWaiting(dataplaneUrl, authConfig, taskRow, testCase, interactions) {
|
|
215
317
|
const peId = extractPeFromTask(taskRow);
|
|
216
318
|
if (!peId) {
|
|
217
319
|
return;
|
|
218
320
|
}
|
|
219
321
|
try {
|
|
322
|
+
testCase._activeProcessExecutionId = peId;
|
|
220
323
|
const statusRow = await getWorkExecutionStatus(dataplaneUrl, authConfig, peId);
|
|
221
324
|
if (String(statusRow?.status || '').toLowerCase() !== 'waiting') {
|
|
222
325
|
return;
|
|
223
326
|
}
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
return;
|
|
228
|
-
}
|
|
229
|
-
const payload = buildRespondPayload(
|
|
327
|
+
await respondToWaitingExecution(
|
|
328
|
+
dataplaneUrl,
|
|
329
|
+
authConfig,
|
|
230
330
|
peId,
|
|
231
|
-
wait,
|
|
232
331
|
testCase,
|
|
233
|
-
statusRow,
|
|
234
332
|
interactions,
|
|
235
|
-
|
|
333
|
+
statusRow
|
|
236
334
|
);
|
|
237
|
-
if (!payload) {
|
|
238
|
-
return;
|
|
239
|
-
}
|
|
240
|
-
await respondWorkExecution(dataplaneUrl, authConfig, peId, payload);
|
|
241
|
-
await continueWorkExecution(dataplaneUrl, authConfig, peId, {});
|
|
242
|
-
interactions.push({ type: 'respond', processExecutionId: peId, fingerprint });
|
|
243
335
|
} catch (_err) {
|
|
244
336
|
// Soft: continue polling task.
|
|
245
337
|
}
|
|
246
338
|
}
|
|
247
339
|
|
|
248
|
-
function assertTerminalWorkHubTask(last, taskId, timeoutMs) {
|
|
340
|
+
function assertTerminalWorkHubTask(last, taskId, timeoutMs, options = {}) {
|
|
249
341
|
if (!last) {
|
|
250
342
|
throw new Error(`Work Hub task ${taskId} produced no status rows.`);
|
|
251
343
|
}
|
|
252
344
|
const finalStatusToken = String(last?.status || last?.taskStatus || '').trim().toLowerCase();
|
|
253
345
|
if (!TERMINAL_TASK.has(finalStatusToken)) {
|
|
346
|
+
if (options.soft === true) {
|
|
347
|
+
return last;
|
|
348
|
+
}
|
|
254
349
|
throw new Error(
|
|
255
350
|
`Work Hub task ${taskId} did not reach a terminal status within ${timeoutMs}ms (last=${finalStatusToken || 'unknown'}).`
|
|
256
351
|
);
|
|
@@ -261,6 +356,7 @@ function assertTerminalWorkHubTask(last, taskId, timeoutMs) {
|
|
|
261
356
|
async function pollWorkHubTask(dataplaneUrl, authConfig, taskId, testCase, options) {
|
|
262
357
|
const timeoutMs = Number(options.pollTimeoutMs) || 180000;
|
|
263
358
|
const intervalMs = Number(options.pollIntervalMs) || 3000;
|
|
359
|
+
const soft = testCase?.expected?.soft === true;
|
|
264
360
|
const started = Date.now();
|
|
265
361
|
let last = null;
|
|
266
362
|
const timeline = [];
|
|
@@ -287,7 +383,7 @@ async function pollWorkHubTask(dataplaneUrl, authConfig, taskId, testCase, optio
|
|
|
287
383
|
}
|
|
288
384
|
|
|
289
385
|
return {
|
|
290
|
-
finalTask: assertTerminalWorkHubTask(last, taskId, timeoutMs),
|
|
386
|
+
finalTask: assertTerminalWorkHubTask(last, taskId, timeoutMs, { soft }),
|
|
291
387
|
timeline,
|
|
292
388
|
interactions: ctx.interactions
|
|
293
389
|
};
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Role Assistant suite discovery for multi-case CLI runs.
|
|
3
|
+
* @fileoverview Discover YAML cases under tests/role-assistant/ra-* suite folders
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
'use strict';
|
|
7
|
+
|
|
8
|
+
const path = require('path');
|
|
9
|
+
const fs = require('../internal/fs-real-sync');
|
|
10
|
+
const { bareRoleKey } = require('../utils/role-assistant-paths');
|
|
11
|
+
|
|
12
|
+
function loadNodeFs() {
|
|
13
|
+
if (global.__AIFABRIX_NODE_FS_UNMOCKED__) {
|
|
14
|
+
return global.__AIFABRIX_NODE_FS_UNMOCKED__;
|
|
15
|
+
}
|
|
16
|
+
return typeof jest !== 'undefined' && typeof jest.requireActual === 'function'
|
|
17
|
+
? jest.requireActual('node:fs')
|
|
18
|
+
: require('node:fs');
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const nodeFs = loadNodeFs();
|
|
22
|
+
|
|
23
|
+
function exists(p) {
|
|
24
|
+
return nodeFs.existsSync(p) || fs.existsSync(p);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function readdir(p) {
|
|
28
|
+
if (nodeFs.existsSync(p)) {
|
|
29
|
+
return nodeFs.readdirSync(p, { withFileTypes: true });
|
|
30
|
+
}
|
|
31
|
+
return fs.readdirSync(p).map((name) => ({
|
|
32
|
+
name,
|
|
33
|
+
isDirectory: () => fs.statSync(path.join(p, name)).isDirectory()
|
|
34
|
+
}));
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Resolve cases root for a role: prefer tests/role-assistant/ra-{bare}, else package tests/.
|
|
39
|
+
* @param {string} roleKey
|
|
40
|
+
* @param {{ casesRoot?: string, cwd?: string }} [options]
|
|
41
|
+
* @returns {string}
|
|
42
|
+
*/
|
|
43
|
+
function resolveCasesRoot(roleKey, options = {}) {
|
|
44
|
+
if (options.casesRoot) {
|
|
45
|
+
return path.resolve(String(options.casesRoot));
|
|
46
|
+
}
|
|
47
|
+
const cwd = options.cwd || process.cwd();
|
|
48
|
+
const bare = bareRoleKey(roleKey);
|
|
49
|
+
const preferred = path.join(cwd, 'tests', 'role-assistant', `ra-${bare}`);
|
|
50
|
+
if (exists(preferred)) {
|
|
51
|
+
return preferred;
|
|
52
|
+
}
|
|
53
|
+
const packageTests = path.join(cwd, 'role-assistant', `ra-${bare}`, 'tests');
|
|
54
|
+
if (exists(packageTests)) {
|
|
55
|
+
return packageTests;
|
|
56
|
+
}
|
|
57
|
+
return preferred;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* @param {string} casesRoot
|
|
62
|
+
* @param {{ suite?: string }} [options]
|
|
63
|
+
* @returns {Array<{ suite: string, file: string, absPath: string, caseIdHint: string }>}
|
|
64
|
+
*/
|
|
65
|
+
function discoverSuiteCaseFiles(casesRoot, options = {}) {
|
|
66
|
+
if (!exists(casesRoot)) {
|
|
67
|
+
return [];
|
|
68
|
+
}
|
|
69
|
+
const suiteFilter = options.suite ? String(options.suite).trim() : '';
|
|
70
|
+
const rows = [];
|
|
71
|
+
for (const ent of readdir(casesRoot)) {
|
|
72
|
+
if (!ent.isDirectory() || String(ent.name).startsWith('.')) {
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
if (suiteFilter && ent.name !== suiteFilter) {
|
|
76
|
+
continue;
|
|
77
|
+
}
|
|
78
|
+
const suitePath = path.join(casesRoot, ent.name);
|
|
79
|
+
for (const fileEnt of readdir(suitePath)) {
|
|
80
|
+
const name = fileEnt.name || fileEnt;
|
|
81
|
+
if (typeof name !== 'string' || !/\.(ya?ml)$/i.test(name)) {
|
|
82
|
+
continue;
|
|
83
|
+
}
|
|
84
|
+
const absPath = path.join(suitePath, name);
|
|
85
|
+
rows.push({
|
|
86
|
+
suite: ent.name,
|
|
87
|
+
file: name,
|
|
88
|
+
absPath,
|
|
89
|
+
caseIdHint: path.basename(name, path.extname(name))
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
return rows.sort((a, b) => a.absPath.localeCompare(b.absPath));
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Filter discovered rows by --case id or basename.
|
|
98
|
+
* @param {Array<{ absPath: string, caseIdHint: string, file: string }>} rows
|
|
99
|
+
* @param {string} caseSel
|
|
100
|
+
* @returns {Array}
|
|
101
|
+
*/
|
|
102
|
+
function filterDiscoveredCases(rows, caseSel) {
|
|
103
|
+
const sel = String(caseSel || '').trim();
|
|
104
|
+
if (!sel) {
|
|
105
|
+
return rows;
|
|
106
|
+
}
|
|
107
|
+
return rows.filter((row) => {
|
|
108
|
+
if (row.absPath === path.resolve(sel) || row.file === sel) {
|
|
109
|
+
return true;
|
|
110
|
+
}
|
|
111
|
+
return row.caseIdHint === sel || row.caseIdHint.endsWith(`-${sel}`) || sel === row.caseIdHint;
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
module.exports = {
|
|
116
|
+
resolveCasesRoot,
|
|
117
|
+
discoverSuiteCaseFiles,
|
|
118
|
+
filterDiscoveredCases
|
|
119
|
+
};
|
|
@@ -7,12 +7,12 @@
|
|
|
7
7
|
"key": "application-schema",
|
|
8
8
|
"name": "Application Configuration Schema",
|
|
9
9
|
"description": "JSON schema for validating AI Fabrix application configuration files",
|
|
10
|
-
"version": "1.6.
|
|
10
|
+
"version": "1.6.2",
|
|
11
11
|
"type": "schema",
|
|
12
12
|
"category": "infrastructure",
|
|
13
13
|
"author": "AI Fabrix Team",
|
|
14
14
|
"createdAt": "2024-01-01T00:00:00Z",
|
|
15
|
-
"updatedAt": "2026-
|
|
15
|
+
"updatedAt": "2026-08-06T00:00:00Z",
|
|
16
16
|
"compatibility": {
|
|
17
17
|
"minVersion": "1.0.0",
|
|
18
18
|
"maxVersion": "2.0.0",
|
|
@@ -30,6 +30,22 @@
|
|
|
30
30
|
"external-datasource.schema.json"
|
|
31
31
|
],
|
|
32
32
|
"changelog": [
|
|
33
|
+
{
|
|
34
|
+
"version": "1.6.2",
|
|
35
|
+
"date": "2026-08-06T00:00:00Z",
|
|
36
|
+
"changes": [
|
|
37
|
+
"portalInput.options items may be plain strings (including empty for env-default) or {label, value} objects (matches miso-ui Configuration tab and miso-controller types)."
|
|
38
|
+
],
|
|
39
|
+
"breaking": false
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
"version": "1.6.1",
|
|
43
|
+
"date": "2026-08-03T00:00:00Z",
|
|
44
|
+
"changes": [
|
|
45
|
+
"registryMode external no longer restricts configuration item names to DOCKER_REGISTRY_SERVER_*; external is image pull source only and full env/portal configuration remains valid."
|
|
46
|
+
],
|
|
47
|
+
"breaking": false
|
|
48
|
+
},
|
|
33
49
|
{
|
|
34
50
|
"version": "1.6.0",
|
|
35
51
|
"date": "2026-07-28T00:00:00Z",
|
|
@@ -273,9 +289,30 @@
|
|
|
273
289
|
},
|
|
274
290
|
"options": {
|
|
275
291
|
"type": "array",
|
|
276
|
-
"description": "Options for select fields",
|
|
292
|
+
"description": "Options for select fields (plain string or {label, value}; empty string allowed for env-default)",
|
|
277
293
|
"items": {
|
|
278
|
-
"
|
|
294
|
+
"oneOf": [
|
|
295
|
+
{
|
|
296
|
+
"type": "string"
|
|
297
|
+
},
|
|
298
|
+
{
|
|
299
|
+
"type": "object",
|
|
300
|
+
"required": [
|
|
301
|
+
"label",
|
|
302
|
+
"value"
|
|
303
|
+
],
|
|
304
|
+
"properties": {
|
|
305
|
+
"label": {
|
|
306
|
+
"type": "string",
|
|
307
|
+
"minLength": 1
|
|
308
|
+
},
|
|
309
|
+
"value": {
|
|
310
|
+
"type": "string"
|
|
311
|
+
}
|
|
312
|
+
},
|
|
313
|
+
"additionalProperties": false
|
|
314
|
+
}
|
|
315
|
+
]
|
|
279
316
|
}
|
|
280
317
|
}
|
|
281
318
|
},
|
|
@@ -391,9 +428,30 @@
|
|
|
391
428
|
},
|
|
392
429
|
"options": {
|
|
393
430
|
"type": "array",
|
|
394
|
-
"description": "Options for select fields",
|
|
431
|
+
"description": "Options for select fields (plain string or {label, value}; empty string allowed for env-default)",
|
|
395
432
|
"items": {
|
|
396
|
-
"
|
|
433
|
+
"oneOf": [
|
|
434
|
+
{
|
|
435
|
+
"type": "string"
|
|
436
|
+
},
|
|
437
|
+
{
|
|
438
|
+
"type": "object",
|
|
439
|
+
"required": [
|
|
440
|
+
"label",
|
|
441
|
+
"value"
|
|
442
|
+
],
|
|
443
|
+
"properties": {
|
|
444
|
+
"label": {
|
|
445
|
+
"type": "string",
|
|
446
|
+
"minLength": 1
|
|
447
|
+
},
|
|
448
|
+
"value": {
|
|
449
|
+
"type": "string"
|
|
450
|
+
}
|
|
451
|
+
},
|
|
452
|
+
"additionalProperties": false
|
|
453
|
+
}
|
|
454
|
+
]
|
|
397
455
|
}
|
|
398
456
|
}
|
|
399
457
|
},
|
|
@@ -974,32 +1032,6 @@
|
|
|
974
1032
|
"databases"
|
|
975
1033
|
]
|
|
976
1034
|
}
|
|
977
|
-
},
|
|
978
|
-
{
|
|
979
|
-
"if": {
|
|
980
|
-
"properties": {
|
|
981
|
-
"registryMode": {
|
|
982
|
-
"const": "external"
|
|
983
|
-
}
|
|
984
|
-
}
|
|
985
|
-
},
|
|
986
|
-
"then": {
|
|
987
|
-
"properties": {
|
|
988
|
-
"configuration": {
|
|
989
|
-
"items": {
|
|
990
|
-
"properties": {
|
|
991
|
-
"name": {
|
|
992
|
-
"enum": [
|
|
993
|
-
"DOCKER_REGISTRY_SERVER_URL",
|
|
994
|
-
"DOCKER_REGISTRY_SERVER_USERNAME",
|
|
995
|
-
"DOCKER_REGISTRY_SERVER_PASSWORD"
|
|
996
|
-
]
|
|
997
|
-
}
|
|
998
|
-
}
|
|
999
|
-
}
|
|
1000
|
-
}
|
|
1001
|
-
}
|
|
1002
|
-
}
|
|
1003
1035
|
}
|
|
1004
1036
|
]
|
|
1005
1037
|
}
|
|
@@ -7,12 +7,12 @@
|
|
|
7
7
|
"key":"external-datasource-schema",
|
|
8
8
|
"name":"External Data Source Configuration Schema",
|
|
9
9
|
"description":"JSON schema for validating ExternalDataSource configuration files",
|
|
10
|
-
"version":"2.9.
|
|
10
|
+
"version":"2.9.6",
|
|
11
11
|
"type":"schema",
|
|
12
12
|
"category":"integration",
|
|
13
13
|
"author":"AI Fabrix Team",
|
|
14
14
|
"createdAt":"2024-01-01T00:00:00Z",
|
|
15
|
-
"updatedAt":"2026-
|
|
15
|
+
"updatedAt":"2026-08-07T00:00:00Z",
|
|
16
16
|
"compatibility":{
|
|
17
17
|
"minVersion":"1.0.0",
|
|
18
18
|
"maxVersion":"3.0.0",
|
|
@@ -390,6 +390,12 @@
|
|
|
390
390
|
"enum":["documentStorage","vectorStore","recordStorage","messageService","serviceContract","none"],
|
|
391
391
|
"description":"Entity type identifier. Defines storage semantics and behavior: 'documentStorage' (creates document storage service with vector-storage), 'vectorStore' (external vector storage system), 'recordStorage' (creates record-based system with metadata sync and access rights), 'messageService' (message service for notifications - Slack, Teams, Email), 'serviceContract' (normalized executable service adapter without persistence — requires metadataSchema, fieldMappings, exposed), 'none' (orchestration/composition only). Required field. Canonical values are camelCase."
|
|
392
392
|
},
|
|
393
|
+
"storageType":{
|
|
394
|
+
"type":"string",
|
|
395
|
+
"enum":["table","view"],
|
|
396
|
+
"default":"table",
|
|
397
|
+
"description":"recordStorage persistence mode. 'table' (default) is ordinary synced record storage. 'view' is calculated read-only CIP perspectives with fetch.source viewpoint SQL; not a permanent PostgreSQL VIEW object."
|
|
398
|
+
},
|
|
393
399
|
"resourceType":{
|
|
394
400
|
"type":"string",
|
|
395
401
|
"pattern":"^[a-z0-9-]+$",
|
|
@@ -1307,6 +1313,33 @@
|
|
|
1307
1313
|
]
|
|
1308
1314
|
}
|
|
1309
1315
|
},
|
|
1316
|
+
{
|
|
1317
|
+
"if":{
|
|
1318
|
+
"properties":{
|
|
1319
|
+
"storageType":{
|
|
1320
|
+
"const":"view"
|
|
1321
|
+
}
|
|
1322
|
+
},
|
|
1323
|
+
"required":[
|
|
1324
|
+
"storageType"
|
|
1325
|
+
]
|
|
1326
|
+
},
|
|
1327
|
+
"then":{
|
|
1328
|
+
"properties":{
|
|
1329
|
+
"entityType":{
|
|
1330
|
+
"const":"recordStorage"
|
|
1331
|
+
},
|
|
1332
|
+
"execution":{
|
|
1333
|
+
"type":"object",
|
|
1334
|
+
"properties":{
|
|
1335
|
+
"engine":{
|
|
1336
|
+
"const":"cip"
|
|
1337
|
+
}
|
|
1338
|
+
}
|
|
1339
|
+
}
|
|
1340
|
+
}
|
|
1341
|
+
}
|
|
1342
|
+
},
|
|
1310
1343
|
{
|
|
1311
1344
|
"if":{
|
|
1312
1345
|
"properties":{
|
|
@@ -2498,11 +2531,12 @@
|
|
|
2498
2531
|
"source":{
|
|
2499
2532
|
"type":"string",
|
|
2500
2533
|
"enum":[
|
|
2501
|
-
|
|
2534
|
+
"openapi",
|
|
2502
2535
|
"http",
|
|
2503
|
-
"datasource"
|
|
2536
|
+
"datasource",
|
|
2537
|
+
"viewpoint"
|
|
2504
2538
|
],
|
|
2505
|
-
"description":"If 'openapi', refer to openapi.operations.*. If 'http', use explicit method/path. If 'datasource', execute another datasource operation."
|
|
2539
|
+
"description":"If 'openapi', refer to openapi.operations.*. If 'http', use explicit method/path. If 'datasource', execute another datasource operation. If 'viewpoint', execute saved SQL in fetch.query against records storage (storageType view)."
|
|
2506
2540
|
},
|
|
2507
2541
|
"openapiRef":{
|
|
2508
2542
|
"type":"string",
|
|
@@ -2555,16 +2589,7 @@
|
|
|
2555
2589
|
"description":"When true, append fetch items to the pipeline accumulator for a following merge step."
|
|
2556
2590
|
},
|
|
2557
2591
|
"query":{
|
|
2558
|
-
"
|
|
2559
|
-
"description":"Static query parameters. ABAC/runtime can still override or append at runtime.",
|
|
2560
|
-
"additionalProperties":{
|
|
2561
|
-
"type":[
|
|
2562
|
-
"string",
|
|
2563
|
-
"number",
|
|
2564
|
-
"boolean",
|
|
2565
|
-
"null"
|
|
2566
|
-
]
|
|
2567
|
-
}
|
|
2592
|
+
"description":"When source is openapi/http/datasource: static query parameter object. When source is viewpoint: non-empty SQL SELECT string. ABAC/runtime may still append supported filters at execution for viewpoint."
|
|
2568
2593
|
},
|
|
2569
2594
|
"bodyTemplate":{
|
|
2570
2595
|
"description":"Optional static request body or template for POST/PUT/PATCH fetches.",
|
|
@@ -2606,7 +2631,20 @@
|
|
|
2606
2631
|
"operationId"
|
|
2607
2632
|
]
|
|
2608
2633
|
}
|
|
2609
|
-
]
|
|
2634
|
+
],
|
|
2635
|
+
"properties":{
|
|
2636
|
+
"query":{
|
|
2637
|
+
"type":"object",
|
|
2638
|
+
"additionalProperties":{
|
|
2639
|
+
"type":[
|
|
2640
|
+
"string",
|
|
2641
|
+
"number",
|
|
2642
|
+
"boolean",
|
|
2643
|
+
"null"
|
|
2644
|
+
]
|
|
2645
|
+
}
|
|
2646
|
+
}
|
|
2647
|
+
}
|
|
2610
2648
|
}
|
|
2611
2649
|
},
|
|
2612
2650
|
{
|
|
@@ -2621,7 +2659,20 @@
|
|
|
2621
2659
|
"required":[
|
|
2622
2660
|
"method",
|
|
2623
2661
|
"path"
|
|
2624
|
-
]
|
|
2662
|
+
],
|
|
2663
|
+
"properties":{
|
|
2664
|
+
"query":{
|
|
2665
|
+
"type":"object",
|
|
2666
|
+
"additionalProperties":{
|
|
2667
|
+
"type":[
|
|
2668
|
+
"string",
|
|
2669
|
+
"number",
|
|
2670
|
+
"boolean",
|
|
2671
|
+
"null"
|
|
2672
|
+
]
|
|
2673
|
+
}
|
|
2674
|
+
}
|
|
2675
|
+
}
|
|
2625
2676
|
}
|
|
2626
2677
|
},
|
|
2627
2678
|
{
|
|
@@ -2637,9 +2688,44 @@
|
|
|
2637
2688
|
"datasource":{
|
|
2638
2689
|
"type":"string",
|
|
2639
2690
|
"description":"Target child datasource key. Omitted when parent manifest composition.datasources has two or more entries; runtime resolves via selector input."
|
|
2691
|
+
},
|
|
2692
|
+
"query":{
|
|
2693
|
+
"type":"object",
|
|
2694
|
+
"additionalProperties":{
|
|
2695
|
+
"type":[
|
|
2696
|
+
"string",
|
|
2697
|
+
"number",
|
|
2698
|
+
"boolean",
|
|
2699
|
+
"null"
|
|
2700
|
+
]
|
|
2701
|
+
}
|
|
2640
2702
|
}
|
|
2641
2703
|
}
|
|
2642
2704
|
}
|
|
2705
|
+
},
|
|
2706
|
+
{
|
|
2707
|
+
"if":{
|
|
2708
|
+
"properties":{
|
|
2709
|
+
"source":{
|
|
2710
|
+
"const":"viewpoint"
|
|
2711
|
+
}
|
|
2712
|
+
},
|
|
2713
|
+
"required":[
|
|
2714
|
+
"source"
|
|
2715
|
+
]
|
|
2716
|
+
},
|
|
2717
|
+
"then":{
|
|
2718
|
+
"required":[
|
|
2719
|
+
"query"
|
|
2720
|
+
],
|
|
2721
|
+
"properties":{
|
|
2722
|
+
"query":{
|
|
2723
|
+
"type":"string",
|
|
2724
|
+
"minLength":1,
|
|
2725
|
+
"description":"Saved read-only SQL SELECT for viewpoint fetch. Parameter-free in first implementation."
|
|
2726
|
+
}
|
|
2727
|
+
}
|
|
2728
|
+
}
|
|
2643
2729
|
}
|
|
2644
2730
|
],
|
|
2645
2731
|
"additionalProperties":false
|