@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
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Builder API HTTP server (
|
|
2
|
+
* Builder API HTTP server (pre-Nest unified router).
|
|
3
3
|
*
|
|
4
4
|
* @fileoverview Full V1 route set via capability registry
|
|
5
5
|
* @author AI Fabrix Team
|
|
@@ -18,6 +18,8 @@ const { writeAccessLog } = require('./access-log');
|
|
|
18
18
|
const { dispatchCapabilityRoute } = require('./builder-api-route-dispatch');
|
|
19
19
|
const { renderBuilderApiRedocHtml, resolvePublicBaseUrl } = require('./openapi-docs-serving');
|
|
20
20
|
const { buildProblemDetails, writeProblemDetails } = require('./http-problem-details');
|
|
21
|
+
const { sendRaw } = require('./http-raw-response');
|
|
22
|
+
const { handleCorsPreflight } = require('./builder-api-cors');
|
|
21
23
|
|
|
22
24
|
/**
|
|
23
25
|
* @param {import('http').ServerResponse} res
|
|
@@ -171,6 +173,10 @@ async function handleRequest(req, res) {
|
|
|
171
173
|
const url = new URL(req.url || '/', `http://${req.headers.host || 'localhost'}`);
|
|
172
174
|
const pathname = url.pathname;
|
|
173
175
|
|
|
176
|
+
if (handleCorsPreflight(req, res, requestId, pathname, logRequestOutcome)) {
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
|
|
174
180
|
if (isRedocHtmlPath(pathname) || isOpenApiJsonPath(pathname)) {
|
|
175
181
|
handleOpenApiDocs(req, res, pathname, requestId);
|
|
176
182
|
logRequestOutcome({
|
|
@@ -196,6 +202,7 @@ async function handleRequest(req, res) {
|
|
|
196
202
|
res,
|
|
197
203
|
sendJson,
|
|
198
204
|
sendProblem,
|
|
205
|
+
sendRaw,
|
|
199
206
|
readJsonBody,
|
|
200
207
|
resolved,
|
|
201
208
|
query,
|
|
@@ -76,8 +76,62 @@ const CAPABILITIES = Object.freeze([
|
|
|
76
76
|
{ method: 'GET', route: '/systems/:systemKey/channels/package/zip', handlerId: 'getChannelPackageZip', operationType: 'channel.read', mutating: false },
|
|
77
77
|
{ method: 'GET', route: '/systems/:systemKey/channels/branding', handlerId: 'getChannelBranding', operationType: 'channel.read', mutating: false },
|
|
78
78
|
{ method: 'PATCH', route: '/systems/:systemKey/channels/branding', handlerId: 'patchChannelBranding', operationType: 'channel', mutating: true },
|
|
79
|
+
{ method: 'GET', route: '/channels', handlerId: 'listChannelTargets', operationType: 'channel.read', mutating: false },
|
|
80
|
+
{ method: 'GET', route: '/channels/:definitionKey', handlerId: 'getChannelDefinitionDetail', operationType: 'channel.read', mutating: false },
|
|
81
|
+
{
|
|
82
|
+
method: 'GET',
|
|
83
|
+
route: '/channels/:definitionKey/installations/:systemKey',
|
|
84
|
+
handlerId: 'getChannelInstallation',
|
|
85
|
+
operationType: 'channel.read',
|
|
86
|
+
mutating: false
|
|
87
|
+
},
|
|
88
|
+
{ method: 'POST', route: '/systems/:systemKey/channels/install', handlerId: 'runChannelInstall', operationType: 'channel', mutating: true },
|
|
89
|
+
{ method: 'GET', route: '/systems/:systemKey/channels/install', handlerId: 'getChannelInstall', operationType: 'channel.read', mutating: false },
|
|
90
|
+
{
|
|
91
|
+
method: 'GET',
|
|
92
|
+
route: '/systems/:systemKey/channels/install/help.md',
|
|
93
|
+
handlerId: 'getChannelInstallHelp',
|
|
94
|
+
operationType: 'channel.read',
|
|
95
|
+
mutating: false
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
method: 'POST',
|
|
99
|
+
route: '/systems/:systemKey/channels/install/continue',
|
|
100
|
+
handlerId: 'continueChannelInstall',
|
|
101
|
+
operationType: 'channel',
|
|
102
|
+
mutating: true
|
|
103
|
+
},
|
|
104
|
+
{ method: 'GET', route: '/systems/:systemKey/channels/artifacts', handlerId: 'listChannelArtifacts', operationType: 'channel.read', mutating: false },
|
|
105
|
+
{
|
|
106
|
+
method: 'GET',
|
|
107
|
+
route: '/systems/:systemKey/channels/artifacts/:artifactId',
|
|
108
|
+
handlerId: 'getChannelArtifact',
|
|
109
|
+
operationType: 'channel.read',
|
|
110
|
+
mutating: false
|
|
111
|
+
},
|
|
112
|
+
{
|
|
113
|
+
method: 'GET',
|
|
114
|
+
route: '/systems/:systemKey/channels/callbacks/:provider',
|
|
115
|
+
handlerId: 'getChannelCallback',
|
|
116
|
+
operationType: 'channel',
|
|
117
|
+
mutating: true
|
|
118
|
+
},
|
|
119
|
+
{
|
|
120
|
+
method: 'POST',
|
|
121
|
+
route: '/systems/:systemKey/channels/callbacks/:provider',
|
|
122
|
+
handlerId: 'postChannelCallback',
|
|
123
|
+
operationType: 'channel',
|
|
124
|
+
mutating: true
|
|
125
|
+
},
|
|
79
126
|
{ method: 'GET', route: '/health', handlerId: 'health', operationType: 'infra', mutating: false },
|
|
80
127
|
{ method: 'GET', route: '/health/ready', handlerId: 'ready', operationType: 'infra', mutating: false },
|
|
128
|
+
{
|
|
129
|
+
method: 'GET',
|
|
130
|
+
route: '/auth/permissions',
|
|
131
|
+
handlerId: 'getAuthPermissions',
|
|
132
|
+
operationType: 'infra',
|
|
133
|
+
mutating: false
|
|
134
|
+
},
|
|
81
135
|
{
|
|
82
136
|
method: 'GET',
|
|
83
137
|
route: '/help',
|
|
@@ -0,0 +1,359 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resolve channel install help for a definition or instance key.
|
|
3
|
+
*
|
|
4
|
+
* @fileoverview POST identity + per-instance GET (session or synthesized)
|
|
5
|
+
* @author AI Fabrix Team
|
|
6
|
+
* @version 1.0.0
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
'use strict';
|
|
10
|
+
|
|
11
|
+
const path = require('path');
|
|
12
|
+
const fs = require('../internal/fs-real-sync');
|
|
13
|
+
const { toChannelInstallEnvelope } = require('../channels/channel-install-envelope');
|
|
14
|
+
const { toChannelInstallHttpPayload } = require('../channels/channel-install-http-payload');
|
|
15
|
+
const { resolveChannelRuntimeValues } = require('../channels/channel-runtime-resolver');
|
|
16
|
+
const { vendorFacingUrlsFromCtx } = require('../channels/vendor-dataplane-url');
|
|
17
|
+
const {
|
|
18
|
+
getChannelDefinition,
|
|
19
|
+
listChannelDefinitionKeys
|
|
20
|
+
} = require('../channels/channel-definition-registry');
|
|
21
|
+
const {
|
|
22
|
+
readChannelInstallSession,
|
|
23
|
+
writeChannelInstallSession,
|
|
24
|
+
httpError
|
|
25
|
+
} = require('./channel-install-session');
|
|
26
|
+
const { peekPortalMicrosoftLogin } = require('../channels/microsoft-provisioning/login-portal');
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* @param {Object} payload
|
|
30
|
+
* @param {string} definitionKey
|
|
31
|
+
* @param {string} installationKey
|
|
32
|
+
* @returns {Object}
|
|
33
|
+
*/
|
|
34
|
+
function attachInstallIdentity(payload, definitionKey, installationKey) {
|
|
35
|
+
return {
|
|
36
|
+
...payload,
|
|
37
|
+
definitionKey,
|
|
38
|
+
installationKey,
|
|
39
|
+
systemKey: installationKey
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* @param {Object[]} [actions]
|
|
45
|
+
* @param {string} id
|
|
46
|
+
* @returns {string}
|
|
47
|
+
*/
|
|
48
|
+
function copyActionValue(actions, id) {
|
|
49
|
+
const row = (actions || []).find((action) => action && action.id === id);
|
|
50
|
+
return row && typeof row.value === 'string' ? row.value.trim() : '';
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* @param {Object} session
|
|
55
|
+
* @returns {Object}
|
|
56
|
+
*/
|
|
57
|
+
function runtimeValuesFromStoredEnvelope(session) {
|
|
58
|
+
const envelope = session.envelope || {};
|
|
59
|
+
const use = envelope.useValues || {};
|
|
60
|
+
const actions = envelope.userActions || [];
|
|
61
|
+
return {
|
|
62
|
+
appName:
|
|
63
|
+
use.Name ||
|
|
64
|
+
copyActionValue(actions, 'copy-name') ||
|
|
65
|
+
session.displayName ||
|
|
66
|
+
session.name ||
|
|
67
|
+
'',
|
|
68
|
+
appDescription: use.Description || copyActionValue(actions, 'copy-description') || '',
|
|
69
|
+
mcpUrl: use['Server URL'] || copyActionValue(actions, 'copy-server-url') || ''
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* @param {Object} session
|
|
75
|
+
* @returns {Object}
|
|
76
|
+
*/
|
|
77
|
+
function copilotRuntimeFromSession(session) {
|
|
78
|
+
const peek = peekPortalMicrosoftLogin(session.installationKey) || {};
|
|
79
|
+
return {
|
|
80
|
+
appName: session.displayName || session.name,
|
|
81
|
+
oauthPluginVaultReferenceId: session.oauthPluginVaultReferenceId,
|
|
82
|
+
packageAppId: session.packageAppId,
|
|
83
|
+
microsoftLoginUrl: peek.loginUrl || session.microsoftLoginUrl,
|
|
84
|
+
microsoftLoginUserCode: peek.userCode || session.microsoftLoginUserCode,
|
|
85
|
+
microsoftAuthConfigFallback: session.microsoftAuthConfigFallback === true
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* @param {Object} session
|
|
91
|
+
* @returns {Object}
|
|
92
|
+
*/
|
|
93
|
+
function refreshCopilotEnvelope(session, ctx) {
|
|
94
|
+
const definitionKey = session.definitionKey || session.channelTarget;
|
|
95
|
+
if (definitionKey !== 'microsoft-copilot') {
|
|
96
|
+
return session.envelope;
|
|
97
|
+
}
|
|
98
|
+
return toChannelInstallEnvelope({
|
|
99
|
+
channelTarget: definitionKey,
|
|
100
|
+
displayName: session.displayName || session.name || 'Microsoft Copilot',
|
|
101
|
+
ctx: {
|
|
102
|
+
instanceSystemKey: session.installationKey,
|
|
103
|
+
projectRoot: ctx && typeof ctx.getUserRoot === 'function' ? ctx.getUserRoot() : undefined
|
|
104
|
+
},
|
|
105
|
+
runtimeValues: copilotRuntimeFromSession(session),
|
|
106
|
+
verdicts: (session.envelope && session.envelope.verdicts) || {
|
|
107
|
+
runtime: { ok: true, label: 'Deployed' }
|
|
108
|
+
}
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Rebuild ChatGPT help so Open ChatGPT Apps uses the current drafts URL.
|
|
114
|
+
* @param {Object} session
|
|
115
|
+
* @returns {Object}
|
|
116
|
+
*/
|
|
117
|
+
function refreshChatGptEnvelope(session) {
|
|
118
|
+
const definitionKey = session.definitionKey || session.channelTarget;
|
|
119
|
+
if (definitionKey !== 'chatgpt') {
|
|
120
|
+
return session.envelope;
|
|
121
|
+
}
|
|
122
|
+
return toChannelInstallEnvelope({
|
|
123
|
+
channelTarget: definitionKey,
|
|
124
|
+
displayName: session.displayName || session.name || 'ChatGPT',
|
|
125
|
+
ctx: {},
|
|
126
|
+
runtimeValues: runtimeValuesFromStoredEnvelope(session),
|
|
127
|
+
verdicts: (session.envelope && session.envelope.verdicts) || {
|
|
128
|
+
runtime: { ok: true, label: 'Deployed' }
|
|
129
|
+
}
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Rebuild Claude help so connectors URL and Name/Server URL stay current.
|
|
135
|
+
* @param {Object} session
|
|
136
|
+
* @returns {Object}
|
|
137
|
+
*/
|
|
138
|
+
function refreshClaudeEnvelope(session) {
|
|
139
|
+
const definitionKey = session.definitionKey || session.channelTarget;
|
|
140
|
+
if (definitionKey !== 'claude') {
|
|
141
|
+
return session.envelope;
|
|
142
|
+
}
|
|
143
|
+
return toChannelInstallEnvelope({
|
|
144
|
+
channelTarget: definitionKey,
|
|
145
|
+
displayName: session.displayName || session.name || 'Claude',
|
|
146
|
+
ctx: {},
|
|
147
|
+
runtimeValues: runtimeValuesFromStoredEnvelope(session),
|
|
148
|
+
verdicts: (session.envelope && session.envelope.verdicts) || {
|
|
149
|
+
runtime: { ok: true, label: 'Deployed' }
|
|
150
|
+
}
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* @param {Object} session
|
|
156
|
+
* @returns {Object}
|
|
157
|
+
*/
|
|
158
|
+
function refreshStoredEnvelope(session, ctx) {
|
|
159
|
+
const definitionKey = session.definitionKey || session.channelTarget;
|
|
160
|
+
if (definitionKey === 'microsoft-copilot') {
|
|
161
|
+
return refreshCopilotEnvelope(session, ctx);
|
|
162
|
+
}
|
|
163
|
+
if (definitionKey === 'chatgpt') {
|
|
164
|
+
return refreshChatGptEnvelope(session);
|
|
165
|
+
}
|
|
166
|
+
if (definitionKey === 'claude') {
|
|
167
|
+
return refreshClaudeEnvelope(session);
|
|
168
|
+
}
|
|
169
|
+
return session.envelope;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
function helpPayloadFromSession(session, ctx) {
|
|
173
|
+
const envelope = refreshStoredEnvelope(session, ctx) || session.envelope;
|
|
174
|
+
if (envelope && typeof envelope === 'object') {
|
|
175
|
+
const payload = toChannelInstallHttpPayload(envelope);
|
|
176
|
+
const definitionKey = session.definitionKey || session.channelTarget;
|
|
177
|
+
const installationKey = session.installationKey;
|
|
178
|
+
if (!definitionKey || !installationKey) {
|
|
179
|
+
return payload;
|
|
180
|
+
}
|
|
181
|
+
return attachInstallIdentity(payload, definitionKey, installationKey);
|
|
182
|
+
}
|
|
183
|
+
const payload = {
|
|
184
|
+
helpMarkdown: session.helpMarkdown,
|
|
185
|
+
helpMarkdownContentType: 'text/markdown'
|
|
186
|
+
};
|
|
187
|
+
if (session.phase === 'waitingForUser') {
|
|
188
|
+
payload.refreshHintSeconds = 15;
|
|
189
|
+
}
|
|
190
|
+
const definitionKey = session.definitionKey || session.channelTarget;
|
|
191
|
+
const installationKey = session.installationKey;
|
|
192
|
+
if (!definitionKey || !installationKey) {
|
|
193
|
+
return payload;
|
|
194
|
+
}
|
|
195
|
+
return attachInstallIdentity(payload, definitionKey, installationKey);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* @param {string} instanceKey
|
|
200
|
+
* @returns {string|null}
|
|
201
|
+
*/
|
|
202
|
+
function definitionKeyFromInstanceKey(instanceKey) {
|
|
203
|
+
const keys = listChannelDefinitionKeys().sort((left, right) => right.length - left.length);
|
|
204
|
+
for (const key of keys) {
|
|
205
|
+
if (instanceKey === key || instanceKey.startsWith(`${key}-`)) {
|
|
206
|
+
return key;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
return null;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Catalog key for install/continue. Path may be the definition or the instance.
|
|
214
|
+
* @param {import('./workspace-context').WorkspaceContext} ctx
|
|
215
|
+
* @returns {string}
|
|
216
|
+
*/
|
|
217
|
+
function resolveInstallDefinitionKey(ctx) {
|
|
218
|
+
if (getChannelDefinition(ctx.systemKey)) {
|
|
219
|
+
return ctx.systemKey;
|
|
220
|
+
}
|
|
221
|
+
const session = readChannelInstallSession(ctx);
|
|
222
|
+
const fromSession = session && (session.definitionKey || session.channelTarget);
|
|
223
|
+
if (typeof fromSession === 'string' && getChannelDefinition(fromSession)) {
|
|
224
|
+
return fromSession;
|
|
225
|
+
}
|
|
226
|
+
const fromInstance = definitionKeyFromInstanceKey(ctx.systemKey);
|
|
227
|
+
if (fromInstance && getChannelDefinition(fromInstance)) {
|
|
228
|
+
return fromInstance;
|
|
229
|
+
}
|
|
230
|
+
return ctx.systemKey;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
* @param {import('./workspace-context').WorkspaceContext} ctx
|
|
235
|
+
* @returns {Object|null}
|
|
236
|
+
*/
|
|
237
|
+
function readInstanceSystemDoc(ctx) {
|
|
238
|
+
const filePath = path.join(ctx.getIntegrationPath(), `${ctx.systemKey}-system.json`);
|
|
239
|
+
if (!fs.existsSync(filePath)) {
|
|
240
|
+
return null;
|
|
241
|
+
}
|
|
242
|
+
try {
|
|
243
|
+
const parsed = JSON.parse(fs.readFileSync(filePath, 'utf8'));
|
|
244
|
+
return parsed && typeof parsed === 'object' ? parsed : null;
|
|
245
|
+
} catch {
|
|
246
|
+
return null;
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
/**
|
|
251
|
+
* @param {Object} doc
|
|
252
|
+
* @param {string} instanceKey
|
|
253
|
+
* @returns {string|null}
|
|
254
|
+
*/
|
|
255
|
+
function definitionKeyFromDoc(doc, instanceKey) {
|
|
256
|
+
const stamped = doc && doc.channel && doc.channel.definitionKey;
|
|
257
|
+
if (typeof stamped === 'string' && stamped.trim()) {
|
|
258
|
+
return stamped.trim();
|
|
259
|
+
}
|
|
260
|
+
return definitionKeyFromInstanceKey(instanceKey);
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
/**
|
|
264
|
+
* @param {import('./workspace-context').WorkspaceContext} ctx
|
|
265
|
+
* @param {string} definitionKey
|
|
266
|
+
* @param {string} displayName
|
|
267
|
+
* @param {string} [description]
|
|
268
|
+
* @returns {Object}
|
|
269
|
+
*/
|
|
270
|
+
function runtimeValuesForHelp(ctx, definitionKey, displayName, description) {
|
|
271
|
+
const vendor = vendorFacingUrlsFromCtx(ctx);
|
|
272
|
+
return resolveChannelRuntimeValues({
|
|
273
|
+
dataplaneBaseUrl: vendor.dataplaneBaseUrl,
|
|
274
|
+
controllerUrl: vendor.controllerUrl,
|
|
275
|
+
channelTarget: definitionKey,
|
|
276
|
+
appName: displayName,
|
|
277
|
+
instanceSystemKey: ctx.systemKey,
|
|
278
|
+
environment: ctx.environment,
|
|
279
|
+
appDescription: description || ''
|
|
280
|
+
});
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
/**
|
|
284
|
+
* @param {import('./workspace-context').WorkspaceContext} ctx
|
|
285
|
+
* @param {Object} doc
|
|
286
|
+
* @returns {{ payload: Object, envelope: Object }|null}
|
|
287
|
+
*/
|
|
288
|
+
function synthesizeInstallHelp(ctx, doc) {
|
|
289
|
+
const installationKey = ctx.systemKey;
|
|
290
|
+
const definitionKey = definitionKeyFromDoc(doc, installationKey);
|
|
291
|
+
if (!definitionKey || !getChannelDefinition(definitionKey)) {
|
|
292
|
+
return null;
|
|
293
|
+
}
|
|
294
|
+
const displayName =
|
|
295
|
+
(typeof doc.displayName === 'string' && doc.displayName.trim()) || installationKey;
|
|
296
|
+
const envelope = toChannelInstallEnvelope({
|
|
297
|
+
channelTarget: definitionKey,
|
|
298
|
+
displayName,
|
|
299
|
+
ctx: {
|
|
300
|
+
dataplaneUrl: ctx.dataplaneUrl,
|
|
301
|
+
environment: ctx.environment,
|
|
302
|
+
projectRoot: ctx.getUserRoot()
|
|
303
|
+
},
|
|
304
|
+
runtimeValues: runtimeValuesForHelp(ctx, definitionKey, displayName, doc.description),
|
|
305
|
+
verdicts: { runtime: { ok: true, label: 'Deployed' } }
|
|
306
|
+
});
|
|
307
|
+
const payload = attachInstallIdentity(
|
|
308
|
+
toChannelInstallHttpPayload(envelope),
|
|
309
|
+
definitionKey,
|
|
310
|
+
installationKey
|
|
311
|
+
);
|
|
312
|
+
writeChannelInstallSession(ctx, {
|
|
313
|
+
name: displayName,
|
|
314
|
+
displayName,
|
|
315
|
+
phase: envelope.phase,
|
|
316
|
+
envelope,
|
|
317
|
+
definitionKey,
|
|
318
|
+
installationKey
|
|
319
|
+
});
|
|
320
|
+
return { payload, envelope };
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
/**
|
|
324
|
+
* @param {import('./workspace-context').WorkspaceContext} ctx
|
|
325
|
+
* @param {Object} [query]
|
|
326
|
+
* @returns {Object}
|
|
327
|
+
*/
|
|
328
|
+
function resolveChannelInstallHelp(ctx, query = {}) {
|
|
329
|
+
const session = readChannelInstallSession(ctx);
|
|
330
|
+
if (session) {
|
|
331
|
+
if (query.verbose === 'true' || query.verbose === true) {
|
|
332
|
+
return refreshStoredEnvelope(session, ctx) || session.envelope;
|
|
333
|
+
}
|
|
334
|
+
return helpPayloadFromSession(session, ctx);
|
|
335
|
+
}
|
|
336
|
+
if (getChannelDefinition(ctx.systemKey)) {
|
|
337
|
+
throw httpError('Channel install session not found. POST install first.', 404);
|
|
338
|
+
}
|
|
339
|
+
const doc = readInstanceSystemDoc(ctx);
|
|
340
|
+
if (!doc) {
|
|
341
|
+
throw httpError('Channel install session not found. POST install first.', 404);
|
|
342
|
+
}
|
|
343
|
+
const synthesized = synthesizeInstallHelp(ctx, doc);
|
|
344
|
+
if (!synthesized) {
|
|
345
|
+
throw httpError('Channel install session not found. POST install first.', 404);
|
|
346
|
+
}
|
|
347
|
+
if (query.verbose === 'true' || query.verbose === true) {
|
|
348
|
+
return synthesized.envelope;
|
|
349
|
+
}
|
|
350
|
+
return synthesized.payload;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
module.exports = {
|
|
354
|
+
attachInstallIdentity,
|
|
355
|
+
helpPayloadFromSession,
|
|
356
|
+
definitionKeyFromInstanceKey,
|
|
357
|
+
resolveInstallDefinitionKey,
|
|
358
|
+
resolveChannelInstallHelp
|
|
359
|
+
};
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Non-secret channel install session under the workspace integration folder.
|
|
3
|
+
*
|
|
4
|
+
* @fileoverview Persist help markdown + envelope for GET continue
|
|
5
|
+
* @author AI Fabrix Team
|
|
6
|
+
* @version 1.0.0
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
'use strict';
|
|
10
|
+
|
|
11
|
+
const path = require('path');
|
|
12
|
+
const fs = require('../internal/fs-real-sync');
|
|
13
|
+
const { ensureIntegrationDirectory } = require('./workspace-context');
|
|
14
|
+
|
|
15
|
+
const SESSION_FILE = '.channel-install.json';
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* @param {import('./workspace-context').WorkspaceContext} ctx
|
|
19
|
+
* @returns {string}
|
|
20
|
+
*/
|
|
21
|
+
function sessionPath(ctx) {
|
|
22
|
+
return path.join(ctx.getIntegrationPath(), SESSION_FILE);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* @param {string} filePath
|
|
27
|
+
* @param {Object} stored
|
|
28
|
+
*/
|
|
29
|
+
function writeSessionFile(filePath, stored) {
|
|
30
|
+
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
31
|
+
fs.writeFileSync(filePath, `${JSON.stringify(stored, null, 2)}\n`, 'utf8');
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* @param {import('./workspace-context').WorkspaceContext} ctx
|
|
36
|
+
* @returns {string|null}
|
|
37
|
+
*/
|
|
38
|
+
function instanceSessionPath(ctx) {
|
|
39
|
+
const instanceKey = typeof ctx.instanceSystemKey === 'string' ? ctx.instanceSystemKey.trim() : '';
|
|
40
|
+
if (!instanceKey || instanceKey === ctx.systemKey) {
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
return path.join(ctx.getIntegrationRoot(), instanceKey, SESSION_FILE);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* @param {import('./workspace-context').WorkspaceContext} ctx
|
|
48
|
+
* @returns {Object|null}
|
|
49
|
+
*/
|
|
50
|
+
function readChannelInstallSession(ctx) {
|
|
51
|
+
const filePath = sessionPath(ctx);
|
|
52
|
+
if (!fs.existsSync(filePath)) {
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
55
|
+
try {
|
|
56
|
+
const parsed = JSON.parse(fs.readFileSync(filePath, 'utf8'));
|
|
57
|
+
return parsed && typeof parsed === 'object' ? parsed : null;
|
|
58
|
+
} catch {
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* @param {import('./workspace-context').WorkspaceContext} ctx
|
|
65
|
+
* @param {Object} session
|
|
66
|
+
* @returns {Object}
|
|
67
|
+
*/
|
|
68
|
+
function writeChannelInstallSession(ctx, session) {
|
|
69
|
+
ensureIntegrationDirectory(ctx);
|
|
70
|
+
const installationKey = ctx.instanceSystemKey || session.installationKey || ctx.systemKey;
|
|
71
|
+
const definitionKey = session.definitionKey || ctx.systemKey;
|
|
72
|
+
const stored = {
|
|
73
|
+
channelTarget: definitionKey,
|
|
74
|
+
definitionKey,
|
|
75
|
+
installationKey,
|
|
76
|
+
name: session.name,
|
|
77
|
+
displayName: session.displayName,
|
|
78
|
+
profile: session.profile || null,
|
|
79
|
+
allowExperimental: session.allowExperimental === true,
|
|
80
|
+
phase: session.phase,
|
|
81
|
+
helpMarkdown: session.helpMarkdown,
|
|
82
|
+
envelope: session.envelope,
|
|
83
|
+
slackAuthTicket: typeof session.slackAuthTicket === 'string' ? session.slackAuthTicket : undefined,
|
|
84
|
+
commandPrefix: trimSessionString(session.commandPrefix),
|
|
85
|
+
packageVersion: trimSessionString(session.packageVersion),
|
|
86
|
+
packageAppId: trimSessionString(session.packageAppId),
|
|
87
|
+
oauthPluginVaultReferenceId: trimSessionString(session.oauthPluginVaultReferenceId),
|
|
88
|
+
microsoftLoginUrl: trimSessionString(session.microsoftLoginUrl),
|
|
89
|
+
microsoftLoginUserCode: trimSessionString(session.microsoftLoginUserCode),
|
|
90
|
+
microsoftAuthConfigFallback: session.microsoftAuthConfigFallback === true ? true : undefined,
|
|
91
|
+
updatedAt: new Date().toISOString()
|
|
92
|
+
};
|
|
93
|
+
writeSessionFile(sessionPath(ctx), stored);
|
|
94
|
+
const extra = instanceSessionPath(ctx);
|
|
95
|
+
if (extra) {
|
|
96
|
+
writeSessionFile(extra, stored);
|
|
97
|
+
}
|
|
98
|
+
return stored;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* @param {string} message
|
|
103
|
+
* @param {number} statusCode
|
|
104
|
+
* @returns {Error}
|
|
105
|
+
*/
|
|
106
|
+
function httpError(message, statusCode) {
|
|
107
|
+
const err = new Error(message);
|
|
108
|
+
err.statusCode = statusCode;
|
|
109
|
+
return err;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* @param {unknown} value
|
|
114
|
+
* @returns {string|undefined}
|
|
115
|
+
*/
|
|
116
|
+
function trimSessionString(value) {
|
|
117
|
+
if (typeof value !== 'string' || !value.trim()) {
|
|
118
|
+
return undefined;
|
|
119
|
+
}
|
|
120
|
+
return value.trim();
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* @param {string} integrationRoot
|
|
125
|
+
* @param {string} folderName
|
|
126
|
+
* @returns {Object|null}
|
|
127
|
+
*/
|
|
128
|
+
function readNamedSession(integrationRoot, folderName) {
|
|
129
|
+
const filePath = path.join(integrationRoot, folderName, SESSION_FILE);
|
|
130
|
+
if (!fs.existsSync(filePath)) {
|
|
131
|
+
return null;
|
|
132
|
+
}
|
|
133
|
+
try {
|
|
134
|
+
const parsed = JSON.parse(fs.readFileSync(filePath, 'utf8'));
|
|
135
|
+
return parsed && typeof parsed === 'object' ? parsed : null;
|
|
136
|
+
} catch {
|
|
137
|
+
return null;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* @param {Object|null} session
|
|
143
|
+
* @param {string} definitionKey
|
|
144
|
+
* @returns {boolean}
|
|
145
|
+
*/
|
|
146
|
+
function sessionMatchesDefinition(session, definitionKey) {
|
|
147
|
+
if (!session) {
|
|
148
|
+
return false;
|
|
149
|
+
}
|
|
150
|
+
const key = session.definitionKey || session.channelTarget;
|
|
151
|
+
return key === definitionKey;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Catalog GET uses the definition key; the session lives on the instance folder.
|
|
156
|
+
* @param {import('./workspace-context').WorkspaceContext} ctx
|
|
157
|
+
* @param {string} definitionKey
|
|
158
|
+
* @returns {Object|null}
|
|
159
|
+
*/
|
|
160
|
+
function readChannelInstallSessionForDefinition(ctx, definitionKey) {
|
|
161
|
+
const direct = readChannelInstallSession(ctx);
|
|
162
|
+
if (sessionMatchesDefinition(direct, definitionKey)) {
|
|
163
|
+
return direct;
|
|
164
|
+
}
|
|
165
|
+
const root = typeof ctx.getIntegrationRoot === 'function' ? ctx.getIntegrationRoot() : '';
|
|
166
|
+
if (!root || !fs.existsSync(root)) {
|
|
167
|
+
return direct;
|
|
168
|
+
}
|
|
169
|
+
return newestMatchingSession(root, definitionKey, direct);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* @param {string} root
|
|
174
|
+
* @param {string} definitionKey
|
|
175
|
+
* @param {Object|null} seed
|
|
176
|
+
* @returns {Object|null}
|
|
177
|
+
*/
|
|
178
|
+
function newestMatchingSession(root, definitionKey, seed) {
|
|
179
|
+
let best = seed;
|
|
180
|
+
let bestAt = (seed && seed.updatedAt) || '';
|
|
181
|
+
for (const name of fs.readdirSync(root)) {
|
|
182
|
+
if (name !== definitionKey && !String(name).startsWith(`${definitionKey}-`)) {
|
|
183
|
+
continue;
|
|
184
|
+
}
|
|
185
|
+
const session = readNamedSession(root, name);
|
|
186
|
+
if (!sessionMatchesDefinition(session, definitionKey)) {
|
|
187
|
+
continue;
|
|
188
|
+
}
|
|
189
|
+
const updated = session.updatedAt || '';
|
|
190
|
+
if (!best || updated >= bestAt) {
|
|
191
|
+
best = session;
|
|
192
|
+
bestAt = updated;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
return best;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* @param {import('./workspace-context').WorkspaceContext} ctx
|
|
200
|
+
* @returns {Object}
|
|
201
|
+
*/
|
|
202
|
+
function requireChannelInstallSession(ctx) {
|
|
203
|
+
const session = readChannelInstallSession(ctx);
|
|
204
|
+
if (!session) {
|
|
205
|
+
throw httpError('Channel install session not found. POST install first.', 404);
|
|
206
|
+
}
|
|
207
|
+
return session;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
module.exports = {
|
|
211
|
+
SESSION_FILE,
|
|
212
|
+
sessionPath,
|
|
213
|
+
readChannelInstallSession,
|
|
214
|
+
readChannelInstallSessionForDefinition,
|
|
215
|
+
writeChannelInstallSession,
|
|
216
|
+
requireChannelInstallSession,
|
|
217
|
+
httpError
|
|
218
|
+
};
|
|
@@ -18,6 +18,8 @@ Complete validate and upload for the target systemKey so the dataplane exposes M
|
|
|
18
18
|
| slack | Slack workspace assistant |
|
|
19
19
|
| microsoft-copilot | Microsoft Copilot package (runtime-orchestration MCP) |
|
|
20
20
|
| chatgpt | ChatGPT custom MCP app (runtime-orchestration) |
|
|
21
|
+
| claude | Claude custom connector (runtime-orchestration) |
|
|
22
|
+
| mcp-client | Developer MCP client (Cursor, VS Code, Copilot IDE profile) |
|
|
21
23
|
| web-embed | Experimental embedded web assistant |
|
|
22
24
|
|
|
23
25
|
Each target requires credentials and deploy approval. Read topic connectedSystemUi after upload for dataplane handoff paths.
|
|
@@ -10,7 +10,9 @@ Every operation contains an ordered steps array. Each step is a single keyed obj
|
|
|
10
10
|
|
|
11
11
|
## Common step patterns
|
|
12
12
|
|
|
13
|
-
Fetch resolves data from OpenAPI references, raw HTTP, or
|
|
13
|
+
Fetch resolves data from OpenAPI references, raw HTTP, another datasource, a viewpoint SQL query, or local record storage. Map applies fieldMappings to normalize vendor payloads. Filter applies optional ABAC or JMESPath expressions. Output emits records for sync and storage.
|
|
14
|
+
|
|
15
|
+
`fetch.source` is a closed enum: `openapi`, `http`, `datasource`, `viewpoint`, `record`. The fetch token `record` is singular; `output.mode` stays plural `records`. Persist vs read on `record` follows operation `shape` (`create` or `update`), not the capability key. CIP `fetch.source` compiles to COM `source`.
|
|
14
16
|
|
|
15
17
|
Paginate handles cursor, page, or offset strategies for list operations. Python inline runs sandboxed transforms when declarative steps are insufficient.
|
|
16
18
|
|