@aifabrix/builder 2.53.1 → 2.54.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/bin/aifabrix.js +2 -2
- package/lib/api/assistants.api.js +8 -5
- package/lib/api/channel-identity.api.js +41 -0
- package/lib/api/evidence-authoring.api.js +5 -1
- package/lib/api/evidence.api.js +2 -0
- package/lib/api/types/evidence.types.js +2 -0
- package/lib/api/work-search.api.js +136 -0
- package/lib/channels/add-channel.js +163 -126
- package/lib/channels/approval-guides/chatgpt.js +14 -18
- package/lib/channels/approval-guides/claude.js +29 -0
- package/lib/channels/approval-guides/index.js +88 -22
- package/lib/channels/approval-guides/mcp-client.js +27 -0
- package/lib/channels/approval-guides/microsoft-copilot.js +52 -3
- package/lib/channels/approval-guides/slack.js +10 -10
- package/lib/channels/approval-guides/teams-bot.js +8 -12
- package/lib/channels/channel-add-continuations.js +91 -0
- package/lib/channels/channel-add-ensure.js +124 -27
- package/lib/channels/channel-add-existing.js +160 -28
- package/lib/channels/channel-add-failure.js +60 -0
- package/lib/channels/channel-add-remaining.js +486 -0
- package/lib/channels/channel-add-tty.js +190 -0
- package/lib/channels/channel-add-wizard-config.js +138 -0
- package/lib/channels/channel-add-wizard.js +250 -0
- package/lib/channels/channel-artifact-generator.js +27 -13
- package/lib/channels/channel-cardinality.js +83 -0
- package/lib/channels/channel-catalog-icon.js +45 -0
- package/lib/channels/channel-cli-instructions.js +75 -0
- package/lib/channels/channel-configuration-map.js +213 -0
- package/lib/channels/channel-credential.js +11 -14
- package/lib/channels/channel-definition-project.js +101 -0
- package/lib/channels/channel-definition-registry.js +154 -0
- package/lib/channels/channel-description.js +59 -2
- package/lib/channels/channel-discovery.js +16 -2
- package/lib/channels/channel-external-system.js +86 -9
- package/lib/channels/channel-install-envelope-actions.js +88 -0
- package/lib/channels/channel-install-envelope-microsoft.js +155 -0
- package/lib/channels/channel-install-envelope.js +356 -0
- package/lib/channels/channel-install-help-md.js +172 -0
- package/lib/channels/channel-install-http-payload.js +62 -0
- package/lib/channels/channel-install-inventory.js +115 -0
- package/lib/channels/channel-install-profiles.js +90 -0
- package/lib/channels/channel-instance-collision.js +118 -0
- package/lib/channels/channel-instance-key.js +100 -0
- package/lib/channels/channel-instance-materialize.js +240 -0
- package/lib/channels/channel-integration-generator.js +16 -9
- package/lib/channels/channel-integration-template.js +101 -22
- package/lib/channels/channel-inventory-fetch.js +79 -0
- package/lib/channels/channel-oauth-preflight.js +104 -0
- package/lib/channels/channel-package-version.js +76 -0
- package/lib/channels/channel-predefined-client.js +154 -0
- package/lib/channels/channel-probe.js +7 -2
- package/lib/channels/channel-runtime-resolver.js +180 -72
- package/lib/channels/channel-status-display.js +86 -16
- package/lib/channels/channel-targets.js +106 -92
- package/lib/channels/chatgpt-callback.js +318 -0
- package/lib/channels/chatgpt-channel-add.js +229 -0
- package/lib/channels/chatgpt-preflight.js +396 -0
- package/lib/channels/chatgpt-runtime-contract.js +17 -0
- package/lib/channels/chatgpt-setup-values.js +124 -0
- package/lib/channels/claude-callback.js +67 -0
- package/lib/channels/claude-channel-add.js +254 -0
- package/lib/channels/claude-runtime-contract.js +40 -0
- package/lib/channels/claude-runtime.js +58 -0
- package/lib/channels/claude-setup-values.js +89 -0
- package/lib/channels/mcp-client/config-merge.js +148 -0
- package/lib/channels/mcp-client/ensure-client.js +231 -0
- package/lib/channels/mcp-client/paths.js +53 -0
- package/lib/channels/mcp-client/profiles.js +122 -0
- package/lib/channels/mcp-client/redirect-uris.js +99 -0
- package/lib/channels/mcp-client/runtime.js +59 -0
- package/lib/channels/mcp-client-channel-add.js +186 -0
- package/lib/channels/microsoft-copilot-channel-add.js +117 -7
- package/lib/channels/microsoft-copilot-entra-identifier-uris.js +187 -0
- package/lib/channels/microsoft-copilot-entra-sso-form.js +106 -18
- package/lib/channels/microsoft-copilot-entra-sso.js +31 -2
- package/lib/channels/microsoft-copilot-package-prior.js +107 -0
- package/lib/channels/microsoft-copilot-package.js +29 -28
- package/lib/channels/microsoft-provisioning/a365.js +80 -0
- package/lib/channels/microsoft-provisioning/auth-config.js +240 -0
- package/lib/channels/microsoft-provisioning/auth.js +179 -0
- package/lib/channels/microsoft-provisioning/cli.js +71 -0
- package/lib/channels/microsoft-provisioning/constants.js +75 -0
- package/lib/channels/microsoft-provisioning/errors.js +117 -0
- package/lib/channels/microsoft-provisioning/index.js +55 -0
- package/lib/channels/microsoft-provisioning/install-atk.js +158 -0
- package/lib/channels/microsoft-provisioning/install-teams.js +115 -0
- package/lib/channels/microsoft-provisioning/login-portal-device.js +236 -0
- package/lib/channels/microsoft-provisioning/login-portal-parse.js +71 -0
- package/lib/channels/microsoft-provisioning/login-portal-teams.js +193 -0
- package/lib/channels/microsoft-provisioning/login-portal.js +230 -0
- package/lib/channels/microsoft-provisioning/persist.js +110 -0
- package/lib/channels/microsoft-provisioning/teams-bot-args.js +119 -0
- package/lib/channels/microsoft-provisioning/teams-bot-controller.js +123 -0
- package/lib/channels/microsoft-provisioning/teams-bot-parse.js +134 -0
- package/lib/channels/microsoft-provisioning/teams-bot-persist.js +113 -0
- package/lib/channels/microsoft-provisioning/teams-bot-provision.js +234 -0
- package/lib/channels/microsoft-provisioning/teams-bot-secrets.js +45 -0
- package/lib/channels/microsoft-provisioning/teams-cli.js +82 -0
- package/lib/channels/runtime-tool-catalog.js +5 -2
- package/lib/channels/slack-channel-add-portal.js +130 -0
- package/lib/channels/slack-channel-add-tty-auth.js +95 -0
- package/lib/channels/slack-channel-add.js +258 -0
- package/lib/channels/slack-provisioning/apps.js +181 -0
- package/lib/channels/slack-provisioning/auth-store.js +110 -0
- package/lib/channels/slack-provisioning/auth-ticket.js +185 -0
- package/lib/channels/slack-provisioning/auth.js +115 -0
- package/lib/channels/slack-provisioning/callbacks.js +41 -0
- package/lib/channels/slack-provisioning/cli.js +226 -0
- package/lib/channels/slack-provisioning/constants.js +37 -0
- package/lib/channels/slack-provisioning/credentials.js +169 -0
- package/lib/channels/slack-provisioning/curl-shim.js +132 -0
- package/lib/channels/slack-provisioning/developer-install.js +118 -0
- package/lib/channels/slack-provisioning/env-file.js +120 -0
- package/lib/channels/slack-provisioning/errors.js +47 -0
- package/lib/channels/slack-provisioning/go-ca-bundle.js +137 -0
- package/lib/channels/slack-provisioning/index.js +250 -0
- package/lib/channels/slack-provisioning/install-cli.js +323 -0
- package/lib/channels/slack-provisioning/manifest.js +89 -0
- package/lib/channels/slack-provisioning/oauth-lock.js +33 -0
- package/lib/channels/slack-provisioning/offer-cli.js +113 -0
- package/lib/channels/slack-provisioning/onboard-members.js +122 -0
- package/lib/channels/slack-provisioning/onboard.js +89 -0
- package/lib/channels/slack-provisioning/persist.js +95 -0
- package/lib/channels/slack-provisioning/remaining-urls.js +158 -0
- package/lib/channels/slack-provisioning/with-curl-on-path.js +48 -0
- package/lib/channels/teams-bot-channel-add.js +94 -0
- package/lib/channels/vendor-dataplane-url.js +249 -0
- package/lib/cli/setup-certification.help.js +2 -0
- package/lib/cli/setup-external-system-help.js +2 -0
- package/lib/cli/setup-external-system.js +1 -1
- package/lib/commands/channel-add-help.js +366 -0
- package/lib/commands/channel-add-setup.js +162 -80
- package/lib/commands/channel-configure.js +118 -0
- package/lib/commands/channel.js +10 -1
- package/lib/commands/governance-upload-external.js +6 -3
- package/lib/commands/lifecycle-command-action.js +2 -5
- package/lib/commands/lifecycle-external.js +93 -21
- package/lib/commands/repair-datasource-test-payload.js +4 -2
- package/lib/commands/repair-datasource-webhooks-sync-bridge.js +23 -2
- package/lib/commands/repair-datasource.js +3 -0
- package/lib/commands/repair-external-run.js +59 -25
- package/lib/commands/repair-openapi-rest-runtime.js +127 -0
- package/lib/commands/repair-orchestration-tag-merge.js +339 -0
- package/lib/commands/repair-rbac-catalog-defaults.js +96 -0
- package/lib/commands/repair-rbac-platform-preset.js +26 -15
- package/lib/commands/repair-rbac-reader-roles.js +8 -21
- package/lib/commands/repair-rbac.js +10 -34
- package/lib/commands/repair-system-permissions.js +24 -22
- package/lib/commands/role-assistant-test-output.js +14 -0
- package/lib/commands/role-assistant.js +59 -21
- package/lib/commands/test-e2e-external.js +148 -21
- package/lib/commands/upload-credential-push-log.js +2 -1
- package/lib/commands/upload-publish-context.js +1 -1
- package/lib/commands/upload-role-assistant-shell.js +124 -0
- package/lib/commands/upload-sidecar-followups.js +6 -3
- package/lib/commands/upload.js +18 -3
- package/lib/core/secrets-docker-env.js +4 -1
- package/lib/core/secrets-env-content.js +17 -3
- package/lib/datasource/capability/capability-manifest.js +0 -3
- package/lib/datasource/capability/copy-test-payload.js +3 -0
- package/lib/external-system/deploy-helpers.js +26 -1
- package/lib/external-system/deploy.js +10 -6
- package/lib/external-system/download-helpers.js +29 -2
- package/lib/external-system/download-role-assistant.js +83 -4
- package/lib/external-system/download.js +40 -30
- package/lib/external-system/sync-deploy-manifest.js +3 -2
- package/lib/external-system/upload-identity.js +42 -0
- package/lib/generator/index.js +19 -15
- package/lib/generator/split.js +13 -2
- package/lib/lifecycle/role-assistant-governance.js +146 -0
- package/lib/lifecycle/scenario-upload.js +181 -30
- package/lib/programmatic/builder-api-cors.js +143 -0
- package/lib/programmatic/builder-api-request.js +6 -0
- package/lib/programmatic/builder-api-route-dispatch.js +56 -42
- package/lib/programmatic/builder-api-server.js +8 -1
- package/lib/programmatic/capability-registry.js +54 -0
- package/lib/programmatic/channel-install-help-resolve.js +359 -0
- package/lib/programmatic/channel-install-session.js +218 -0
- package/lib/programmatic/help-content/channel-onboarding.md +2 -0
- package/lib/programmatic/help-content/cip-overview.md +3 -1
- package/lib/programmatic/http-raw-response.js +62 -0
- package/lib/programmatic/openapi-channel-install-bindings.js +179 -0
- package/lib/programmatic/openapi-channel-install-schemas.js +286 -0
- package/lib/programmatic/openapi-component-schemas.js +18 -1
- package/lib/programmatic/openapi-description-enricher.js +3 -1
- package/lib/programmatic/openapi-descriptions.js +74 -3
- package/lib/programmatic/openapi-envelope-examples.js +117 -0
- package/lib/programmatic/openapi-operation-bindings.js +4 -1
- package/lib/programmatic/openapi-schema-descriptions-channel.js +316 -0
- package/lib/programmatic/openapi-spec.js +12 -8
- package/lib/programmatic/platform-rbac-catalog.js +36 -18
- package/lib/programmatic/route-handler-map.js +51 -1
- package/lib/programmatic/route-handlers-context.js +2 -1
- package/lib/programmatic/run-auth-permissions.js +54 -0
- package/lib/programmatic/run-channel-catalog.js +196 -0
- package/lib/programmatic/run-channel-install-artifacts.js +137 -0
- package/lib/programmatic/run-channel-install-copilot.js +353 -0
- package/lib/programmatic/run-channel-install-options.js +232 -0
- package/lib/programmatic/run-channel-install-teams-bot.js +204 -0
- package/lib/programmatic/run-channel-install.js +346 -0
- package/lib/programmatic/run-channel-package.js +87 -7
- package/lib/programmatic/workspace-context.js +2 -0
- package/lib/role-assistant/evidence-sync.js +4 -2
- package/lib/role-assistant/evidence-validation-issues.js +51 -0
- package/lib/role-assistant/knowledge-sync.js +10 -4
- package/lib/role-assistant/settings.js +49 -2
- package/lib/role-assistant/test-cases-evaluate.js +30 -2
- package/lib/role-assistant/test-cases-evidence.js +175 -0
- package/lib/role-assistant/test-cases-search.js +229 -0
- package/lib/role-assistant/test-cases.js +37 -4
- package/lib/role-assistant/test-runner-search.js +149 -0
- package/lib/role-assistant/test-runner-workhub-helpers.js +40 -2
- package/lib/role-assistant/test-runner-workhub-soft.js +9 -3
- package/lib/role-assistant/test-runner-workhub.js +29 -11
- package/lib/role-assistant/test-runner.js +16 -1
- package/lib/schema/external-datasource.schema.json +112 -18
- package/lib/schema/external-system.schema.json +159 -2
- package/lib/schema/mcp-client-profile.schema.json +103 -0
- package/lib/utils/app-path-resolve.js +4 -0
- package/lib/utils/authentication-env-template.js +44 -6
- package/lib/utils/credential-secrets-env.js +6 -1
- package/lib/utils/env-copy.js +19 -7
- package/lib/utils/external-env-template.js +20 -2
- package/lib/utils/paths.js +18 -1
- package/lib/utils/platform-shell-package.js +69 -0
- package/lib/utils/port-resolver.js +50 -1
- package/lib/utils/rewrite-name-flag.js +20 -0
- package/lib/utils/role-assistant-paths.js +13 -10
- package/lib/utils/schema-resolver.js +11 -1
- package/lib/utils/secrets-helpers.js +12 -21
- package/lib/utils/token-manager.js +10 -5
- package/lib/validation/validate.js +40 -0
- package/package.json +1 -1
- package/templates/README.md +1 -1
- package/templates/applications/builder-api/Dockerfile +1 -1
- package/templates/applications/builder-api/env.template +7 -0
- package/templates/applications/dataplane/application.yaml +6 -5
- package/templates/applications/dataplane/env.template +2 -2
- package/templates/applications/dataplane/rbac.yaml +7 -2
- package/templates/applications/keycloak/Dockerfile +30 -4
- package/templates/applications/keycloak/application.yaml +2 -2
- package/templates/applications/keycloak/cimd-lenient-parse/AbstractClientIdMetadataDocumentExecutor.java +896 -0
- package/templates/applications/keycloak/cimd-lenient-parse/SOURCE.md +19 -0
- package/templates/applications/keycloak/env.template +3 -0
- package/templates/applications/miso-controller/application.yaml +1 -2
- package/templates/applications/miso-controller/env.template +2 -0
- package/templates/channels/README.md +5 -5
- package/templates/channels/chatgpt/TROUBLESHOOTING.md +51 -0
- package/templates/channels/chatgpt/channel.json +59 -0
- package/templates/channels/chatgpt/icon.svg +1 -0
- package/templates/channels/chatgpt/install.template.md.hbs +20 -95
- package/templates/channels/chatgpt/integration/README.md +1 -1
- package/templates/channels/chatgpt/integration/application.json +2 -2
- package/templates/channels/chatgpt/integration/chatgpt-system.json +49 -4
- package/templates/channels/chatgpt/integration/env.template +2 -4
- package/templates/channels/chatgpt/integration/rbac.json +13 -0
- package/templates/channels/claude/TROUBLESHOOTING.md +41 -0
- package/templates/channels/claude/channel.json +59 -0
- package/templates/channels/claude/icon.svg +1 -0
- package/templates/channels/claude/install.template.md.hbs +21 -0
- package/templates/channels/claude/integration/README.md +14 -0
- package/templates/channels/claude/integration/application.json +15 -0
- package/templates/channels/claude/integration/claude-system.json +59 -0
- package/templates/channels/claude/integration/env.template +2 -0
- package/templates/channels/claude/integration/rbac.json +13 -0
- package/templates/channels/claude/manifest.template.json.hbs +40 -0
- package/templates/channels/mcp-client/channel.json +72 -0
- package/templates/channels/mcp-client/icon.svg +1 -0
- package/templates/channels/mcp-client/install.template.md.hbs +24 -0
- package/templates/channels/mcp-client/integration/README.md +13 -0
- package/templates/channels/mcp-client/integration/application.json +15 -0
- package/templates/channels/mcp-client/integration/env.template +2 -0
- package/templates/channels/mcp-client/integration/mcp-client-system.json +72 -0
- package/templates/channels/mcp-client/integration/rbac.json +13 -0
- package/templates/channels/mcp-client/manifest.template.json.hbs +41 -0
- package/templates/channels/mcp-client/profiles/cursor.json +26 -0
- package/templates/channels/mcp-client/profiles/github-copilot.json +29 -0
- package/templates/channels/mcp-client/profiles/vscode.json +29 -0
- package/templates/channels/microsoft-copilot/channel.json +73 -0
- package/templates/channels/microsoft-copilot/declarative-agent.template.json.hbs +8 -4
- package/templates/channels/microsoft-copilot/entra-sso-registration.template.md.hbs +65 -5
- package/templates/channels/microsoft-copilot/icon.svg +1 -0
- package/templates/channels/microsoft-copilot/install.template.md.hbs +9 -9
- package/templates/channels/microsoft-copilot/integration/README.md +1 -1
- package/templates/channels/microsoft-copilot/integration/application.json +2 -2
- package/templates/channels/microsoft-copilot/integration/env.template +3 -6
- package/templates/channels/microsoft-copilot/integration/microsoft-copilot-system.json +63 -4
- package/templates/channels/microsoft-copilot/integration/rbac.json +13 -0
- package/templates/channels/microsoft-copilot/oauth-plugin-vault-required.template.md.hbs +5 -5
- package/templates/channels/slack/channel.json +223 -0
- package/templates/channels/slack/icon.svg +1 -0
- package/templates/channels/slack/install.template.md.hbs +36 -28
- package/templates/channels/slack/integration/README.md +245 -22
- package/templates/channels/slack/integration/application.json +44 -7
- package/templates/channels/slack/integration/env.template +15 -6
- package/templates/channels/slack/integration/openapi/openapi.json +126 -0
- package/templates/channels/slack/integration/rbac.json +84 -0
- package/templates/channels/slack/integration/slack-datasource-commands.json +154 -0
- package/templates/channels/slack/integration/slack-datasource-conversations.json +28 -3
- package/templates/channels/slack/integration/slack-datasource-events.json +154 -0
- package/templates/channels/slack/integration/slack-datasource-interactive.json +154 -0
- package/templates/channels/slack/integration/slack-datasource-messages.json +40 -2
- package/templates/channels/slack/integration/slack-system.json +155 -77
- package/templates/channels/slack/manifest.template.json.hbs +5 -2
- package/templates/channels/teams-bot/channel.json +161 -0
- package/templates/channels/teams-bot/icon.svg +1 -0
- package/templates/channels/teams-bot/install.template.md.hbs +12 -13
- package/templates/channels/teams-bot/integration/README.md +5 -4
- package/templates/channels/teams-bot/integration/application.json +6 -4
- package/templates/channels/teams-bot/integration/env.template +5 -4
- package/templates/channels/teams-bot/integration/openapi/openapi.json +68 -0
- package/templates/channels/teams-bot/integration/rbac.json +21 -0
- package/templates/channels/teams-bot/integration/teams-bot-datasource-activities.json +154 -0
- package/templates/channels/teams-bot/integration/teams-bot-system.json +128 -23
- package/templates/channels/web-embed/channel.json +60 -0
- package/templates/channels/web-embed/install.template.md.hbs +1 -1
- package/templates/channels/web-embed/integration/README.md +2 -2
- package/templates/channels/web-embed/integration/application.json +2 -2
- package/templates/channels/web-embed/integration/env.template +1 -1
- package/templates/channels/web-embed/integration/rbac.json +13 -0
- package/templates/channels/web-embed/integration/web-embed-system.json +50 -4
|
@@ -12,6 +12,7 @@ const fs = require('../internal/fs-real-sync');
|
|
|
12
12
|
const path = require('path');
|
|
13
13
|
const { ensureIntegrationDirectory } = require('./workspace-context');
|
|
14
14
|
const { resolveChannelRuntimeValues } = require('../channels/channel-runtime-resolver');
|
|
15
|
+
const { vendorFacingUrlsFromCtx } = require('../channels/vendor-dataplane-url');
|
|
15
16
|
const { generateChannelArtifact } = require('../channels/channel-artifact-generator');
|
|
16
17
|
const { loadChannelPackageContext } = require('../channels/channel-discovery');
|
|
17
18
|
const {
|
|
@@ -20,6 +21,10 @@ const {
|
|
|
20
21
|
mergeChannelBranding
|
|
21
22
|
} = require('../channels/channel-branding');
|
|
22
23
|
const { isChannelTargetKey } = require('../channels/channel-targets');
|
|
24
|
+
const {
|
|
25
|
+
httpError,
|
|
26
|
+
readChannelInstallSessionForDefinition
|
|
27
|
+
} = require('./channel-install-session');
|
|
23
28
|
|
|
24
29
|
/**
|
|
25
30
|
* @param {import('./workspace-context').WorkspaceContext} ctx
|
|
@@ -43,6 +48,16 @@ function requireChannelTarget(systemKey) {
|
|
|
43
48
|
return systemKey;
|
|
44
49
|
}
|
|
45
50
|
|
|
51
|
+
/**
|
|
52
|
+
* Catalog packaging key: instance paths (name slug) still read dist/channels/<catalog>/.
|
|
53
|
+
* @param {import('./workspace-context').WorkspaceContext} ctx
|
|
54
|
+
* @returns {string}
|
|
55
|
+
*/
|
|
56
|
+
function packagingChannelTarget(ctx) {
|
|
57
|
+
const { resolveInstallDefinitionKey } = require('./channel-install-help-resolve');
|
|
58
|
+
return requireChannelTarget(resolveInstallDefinitionKey(ctx));
|
|
59
|
+
}
|
|
60
|
+
|
|
46
61
|
/**
|
|
47
62
|
* @param {import('./workspace-context').WorkspaceContext} ctx
|
|
48
63
|
* @returns {{ type: string, token: string }}
|
|
@@ -82,8 +97,13 @@ function generatePackageArtifact(ctx, body, channelTarget, branding, dataplaneUr
|
|
|
82
97
|
branding.displayName || body.name || packageContext.displayName || channelTarget;
|
|
83
98
|
const description =
|
|
84
99
|
branding.description || body.description || packageContext.description || '';
|
|
100
|
+
const vendor = vendorFacingUrlsFromCtx({
|
|
101
|
+
...ctx,
|
|
102
|
+
dataplaneUrl
|
|
103
|
+
});
|
|
85
104
|
const runtimeValues = resolveChannelRuntimeValues({
|
|
86
|
-
dataplaneBaseUrl:
|
|
105
|
+
dataplaneBaseUrl: vendor.dataplaneBaseUrl,
|
|
106
|
+
controllerUrl: vendor.controllerUrl,
|
|
87
107
|
channelTarget,
|
|
88
108
|
appName,
|
|
89
109
|
baseName: body.baseName || appName,
|
|
@@ -91,6 +111,7 @@ function generatePackageArtifact(ctx, body, channelTarget, branding, dataplaneUr
|
|
|
91
111
|
appDescription: description,
|
|
92
112
|
layerBToolNames: packageContext.governedToolNames || [],
|
|
93
113
|
packageContext,
|
|
114
|
+
oauthPluginVaultReferenceId: body.oauthPluginVaultReferenceId,
|
|
94
115
|
iconColorPath: branding.iconColorPath,
|
|
95
116
|
iconOutlinePath: branding.iconOutlinePath
|
|
96
117
|
});
|
|
@@ -108,7 +129,7 @@ function generatePackageArtifact(ctx, body, channelTarget, branding, dataplaneUr
|
|
|
108
129
|
* @returns {Promise<Object>}
|
|
109
130
|
*/
|
|
110
131
|
async function buildChannelPackage(ctx, body = {}) {
|
|
111
|
-
const channelTarget =
|
|
132
|
+
const channelTarget = packagingChannelTarget(ctx);
|
|
112
133
|
ensureIntegrationDirectory(ctx);
|
|
113
134
|
const dir = workspaceSystemDir(ctx);
|
|
114
135
|
const branding = mergeChannelBranding(readChannelBranding(dir), body.branding || {});
|
|
@@ -151,7 +172,7 @@ async function buildChannelPackage(ctx, body = {}) {
|
|
|
151
172
|
* @returns {Object}
|
|
152
173
|
*/
|
|
153
174
|
function getChannelPackageSummary(ctx) {
|
|
154
|
-
const channelTarget =
|
|
175
|
+
const channelTarget = packagingChannelTarget(ctx);
|
|
155
176
|
const projectRoot = ctx.getUserRoot();
|
|
156
177
|
const summaryPath = path.join(
|
|
157
178
|
projectRoot,
|
|
@@ -161,7 +182,7 @@ function getChannelPackageSummary(ctx) {
|
|
|
161
182
|
`channel-${channelTarget}.json`
|
|
162
183
|
);
|
|
163
184
|
if (!fs.existsSync(summaryPath)) {
|
|
164
|
-
throw
|
|
185
|
+
throw httpError(`Channel package summary not found: ${summaryPath}`, 404);
|
|
165
186
|
}
|
|
166
187
|
const summary = JSON.parse(fs.readFileSync(summaryPath, 'utf8'));
|
|
167
188
|
return {
|
|
@@ -177,8 +198,8 @@ function getChannelPackageSummary(ctx) {
|
|
|
177
198
|
* @param {import('./workspace-context').WorkspaceContext} ctx
|
|
178
199
|
* @returns {{ fileName: string, contentType: string, byteLength: number, zipBase64: string }}
|
|
179
200
|
*/
|
|
180
|
-
function
|
|
181
|
-
const channelTarget =
|
|
201
|
+
function readChannelPackageZip(ctx) {
|
|
202
|
+
const channelTarget = packagingChannelTarget(ctx);
|
|
182
203
|
const summary = getChannelPackageSummary(ctx).summary;
|
|
183
204
|
const zipPath =
|
|
184
205
|
typeof summary?.links?.zip === 'string' && summary.links.zip
|
|
@@ -192,7 +213,7 @@ function getChannelPackageZip(ctx) {
|
|
|
192
213
|
`appPackage.${summary.environment || 'dev'}.zip`
|
|
193
214
|
);
|
|
194
215
|
if (!fs.existsSync(zipPath)) {
|
|
195
|
-
throw
|
|
216
|
+
throw httpError(`Channel package ZIP not found: ${zipPath}`, 404);
|
|
196
217
|
}
|
|
197
218
|
const buffer = fs.readFileSync(zipPath);
|
|
198
219
|
return {
|
|
@@ -203,6 +224,65 @@ function getChannelPackageZip(ctx) {
|
|
|
203
224
|
};
|
|
204
225
|
}
|
|
205
226
|
|
|
227
|
+
/**
|
|
228
|
+
* @param {Object|null} session
|
|
229
|
+
* @returns {Object}
|
|
230
|
+
*/
|
|
231
|
+
function packageBodyFromSession(session) {
|
|
232
|
+
if (!session || typeof session !== 'object') {
|
|
233
|
+
return {};
|
|
234
|
+
}
|
|
235
|
+
return {
|
|
236
|
+
name: session.displayName || session.name,
|
|
237
|
+
oauthPluginVaultReferenceId: session.oauthPluginVaultReferenceId
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* Portal ZIP GET must not 500 with CLI `--oauth-plugin-vault-reference-id` text.
|
|
243
|
+
* @param {unknown} err
|
|
244
|
+
* @returns {unknown}
|
|
245
|
+
*/
|
|
246
|
+
function mapCopilotPackageHttpError(err) {
|
|
247
|
+
if (err && err.statusCode) {
|
|
248
|
+
return err;
|
|
249
|
+
}
|
|
250
|
+
const msg = String((err && err.message) || '');
|
|
251
|
+
if (/oauth-plugin-vault-reference-id|OAuthPluginVault/i.test(msg)) {
|
|
252
|
+
return httpError(
|
|
253
|
+
'Copilot app package is not ready until Teams Entra SSO registration is stored on this install.',
|
|
254
|
+
409
|
|
255
|
+
);
|
|
256
|
+
}
|
|
257
|
+
return err;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
/**
|
|
261
|
+
* Rebuild the Copilot ZIP when the workspace lost dist/ after an image recreate.
|
|
262
|
+
* @param {import('./workspace-context').WorkspaceContext} ctx
|
|
263
|
+
* @returns {Promise<{ fileName: string, contentType: string, byteLength: number, zipBase64: string }>}
|
|
264
|
+
*/
|
|
265
|
+
async function getChannelPackageZip(ctx) {
|
|
266
|
+
let summaryMissing = false;
|
|
267
|
+
try {
|
|
268
|
+
getChannelPackageSummary(ctx);
|
|
269
|
+
} catch (err) {
|
|
270
|
+
if (!err || err.statusCode !== 404) {
|
|
271
|
+
throw mapCopilotPackageHttpError(err);
|
|
272
|
+
}
|
|
273
|
+
summaryMissing = true;
|
|
274
|
+
}
|
|
275
|
+
if (summaryMissing) {
|
|
276
|
+
try {
|
|
277
|
+
const session = readChannelInstallSessionForDefinition(ctx, packagingChannelTarget(ctx));
|
|
278
|
+
await buildChannelPackage(ctx, packageBodyFromSession(session));
|
|
279
|
+
} catch (err) {
|
|
280
|
+
throw mapCopilotPackageHttpError(err);
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
return readChannelPackageZip(ctx);
|
|
284
|
+
}
|
|
285
|
+
|
|
206
286
|
/**
|
|
207
287
|
* @param {import('./workspace-context').WorkspaceContext} ctx
|
|
208
288
|
* @param {Object} body
|
|
@@ -51,6 +51,7 @@ function resolveWorkspaceRoot(segment) {
|
|
|
51
51
|
* @param {string} [params.environment]
|
|
52
52
|
* @param {string} [params.controllerUrl]
|
|
53
53
|
* @param {string} [params.dataplaneUrl]
|
|
54
|
+
* @param {string} [params.publicOrigin]
|
|
54
55
|
* @returns {WorkspaceContext}
|
|
55
56
|
*/
|
|
56
57
|
function createWorkspaceContext(params) {
|
|
@@ -88,6 +89,7 @@ function createWorkspaceContext(params) {
|
|
|
88
89
|
process.env.CONTROLLER_URL ||
|
|
89
90
|
'',
|
|
90
91
|
dataplaneUrl: params.dataplaneUrl || process.env.DATAPLANE_URL || '',
|
|
92
|
+
publicOrigin: typeof params.publicOrigin === 'string' ? params.publicOrigin.trim() : '',
|
|
91
93
|
bearerToken,
|
|
92
94
|
getUserRoot: () => userRoot,
|
|
93
95
|
getIntegrationRoot: () => integrationRoot,
|
|
@@ -22,6 +22,7 @@ const {
|
|
|
22
22
|
} = require('../api/evidence.api');
|
|
23
23
|
const { resolveExternalSystemPath } = require('../utils/app-path-resolve');
|
|
24
24
|
const { SYSTEM_KIND_DIGITAL_WORKER } = require('./settings');
|
|
25
|
+
const { formatEvidenceValidationIssues } = require('./evidence-validation-issues');
|
|
25
26
|
|
|
26
27
|
const EVIDENCE_DIR_NAME = 'evidence';
|
|
27
28
|
const EVIDENCE_EXPORT_FILE = 'evidence.json';
|
|
@@ -288,8 +289,9 @@ function isActivatedEvidenceRow(row) {
|
|
|
288
289
|
async function runEvidenceLifecycleForRef(dataplaneUrl, authConfig, evidenceRef) {
|
|
289
290
|
const validation = await validateEvidenceCandidate(dataplaneUrl, authConfig, evidenceRef);
|
|
290
291
|
if (validationFailed(validation)) {
|
|
291
|
-
|
|
292
|
-
|
|
292
|
+
throw new Error(
|
|
293
|
+
`Validation failed for evidence ${evidenceRef} (${formatEvidenceValidationIssues(validation?.issues)}).`
|
|
294
|
+
);
|
|
293
295
|
}
|
|
294
296
|
if (String(validation?.status || '').trim().toLowerCase() !== VALIDATION_PASSED) {
|
|
295
297
|
throw new Error(`Validation status for evidence ${evidenceRef} is "${validation?.status || 'unknown'}".`);
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Format Evidence validate issues[] for CLI errors.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
'use strict';
|
|
6
|
+
|
|
7
|
+
function formatOneEvidenceIssue(issue) {
|
|
8
|
+
if (issue === undefined || issue === null) {
|
|
9
|
+
return '';
|
|
10
|
+
}
|
|
11
|
+
if (typeof issue !== 'object') {
|
|
12
|
+
return String(issue);
|
|
13
|
+
}
|
|
14
|
+
const bits = [];
|
|
15
|
+
if (issue.field) {
|
|
16
|
+
bits.push(String(issue.field));
|
|
17
|
+
}
|
|
18
|
+
if (issue.code) {
|
|
19
|
+
bits.push(String(issue.code));
|
|
20
|
+
}
|
|
21
|
+
if (issue.message) {
|
|
22
|
+
bits.push(String(issue.message));
|
|
23
|
+
}
|
|
24
|
+
if (bits.length > 0) {
|
|
25
|
+
return bits.join(' — ');
|
|
26
|
+
}
|
|
27
|
+
try {
|
|
28
|
+
return JSON.stringify(issue);
|
|
29
|
+
} catch {
|
|
30
|
+
return String(issue);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* @param {unknown} issues
|
|
36
|
+
* @returns {string}
|
|
37
|
+
*/
|
|
38
|
+
function formatEvidenceValidationIssues(issues) {
|
|
39
|
+
if (!Array.isArray(issues) || issues.length === 0) {
|
|
40
|
+
return '0 issues';
|
|
41
|
+
}
|
|
42
|
+
const parts = issues.map(formatOneEvidenceIssue).filter(Boolean);
|
|
43
|
+
const noun = issues.length === 1 ? 'issue' : 'issues';
|
|
44
|
+
const detail = parts.length > 0 ? `: ${parts.join('; ')}` : '';
|
|
45
|
+
return `${issues.length} ${noun}${detail}`;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
module.exports = {
|
|
49
|
+
formatOneEvidenceIssue,
|
|
50
|
+
formatEvidenceValidationIssues
|
|
51
|
+
};
|
|
@@ -19,6 +19,7 @@ const {
|
|
|
19
19
|
updateAssistantSettings
|
|
20
20
|
} = require('../api/assistants.api');
|
|
21
21
|
const { resolveExternalSystemPath } = require('../utils/app-path-resolve');
|
|
22
|
+
const { bareRoleKey } = require('../utils/role-assistant-paths');
|
|
22
23
|
const {
|
|
23
24
|
SYSTEM_KIND_DIGITAL_WORKER,
|
|
24
25
|
getRoleAssistantYamlPath,
|
|
@@ -75,7 +76,11 @@ async function resolveDocumentStorageKeyFromDataplane(dataplaneUrl, authConfig,
|
|
|
75
76
|
return null;
|
|
76
77
|
}
|
|
77
78
|
try {
|
|
78
|
-
const response = await getAssistantDetails(
|
|
79
|
+
const response = await getAssistantDetails(
|
|
80
|
+
dataplaneUrl,
|
|
81
|
+
systemKey,
|
|
82
|
+
authConfig
|
|
83
|
+
);
|
|
79
84
|
return extractDocumentStorageKeyFromAssistantDetails(response);
|
|
80
85
|
} catch (_error) {
|
|
81
86
|
return null;
|
|
@@ -141,16 +146,17 @@ async function applyAssistantSettingsPush(dataplaneUrl, systemKey, authConfig, s
|
|
|
141
146
|
const { documentStorageKey, debugBody, patchBody } = settingsPush;
|
|
142
147
|
const hasDocumentStorageKey = documentStorageKey !== '';
|
|
143
148
|
const hasDebugSettings = Object.keys(debugBody).length > 0;
|
|
149
|
+
const assistantKey = String(systemKey || '').trim();
|
|
144
150
|
let response = await updateAssistantSettings(
|
|
145
151
|
dataplaneUrl,
|
|
146
|
-
|
|
152
|
+
assistantKey,
|
|
147
153
|
authConfig,
|
|
148
154
|
patchBody
|
|
149
155
|
);
|
|
150
156
|
if (response && response.success === false && hasDocumentStorageKey && hasDebugSettings) {
|
|
151
157
|
response = await updateAssistantSettings(
|
|
152
158
|
dataplaneUrl,
|
|
153
|
-
|
|
159
|
+
assistantKey,
|
|
154
160
|
authConfig,
|
|
155
161
|
debugBody
|
|
156
162
|
);
|
|
@@ -352,7 +358,7 @@ function writeRoleAssistantSettingsFile(systemKey, docStorageKey, environment =
|
|
|
352
358
|
apiVersion: 'aifabrix.ai/v1',
|
|
353
359
|
version: '1.0.0',
|
|
354
360
|
kind: 'RoleAssistantSettings',
|
|
355
|
-
roleKey: systemKey,
|
|
361
|
+
roleKey: bareRoleKey(systemKey),
|
|
356
362
|
[envKey]: {
|
|
357
363
|
knowledge: docStorageKey ? { documentStorageKey: docStorageKey } : {}
|
|
358
364
|
}
|
|
@@ -13,7 +13,11 @@ const fs = require('../internal/fs-real-sync');
|
|
|
13
13
|
const yaml = require('js-yaml');
|
|
14
14
|
const { resolveExternalSystemPath } = require('../utils/app-path-resolve');
|
|
15
15
|
const { loadRoleAssistantSettingsSchema } = require('../utils/schema-loader');
|
|
16
|
-
const {
|
|
16
|
+
const {
|
|
17
|
+
SYSTEM_KIND_DIGITAL_WORKER,
|
|
18
|
+
WORKSPACE_ROLE_ASSISTANT,
|
|
19
|
+
bareRoleKey
|
|
20
|
+
} = require('../utils/role-assistant-paths');
|
|
17
21
|
|
|
18
22
|
function getRoleAssistantYamlPath(systemKey, options = {}) {
|
|
19
23
|
const appPath = resolveExternalSystemPath(systemKey, options.pathOptions || {});
|
|
@@ -115,6 +119,48 @@ function resolveRoleAssistantSettingsVersion(settings) {
|
|
|
115
119
|
return value.trim();
|
|
116
120
|
}
|
|
117
121
|
|
|
122
|
+
function defaultRoleAssistantSettings(systemKey) {
|
|
123
|
+
return {
|
|
124
|
+
apiVersion: 'aifabrix.ai/v1',
|
|
125
|
+
version: '1.0.0',
|
|
126
|
+
kind: 'RoleAssistantSettings',
|
|
127
|
+
roleKey: bareRoleKey(systemKey)
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function mergeRoleAssistantSettings(existing, systemKey) {
|
|
132
|
+
const next = existing && typeof existing === 'object' ? { ...existing } : {};
|
|
133
|
+
const defaults = defaultRoleAssistantSettings(systemKey);
|
|
134
|
+
return {
|
|
135
|
+
...defaults,
|
|
136
|
+
...next,
|
|
137
|
+
apiVersion: defaults.apiVersion,
|
|
138
|
+
kind: defaults.kind,
|
|
139
|
+
roleKey: defaults.roleKey
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function writeOrMergeRoleAssistantSettings(systemKey, options = {}) {
|
|
144
|
+
const filePath = getRoleAssistantYamlPath(systemKey, {
|
|
145
|
+
...options,
|
|
146
|
+
pathOptions: {
|
|
147
|
+
...(options.pathOptions || {}),
|
|
148
|
+
workspace: WORKSPACE_ROLE_ASSISTANT
|
|
149
|
+
}
|
|
150
|
+
});
|
|
151
|
+
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
152
|
+
let payload = defaultRoleAssistantSettings(systemKey);
|
|
153
|
+
if (!options.force && fs.existsSync(filePath)) {
|
|
154
|
+
try {
|
|
155
|
+
payload = mergeRoleAssistantSettings(parseRoleAssistantYaml(filePath), systemKey);
|
|
156
|
+
} catch {
|
|
157
|
+
payload = defaultRoleAssistantSettings(systemKey);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
fs.writeFileSync(filePath, yaml.dump(payload, { lineWidth: -1 }), 'utf8');
|
|
161
|
+
return filePath;
|
|
162
|
+
}
|
|
163
|
+
|
|
118
164
|
module.exports = {
|
|
119
165
|
SYSTEM_KIND_DIGITAL_WORKER,
|
|
120
166
|
getRoleAssistantYamlPath,
|
|
@@ -122,5 +168,6 @@ module.exports = {
|
|
|
122
168
|
readRoleAssistantSettings,
|
|
123
169
|
resolveRoleAssistantEnvironmentSettings,
|
|
124
170
|
resolveDocumentStorageKeyFromRoleAssistantSettings,
|
|
125
|
-
resolveRoleAssistantSettingsVersion
|
|
171
|
+
resolveRoleAssistantSettingsVersion,
|
|
172
|
+
writeOrMergeRoleAssistantSettings
|
|
126
173
|
};
|
|
@@ -10,6 +10,14 @@ const {
|
|
|
10
10
|
evaluateHonestyExpectations
|
|
11
11
|
} = require('./test-cases-honesty');
|
|
12
12
|
const { collectBidAssertions } = require('./test-cases-bid');
|
|
13
|
+
const {
|
|
14
|
+
evaluateEvidenceExpectations,
|
|
15
|
+
evaluateForbiddenCapabilities
|
|
16
|
+
} = require('./test-cases-evidence');
|
|
17
|
+
const {
|
|
18
|
+
evaluateSearchExpectations,
|
|
19
|
+
evaluateSkillsExpectations
|
|
20
|
+
} = require('./test-cases-search');
|
|
13
21
|
|
|
14
22
|
function getValueAtPath(obj, dottedPath) {
|
|
15
23
|
const parts = String(dottedPath || '').split('.').filter(Boolean);
|
|
@@ -111,6 +119,10 @@ function buildCaseObservation(result, lifecycleVerdict, portalUrl) {
|
|
|
111
119
|
workResult,
|
|
112
120
|
workResultError: result?.workResultError || null,
|
|
113
121
|
honesty: buildHonestyObservation(workResult),
|
|
122
|
+
evidencePreflight: result?.evidencePreflight || null,
|
|
123
|
+
search: result?.search || null,
|
|
124
|
+
skills: result?.skills || null,
|
|
125
|
+
roleRef: result?.roleRef || null,
|
|
114
126
|
interactions: Array.isArray(result?.interactions)
|
|
115
127
|
? result.interactions
|
|
116
128
|
: Array.isArray(result?.workInteractions)
|
|
@@ -136,11 +148,15 @@ function softWrapLifecycleAssertion(lifecycleAssertion, soft) {
|
|
|
136
148
|
};
|
|
137
149
|
}
|
|
138
150
|
|
|
151
|
+
function softWrapFailedAssertion(assertion, soft) {
|
|
152
|
+
return softWrapLifecycleAssertion(assertion, soft);
|
|
153
|
+
}
|
|
154
|
+
|
|
139
155
|
function collectCaseAssertions(expected, observation) {
|
|
140
156
|
const soft = expected.soft === true;
|
|
141
157
|
const observedCapabilities = observation.capabilities?.observed || [];
|
|
142
158
|
const assertions = [];
|
|
143
|
-
const lifecycleAssertion =
|
|
159
|
+
const lifecycleAssertion = softWrapFailedAssertion(
|
|
144
160
|
evaluateLifecycleStateExpectation(expected.lifecycleState, observation.lifecycleState),
|
|
145
161
|
soft
|
|
146
162
|
);
|
|
@@ -148,12 +164,24 @@ function collectCaseAssertions(expected, observation) {
|
|
|
148
164
|
assertions.push(lifecycleAssertion);
|
|
149
165
|
}
|
|
150
166
|
assertions.push(
|
|
151
|
-
...evaluateCapabilityExpectations(
|
|
167
|
+
...evaluateCapabilityExpectations(
|
|
168
|
+
expected?.capabilities?.required,
|
|
169
|
+
observedCapabilities
|
|
170
|
+
).map((row) => softWrapFailedAssertion(row, soft))
|
|
171
|
+
);
|
|
172
|
+
assertions.push(
|
|
173
|
+
...evaluateForbiddenCapabilities(
|
|
174
|
+
expected?.capabilities?.forbidden,
|
|
175
|
+
observedCapabilities
|
|
176
|
+
).map((row) => softWrapFailedAssertion(row, soft))
|
|
152
177
|
);
|
|
153
178
|
assertions.push(...evaluateFactExpectations(expected.facts, observation));
|
|
154
179
|
assertions.push(...evaluateProhibitedExpectations(expected.prohibited, observation));
|
|
155
180
|
assertions.push(...evaluateHonestyExpectations(expected.honesty, observation, { soft }));
|
|
156
181
|
assertions.push(...collectBidAssertions(expected.bid, observation, { soft }));
|
|
182
|
+
assertions.push(...evaluateEvidenceExpectations(expected.evidence, observation));
|
|
183
|
+
assertions.push(...evaluateSearchExpectations(expected.search, observation));
|
|
184
|
+
assertions.push(...evaluateSkillsExpectations(expected.skills, observation));
|
|
157
185
|
return assertions;
|
|
158
186
|
}
|
|
159
187
|
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Role Assistant case Evidence preflight — live dataplane rows only.
|
|
3
|
+
* @fileoverview Hard-stop when required Evidence keys are missing or not active
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
'use strict';
|
|
7
|
+
|
|
8
|
+
const { listEvidence } = require('../api/evidence.api');
|
|
9
|
+
const { bareRoleKey } = require('../utils/role-assistant-paths');
|
|
10
|
+
|
|
11
|
+
function asStringList(value) {
|
|
12
|
+
if (!Array.isArray(value)) {
|
|
13
|
+
return [];
|
|
14
|
+
}
|
|
15
|
+
return value.map((item) => String(item || '').trim()).filter(Boolean);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function evidenceSpecFromCase(testCase) {
|
|
19
|
+
const preflight = testCase?.preflight?.evidence;
|
|
20
|
+
const expected = testCase?.expected?.evidence;
|
|
21
|
+
const requiredKeys = asStringList(preflight?.requiredKeys).concat(
|
|
22
|
+
asStringList(expected?.requiredKeys)
|
|
23
|
+
);
|
|
24
|
+
const unique = [...new Set(requiredKeys)];
|
|
25
|
+
if (unique.length === 0) {
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
const roleKey =
|
|
29
|
+
String(preflight?.roleKey || expected?.roleKey || '').trim() ||
|
|
30
|
+
bareRoleKey(testCase?.input?.businessRoleRef || '') ||
|
|
31
|
+
'';
|
|
32
|
+
const resourceType = String(preflight?.resourceType || expected?.resourceType || '').trim();
|
|
33
|
+
return {
|
|
34
|
+
requiredKeys: unique,
|
|
35
|
+
roleKey,
|
|
36
|
+
resourceType,
|
|
37
|
+
requireStatus: String(preflight?.requireStatus || expected?.requireStatus || 'active')
|
|
38
|
+
.trim()
|
|
39
|
+
.toLowerCase() || 'active'
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function rowKey(row) {
|
|
44
|
+
return String(row?.key || row?.evidenceKey || '').trim();
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function rowStatus(row) {
|
|
48
|
+
return String(row?.status || '').trim().toLowerCase();
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function isActiveStatus(status, requiredStatus) {
|
|
52
|
+
if (requiredStatus === 'active') {
|
|
53
|
+
return !status || status === 'active';
|
|
54
|
+
}
|
|
55
|
+
return status === requiredStatus;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function findMissingEvidenceKeys(spec, rows) {
|
|
59
|
+
const byKey = new Map();
|
|
60
|
+
for (const row of rows) {
|
|
61
|
+
const key = rowKey(row);
|
|
62
|
+
if (key) {
|
|
63
|
+
byKey.set(key, row);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
const missing = [];
|
|
67
|
+
for (const key of spec.requiredKeys) {
|
|
68
|
+
const row = byKey.get(key);
|
|
69
|
+
if (!row) {
|
|
70
|
+
missing.push(`${key} (not in dataplane)`);
|
|
71
|
+
continue;
|
|
72
|
+
}
|
|
73
|
+
const status = rowStatus(row);
|
|
74
|
+
if (!isActiveStatus(status, spec.requireStatus)) {
|
|
75
|
+
missing.push(`${key} (status=${status || 'unknown'}, need ${spec.requireStatus})`);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
return missing;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
async function loadLiveEvidenceRows(authContext, spec) {
|
|
82
|
+
const dataplaneUrl = authContext?.dataplaneUrl;
|
|
83
|
+
const authConfig = authContext?.authConfig;
|
|
84
|
+
if (!dataplaneUrl || !authConfig) {
|
|
85
|
+
throw new Error(
|
|
86
|
+
'Evidence preflight failed: dataplane URL or auth is missing. Run aifabrix login and retry.'
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
const params = {
|
|
90
|
+
roleKey: spec.roleKey || undefined,
|
|
91
|
+
resourceType: spec.resourceType || undefined,
|
|
92
|
+
status: spec.requireStatus === 'active' ? 'active' : spec.requireStatus,
|
|
93
|
+
page: 1,
|
|
94
|
+
pageSize: 100
|
|
95
|
+
};
|
|
96
|
+
try {
|
|
97
|
+
return await listEvidence(dataplaneUrl, authConfig, params);
|
|
98
|
+
} catch (error) {
|
|
99
|
+
const detail = error && error.message ? error.message : String(error);
|
|
100
|
+
throw new Error(`Evidence preflight failed: could not read Evidence from the dataplane (${detail}).`);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Hard-stop before work.start when the case requires live Evidence.
|
|
106
|
+
* @returns {Promise<{ skipped: boolean, requiredKeys: string[], rows: Object[] }>}
|
|
107
|
+
*/
|
|
108
|
+
async function assertRoleAssistantEvidencePreflight(testCase, authContext) {
|
|
109
|
+
const spec = evidenceSpecFromCase(testCase);
|
|
110
|
+
if (!spec) {
|
|
111
|
+
return { skipped: true, requiredKeys: [], rows: [] };
|
|
112
|
+
}
|
|
113
|
+
if (!spec.roleKey) {
|
|
114
|
+
throw new Error(
|
|
115
|
+
'Evidence preflight failed: roleKey is required (preflight.evidence.roleKey or input.businessRoleRef).'
|
|
116
|
+
);
|
|
117
|
+
}
|
|
118
|
+
const rows = await loadLiveEvidenceRows(authContext, spec);
|
|
119
|
+
const missing = findMissingEvidenceKeys(spec, rows);
|
|
120
|
+
if (missing.length > 0) {
|
|
121
|
+
throw new Error(
|
|
122
|
+
'Evidence preflight failed: required Evidence is not active in the dataplane: ' +
|
|
123
|
+
`${missing.join('; ')}. Upload the Role Assistant package to the target dataplane, ` +
|
|
124
|
+
'then retry. Hard stop.'
|
|
125
|
+
);
|
|
126
|
+
}
|
|
127
|
+
return { skipped: false, requiredKeys: spec.requiredKeys, rows };
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function evaluateEvidenceExpectations(evidenceSpec, observation) {
|
|
131
|
+
const requiredKeys = asStringList(evidenceSpec?.requiredKeys);
|
|
132
|
+
if (requiredKeys.length === 0) {
|
|
133
|
+
return [];
|
|
134
|
+
}
|
|
135
|
+
const preflight = observation?.evidencePreflight || {};
|
|
136
|
+
const liveKeys = new Set(
|
|
137
|
+
(Array.isArray(preflight.rows) ? preflight.rows : []).map(rowKey).filter(Boolean)
|
|
138
|
+
);
|
|
139
|
+
return requiredKeys.map((key) => {
|
|
140
|
+
const passed = liveKeys.has(key);
|
|
141
|
+
return {
|
|
142
|
+
id: `expected.evidence.requiredKeys:${key}`,
|
|
143
|
+
passed,
|
|
144
|
+
message: passed
|
|
145
|
+
? `Evidence key "${key}" was active in the dataplane at test start.`
|
|
146
|
+
: `Evidence key "${key}" was not active in the dataplane at test start.`
|
|
147
|
+
};
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
function evaluateForbiddenCapabilities(forbiddenList, observedCapabilities) {
|
|
152
|
+
if (!Array.isArray(forbiddenList) || forbiddenList.length === 0) {
|
|
153
|
+
return [];
|
|
154
|
+
}
|
|
155
|
+
const observedSet = new Set(observedCapabilities);
|
|
156
|
+
return forbiddenList.map((capability) => {
|
|
157
|
+
const key = String(capability || '').trim();
|
|
158
|
+
const passed = !observedSet.has(key);
|
|
159
|
+
return {
|
|
160
|
+
id: `expected.capabilities.forbidden:${key}`,
|
|
161
|
+
passed,
|
|
162
|
+
message: passed
|
|
163
|
+
? `Forbidden capability "${key}" was not observed.`
|
|
164
|
+
: `Forbidden capability "${key}" was observed.`
|
|
165
|
+
};
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
module.exports = {
|
|
170
|
+
evidenceSpecFromCase,
|
|
171
|
+
findMissingEvidenceKeys,
|
|
172
|
+
assertRoleAssistantEvidencePreflight,
|
|
173
|
+
evaluateEvidenceExpectations,
|
|
174
|
+
evaluateForbiddenCapabilities
|
|
175
|
+
};
|