@aifabrix/builder 2.53.0 → 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 +119 -16
- 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,181 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Slack Manifest API via documented `slack api` methods.
|
|
3
|
+
*
|
|
4
|
+
* @fileoverview create / update / validate — signing_secret only on create
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
'use strict';
|
|
8
|
+
|
|
9
|
+
const { runSlackApi, runNativeAppInstall } = require('./cli');
|
|
10
|
+
const { slackProvisioningError } = require('./errors');
|
|
11
|
+
const { pickSlackClientId } = require('./remaining-urls');
|
|
12
|
+
const {
|
|
13
|
+
developerInstallApp,
|
|
14
|
+
pickInstalledSlackTokens
|
|
15
|
+
} = require('./developer-install');
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* @param {Object} body
|
|
19
|
+
* @returns {Object}
|
|
20
|
+
*/
|
|
21
|
+
function requireOk(body) {
|
|
22
|
+
if (body && body.ok === true) {
|
|
23
|
+
return body;
|
|
24
|
+
}
|
|
25
|
+
const detail = body && body.error ? String(body.error) : 'unknown_error';
|
|
26
|
+
const notes = Array.isArray(body && body.errors)
|
|
27
|
+
? body.errors.map((row) => row && row.message).filter(Boolean).join('; ')
|
|
28
|
+
: '';
|
|
29
|
+
const suffix = notes ? ` (${notes})` : '';
|
|
30
|
+
throw slackProvisioningError(`Slack Manifest API failed: ${detail}${suffix}`, 'SLACK_API');
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* @param {Object} manifest
|
|
35
|
+
* @returns {Object}
|
|
36
|
+
*/
|
|
37
|
+
function manifestPayload(manifest) {
|
|
38
|
+
return { manifest: JSON.stringify(manifest) };
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* @param {Object} manifest
|
|
43
|
+
* @param {Object} [deps]
|
|
44
|
+
* @returns {Promise<Object>}
|
|
45
|
+
*/
|
|
46
|
+
async function validateManifestRemote(manifest, deps = {}) {
|
|
47
|
+
const payload = manifestPayload(manifest);
|
|
48
|
+
if (typeof deps.appId === 'string' && deps.appId.trim()) {
|
|
49
|
+
payload.app_id = deps.appId.trim();
|
|
50
|
+
}
|
|
51
|
+
const body = await runSlackApi('apps.manifest.validate', payload, deps);
|
|
52
|
+
return requireOk(body);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* @param {string} appId
|
|
57
|
+
* @param {Object} [deps]
|
|
58
|
+
* @returns {Promise<Object|null>}
|
|
59
|
+
*/
|
|
60
|
+
async function exportManifestApp(appId, deps = {}) {
|
|
61
|
+
const result = await exportManifestLookup(appId, deps);
|
|
62
|
+
return result.manifest;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* @param {string} appId
|
|
67
|
+
* @param {Object} [deps]
|
|
68
|
+
* @returns {Promise<{ missing: boolean, manifest: Object|null }>}
|
|
69
|
+
*/
|
|
70
|
+
async function exportManifestLookup(appId, deps = {}) {
|
|
71
|
+
if (!appId) {
|
|
72
|
+
return { missing: true, manifest: null };
|
|
73
|
+
}
|
|
74
|
+
const body = await runSlackApi('apps.manifest.export', { app_id: appId }, deps);
|
|
75
|
+
if (body && body.ok === false && body.error === 'app_not_found') {
|
|
76
|
+
return { missing: true, manifest: null };
|
|
77
|
+
}
|
|
78
|
+
if (!body || body.ok === false) {
|
|
79
|
+
return { missing: false, manifest: null };
|
|
80
|
+
}
|
|
81
|
+
return { missing: false, manifest: body.manifest || null };
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* @param {Object} manifest
|
|
86
|
+
* @param {Object} [deps]
|
|
87
|
+
* @returns {Promise<Object>}
|
|
88
|
+
*/
|
|
89
|
+
async function createManifestApp(manifest, deps = {}) {
|
|
90
|
+
const body = await runSlackApi('apps.manifest.create', manifestPayload(manifest), deps);
|
|
91
|
+
const created = requireOk(body);
|
|
92
|
+
const createdAppId = created.app_id || created.appId;
|
|
93
|
+
if (!createdAppId) {
|
|
94
|
+
throw slackProvisioningError('Slack create did not return an app id.', 'SLACK_API');
|
|
95
|
+
}
|
|
96
|
+
created.app_id = createdAppId;
|
|
97
|
+
return created;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* @param {string} appId
|
|
102
|
+
* @param {Object} manifest
|
|
103
|
+
* @param {Object} [deps]
|
|
104
|
+
* @returns {Promise<Object>}
|
|
105
|
+
*/
|
|
106
|
+
async function updateManifestApp(appId, manifest, deps = {}) {
|
|
107
|
+
const body = await runSlackApi(
|
|
108
|
+
'apps.manifest.update',
|
|
109
|
+
{ app_id: appId, ...manifestPayload(manifest) },
|
|
110
|
+
deps
|
|
111
|
+
);
|
|
112
|
+
return requireOk(body);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Recover public client id when create response omitted it.
|
|
117
|
+
* @param {Object} [captured]
|
|
118
|
+
* @param {Object} [deps]
|
|
119
|
+
* @returns {Promise<string>}
|
|
120
|
+
*/
|
|
121
|
+
async function resolveSlackClientId(captured, deps = {}) {
|
|
122
|
+
const existing = pickSlackClientId(captured);
|
|
123
|
+
if (existing) {
|
|
124
|
+
return existing;
|
|
125
|
+
}
|
|
126
|
+
const appId = captured && (captured.appId || captured.app_id);
|
|
127
|
+
if (!appId) {
|
|
128
|
+
return '';
|
|
129
|
+
}
|
|
130
|
+
const exported = await runSlackApi('apps.manifest.export', { app_id: appId }, deps);
|
|
131
|
+
const fromExport = pickSlackClientId(exported);
|
|
132
|
+
if (fromExport) {
|
|
133
|
+
return fromExport;
|
|
134
|
+
}
|
|
135
|
+
const info = await runSlackApi('apps.info', { app: appId }, deps);
|
|
136
|
+
return pickSlackClientId(info && (info.app || info));
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Native Slack CLI `app install` (TTY). Portal quiet mode must not call this.
|
|
141
|
+
* @param {string} appId
|
|
142
|
+
* @param {Object} [deps]
|
|
143
|
+
* @returns {Promise<{ botToken?: string, teamId?: string, botId?: string }>}
|
|
144
|
+
*/
|
|
145
|
+
async function nativeInstallApp(appId, deps = {}) {
|
|
146
|
+
if (typeof deps.installApp === 'function') {
|
|
147
|
+
return deps.installApp(appId);
|
|
148
|
+
}
|
|
149
|
+
return pickInstalledSlackTokens(await runNativeAppInstall(appId, deps));
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Workspace install. Prefer apps.developerInstall (HTTP, returns xoxb).
|
|
154
|
+
* @param {string} appId
|
|
155
|
+
* @param {Object} [deps]
|
|
156
|
+
* @returns {Promise<{ botToken?: string, teamId?: string, botId?: string }>}
|
|
157
|
+
*/
|
|
158
|
+
async function installManifestApp(appId, deps = {}) {
|
|
159
|
+
const fromApi = await developerInstallApp(appId, deps);
|
|
160
|
+
if (fromApi.botToken) {
|
|
161
|
+
return fromApi;
|
|
162
|
+
}
|
|
163
|
+
if (deps.quiet === true) {
|
|
164
|
+
return fromApi;
|
|
165
|
+
}
|
|
166
|
+
try {
|
|
167
|
+
return (await nativeInstallApp(appId, deps)) || {};
|
|
168
|
+
} catch {
|
|
169
|
+
return {};
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
module.exports = {
|
|
174
|
+
validateManifestRemote,
|
|
175
|
+
exportManifestApp,
|
|
176
|
+
exportManifestLookup,
|
|
177
|
+
createManifestApp,
|
|
178
|
+
updateManifestApp,
|
|
179
|
+
installManifestApp,
|
|
180
|
+
resolveSlackClientId
|
|
181
|
+
};
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Slack CLI login store (`~/.slack/credentials.json`) and `auth list` text.
|
|
3
|
+
*
|
|
4
|
+
* @fileoverview Never log or return tokens from public auth lists
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
'use strict';
|
|
8
|
+
|
|
9
|
+
const fs = require('fs');
|
|
10
|
+
const path = require('path');
|
|
11
|
+
const { slackCliHomeDir } = require('./install-cli');
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* @param {Object} [deps]
|
|
15
|
+
* @returns {string}
|
|
16
|
+
*/
|
|
17
|
+
function slackCredentialsPath(deps = {}) {
|
|
18
|
+
return path.join(deps.homeDir || slackCliHomeDir(deps.env), '.slack', 'credentials.json');
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* @param {Object} data
|
|
23
|
+
* @returns {Object[]}
|
|
24
|
+
*/
|
|
25
|
+
function entriesFromCredentialsObject(data) {
|
|
26
|
+
if (!data || typeof data !== 'object' || Array.isArray(data)) {
|
|
27
|
+
return [];
|
|
28
|
+
}
|
|
29
|
+
return Object.entries(data).map(([teamId, row]) => ({
|
|
30
|
+
team_id: String((row && (row.team_id || row.teamId)) || teamId),
|
|
31
|
+
team_domain: (row && (row.team_domain || row.teamDomain)) || undefined,
|
|
32
|
+
user_id: (row && (row.user_id || row.userId)) || undefined,
|
|
33
|
+
token: row && row.token ? String(row.token) : undefined
|
|
34
|
+
}));
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* @param {Object} [deps]
|
|
39
|
+
* @returns {Object[]}
|
|
40
|
+
*/
|
|
41
|
+
function readSlackCredentialEntries(deps = {}) {
|
|
42
|
+
if (typeof deps.readCredentials === 'function') {
|
|
43
|
+
const listed = deps.readCredentials();
|
|
44
|
+
return Array.isArray(listed) ? listed : [];
|
|
45
|
+
}
|
|
46
|
+
const filePath = slackCredentialsPath(deps);
|
|
47
|
+
if (!fs.existsSync(filePath)) {
|
|
48
|
+
return [];
|
|
49
|
+
}
|
|
50
|
+
try {
|
|
51
|
+
return entriesFromCredentialsObject(JSON.parse(fs.readFileSync(filePath, 'utf8')));
|
|
52
|
+
} catch {
|
|
53
|
+
return [];
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* @param {Object[]} entries
|
|
59
|
+
* @returns {Object[]}
|
|
60
|
+
*/
|
|
61
|
+
function publicSlackAuths(entries) {
|
|
62
|
+
return (Array.isArray(entries) ? entries : []).map((row) => ({
|
|
63
|
+
team_id: row.team_id,
|
|
64
|
+
team_domain: row.team_domain,
|
|
65
|
+
user_id: row.user_id
|
|
66
|
+
}));
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* @param {Object[]} entries
|
|
71
|
+
* @param {string} [teamId]
|
|
72
|
+
* @returns {string|null}
|
|
73
|
+
*/
|
|
74
|
+
function slackUserTokenForTeam(entries, teamId) {
|
|
75
|
+
const rows = Array.isArray(entries) ? entries : [];
|
|
76
|
+
const match = teamId
|
|
77
|
+
? rows.find((row) => String(row.team_id || '') === String(teamId))
|
|
78
|
+
: (rows.length === 1 ? rows[0] : null);
|
|
79
|
+
const token = match && match.token;
|
|
80
|
+
return typeof token === 'string' && token.trim() ? token.trim() : null;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Slack CLI 4.x `auth list` is human text, not JSON.
|
|
85
|
+
* @param {string} text
|
|
86
|
+
* @returns {Object[]}
|
|
87
|
+
*/
|
|
88
|
+
function parseAuthListText(text) {
|
|
89
|
+
const source = String(text || '');
|
|
90
|
+
const auths = [];
|
|
91
|
+
const re = /([A-Za-z0-9._-]+)\s+\(Team ID:\s*(T[A-Z0-9]+)\)[\s\S]*?User ID:\s*(U[A-Z0-9]+)/gi;
|
|
92
|
+
let match = re.exec(source);
|
|
93
|
+
while (match) {
|
|
94
|
+
auths.push({
|
|
95
|
+
team_domain: match[1],
|
|
96
|
+
team_id: match[2],
|
|
97
|
+
user_id: match[3]
|
|
98
|
+
});
|
|
99
|
+
match = re.exec(source);
|
|
100
|
+
}
|
|
101
|
+
return auths;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
module.exports = {
|
|
105
|
+
slackCredentialsPath,
|
|
106
|
+
readSlackCredentialEntries,
|
|
107
|
+
publicSlackAuths,
|
|
108
|
+
slackUserTokenForTeam,
|
|
109
|
+
parseAuthListText
|
|
110
|
+
};
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Slack CLI ticket handshake for portal channel install.
|
|
3
|
+
*
|
|
4
|
+
* @fileoverview Server runs `slack auth token --no-prompt`; user runs the slash command
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
'use strict';
|
|
8
|
+
|
|
9
|
+
const { promisify } = require('util');
|
|
10
|
+
const execFileDefault = promisify(require('child_process').execFile);
|
|
11
|
+
const { slackProvisioningError, wrapSlackCliError } = require('./errors');
|
|
12
|
+
const { resolveSlackBinary, shouldSkipLiveSlackCli } = require('./cli');
|
|
13
|
+
const { withSlackCliPath, installOfficialSlackCli } = require('./install-cli');
|
|
14
|
+
|
|
15
|
+
const SLASH_RE = /\/slackauthticket\s+(\S+)/i;
|
|
16
|
+
const ACCESS_TOKEN_RE = /\b(xox[peba]-[A-Za-z0-9-]+)\b/i;
|
|
17
|
+
const TICKET_LINE_RE = /^[A-Za-z0-9._-]{16,}$/;
|
|
18
|
+
const AUTH_DEAD_RE = /token_revoked|token_expired|invalid_auth|not_authed|account_inactive/i;
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* @param {string} value
|
|
22
|
+
* @returns {boolean}
|
|
23
|
+
*/
|
|
24
|
+
function looksLikeSlackAccessToken(value) {
|
|
25
|
+
return ACCESS_TOKEN_RE.test(String(value || '').trim());
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* @param {Error|Object|null} error
|
|
30
|
+
* @returns {boolean}
|
|
31
|
+
*/
|
|
32
|
+
function isSlackAuthDeadError(error) {
|
|
33
|
+
const message = String((error && error.message) || '');
|
|
34
|
+
return AUTH_DEAD_RE.test(message);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* @param {string} ticket
|
|
39
|
+
* @returns {string}
|
|
40
|
+
*/
|
|
41
|
+
function slashCommandForTicket(ticket) {
|
|
42
|
+
const value = String(ticket || '').trim();
|
|
43
|
+
return value ? `/slackauthticket ${value}` : '';
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* @param {string} stdout
|
|
48
|
+
* @returns {{ ticket: string, slashCommand: string }}
|
|
49
|
+
*/
|
|
50
|
+
function parseAuthTicketStart(stdout) {
|
|
51
|
+
const text = String(stdout || '');
|
|
52
|
+
const slash = text.match(SLASH_RE);
|
|
53
|
+
if (slash) {
|
|
54
|
+
return { ticket: slash[1], slashCommand: slashCommandForTicket(slash[1]) };
|
|
55
|
+
}
|
|
56
|
+
const ticket = firstTicketLine(text);
|
|
57
|
+
if (!ticket) {
|
|
58
|
+
throw slackProvisioningError('Slack CLI did not return an auth ticket.', 'SLACK_CLI');
|
|
59
|
+
}
|
|
60
|
+
return { ticket, slashCommand: slashCommandForTicket(ticket) };
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* @param {string} text
|
|
65
|
+
* @returns {string}
|
|
66
|
+
*/
|
|
67
|
+
function firstTicketLine(text) {
|
|
68
|
+
for (const line of String(text || '').split(/\r?\n/)) {
|
|
69
|
+
const trimmed = line.trim();
|
|
70
|
+
if (!trimmed || trimmed.startsWith('/') || /challenge|enter|copy/i.test(trimmed)) {
|
|
71
|
+
continue;
|
|
72
|
+
}
|
|
73
|
+
if (TICKET_LINE_RE.test(trimmed) && !looksLikeSlackAccessToken(trimmed)) {
|
|
74
|
+
return trimmed;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
return '';
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* @param {string} stdout
|
|
82
|
+
* @returns {string}
|
|
83
|
+
*/
|
|
84
|
+
function parseAuthTicketComplete(stdout) {
|
|
85
|
+
const match = String(stdout || '').match(ACCESS_TOKEN_RE);
|
|
86
|
+
if (!match) {
|
|
87
|
+
throw slackProvisioningError('Slack CLI did not return a session token.', 'SLACK_CLI');
|
|
88
|
+
}
|
|
89
|
+
return match[1];
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* @param {Object} [deps]
|
|
94
|
+
* @returns {Promise<string>}
|
|
95
|
+
*/
|
|
96
|
+
async function ensureSlackCliForTicket(deps = {}) {
|
|
97
|
+
const existing = await resolveSlackBinary(deps);
|
|
98
|
+
if (existing) {
|
|
99
|
+
return existing;
|
|
100
|
+
}
|
|
101
|
+
if (shouldSkipLiveSlackCli(deps)) {
|
|
102
|
+
throw slackProvisioningError(
|
|
103
|
+
'Slack CLI (`slack`) is not installed or not on PATH.',
|
|
104
|
+
'SLACK_CLI_MISSING'
|
|
105
|
+
);
|
|
106
|
+
}
|
|
107
|
+
await installOfficialSlackCli(deps);
|
|
108
|
+
const installed = await resolveSlackBinary(deps);
|
|
109
|
+
if (!installed) {
|
|
110
|
+
throw slackProvisioningError(
|
|
111
|
+
'Slack CLI (`slack`) is not installed or not on PATH.',
|
|
112
|
+
'SLACK_CLI_MISSING'
|
|
113
|
+
);
|
|
114
|
+
}
|
|
115
|
+
return installed;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* @param {Object} [deps]
|
|
120
|
+
* @returns {Promise<{ ticket: string, slashCommand: string }>}
|
|
121
|
+
*/
|
|
122
|
+
async function startSlackAuthTicket(deps = {}) {
|
|
123
|
+
if (typeof deps.startAuthTicket === 'function') {
|
|
124
|
+
return deps.startAuthTicket();
|
|
125
|
+
}
|
|
126
|
+
const bin = await ensureSlackCliForTicket(deps);
|
|
127
|
+
const execFileImpl = deps.execFile || execFileDefault;
|
|
128
|
+
try {
|
|
129
|
+
const result = await execFileImpl(bin, ['auth', 'token', '--no-prompt', '--no-color', '-s'], {
|
|
130
|
+
timeout: deps.timeout || 30000,
|
|
131
|
+
killSignal: 'SIGKILL',
|
|
132
|
+
maxBuffer: 1024 * 1024,
|
|
133
|
+
env: withSlackCliPath(deps.env || process.env)
|
|
134
|
+
});
|
|
135
|
+
return parseAuthTicketStart(result && result.stdout);
|
|
136
|
+
} catch (error) {
|
|
137
|
+
if (error && error.code === 'SLACK_CLI_MISSING') {
|
|
138
|
+
throw error;
|
|
139
|
+
}
|
|
140
|
+
throw wrapSlackCliError(error);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* @param {{ ticket: string, challenge: string }} params
|
|
146
|
+
* @param {Object} [deps]
|
|
147
|
+
* @returns {Promise<string>}
|
|
148
|
+
*/
|
|
149
|
+
async function completeSlackAuthTicket(params, deps = {}) {
|
|
150
|
+
if (typeof deps.completeAuthTicket === 'function') {
|
|
151
|
+
return deps.completeAuthTicket(params);
|
|
152
|
+
}
|
|
153
|
+
const ticket = String(params && params.ticket ? params.ticket : '').trim();
|
|
154
|
+
const challenge = String(params && params.challenge ? params.challenge : '').trim();
|
|
155
|
+
if (!ticket || !challenge) {
|
|
156
|
+
throw slackProvisioningError('Challenge code and auth ticket are required.', 'SLACK_CLI');
|
|
157
|
+
}
|
|
158
|
+
const bin = await ensureSlackCliForTicket(deps);
|
|
159
|
+
const execFileImpl = deps.execFile || execFileDefault;
|
|
160
|
+
try {
|
|
161
|
+
const result = await execFileImpl(
|
|
162
|
+
bin,
|
|
163
|
+
['auth', 'token', '--no-prompt', '--challenge', challenge, '--ticket', ticket, '--no-color', '-s'],
|
|
164
|
+
{
|
|
165
|
+
timeout: deps.timeout || 45000,
|
|
166
|
+
killSignal: 'SIGKILL',
|
|
167
|
+
maxBuffer: 1024 * 1024,
|
|
168
|
+
env: withSlackCliPath(deps.env || process.env)
|
|
169
|
+
}
|
|
170
|
+
);
|
|
171
|
+
return parseAuthTicketComplete(result && result.stdout);
|
|
172
|
+
} catch (error) {
|
|
173
|
+
throw wrapSlackCliError(error);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
module.exports = {
|
|
178
|
+
looksLikeSlackAccessToken,
|
|
179
|
+
isSlackAuthDeadError,
|
|
180
|
+
slashCommandForTicket,
|
|
181
|
+
parseAuthTicketStart,
|
|
182
|
+
parseAuthTicketComplete,
|
|
183
|
+
startSlackAuthTicket,
|
|
184
|
+
completeSlackAuthTicket
|
|
185
|
+
};
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Slack workspace identity from login store or `slack auth list`.
|
|
3
|
+
*
|
|
4
|
+
* @fileoverview Fail closed when multiple workspaces cannot be proven
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
'use strict';
|
|
8
|
+
|
|
9
|
+
const { promisify } = require('util');
|
|
10
|
+
const execFileDefault = promisify(require('child_process').execFile);
|
|
11
|
+
const { slackProvisioningError, wrapSlackCliError } = require('./errors');
|
|
12
|
+
const { resolveSlackBinary, runSlackApi, shouldSkipLiveSlackCli } = require('./cli');
|
|
13
|
+
const { withSlackCliPath } = require('./install-cli');
|
|
14
|
+
const {
|
|
15
|
+
readSlackCredentialEntries,
|
|
16
|
+
publicSlackAuths,
|
|
17
|
+
parseAuthListText
|
|
18
|
+
} = require('./auth-store');
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* @param {Object} [deps]
|
|
22
|
+
* @returns {Promise<Object[]>}
|
|
23
|
+
*/
|
|
24
|
+
async function listSlackAuthsFromCli(deps = {}) {
|
|
25
|
+
const bin = await resolveSlackBinary(deps);
|
|
26
|
+
if (!bin) {
|
|
27
|
+
return [];
|
|
28
|
+
}
|
|
29
|
+
const execFileImpl = deps.execFile || execFileDefault;
|
|
30
|
+
try {
|
|
31
|
+
const result = await execFileImpl(bin, ['auth', 'list', '--no-color', '-s'], {
|
|
32
|
+
timeout: 30000,
|
|
33
|
+
maxBuffer: 1024 * 1024,
|
|
34
|
+
env: withSlackCliPath(deps.env || process.env)
|
|
35
|
+
});
|
|
36
|
+
return parseAuthListText(result && result.stdout);
|
|
37
|
+
} catch (error) {
|
|
38
|
+
throw wrapSlackCliError(error);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* @param {Object} [deps]
|
|
44
|
+
* @returns {Promise<Object[]>}
|
|
45
|
+
*/
|
|
46
|
+
async function listSlackAuths(deps = {}) {
|
|
47
|
+
if (typeof deps.listAuths === 'function') {
|
|
48
|
+
const listed = await deps.listAuths();
|
|
49
|
+
return Array.isArray(listed) ? listed : [];
|
|
50
|
+
}
|
|
51
|
+
const fromFile = publicSlackAuths(readSlackCredentialEntries(deps));
|
|
52
|
+
if (fromFile.length > 0) {
|
|
53
|
+
return fromFile;
|
|
54
|
+
}
|
|
55
|
+
if (shouldSkipLiveSlackCli(deps) && typeof deps.execFile !== 'function') {
|
|
56
|
+
return [];
|
|
57
|
+
}
|
|
58
|
+
return listSlackAuthsFromCli(deps);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Prove workspace identity from a short-lived session token for this run only.
|
|
63
|
+
* @param {Object} [deps]
|
|
64
|
+
* @returns {Promise<Object|null>}
|
|
65
|
+
*/
|
|
66
|
+
async function workspaceFromSessionToken(deps = {}) {
|
|
67
|
+
const token = typeof deps.slackUserToken === 'string' ? deps.slackUserToken.trim() : '';
|
|
68
|
+
if (!token) {
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
71
|
+
if (typeof deps.authTest === 'function') {
|
|
72
|
+
const listed = await deps.authTest();
|
|
73
|
+
if (!listed || !(listed.team_id || listed.teamId)) {
|
|
74
|
+
throw slackProvisioningError('Session token was not accepted.', 'UNPROVEN_IDENTITY');
|
|
75
|
+
}
|
|
76
|
+
return listed;
|
|
77
|
+
}
|
|
78
|
+
const body = await runSlackApi('auth.test', {}, deps);
|
|
79
|
+
if (!body || body.ok !== true || !(body.team_id || body.teamId)) {
|
|
80
|
+
throw slackProvisioningError('Session token was not accepted.', 'UNPROVEN_IDENTITY');
|
|
81
|
+
}
|
|
82
|
+
return { team_id: body.team_id || body.teamId, team: body.team };
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* @param {Object[]} auths
|
|
87
|
+
* @param {string} [expectedTeamId]
|
|
88
|
+
* @returns {Object}
|
|
89
|
+
*/
|
|
90
|
+
function pickSlackWorkspace(auths, expectedTeamId) {
|
|
91
|
+
const rows = Array.isArray(auths) ? auths : [];
|
|
92
|
+
if (expectedTeamId) {
|
|
93
|
+
const match = rows.find((row) => String(row.team_id || row.teamId || '') === expectedTeamId);
|
|
94
|
+
if (!match) {
|
|
95
|
+
throw slackProvisioningError(
|
|
96
|
+
`Signed-in Slack workspace does not match expected team "${expectedTeamId}".`,
|
|
97
|
+
'UNPROVEN_IDENTITY'
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
return match;
|
|
101
|
+
}
|
|
102
|
+
if (rows.length > 1) {
|
|
103
|
+
throw slackProvisioningError(
|
|
104
|
+
'Multiple Slack workspaces are signed in. Pass the expected team id rather than guessing.',
|
|
105
|
+
'UNPROVEN_IDENTITY'
|
|
106
|
+
);
|
|
107
|
+
}
|
|
108
|
+
return rows[0] || null;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
module.exports = {
|
|
112
|
+
listSlackAuths,
|
|
113
|
+
pickSlackWorkspace,
|
|
114
|
+
workspaceFromSessionToken
|
|
115
|
+
};
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fail before Slack create when callback URLs are missing or not public HTTPS.
|
|
3
|
+
*
|
|
4
|
+
* @fileoverview Events / interactivity / commands Request URLs
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
'use strict';
|
|
8
|
+
|
|
9
|
+
const { REQUIRED_CALLBACK_KEYS } = require('./constants');
|
|
10
|
+
const { slackProvisioningError } = require('./errors');
|
|
11
|
+
const { isPublicHttpsUrl } = require('../vendor-dataplane-url');
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* @param {Object} runtimeValues
|
|
15
|
+
* @returns {void}
|
|
16
|
+
*/
|
|
17
|
+
function assertSlackCallbackUrls(runtimeValues) {
|
|
18
|
+
const missing = REQUIRED_CALLBACK_KEYS.filter((key) => {
|
|
19
|
+
const value = runtimeValues && runtimeValues[key];
|
|
20
|
+
return typeof value !== 'string' || !value.trim();
|
|
21
|
+
});
|
|
22
|
+
if (missing.length > 0) {
|
|
23
|
+
throw slackProvisioningError(
|
|
24
|
+
`Slack callback URLs are missing: ${missing.join(', ')}. ` +
|
|
25
|
+
'Generate the channel package against a public HTTPS dataplane before create.',
|
|
26
|
+
'CALLBACKS'
|
|
27
|
+
);
|
|
28
|
+
}
|
|
29
|
+
const unusable = REQUIRED_CALLBACK_KEYS.filter((key) => !isPublicHttpsUrl(runtimeValues[key]));
|
|
30
|
+
if (unusable.length > 0) {
|
|
31
|
+
throw slackProvisioningError(
|
|
32
|
+
`Slack callback URLs must be public HTTPS: ${unusable.join(', ')}.`,
|
|
33
|
+
'CALLBACKS'
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
module.exports = {
|
|
39
|
+
isPublicHttpsUrl,
|
|
40
|
+
assertSlackCallbackUrls
|
|
41
|
+
};
|