@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
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ChatGPT continuation for channel add: preflight, form values, callback PUT.
|
|
3
|
+
*
|
|
4
|
+
* @fileoverview Keep add-channel.js free of ChatGPT-specific continuation logic
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
'use strict';
|
|
8
|
+
|
|
9
|
+
const {
|
|
10
|
+
printValidating,
|
|
11
|
+
printRuntimeDeployed,
|
|
12
|
+
printRuntimeUnavailable,
|
|
13
|
+
printOAuthConnected,
|
|
14
|
+
printOAuthNotConnected,
|
|
15
|
+
printLabeledUrl,
|
|
16
|
+
printUseLines,
|
|
17
|
+
printRemainingSteps,
|
|
18
|
+
printFixThenRerun
|
|
19
|
+
} = require('./channel-add-tty');
|
|
20
|
+
const { CHATGPT_REMAINING_STEPS } = require('./channel-add-remaining');
|
|
21
|
+
const { formatWarningLine } = require('../utils/cli-layout-chalk');
|
|
22
|
+
const logger = require('../utils/logger');
|
|
23
|
+
const { runChatGptPreflight } = require('./chatgpt-preflight');
|
|
24
|
+
const {
|
|
25
|
+
buildChatGptFormValues,
|
|
26
|
+
formatChatGptFormValueLines,
|
|
27
|
+
CHATGPT_APPS_CREATE_URL
|
|
28
|
+
} = require('./chatgpt-setup-values');
|
|
29
|
+
const {
|
|
30
|
+
registerChatGptCallbackUrls,
|
|
31
|
+
findChatGptIntegrationClient,
|
|
32
|
+
chatgptProfileUrl
|
|
33
|
+
} = require('./chatgpt-callback');
|
|
34
|
+
const { CHATGPT_KNOWN_CALLBACK_URIS } = require('./chatgpt-runtime-contract');
|
|
35
|
+
const { resolveChannelRuntimeValues } = require('./channel-runtime-resolver');
|
|
36
|
+
const { vendorFacingUrlsFromCtx } = require('./vendor-dataplane-url');
|
|
37
|
+
const { loadChannelPackageContext } = require('./channel-discovery');
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* @param {Error} error
|
|
41
|
+
* @returns {boolean}
|
|
42
|
+
*/
|
|
43
|
+
function isChatGptOAuthPreflightError(error) {
|
|
44
|
+
const message = error && error.message ? String(error.message) : '';
|
|
45
|
+
return /pkce|authorization.server|protected resource/i.test(message);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* @param {Object} params
|
|
50
|
+
* @returns {string}
|
|
51
|
+
*/
|
|
52
|
+
function chatgptResumeCommand(params) {
|
|
53
|
+
const name =
|
|
54
|
+
(params.options && params.options.name) ||
|
|
55
|
+
(params.runtimeValues && (params.runtimeValues.baseName || params.runtimeValues.appName)) ||
|
|
56
|
+
'';
|
|
57
|
+
const quoted = name ? `"${name}"` : '"<name>"';
|
|
58
|
+
return `aifabrix channel add chatgpt --name ${quoted}`;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* @param {Object} params
|
|
63
|
+
* @param {Error} error
|
|
64
|
+
* @returns {void}
|
|
65
|
+
*/
|
|
66
|
+
function printChatGptPreflightFailure(params, error) {
|
|
67
|
+
if (isChatGptOAuthPreflightError(error)) {
|
|
68
|
+
printRuntimeDeployed();
|
|
69
|
+
printOAuthNotConnected();
|
|
70
|
+
printFixThenRerun('Fix OAuth discovery', chatgptResumeCommand(params));
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
printRuntimeUnavailable();
|
|
74
|
+
printFixThenRerun('Fix the Runtime connection', chatgptResumeCommand(params));
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Fail-closed MCP/OAuth checks before ChatGPT instructions.
|
|
79
|
+
* @param {Object} params
|
|
80
|
+
* @returns {Promise<Object|null>}
|
|
81
|
+
*/
|
|
82
|
+
async function maybeAssertChatGptPreflight(params) {
|
|
83
|
+
if (params.channelTarget !== 'chatgpt') {
|
|
84
|
+
return null;
|
|
85
|
+
}
|
|
86
|
+
printValidating('ChatGPT');
|
|
87
|
+
try {
|
|
88
|
+
params.preflight = await runChatGptPreflight(params);
|
|
89
|
+
printRuntimeDeployed();
|
|
90
|
+
if (!(params.preflight && params.preflight.skipped)) {
|
|
91
|
+
printOAuthConnected();
|
|
92
|
+
}
|
|
93
|
+
return params.preflight;
|
|
94
|
+
} catch (error) {
|
|
95
|
+
printChatGptPreflightFailure(params, error);
|
|
96
|
+
throw error;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* @param {Object} ctx
|
|
102
|
+
* @returns {Promise<Object|null>}
|
|
103
|
+
*/
|
|
104
|
+
async function loadChatGptClientOrNull(ctx) {
|
|
105
|
+
try {
|
|
106
|
+
return await findChatGptIntegrationClient(ctx);
|
|
107
|
+
} catch {
|
|
108
|
+
return null;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* @param {Object} values
|
|
114
|
+
* @returns {void}
|
|
115
|
+
*/
|
|
116
|
+
function printChatGptFormValues(values) {
|
|
117
|
+
printLabeledUrl('Open ChatGPT Apps', CHATGPT_APPS_CREATE_URL);
|
|
118
|
+
printUseLines(formatChatGptFormValueLines(values));
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* @param {Object} options
|
|
123
|
+
* @returns {string[]}
|
|
124
|
+
*/
|
|
125
|
+
function callbackUrlsForAdd(options = {}) {
|
|
126
|
+
const urls = [...CHATGPT_KNOWN_CALLBACK_URIS];
|
|
127
|
+
const extra = typeof options.callbackUrl === 'string' ? options.callbackUrl.trim() : '';
|
|
128
|
+
if (extra && !urls.includes(extra)) {
|
|
129
|
+
urls.push(extra);
|
|
130
|
+
}
|
|
131
|
+
return urls;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* @param {Object} result
|
|
136
|
+
* @param {Object} ctx
|
|
137
|
+
* @returns {void}
|
|
138
|
+
*/
|
|
139
|
+
function printChatGptCallbackResult(result, ctx) {
|
|
140
|
+
if (!result.fallback) {
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
logger.log(
|
|
144
|
+
formatWarningLine(
|
|
145
|
+
`Missing integration-client:update. Add the callback on the ChatGPT profile: ${result.profileUrl || chatgptProfileUrl(ctx)}`
|
|
146
|
+
)
|
|
147
|
+
);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Remaining OpenAI-owned steps after AI Fabrix prep.
|
|
152
|
+
* @returns {void}
|
|
153
|
+
*/
|
|
154
|
+
function printChatGptRemainingSteps() {
|
|
155
|
+
printRemainingSteps('ChatGPT', CHATGPT_REMAINING_STEPS);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* @param {Object} params
|
|
160
|
+
* @returns {Promise<void>}
|
|
161
|
+
*/
|
|
162
|
+
async function runChatGptChannelAddContinuation(params) {
|
|
163
|
+
const preflight = params.preflight || (await runChatGptPreflight(params));
|
|
164
|
+
const client = await loadChatGptClientOrNull(params.ctx);
|
|
165
|
+
const values = buildChatGptFormValues({
|
|
166
|
+
runtimeValues: params.runtimeValues,
|
|
167
|
+
integrationClient: client,
|
|
168
|
+
asMetadata: preflight && preflight.asMetadata
|
|
169
|
+
});
|
|
170
|
+
printChatGptFormValues(values);
|
|
171
|
+
const result = await registerChatGptCallbackUrls({
|
|
172
|
+
ctx: params.ctx,
|
|
173
|
+
callbackUrls: callbackUrlsForAdd(params.options)
|
|
174
|
+
});
|
|
175
|
+
printChatGptCallbackResult(result, params.ctx);
|
|
176
|
+
printChatGptRemainingSteps();
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* @param {Object} params
|
|
181
|
+
* @returns {Promise<void>}
|
|
182
|
+
*/
|
|
183
|
+
async function maybeRunChatGptContinuation(params) {
|
|
184
|
+
if (params.channelTarget !== 'chatgpt') {
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
await runChatGptChannelAddContinuation(params);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* After callback PUT: re-validate Runtime and OAuth.
|
|
192
|
+
* @param {Object} ctx
|
|
193
|
+
* @returns {Promise<void>}
|
|
194
|
+
*/
|
|
195
|
+
async function runChatGptConfigureValidate(ctx) {
|
|
196
|
+
let packageContext = null;
|
|
197
|
+
try {
|
|
198
|
+
packageContext = await loadChannelPackageContext({
|
|
199
|
+
dataplaneBaseUrl: ctx.dataplaneUrl,
|
|
200
|
+
authConfig: ctx.authConfig,
|
|
201
|
+
channelTarget: 'chatgpt'
|
|
202
|
+
});
|
|
203
|
+
} catch {
|
|
204
|
+
packageContext = null;
|
|
205
|
+
}
|
|
206
|
+
const vendor = vendorFacingUrlsFromCtx(ctx);
|
|
207
|
+
const runtimeValues = resolveChannelRuntimeValues({
|
|
208
|
+
dataplaneBaseUrl: vendor.dataplaneBaseUrl,
|
|
209
|
+
controllerUrl: vendor.controllerUrl,
|
|
210
|
+
channelTarget: 'chatgpt',
|
|
211
|
+
appName: 'ChatGPT',
|
|
212
|
+
environment: ctx.environment,
|
|
213
|
+
packageContext
|
|
214
|
+
});
|
|
215
|
+
await maybeAssertChatGptPreflight({
|
|
216
|
+
channelTarget: 'chatgpt',
|
|
217
|
+
runtimeValues,
|
|
218
|
+
ctx,
|
|
219
|
+
options: {}
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
module.exports = {
|
|
224
|
+
maybeAssertChatGptPreflight,
|
|
225
|
+
maybeRunChatGptContinuation,
|
|
226
|
+
runChatGptChannelAddContinuation,
|
|
227
|
+
runChatGptConfigureValidate,
|
|
228
|
+
callbackUrlsForAdd
|
|
229
|
+
};
|
|
@@ -0,0 +1,396 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fail-closed ChatGPT channel preflight (MCP + OAuth discovery + PKCE S256).
|
|
3
|
+
*
|
|
4
|
+
* @fileoverview Validate Runtime MCP and OAuth before ChatGPT workspace steps
|
|
5
|
+
* @author AI Fabrix Team
|
|
6
|
+
* @version 1.0.0
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
'use strict';
|
|
10
|
+
|
|
11
|
+
const { fetchWithOptionalDevPlatformTls } = require('../utils/dev-platform-tls');
|
|
12
|
+
const { isRuntimeMcpGatewayReachable } = require('./runtime-tool-catalog');
|
|
13
|
+
const { CHATGPT_RUNTIME_SYSTEM_KEY } = require('./chatgpt-runtime-contract');
|
|
14
|
+
|
|
15
|
+
const LOCAL_HOSTS = new Set(['localhost', '127.0.0.1', '::1']);
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* @param {string} message
|
|
19
|
+
* @returns {Error}
|
|
20
|
+
*/
|
|
21
|
+
function createChatGptPreflightError(message) {
|
|
22
|
+
const error = new Error(`ChatGPT preflight: ${message}`);
|
|
23
|
+
error.boundary = 'preflight';
|
|
24
|
+
return error;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* @param {string|undefined} mcpUrl
|
|
29
|
+
* @returns {URL}
|
|
30
|
+
*/
|
|
31
|
+
function parseRequiredMcpUrl(mcpUrl) {
|
|
32
|
+
const trimmed = typeof mcpUrl === 'string' ? mcpUrl.trim() : '';
|
|
33
|
+
if (!trimmed) {
|
|
34
|
+
throw createChatGptPreflightError('Runtime MCP URL is missing.');
|
|
35
|
+
}
|
|
36
|
+
try {
|
|
37
|
+
return new URL(trimmed);
|
|
38
|
+
} catch {
|
|
39
|
+
throw createChatGptPreflightError(`Runtime MCP URL is not a valid URL: ${trimmed}`);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* HTTPS required except localhost HTTP.
|
|
45
|
+
* @param {URL} parsed
|
|
46
|
+
* @returns {void}
|
|
47
|
+
*/
|
|
48
|
+
function assertMcpUrlScheme(parsed) {
|
|
49
|
+
if (parsed.protocol === 'https:') {
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
if (parsed.protocol === 'http:' && LOCAL_HOSTS.has(parsed.hostname)) {
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
throw createChatGptPreflightError(
|
|
56
|
+
'Runtime MCP URL must be HTTPS (localhost HTTP is allowed).'
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Builder-api runs on Docker DNS. Public Traefik hosts resolve to 127.0.0.1:443
|
|
62
|
+
* inside the container (ECONNREFUSED). Rewrite same-host dataplane/auth URLs
|
|
63
|
+
* to in-cluster origins when ctx.dataplaneUrl is already internal.
|
|
64
|
+
*
|
|
65
|
+
* @param {string} url
|
|
66
|
+
* @param {{ dataplaneUrl?: string, publicHostname?: string }} [ctx]
|
|
67
|
+
* @returns {string}
|
|
68
|
+
*/
|
|
69
|
+
function keycloakInClusterOrigin() {
|
|
70
|
+
const raw = process.env.KEYCLOAK_INTERNAL_SERVER_URL || 'http://keycloak:8080';
|
|
71
|
+
try {
|
|
72
|
+
const parsed = new URL(raw);
|
|
73
|
+
if (parsed.hostname === 'localhost' || parsed.hostname === '127.0.0.1') {
|
|
74
|
+
return 'http://keycloak:8080';
|
|
75
|
+
}
|
|
76
|
+
return parsed.origin;
|
|
77
|
+
} catch {
|
|
78
|
+
return 'http://keycloak:8080';
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function rewriteDevPlatformPath(target, dp) {
|
|
83
|
+
const dpPath = (dp.pathname || '').replace(/\/$/, '');
|
|
84
|
+
if (dpPath && target.pathname.startsWith(`${dpPath}/`)) {
|
|
85
|
+
return `${dp.origin}${target.pathname}${target.search}`;
|
|
86
|
+
}
|
|
87
|
+
if (target.pathname === '/auth' || target.pathname.startsWith('/auth/')) {
|
|
88
|
+
return `${keycloakInClusterOrigin()}${target.pathname}${target.search}`;
|
|
89
|
+
}
|
|
90
|
+
return null;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function rewriteUrlForInClusterFetch(url, ctx = {}) {
|
|
94
|
+
const dataplaneUrl = ctx.dataplaneUrl;
|
|
95
|
+
if (!dataplaneUrl) {
|
|
96
|
+
return url;
|
|
97
|
+
}
|
|
98
|
+
try {
|
|
99
|
+
const target = new URL(url);
|
|
100
|
+
const dp = new URL(dataplaneUrl);
|
|
101
|
+
if (target.origin === dp.origin) {
|
|
102
|
+
return url;
|
|
103
|
+
}
|
|
104
|
+
const publicHostname = ctx.publicHostname;
|
|
105
|
+
const hostMatch = Boolean(publicHostname && target.hostname === publicHostname);
|
|
106
|
+
const inCluster = dp.hostname === 'dataplane' || !dp.hostname.includes('.');
|
|
107
|
+
if (!hostMatch && !inCluster) {
|
|
108
|
+
return url;
|
|
109
|
+
}
|
|
110
|
+
return rewriteDevPlatformPath(target, dp) || url;
|
|
111
|
+
} catch {
|
|
112
|
+
return url;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* @param {{ dataplaneUrl?: string, publicHostname?: string }} [rewriteCtx]
|
|
118
|
+
* @returns {(url: string) => Promise<Object>}
|
|
119
|
+
*/
|
|
120
|
+
function createDefaultFetchJson(rewriteCtx = {}) {
|
|
121
|
+
return async function defaultFetchJson(url) {
|
|
122
|
+
const fetchUrl = rewriteUrlForInClusterFetch(url, rewriteCtx);
|
|
123
|
+
try {
|
|
124
|
+
const response = await fetchWithOptionalDevPlatformTls(fetchUrl, {
|
|
125
|
+
headers: { Accept: 'application/json' }
|
|
126
|
+
});
|
|
127
|
+
if (!response.ok) {
|
|
128
|
+
throw new Error(`HTTP ${response.status} from ${fetchUrl}`);
|
|
129
|
+
}
|
|
130
|
+
return response.json();
|
|
131
|
+
} catch (error) {
|
|
132
|
+
const cause = error && error.cause ? error.cause.code || error.cause.message : '';
|
|
133
|
+
if (error && error.message && error.message.startsWith('HTTP ')) {
|
|
134
|
+
throw error;
|
|
135
|
+
}
|
|
136
|
+
throw new Error(`fetch failed ${fetchUrl}${cause ? ` (${cause})` : ''}`);
|
|
137
|
+
}
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* RFC 9728 well-known URL derived from the MCP resource path.
|
|
143
|
+
* @param {URL} mcpUrl
|
|
144
|
+
* @returns {string}
|
|
145
|
+
*/
|
|
146
|
+
function rfc9728ProtectedResourceUrl(mcpUrl) {
|
|
147
|
+
const pathname = (mcpUrl.pathname || '/').replace(/\/$/, '') || '/';
|
|
148
|
+
const pathWithQuery = mcpUrl.search ? `${pathname}${mcpUrl.search}` : pathname;
|
|
149
|
+
return `${mcpUrl.origin}/.well-known/oauth-protected-resource${pathWithQuery}`;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Public dataplane base (origin plus optional vdir such as /data).
|
|
154
|
+
* @param {URL} mcpUrl
|
|
155
|
+
* @returns {string}
|
|
156
|
+
*/
|
|
157
|
+
function dataplanePublicBaseFromMcpUrl(mcpUrl) {
|
|
158
|
+
const path = (mcpUrl.pathname || '').replace(/\/$/, '');
|
|
159
|
+
const browser = path.match(/^(.*)\/mcp\/v1\/[^/]+$/);
|
|
160
|
+
if (browser) {
|
|
161
|
+
return browser[1] ? `${mcpUrl.origin}${browser[1]}` : mcpUrl.origin;
|
|
162
|
+
}
|
|
163
|
+
const api = path.match(/^(.*)\/api\/v1\/external\/systems\/[^/]+\/mcp$/);
|
|
164
|
+
if (api) {
|
|
165
|
+
return api[1] ? `${mcpUrl.origin}${api[1]}` : mcpUrl.origin;
|
|
166
|
+
}
|
|
167
|
+
return mcpUrl.origin;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* @param {string} pathname
|
|
172
|
+
* @returns {string}
|
|
173
|
+
*/
|
|
174
|
+
function runtimeSystemKeyFromMcpPath(pathname) {
|
|
175
|
+
const path = String(pathname || '').replace(/\/$/, '');
|
|
176
|
+
const browserMcp = path.match(/\/mcp\/v1\/([^/]+)$/);
|
|
177
|
+
if (browserMcp) {
|
|
178
|
+
return browserMcp[1];
|
|
179
|
+
}
|
|
180
|
+
const apiMcp = path.match(/\/external\/systems\/([^/]+)\/mcp$/);
|
|
181
|
+
if (apiMcp) {
|
|
182
|
+
return apiMcp[1];
|
|
183
|
+
}
|
|
184
|
+
return CHATGPT_RUNTIME_SYSTEM_KEY;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* Traefik-reachable discovery first; RFC 9728 origin path last (often unrouted).
|
|
189
|
+
* @param {URL} mcpUrl
|
|
190
|
+
* @returns {string[]}
|
|
191
|
+
*/
|
|
192
|
+
function protectedResourceCandidateUrls(mcpUrl) {
|
|
193
|
+
const publicBase = dataplanePublicBaseFromMcpUrl(mcpUrl);
|
|
194
|
+
const key = runtimeSystemKeyFromMcpPath(mcpUrl.pathname);
|
|
195
|
+
return [
|
|
196
|
+
`${publicBase}/api/v1/external/systems/${key}/discovery/oauth-protected-resource`,
|
|
197
|
+
`${publicBase}/api/v1/external/systems/${key}/.well-known/oauth-protected-resource`,
|
|
198
|
+
rfc9728ProtectedResourceUrl(mcpUrl)
|
|
199
|
+
];
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* @param {string[]} urls
|
|
204
|
+
* @param {(url: string) => Promise<Object>} fetchJson
|
|
205
|
+
* @returns {Promise<Object>}
|
|
206
|
+
*/
|
|
207
|
+
async function fetchFirstJson(urls, fetchJson) {
|
|
208
|
+
const errors = [];
|
|
209
|
+
for (const url of urls) {
|
|
210
|
+
try {
|
|
211
|
+
const body = await fetchJson(url);
|
|
212
|
+
if (body && typeof body === 'object') {
|
|
213
|
+
return body;
|
|
214
|
+
}
|
|
215
|
+
} catch (error) {
|
|
216
|
+
errors.push(`${url}: ${error.message}`);
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
throw createChatGptPreflightError(errors.join('; ') || 'No document returned.');
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* @param {Object} prm
|
|
224
|
+
* @param {URL} mcpUrl
|
|
225
|
+
* @returns {void}
|
|
226
|
+
*/
|
|
227
|
+
function assertProtectedResource(prm, mcpUrl) {
|
|
228
|
+
const resource = typeof prm.resource === 'string' ? prm.resource.trim() : '';
|
|
229
|
+
if (!resource) {
|
|
230
|
+
throw createChatGptPreflightError(
|
|
231
|
+
'OAuth protected-resource metadata is missing a resource/audience.'
|
|
232
|
+
);
|
|
233
|
+
}
|
|
234
|
+
let resourceHost = '';
|
|
235
|
+
try {
|
|
236
|
+
resourceHost = new URL(resource).hostname;
|
|
237
|
+
} catch {
|
|
238
|
+
resourceHost = '';
|
|
239
|
+
}
|
|
240
|
+
if (resourceHost && resourceHost !== mcpUrl.hostname) {
|
|
241
|
+
throw createChatGptPreflightError(
|
|
242
|
+
`Protected resource audience host ${resourceHost} does not match MCP host ${mcpUrl.hostname}.`
|
|
243
|
+
);
|
|
244
|
+
}
|
|
245
|
+
if (!Array.isArray(prm.authorization_servers) || prm.authorization_servers.length === 0) {
|
|
246
|
+
throw createChatGptPreflightError(
|
|
247
|
+
'OAuth protected-resource metadata is missing authorization_servers.'
|
|
248
|
+
);
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
/**
|
|
253
|
+
* @param {string} issuer
|
|
254
|
+
* @returns {string[]}
|
|
255
|
+
*/
|
|
256
|
+
function authorizationServerDiscoveryUrls(issuer) {
|
|
257
|
+
const base = String(issuer || '').replace(/\/$/, '');
|
|
258
|
+
return [
|
|
259
|
+
`${base}/.well-known/oauth-authorization-server`,
|
|
260
|
+
`${base}/.well-known/openid-configuration`
|
|
261
|
+
];
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* Microsoft Entra is advertised on the same MCP resource for Copilot SSO.
|
|
266
|
+
* ChatGPT uses the customer OAuth profile, not Entra.
|
|
267
|
+
* @param {string} issuer
|
|
268
|
+
* @returns {boolean}
|
|
269
|
+
*/
|
|
270
|
+
function isMicrosoftEntraIssuer(issuer) {
|
|
271
|
+
try {
|
|
272
|
+
return new URL(String(issuer)).hostname === 'login.microsoftonline.com';
|
|
273
|
+
} catch {
|
|
274
|
+
return /login\.microsoftonline\.com/i.test(String(issuer || ''));
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
/**
|
|
279
|
+
* @param {string[]|undefined} issuers
|
|
280
|
+
* @returns {string[]}
|
|
281
|
+
*/
|
|
282
|
+
function chatgptAuthorizationServers(issuers) {
|
|
283
|
+
const list = Array.isArray(issuers)
|
|
284
|
+
? issuers.filter((value) => typeof value === 'string' && value.trim())
|
|
285
|
+
: [];
|
|
286
|
+
const customerOauth = list.filter((value) => !isMicrosoftEntraIssuer(value));
|
|
287
|
+
return customerOauth.length ? customerOauth : list;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
/**
|
|
291
|
+
* RFC 8414: grant_types_supported is optional; omitted defaults include
|
|
292
|
+
* authorization_code when an authorization endpoint is present.
|
|
293
|
+
* @param {Object} asMetadata
|
|
294
|
+
* @returns {void}
|
|
295
|
+
*/
|
|
296
|
+
function assertAuthorizationCodePkce(asMetadata) {
|
|
297
|
+
const grants = Array.isArray(asMetadata.grant_types_supported)
|
|
298
|
+
? asMetadata.grant_types_supported
|
|
299
|
+
: null;
|
|
300
|
+
const hasAuthorizationEndpoint =
|
|
301
|
+
typeof asMetadata.authorization_endpoint === 'string' &&
|
|
302
|
+
asMetadata.authorization_endpoint.trim() !== '';
|
|
303
|
+
const supportsCode =
|
|
304
|
+
grants && grants.length > 0
|
|
305
|
+
? grants.includes('authorization_code')
|
|
306
|
+
: hasAuthorizationEndpoint;
|
|
307
|
+
if (!supportsCode) {
|
|
308
|
+
throw createChatGptPreflightError(
|
|
309
|
+
'Authorization server must advertise the authorization_code grant.'
|
|
310
|
+
);
|
|
311
|
+
}
|
|
312
|
+
const methods = Array.isArray(asMetadata.code_challenge_methods_supported)
|
|
313
|
+
? asMetadata.code_challenge_methods_supported
|
|
314
|
+
: [];
|
|
315
|
+
if (!methods.includes('S256')) {
|
|
316
|
+
throw createChatGptPreflightError(
|
|
317
|
+
'Authorization server must advertise PKCE S256.'
|
|
318
|
+
);
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
/**
|
|
323
|
+
* @param {string[]} issuers
|
|
324
|
+
* @param {(url: string) => Promise<Object>} fetchJson
|
|
325
|
+
* @returns {Promise<Object>}
|
|
326
|
+
*/
|
|
327
|
+
async function fetchChatGptAuthorizationServerMetadata(issuers, fetchJson) {
|
|
328
|
+
const errors = [];
|
|
329
|
+
for (const issuer of chatgptAuthorizationServers(issuers)) {
|
|
330
|
+
try {
|
|
331
|
+
const metadata = await fetchFirstJson(
|
|
332
|
+
authorizationServerDiscoveryUrls(issuer),
|
|
333
|
+
fetchJson
|
|
334
|
+
);
|
|
335
|
+
assertAuthorizationCodePkce(metadata);
|
|
336
|
+
return metadata;
|
|
337
|
+
} catch (error) {
|
|
338
|
+
errors.push(`${issuer}: ${error.message}`);
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
throw createChatGptPreflightError(
|
|
342
|
+
errors.join('; ') || 'No authorization server metadata returned.'
|
|
343
|
+
);
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
/**
|
|
347
|
+
* @param {Object} params
|
|
348
|
+
* @returns {Promise<boolean>}
|
|
349
|
+
*/
|
|
350
|
+
async function defaultMcpReachable(params) {
|
|
351
|
+
return isRuntimeMcpGatewayReachable(
|
|
352
|
+
params.ctx && params.ctx.dataplaneUrl,
|
|
353
|
+
params.ctx && params.ctx.authConfig,
|
|
354
|
+
CHATGPT_RUNTIME_SYSTEM_KEY
|
|
355
|
+
);
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
/**
|
|
359
|
+
* Live MCP + OAuth discovery. Skipped on --dry-run.
|
|
360
|
+
* @param {Object} params
|
|
361
|
+
* @returns {Promise<{ skipped?: boolean, mcpUrl?: string, prm?: Object, asMetadata?: Object }>}
|
|
362
|
+
*/
|
|
363
|
+
async function runChatGptPreflight(params) {
|
|
364
|
+
if (params.options && params.options.dryRun === true) {
|
|
365
|
+
return { skipped: true };
|
|
366
|
+
}
|
|
367
|
+
const parsed = parseRequiredMcpUrl(params.runtimeValues && params.runtimeValues.mcpUrl);
|
|
368
|
+
assertMcpUrlScheme(parsed);
|
|
369
|
+
const reachable = params.mcpReachable || defaultMcpReachable;
|
|
370
|
+
if (!(await reachable(params))) {
|
|
371
|
+
throw createChatGptPreflightError('Runtime MCP endpoint is not reachable.');
|
|
372
|
+
}
|
|
373
|
+
const fetchJson =
|
|
374
|
+
params.fetchJson ||
|
|
375
|
+
createDefaultFetchJson({
|
|
376
|
+
dataplaneUrl: params.ctx && params.ctx.dataplaneUrl,
|
|
377
|
+
publicHostname: parsed.hostname
|
|
378
|
+
});
|
|
379
|
+
const prm = await fetchFirstJson(protectedResourceCandidateUrls(parsed), fetchJson);
|
|
380
|
+
assertProtectedResource(prm, parsed);
|
|
381
|
+
const asMetadata = await fetchChatGptAuthorizationServerMetadata(
|
|
382
|
+
prm.authorization_servers,
|
|
383
|
+
fetchJson
|
|
384
|
+
);
|
|
385
|
+
return { mcpUrl: parsed.href, prm, asMetadata };
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
module.exports = {
|
|
389
|
+
createChatGptPreflightError,
|
|
390
|
+
parseRequiredMcpUrl,
|
|
391
|
+
assertMcpUrlScheme,
|
|
392
|
+
protectedResourceCandidateUrls,
|
|
393
|
+
chatgptAuthorizationServers,
|
|
394
|
+
rewriteUrlForInClusterFetch,
|
|
395
|
+
runChatGptPreflight
|
|
396
|
+
};
|
|
@@ -13,6 +13,15 @@ const RUNTIME_ORCHESTRATION_SYSTEM_KEY = 'runtime-orchestration';
|
|
|
13
13
|
const CHATGPT_RUNTIME_SYSTEM_KEY = RUNTIME_ORCHESTRATION_SYSTEM_KEY;
|
|
14
14
|
const CHATGPT_RUNTIME_TRANSPORT = 'streamable-http';
|
|
15
15
|
const CHATGPT_RUNTIME_AUTH_MODE = 'oauth21-authorization-code-pkce-s256';
|
|
16
|
+
const CHATGPT_OAUTH2_PROFILE_ID = 'miso-chatgpt';
|
|
17
|
+
const CHATGPT_OAUTH2_PROFILE_PATH = '/external-access/oauth2-profiles/miso-chatgpt';
|
|
18
|
+
const CHATGPT_INTEGRATION_CLIENT_KEY = 'chatgpt';
|
|
19
|
+
const CHATGPT_APPS_CREATE_URL = 'https://chatgpt.com/admin/apps?tab=drafts';
|
|
20
|
+
const CHATGPT_CATALOG_CALLBACK_URI = 'https://chat.openai.com/aip/oauth/callback';
|
|
21
|
+
const CHATGPT_APPS_CALLBACK_URI = 'https://chatgpt.com/connector/oauth/callback';
|
|
22
|
+
const CHATGPT_KNOWN_CALLBACK_URIS = [CHATGPT_CATALOG_CALLBACK_URI, CHATGPT_APPS_CALLBACK_URI];
|
|
23
|
+
const CHATGPT_CONNECTOR_DESCRIPTION =
|
|
24
|
+
'Use governed enterprise work tools only. Resolve Role Assistants dynamically and keep business reasoning inside authorized Role Assistant boundaries.';
|
|
16
25
|
|
|
17
26
|
const COPILOT_RUNTIME_SYSTEM_KEY = RUNTIME_ORCHESTRATION_SYSTEM_KEY;
|
|
18
27
|
const COPILOT_RUNTIME_TRANSPORT = 'streamable-http';
|
|
@@ -23,6 +32,14 @@ module.exports = {
|
|
|
23
32
|
CHATGPT_RUNTIME_SYSTEM_KEY,
|
|
24
33
|
CHATGPT_RUNTIME_TRANSPORT,
|
|
25
34
|
CHATGPT_RUNTIME_AUTH_MODE,
|
|
35
|
+
CHATGPT_OAUTH2_PROFILE_ID,
|
|
36
|
+
CHATGPT_OAUTH2_PROFILE_PATH,
|
|
37
|
+
CHATGPT_INTEGRATION_CLIENT_KEY,
|
|
38
|
+
CHATGPT_APPS_CREATE_URL,
|
|
39
|
+
CHATGPT_CATALOG_CALLBACK_URI,
|
|
40
|
+
CHATGPT_APPS_CALLBACK_URI,
|
|
41
|
+
CHATGPT_KNOWN_CALLBACK_URIS,
|
|
42
|
+
CHATGPT_CONNECTOR_DESCRIPTION,
|
|
26
43
|
COPILOT_RUNTIME_SYSTEM_KEY,
|
|
27
44
|
COPILOT_RUNTIME_TRANSPORT,
|
|
28
45
|
COPILOT_RUNTIME_AUTH_MODE
|