@aifabrix/builder 2.51.0 → 2.52.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/jest.isolated-projects.js +77 -1
- package/jest.projects.js +5 -426
- package/lib/api/assistants.api.js +67 -0
- package/lib/api/datasources-extended.api.js +6 -6
- package/lib/api/evidence-authoring-helpers.js +267 -0
- package/lib/api/evidence-authoring.api.js +337 -0
- package/lib/api/evidence.api.js +57 -1
- package/lib/api/index.js +24 -7
- package/lib/api/pipeline.api.js +43 -3
- package/lib/api/records-bulk.api.js +28 -3
- package/lib/api/types/evidence.types.js +156 -8
- package/lib/api/work.api.js +283 -0
- package/lib/app/run-container-start.js +4 -0
- package/lib/app/run-deploy-job.js +216 -0
- package/lib/app/run-env-compose.js +32 -0
- package/lib/app/run-env-recovery.js +35 -0
- package/lib/app/run-helpers.js +15 -9
- package/lib/app/run.js +18 -2
- package/lib/channels/add-channel.js +54 -23
- package/lib/channels/approval-guides/chatgpt.js +13 -9
- package/lib/channels/approval-guides/microsoft-copilot.js +7 -8
- package/lib/channels/channel-artifact-generator.js +14 -2
- package/lib/channels/channel-credential.js +3 -1
- package/lib/channels/channel-probe-evidence.js +79 -0
- package/lib/channels/channel-probe.js +265 -13
- package/lib/channels/channel-runtime-resolver.js +120 -29
- package/lib/channels/channel-status-display.js +44 -0
- package/lib/channels/channel-targets.js +25 -1
- package/lib/channels/chatgpt-runtime-contract.js +29 -0
- package/lib/channels/microsoft-copilot-package.js +280 -0
- package/lib/channels/runtime-tool-catalog.js +165 -0
- package/lib/cli/index.js +2 -0
- package/lib/cli/infra-guided-footers.js +1 -1
- package/lib/cli/infra-guided-public-url-warn.js +55 -0
- package/lib/cli/infra-guided.js +65 -56
- package/lib/cli/setup-app.help.js +4 -0
- package/lib/cli/setup-app.js +4 -0
- package/lib/cli/setup-app.test-commands.js +59 -28
- package/lib/cli/setup-external-system-help.js +8 -2
- package/lib/cli/setup-external-system.js +62 -12
- package/lib/commands/app-test.js +2 -14
- package/lib/commands/channel-add-setup.js +8 -2
- package/lib/commands/channel.js +16 -5
- package/lib/commands/datasource-load-export-cli.js +6 -1
- package/lib/commands/deploy-gate-failure-details.js +19 -17
- package/lib/commands/deploy-gate-orchestrator.js +3 -2
- package/lib/commands/dev-down.js +2 -6
- package/lib/commands/evidence-kind.js +65 -15
- package/lib/commands/governance-generate-external.js +56 -21
- package/lib/commands/governance-verify-external.js +2 -5
- package/lib/commands/lifecycle-external.js +21 -8
- package/lib/commands/repair-datasource-apply.js +10 -1
- package/lib/commands/repair-datasource-run.js +4 -2
- package/lib/commands/repair-datasource-test-payload.js +80 -19
- package/lib/commands/repair-datasource-webhooks-sync-bridge.js +42 -3
- package/lib/commands/repair-internal.js +9 -1
- package/lib/commands/repair-openapi-sync.js +34 -17
- package/lib/commands/role-assistant.js +371 -0
- package/lib/commands/test-governance-external.js +2 -5
- package/lib/commands/up-miso.js +61 -24
- package/lib/commands/upload-data-sync-finalize.js +25 -7
- package/lib/commands/upload-sidecar-followups.js +144 -47
- package/lib/commands/upload.js +43 -6
- package/lib/commands/verify-governance-combined-external.js +73 -9
- package/lib/commands/verify-governance-db.js +80 -25
- package/lib/commands/verify-governance-external.js +31 -10
- package/lib/commands/verify-operations-external.js +2 -5
- package/lib/commands/verify-operations-step-plan.js +31 -0
- package/lib/commands/verify-trust-command-action.js +2 -5
- package/lib/commands/wizard-core-helpers.js +7 -3
- package/lib/commands/wizard-core.js +78 -9
- package/lib/datasource/bulk-loader-run-batches.js +4 -2
- package/lib/datasource/bulk-loader-service.js +5 -1
- package/lib/datasource/datasource-identifier.js +4 -3
- package/lib/datasource/datasource-validate-summary.js +2 -2
- package/lib/datasource/integration-context.js +3 -3
- package/lib/datasource/resolve-app.js +90 -8
- package/lib/datasource/test-e2e.js +2 -2
- package/lib/datasource/unified-validation-run-body.js +15 -1
- package/lib/datasource/unified-validation-run-resolve.js +2 -2
- package/lib/datasource/validate.js +13 -6
- package/lib/external-system/deploy-helpers.js +39 -1
- package/lib/external-system/deploy.js +25 -11
- package/lib/external-system/download-helpers.js +111 -2
- package/lib/external-system/download-role-assistant.js +55 -0
- package/lib/external-system/download.js +45 -26
- package/lib/external-system/test-helpers.js +1 -1
- package/lib/external-system/test-system-level.js +21 -3
- package/lib/external-system/test.js +43 -7
- package/lib/generator/builders.js +12 -15
- package/lib/generator/wizard.js +51 -4
- package/lib/lifecycle/access-control-abac.js +121 -0
- package/lib/lifecycle/lifecycle-governance-options.js +4 -1
- package/lib/lifecycle/product-model.js +3 -2
- package/lib/lifecycle/report-display.js +14 -2
- package/lib/programmatic/capability-registry.js +20 -1
- package/lib/programmatic/command-options.js +1 -0
- package/lib/programmatic/index.js +4 -0
- package/lib/programmatic/manifest-patch-section.js +48 -9
- package/lib/programmatic/manifest-section-registry-data.js +3 -2
- package/lib/programmatic/openapi-apply-result-schemas.js +44 -0
- package/lib/programmatic/openapi-component-schemas.js +16 -1
- package/lib/programmatic/openapi-description-enricher.js +3 -1
- package/lib/programmatic/openapi-descriptions.js +55 -5
- package/lib/programmatic/openapi-evidence-schemas.js +161 -0
- package/lib/programmatic/openapi-manifest-schemas.js +6 -2
- package/lib/programmatic/openapi-operation-bindings.js +99 -0
- package/lib/programmatic/openapi-request-schemas.js +36 -1
- package/lib/programmatic/openapi-result-schemas.js +1 -1
- package/lib/programmatic/openapi-schema-descriptions-certify.js +5 -1
- package/lib/programmatic/openapi-schema-descriptions-core.js +1 -1
- package/lib/programmatic/openapi-schema-descriptions-evidence.js +156 -0
- package/lib/programmatic/openapi-schema-descriptions-manifest.js +4 -3
- package/lib/programmatic/openapi-schema-descriptions-operations.js +70 -0
- package/lib/programmatic/openapi-spec.js +47 -28
- package/lib/programmatic/orchestration-openapi-meta.js +5 -0
- package/lib/programmatic/request-timeout.js +3 -0
- package/lib/programmatic/route-handler-map.js +89 -2
- package/lib/programmatic/run-certify.js +15 -2
- package/lib/programmatic/run-deploy.js +74 -0
- package/lib/programmatic/run-evidence-authoring.js +80 -0
- package/lib/programmatic/run-protection.js +124 -0
- package/lib/programmatic/run-template-workspace.js +28 -1
- package/lib/programmatic/workspace-context.js +6 -0
- package/lib/protection/auth-context.js +48 -6
- package/lib/protection/paths.js +22 -0
- package/lib/protection/run-protection-create-helpers.js +22 -1
- package/lib/protection/run-protection-create.js +1 -1
- package/lib/protection/sync-after-upload.js +1 -1
- package/lib/role-assistant/evidence-sync.js +450 -0
- package/lib/role-assistant/github-workflow.js +96 -0
- package/lib/role-assistant/knowledge-markdown-frontmatter.js +75 -0
- package/lib/role-assistant/knowledge-sync.js +485 -0
- package/lib/role-assistant/settings.js +126 -0
- package/lib/role-assistant/test-cases-evaluate.js +170 -0
- package/lib/role-assistant/test-cases-honesty.js +184 -0
- package/lib/role-assistant/test-cases.js +364 -0
- package/lib/role-assistant/test-runner-workhub-answers.js +221 -0
- package/lib/role-assistant/test-runner-workhub.js +399 -0
- package/lib/role-assistant/test-runner.js +500 -0
- package/lib/schema/application-schema.json +24 -28
- package/lib/schema/deployment-rules.yaml +1 -0
- package/lib/schema/external-datasource.schema.json +38 -4
- package/lib/schema/external-system.schema.json +4 -4
- package/lib/schema/role-assistant-settings.schema.json +108 -0
- package/lib/utils/api.js +52 -27
- package/lib/utils/app-path-resolve.js +72 -24
- package/lib/utils/authentication-shape.js +3 -1
- package/lib/utils/certify-external-workspace.js +40 -0
- package/lib/utils/deployment-validation.js +28 -3
- package/lib/utils/docker-run-ephemeral.js +115 -0
- package/lib/utils/enterprise-knowledge-paths.js +29 -12
- package/lib/utils/env-copy-keycloak.js +196 -0
- package/lib/utils/env-copy.js +34 -1
- package/lib/utils/external-system-readiness-compact-display.js +3 -0
- package/lib/utils/health-check-public-warn.js +31 -13
- package/lib/utils/health-check.js +11 -1
- package/lib/utils/help-builder.js +1 -0
- package/lib/utils/paths.js +80 -38
- package/lib/utils/pipeline-upload-auth-retry.js +15 -3
- package/lib/utils/role-assistant-paths.js +160 -0
- package/lib/utils/schema-loader.js +41 -0
- package/lib/utils/schema-resolver.js +36 -4
- package/lib/utils/token-manager.js +38 -29
- package/lib/utils/upload-sync-options.js +7 -4
- package/lib/utils/url-declarative-resolve-load-doc.js +38 -1
- package/lib/validation/external-manifest-validator.js +31 -8
- package/lib/validation/reserved-system-key-prefixes.js +56 -0
- package/lib/validation/validate-external-file.js +8 -1
- package/lib/validation/validate.js +54 -19
- package/package.json +2 -1
- package/scripts/export-builder-api-evidence.js +1 -1
- package/templates/applications/builder-api/application.yaml +3 -3
- package/templates/applications/dataplane/application.yaml +24 -17
- package/templates/applications/dataplane/env.template +2 -2
- package/templates/applications/dataplane/rbac.yaml +24 -10
- package/templates/applications/keycloak/application.yaml +2 -2
- package/templates/applications/keycloak/env.template +5 -2
- package/templates/applications/miso-controller/application.yaml +3 -3
- package/templates/applications/miso-controller/env.template +17 -2
- package/templates/channels/chatgpt/install.template.md.hbs +82 -26
- package/templates/channels/chatgpt/integration/README.md +11 -7
- package/templates/channels/chatgpt/integration/application.json +2 -2
- package/templates/channels/chatgpt/integration/chatgpt-system.json +3 -3
- package/templates/channels/chatgpt/integration/env.template +1 -1
- package/templates/channels/chatgpt/manifest.template.json.hbs +36 -5
- package/templates/channels/microsoft-copilot/ai-plugin.template.json.hbs +28 -0
- package/templates/channels/microsoft-copilot/declarative-agent.template.json.hbs +22 -0
- package/templates/channels/microsoft-copilot/install.template.md.hbs +32 -22
- package/templates/channels/microsoft-copilot/integration/README.md +9 -7
- package/templates/channels/microsoft-copilot/integration/application.json +2 -2
- package/templates/channels/microsoft-copilot/integration/env.template +1 -1
- package/templates/channels/microsoft-copilot/integration/microsoft-copilot-system.json +7 -6
- package/templates/channels/microsoft-copilot/manifest.template.json.hbs +34 -6
- package/templates/channels/slack/install.template.md.hbs +6 -1
- package/templates/python/docker-compose.hbs +10 -0
- package/templates/typescript/docker-compose.hbs +10 -0
- /package/.cursor/plans/{157.1-security-quality-violations-by-type.csv → Done/157.1-security-quality-violations-by-type.csv} +0 -0
|
@@ -141,6 +141,7 @@ async function startContainer(appName, composePath, port, appConfig = null, opts
|
|
|
141
141
|
debug = false,
|
|
142
142
|
runEnvPath = null,
|
|
143
143
|
runEnvAdminPath = null,
|
|
144
|
+
ensureRunEnvFiles = null,
|
|
144
145
|
runOptions = {},
|
|
145
146
|
devMountPath = null,
|
|
146
147
|
misoEnvironment = 'dev'
|
|
@@ -166,6 +167,9 @@ async function startContainer(appName, composePath, port, appConfig = null, opts
|
|
|
166
167
|
}
|
|
167
168
|
|
|
168
169
|
if (!usedDockerRunFallback) {
|
|
170
|
+
if (typeof ensureRunEnvFiles === 'function') {
|
|
171
|
+
await ensureRunEnvFiles();
|
|
172
|
+
}
|
|
169
173
|
const env = await prepareContainerEnv(debug);
|
|
170
174
|
await executeComposeUp(composeCmdBase, composePath, env, debug);
|
|
171
175
|
}
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Optional build.deployJob — one-shot before long-running aifabrix run start.
|
|
3
|
+
*
|
|
4
|
+
* @fileoverview Run deployJob once per aifabrix run
|
|
5
|
+
* @author AI Fabrix Team
|
|
6
|
+
* @version 2.0.0
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
const config = require('../core/config');
|
|
10
|
+
const logger = require('../utils/logger');
|
|
11
|
+
const {
|
|
12
|
+
infoLine,
|
|
13
|
+
formatSuccessLine,
|
|
14
|
+
formatBlockingError,
|
|
15
|
+
metadata
|
|
16
|
+
} = require('../utils/cli-test-layout-chalk');
|
|
17
|
+
const { runDockerRunEphemeral } = require('../utils/docker-run-ephemeral');
|
|
18
|
+
const { getInfraBridgeNetworkName } = require('../infrastructure/orphan-infra-docker-teardown');
|
|
19
|
+
|
|
20
|
+
/** Migrate status lines: `[system] head=… applied=3 pending=[]` */
|
|
21
|
+
const DEPLOY_JOB_STATUS_RE =
|
|
22
|
+
/^\[([^\]]+)\]\s+head=\S+\s+applied=(\d+)\s+pending=(\[[^\]]*\])/i;
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Resolve trimmed deployJob from app config, or null if unset.
|
|
26
|
+
* @param {Object} appConfig - Application configuration
|
|
27
|
+
* @returns {string|null}
|
|
28
|
+
*/
|
|
29
|
+
function resolveDeployJobCommand(appConfig) {
|
|
30
|
+
const raw = appConfig?.build?.deployJob;
|
|
31
|
+
if (typeof raw !== 'string') {
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
const trimmed = raw.trim();
|
|
35
|
+
return trimmed ? trimmed : null;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Developer infra bridge so ephemeral jobs resolve compose DNS (e.g. postgres).
|
|
40
|
+
* @returns {Promise<string>}
|
|
41
|
+
*/
|
|
42
|
+
async function resolveDeployJobNetwork() {
|
|
43
|
+
const developerId = await config.getDeveloperId();
|
|
44
|
+
return getInfraBridgeNetworkName(developerId);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Quiet docker stdio by default (hide app bootstrap warnings). Full inherit only with --debug.
|
|
49
|
+
* @param {{ quiet?: boolean, debug?: boolean }} params
|
|
50
|
+
* @returns {boolean}
|
|
51
|
+
*/
|
|
52
|
+
function shouldQuietDeployJob(params = {}) {
|
|
53
|
+
if (params.debug === true) {
|
|
54
|
+
return false;
|
|
55
|
+
}
|
|
56
|
+
return true;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Parse migrate CLI status lines into totals (no per-chain head/pending dump).
|
|
61
|
+
* @param {string} output
|
|
62
|
+
* @returns {{ chains: number, applied: number, pending: number }|null}
|
|
63
|
+
*/
|
|
64
|
+
function summarizeDeployJobMigrateOutput(output) {
|
|
65
|
+
if (typeof output !== 'string' || !output.trim()) {
|
|
66
|
+
return null;
|
|
67
|
+
}
|
|
68
|
+
let chains = 0;
|
|
69
|
+
let applied = 0;
|
|
70
|
+
let pending = 0;
|
|
71
|
+
for (const line of output.split(/\r?\n/)) {
|
|
72
|
+
const match = DEPLOY_JOB_STATUS_RE.exec(line.trim());
|
|
73
|
+
if (!match) {
|
|
74
|
+
continue;
|
|
75
|
+
}
|
|
76
|
+
chains += 1;
|
|
77
|
+
applied += Number.parseInt(match[2], 10) || 0;
|
|
78
|
+
const inner = match[3].slice(1, -1).trim();
|
|
79
|
+
if (inner) {
|
|
80
|
+
pending += inner.split(',').filter((part) => part.trim()).length;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
if (chains === 0) {
|
|
84
|
+
return null;
|
|
85
|
+
}
|
|
86
|
+
return { chains, applied, pending };
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* One-line migrate totals from quiet deployJob output (replaces per-chain dump).
|
|
91
|
+
* @param {string} output
|
|
92
|
+
* @returns {string|null} Summary text for the completed line, or null
|
|
93
|
+
*/
|
|
94
|
+
function formatDeployJobMigrateSummary(output) {
|
|
95
|
+
const summary = summarizeDeployJobMigrateOutput(output);
|
|
96
|
+
if (!summary) {
|
|
97
|
+
return null;
|
|
98
|
+
}
|
|
99
|
+
return `${summary.applied} applied, ${summary.pending} pending (${summary.chains} chains)`;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* @deprecated Prefer formatDeployJobMigrateSummary — kept for tests.
|
|
104
|
+
* @param {string} output
|
|
105
|
+
*/
|
|
106
|
+
function emitDeployJobSummaryLines(output) {
|
|
107
|
+
const text = formatDeployJobMigrateSummary(output);
|
|
108
|
+
if (text) {
|
|
109
|
+
logger.log(metadata(`DB migrate: ${text}`));
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* @param {string|null|undefined} devMountPath
|
|
115
|
+
* @returns {Array<{ host: string, container: string }>}
|
|
116
|
+
*/
|
|
117
|
+
function _deployJobVolumeMounts(devMountPath) {
|
|
118
|
+
if (typeof devMountPath !== 'string' || !devMountPath.trim()) {
|
|
119
|
+
return [];
|
|
120
|
+
}
|
|
121
|
+
return [{ host: devMountPath.trim(), container: '/app' }];
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Always announce the job; debug adds command/network/mount detail.
|
|
126
|
+
* @param {{ fullImage: string, cmd: string, network: string, volumeMounts: Array, debug: boolean }} params
|
|
127
|
+
*/
|
|
128
|
+
function _logDeployJobStart(params) {
|
|
129
|
+
logger.log(infoLine(`Running build.deployJob once before start (${params.fullImage})…`));
|
|
130
|
+
if (!params.debug) {
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
logger.log(infoLine(`deployJob: ${params.cmd}`));
|
|
134
|
+
logger.log(infoLine(`deployJob network: ${params.network}`));
|
|
135
|
+
if (params.volumeMounts.length) {
|
|
136
|
+
logger.log(infoLine(`deployJob mount: ${params.volumeMounts[0].host} -> /app`));
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* @param {{ result: any, quietJob: boolean }} params
|
|
142
|
+
* @throws {Error}
|
|
143
|
+
*/
|
|
144
|
+
function _assertDeployJobExit(params) {
|
|
145
|
+
const code = params.quietJob ? params.result.code : params.result;
|
|
146
|
+
if (code === 0) {
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
if (params.quietJob && params.result.output) {
|
|
150
|
+
logger.log(params.result.output.trimEnd());
|
|
151
|
+
}
|
|
152
|
+
logger.log(formatBlockingError(`build.deployJob failed with exit code ${code}`));
|
|
153
|
+
throw new Error(`build.deployJob failed with exit code ${code}`);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* If build.deployJob is set, run it once in an ephemeral container (same image + env).
|
|
158
|
+
* Fail-closed on non-zero exit. No-op when unset.
|
|
159
|
+
*
|
|
160
|
+
* When `devMountPath` is set (aifabrix run --reload), bind-mount that path at `/app`
|
|
161
|
+
* so migrate/deploy sees the same tree as the long-running reload container.
|
|
162
|
+
*
|
|
163
|
+
* @async
|
|
164
|
+
* @param {Object} params
|
|
165
|
+
* @param {Object} params.appConfig - Application configuration
|
|
166
|
+
* @param {string} params.fullImage - Image:tag used for the run
|
|
167
|
+
* @param {string|null} [params.runEnvPath] - Ephemeral .env.run path for --env-file
|
|
168
|
+
* @param {string|null} [params.devMountPath] - Host path mounted at /app for --reload
|
|
169
|
+
* @param {boolean} [params.debug]
|
|
170
|
+
* @param {boolean} [params.quiet] - Unused legacy flag; quiet is default unless debug
|
|
171
|
+
* @returns {Promise<void>}
|
|
172
|
+
* @throws {Error} If deployJob exits non-zero
|
|
173
|
+
*/
|
|
174
|
+
async function runDeployJobIfConfigured({
|
|
175
|
+
appConfig,
|
|
176
|
+
fullImage,
|
|
177
|
+
runEnvPath = null,
|
|
178
|
+
devMountPath = null,
|
|
179
|
+
debug = false,
|
|
180
|
+
quiet = false
|
|
181
|
+
}) {
|
|
182
|
+
const cmd = resolveDeployJobCommand(appConfig);
|
|
183
|
+
if (!cmd) {
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
186
|
+
if (!fullImage || typeof fullImage !== 'string') {
|
|
187
|
+
throw new Error('build.deployJob requires a resolved container image');
|
|
188
|
+
}
|
|
189
|
+
const network = await resolveDeployJobNetwork();
|
|
190
|
+
const volumeMounts = _deployJobVolumeMounts(devMountPath);
|
|
191
|
+
const quietJob = shouldQuietDeployJob({ quiet, debug });
|
|
192
|
+
_logDeployJobStart({ fullImage, cmd, network, volumeMounts, debug });
|
|
193
|
+
const result = await runDockerRunEphemeral(fullImage, cmd, runEnvPath, {
|
|
194
|
+
network,
|
|
195
|
+
volumeMounts,
|
|
196
|
+
quiet: quietJob
|
|
197
|
+
});
|
|
198
|
+
_assertDeployJobExit({ result, quietJob });
|
|
199
|
+
if (quietJob && result.output) {
|
|
200
|
+
const migrateSummary = formatDeployJobMigrateSummary(result.output);
|
|
201
|
+
if (migrateSummary) {
|
|
202
|
+
logger.log(formatSuccessLine(`build.deployJob completed — ${migrateSummary}`));
|
|
203
|
+
return;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
logger.log(formatSuccessLine('build.deployJob completed'));
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
module.exports = {
|
|
210
|
+
resolveDeployJobCommand,
|
|
211
|
+
runDeployJobIfConfigured,
|
|
212
|
+
shouldQuietDeployJob,
|
|
213
|
+
emitDeployJobSummaryLines,
|
|
214
|
+
formatDeployJobMigrateSummary,
|
|
215
|
+
summarizeDeployJobMigrateOutput
|
|
216
|
+
};
|
|
@@ -315,6 +315,37 @@ async function buildMergedRunEnvAndWrite(appName, appConfig, devDir, developerId
|
|
|
315
315
|
return { runEnvPath, runEnvAdminPath };
|
|
316
316
|
}
|
|
317
317
|
|
|
318
|
+
/**
|
|
319
|
+
* Rebuild run env files when another use of the shared applications directory removed them.
|
|
320
|
+
* @async
|
|
321
|
+
* @param {string} appName - Application name
|
|
322
|
+
* @param {Object} appConfig - Application configuration
|
|
323
|
+
* @param {string} runEnvPath - Expected .env.run path
|
|
324
|
+
* @param {string} runEnvAdminPath - Expected .env.run.admin path
|
|
325
|
+
* @param {number|string} developerId - Developer ID
|
|
326
|
+
* @param {Object|null} scopeOpts - Environment-scoped run options
|
|
327
|
+
* @returns {Promise<void>}
|
|
328
|
+
*/
|
|
329
|
+
async function ensureRunEnvFilesPresent(
|
|
330
|
+
appName,
|
|
331
|
+
appConfig,
|
|
332
|
+
runEnvPath,
|
|
333
|
+
runEnvAdminPath,
|
|
334
|
+
developerId,
|
|
335
|
+
scopeOpts = null
|
|
336
|
+
) {
|
|
337
|
+
if (fsSync.existsSync(runEnvPath) && fsSync.existsSync(runEnvAdminPath)) {
|
|
338
|
+
return;
|
|
339
|
+
}
|
|
340
|
+
await buildMergedRunEnvAndWrite(
|
|
341
|
+
appName,
|
|
342
|
+
appConfig,
|
|
343
|
+
path.dirname(runEnvPath),
|
|
344
|
+
developerId,
|
|
345
|
+
scopeOpts
|
|
346
|
+
);
|
|
347
|
+
}
|
|
348
|
+
|
|
318
349
|
/**
|
|
319
350
|
* Assert generated compose does not contain password literals in environment (ISO 27K).
|
|
320
351
|
* @param {string} composeContent - Generated docker-compose content
|
|
@@ -330,6 +361,7 @@ function assertNoPasswordLiteralsInCompose(composeContent) {
|
|
|
330
361
|
module.exports = {
|
|
331
362
|
cleanApplicationsDir,
|
|
332
363
|
buildMergedRunEnvAndWrite,
|
|
364
|
+
ensureRunEnvFilesPresent,
|
|
333
365
|
assertNoPasswordLiteralsInCompose,
|
|
334
366
|
ensureRunSecretsForApp
|
|
335
367
|
};
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Last-moment run env recovery for the shared applications workspace.
|
|
3
|
+
*
|
|
4
|
+
* @fileoverview Builds the callback used immediately before Docker Compose starts
|
|
5
|
+
* @author AI Fabrix Team
|
|
6
|
+
* @version 2.0.0
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
'use strict';
|
|
10
|
+
|
|
11
|
+
const runEnvCompose = require('./run-env-compose');
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Build a recovery hook for run env files in the shared workspace.
|
|
15
|
+
* @param {string} appName - Application name
|
|
16
|
+
* @param {Object} prepared - Prepared run context
|
|
17
|
+
* @returns {Function} Async recovery hook
|
|
18
|
+
*/
|
|
19
|
+
function buildRunEnvRecovery(appName, prepared) {
|
|
20
|
+
return () =>
|
|
21
|
+
runEnvCompose.ensureRunEnvFilesPresent(
|
|
22
|
+
appName,
|
|
23
|
+
prepared.appConfig,
|
|
24
|
+
prepared.runEnvPath,
|
|
25
|
+
prepared.runEnvAdminPath,
|
|
26
|
+
prepared.appConfig.developerId,
|
|
27
|
+
{
|
|
28
|
+
runEnvKey: prepared.mergedRunOptions.env,
|
|
29
|
+
effectiveEnvironmentScopedResources:
|
|
30
|
+
prepared.mergedRunOptions.effectiveEnvironmentScopedResources
|
|
31
|
+
}
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
module.exports = { buildRunEnvRecovery };
|
package/lib/app/run-helpers.js
CHANGED
|
@@ -34,7 +34,12 @@ const runEnvCompose = require('./run-env-compose');
|
|
|
34
34
|
const { getAppInfraRequirements } = require('./run-infra-requirements');
|
|
35
35
|
const { resolveRunImage } = require('./run-resolve-image');
|
|
36
36
|
const { startContainer } = require('./run-container-start');
|
|
37
|
-
const {
|
|
37
|
+
const {
|
|
38
|
+
resolveEnvOutputPath,
|
|
39
|
+
resolveEnvLocalOutputPath,
|
|
40
|
+
writeEnvOutputForReload,
|
|
41
|
+
writeEnvOutputForLocal
|
|
42
|
+
} = require('../utils/env-copy');
|
|
38
43
|
const { resolveVersionForApp } = require('../utils/image-version');
|
|
39
44
|
const healthCheckUtil = require('../utils/health-check');
|
|
40
45
|
const { computeTraefikPublicAppUrl } = require('../utils/health-check-url');
|
|
@@ -248,18 +253,17 @@ async function checkInfraHealthOrThrow(debug, appConfig) {
|
|
|
248
253
|
}
|
|
249
254
|
|
|
250
255
|
/**
|
|
251
|
-
*
|
|
256
|
+
* Refresh the shared developer run directory for the current application.
|
|
257
|
+
*
|
|
258
|
+
* The applications-dev-* directory is shared by all applications. Always copy
|
|
259
|
+
* the selected app so files from a previous run cannot leak into this run.
|
|
252
260
|
* @async
|
|
253
261
|
* @param {string} appName - Application name
|
|
254
262
|
* @param {string} developerId - Developer ID
|
|
255
263
|
* @returns {Promise<string>} Dev directory path
|
|
256
264
|
*/
|
|
257
265
|
async function ensureDevDirectory(appName, developerId) {
|
|
258
|
-
|
|
259
|
-
if (!fsSync.existsSync(devDir)) {
|
|
260
|
-
await buildCopy.copyBuilderToDevDirectory(appName, developerId);
|
|
261
|
-
}
|
|
262
|
-
return devDir;
|
|
266
|
+
return buildCopy.copyBuilderToDevDirectory(appName, developerId);
|
|
263
267
|
}
|
|
264
268
|
|
|
265
269
|
/**
|
|
@@ -303,8 +307,8 @@ async function generateComposeFile(appName, appConfig, composeOptions, devDir, l
|
|
|
303
307
|
|
|
304
308
|
/**
|
|
305
309
|
* Writes `build.envOutputPath` after each `aifabrix run`: **local**-flavored env for the host/IDE
|
|
306
|
-
* when not using `--reload`; with **`--reload`**, merges container `.env.run` into
|
|
307
|
-
*
|
|
310
|
+
* when not using `--reload`; with **`--reload`**, merges container `.env.run` into `.env` and also
|
|
311
|
+
* writes a sibling **`.env.local`** with localhost / published-port URLs for host CLI and manual tests.
|
|
308
312
|
*
|
|
309
313
|
* @async
|
|
310
314
|
* @param {string} appName - Application name
|
|
@@ -326,6 +330,8 @@ async function writeEnvOutputIfConfigured(appName, appConfig, runEnvPath, option
|
|
|
326
330
|
}
|
|
327
331
|
if (options && options.reload === true) {
|
|
328
332
|
await writeEnvOutputForReload(outputPath, runEnvPath, appName);
|
|
333
|
+
const localOutputPath = resolveEnvLocalOutputPath(outputPath);
|
|
334
|
+
await writeEnvOutputForLocal(appName, localOutputPath);
|
|
329
335
|
} else {
|
|
330
336
|
await writeEnvOutputForLocal(appName, outputPath);
|
|
331
337
|
}
|
package/lib/app/run.js
CHANGED
|
@@ -28,6 +28,7 @@ const helpers = require('./run-helpers');
|
|
|
28
28
|
const { ensureReloadSync, logReloadDevSummary } = require('./run-reload-sync');
|
|
29
29
|
const { logRestartDevMountSummary } = require('./restart-display');
|
|
30
30
|
const { checkAndStopContainer } = require('./run-stop-containers');
|
|
31
|
+
const { buildRunEnvRecovery } = require('./run-env-recovery');
|
|
31
32
|
const { execWithDockerEnv } = require('../utils/docker-exec');
|
|
32
33
|
const { resolveRunProxyPersistEnabled, resolveRunEffectiveNoProxy } = require('../utils/run-cli-flags');
|
|
33
34
|
|
|
@@ -178,7 +179,8 @@ async function startAppContainer(appName, tempComposePath, hostPort, appConfig,
|
|
|
178
179
|
runEnvPath = null,
|
|
179
180
|
runEnvAdminPath = null,
|
|
180
181
|
runOptions = {},
|
|
181
|
-
devMountPath = null
|
|
182
|
+
devMountPath = null,
|
|
183
|
+
ensureRunEnvFiles = null
|
|
182
184
|
} = opts;
|
|
183
185
|
const misoEnvironment = opts.misoEnvironment ?? composeGenerator.resolveMisoEnvironment(runOptions);
|
|
184
186
|
try {
|
|
@@ -188,6 +190,7 @@ async function startAppContainer(appName, tempComposePath, hostPort, appConfig,
|
|
|
188
190
|
runEnvAdminPath,
|
|
189
191
|
runOptions,
|
|
190
192
|
devMountPath,
|
|
193
|
+
ensureRunEnvFiles,
|
|
191
194
|
misoEnvironment
|
|
192
195
|
});
|
|
193
196
|
await helpers.displayRunStatus(
|
|
@@ -404,13 +407,26 @@ async function runApp(appName, options = {}) {
|
|
|
404
407
|
logger.log(chalk.gray(`[DEBUG] Compose file generated: ${prepared.tempComposePath}`));
|
|
405
408
|
}
|
|
406
409
|
logReloadDevSummary(Boolean(prepared.mergedRunOptions.reload), prepared.mergedRunOptions.reloadSyncSummary);
|
|
410
|
+
const { runDeployJobIfConfigured } = require('./run-deploy-job');
|
|
411
|
+
await runDeployJobIfConfigured({
|
|
412
|
+
appConfig: prepared.appConfig,
|
|
413
|
+
fullImage: prepared.mergedRunOptions.image,
|
|
414
|
+
runEnvPath: prepared.runEnvPath,
|
|
415
|
+
devMountPath: prepared.devMountPath || null,
|
|
416
|
+
debug,
|
|
417
|
+
// Guided installers already show a top-level spinner; keep migrate/deployJob logs out of TTY.
|
|
418
|
+
quiet:
|
|
419
|
+
options.silentPoll === true || typeof options.onPollText === 'function'
|
|
420
|
+
});
|
|
421
|
+
const ensureRunEnvFiles = buildRunEnvRecovery(appName, prepared);
|
|
407
422
|
await startAppContainer(appName, prepared.tempComposePath, prepared.hostPort, prepared.appConfig, {
|
|
408
423
|
debug,
|
|
409
424
|
runEnvPath: prepared.runEnvPath,
|
|
410
425
|
runEnvAdminPath: prepared.runEnvAdminPath,
|
|
411
426
|
runOptions: prepared.mergedRunOptions,
|
|
412
427
|
devMountPath: prepared.devMountPath,
|
|
413
|
-
runScopeOpts: prepared.runScopeOpts
|
|
428
|
+
runScopeOpts: prepared.runScopeOpts,
|
|
429
|
+
ensureRunEnvFiles
|
|
414
430
|
});
|
|
415
431
|
} catch (error) {
|
|
416
432
|
if (debug) {
|
|
@@ -12,6 +12,7 @@ const { getChannelTarget, getDefaultChannelDescription } = require('./channel-ta
|
|
|
12
12
|
const { resolveAppDescription } = require('./channel-description');
|
|
13
13
|
const { resolveChannelRuntimeValues } = require('./channel-runtime-resolver');
|
|
14
14
|
const { generateChannelArtifact } = require('./channel-artifact-generator');
|
|
15
|
+
const { fetchRuntimeLayerBToolNames } = require('./runtime-tool-catalog');
|
|
15
16
|
const {
|
|
16
17
|
getExistingChannelExtension,
|
|
17
18
|
buildChannelExtensionBlock,
|
|
@@ -66,7 +67,9 @@ function toValidationDisplay(validation) {
|
|
|
66
67
|
callbackReachable: validation.callbackReachable,
|
|
67
68
|
credentialValid: validation.credentialValid,
|
|
68
69
|
assistantCatalogReachable: validation.assistantCatalogReachable,
|
|
69
|
-
inboundVerification: Object.values(validation.inboundVerification).every(Boolean)
|
|
70
|
+
inboundVerification: Object.values(validation.inboundVerification).every(Boolean),
|
|
71
|
+
probeTiers: validation.probeTiers || null,
|
|
72
|
+
runtimeChecks: validation.runtimeChecks || null
|
|
70
73
|
};
|
|
71
74
|
}
|
|
72
75
|
|
|
@@ -156,7 +159,13 @@ async function patchConfigWithAudit(ctx, channelTarget, channelBlock, status, ar
|
|
|
156
159
|
async function maybeRunProbe(ctx, channelBlock, channelTarget, probe) {
|
|
157
160
|
if (!probe) return null;
|
|
158
161
|
const validation = await runChannelProbe(
|
|
159
|
-
{
|
|
162
|
+
{
|
|
163
|
+
dataplaneUrl: ctx.dataplaneUrl,
|
|
164
|
+
authConfig: ctx.authConfig,
|
|
165
|
+
environment: ctx.environment,
|
|
166
|
+
controllerUrl: ctx.controllerUrl,
|
|
167
|
+
channelTarget
|
|
168
|
+
},
|
|
160
169
|
channelBlock
|
|
161
170
|
);
|
|
162
171
|
if (validation.passed) {
|
|
@@ -268,6 +277,42 @@ function finishDryRunInstall(params) {
|
|
|
268
277
|
return { dryRun: true, channelBlock: params.channelBlock, artifact: params.artifact };
|
|
269
278
|
}
|
|
270
279
|
|
|
280
|
+
/**
|
|
281
|
+
* @param {Object} params
|
|
282
|
+
* @returns {Promise<{ artifact: Object, channelBlock: Object, status: string }>}
|
|
283
|
+
*/
|
|
284
|
+
async function buildInstallArtifacts(params) {
|
|
285
|
+
const { channelTarget, options, target, existing, name, baseName, description, ctx } = params;
|
|
286
|
+
const runtimeToolNames =
|
|
287
|
+
channelTarget === 'chatgpt' || channelTarget === 'microsoft-copilot'
|
|
288
|
+
? await fetchRuntimeLayerBToolNames(ctx.dataplaneUrl, ctx.authConfig, channelTarget)
|
|
289
|
+
: [];
|
|
290
|
+
const runtimeValues = resolveChannelRuntimeValues({
|
|
291
|
+
dataplaneBaseUrl: ctx.dataplaneUrl,
|
|
292
|
+
channelTarget,
|
|
293
|
+
appName: name,
|
|
294
|
+
baseName,
|
|
295
|
+
environment: ctx.environment,
|
|
296
|
+
appDescription: description,
|
|
297
|
+
icon: options.icon,
|
|
298
|
+
commandPrefix: options.commandPrefix,
|
|
299
|
+
layerBToolNames: runtimeToolNames
|
|
300
|
+
});
|
|
301
|
+
const projectRoot = options.projectRoot || process.cwd();
|
|
302
|
+
const artifact = generateArtifactWithAudit(channelTarget, {
|
|
303
|
+
projectRoot,
|
|
304
|
+
channelTarget,
|
|
305
|
+
runtimeValues,
|
|
306
|
+
dryRun: options.dryRun === true
|
|
307
|
+
});
|
|
308
|
+
const status = resolveInstallStatus(target, existing, options.dryRun === true);
|
|
309
|
+
const channelBlock = buildChannelExtensionBlock(
|
|
310
|
+
{ name, baseName, description, artifactHash: artifact.artifactHash, status },
|
|
311
|
+
target
|
|
312
|
+
);
|
|
313
|
+
return { artifact, channelBlock, status };
|
|
314
|
+
}
|
|
315
|
+
|
|
271
316
|
/**
|
|
272
317
|
* @param {string} channelTarget
|
|
273
318
|
* @param {Object} options
|
|
@@ -289,31 +334,17 @@ async function runAddChannel(channelTarget, options = {}) {
|
|
|
289
334
|
});
|
|
290
335
|
if (skipped) return skipped;
|
|
291
336
|
|
|
292
|
-
const
|
|
293
|
-
dataplaneBaseUrl: ctx.dataplaneUrl,
|
|
337
|
+
const { artifact, channelBlock, status } = await buildInstallArtifacts({
|
|
294
338
|
channelTarget,
|
|
295
|
-
|
|
339
|
+
options,
|
|
340
|
+
target,
|
|
341
|
+
existing,
|
|
342
|
+
name,
|
|
296
343
|
baseName,
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
icon: options.icon,
|
|
300
|
-
commandPrefix: options.commandPrefix
|
|
301
|
-
});
|
|
302
|
-
|
|
303
|
-
const projectRoot = options.projectRoot || process.cwd();
|
|
304
|
-
const artifact = generateArtifactWithAudit(channelTarget, {
|
|
305
|
-
projectRoot,
|
|
306
|
-
channelTarget,
|
|
307
|
-
runtimeValues,
|
|
308
|
-
dryRun: options.dryRun === true
|
|
344
|
+
description,
|
|
345
|
+
ctx
|
|
309
346
|
});
|
|
310
347
|
|
|
311
|
-
const status = resolveInstallStatus(target, existing, options.dryRun === true);
|
|
312
|
-
const channelBlock = buildChannelExtensionBlock(
|
|
313
|
-
{ name, baseName, description, artifactHash: artifact.artifactHash, status },
|
|
314
|
-
target
|
|
315
|
-
);
|
|
316
|
-
|
|
317
348
|
if (options.dryRun) {
|
|
318
349
|
return finishDryRunInstall({ channelTarget, name, baseName, target, artifact, channelBlock });
|
|
319
350
|
}
|
|
@@ -16,16 +16,20 @@ const { formatCredentialPortalStep } = require('./credential-portal');
|
|
|
16
16
|
*/
|
|
17
17
|
function buildChatGptApprovalGuide(ctx) {
|
|
18
18
|
return {
|
|
19
|
-
portalUrl: 'https://
|
|
19
|
+
portalUrl: 'https://chatgpt.com/',
|
|
20
20
|
steps: [
|
|
21
|
-
'Sign in to
|
|
22
|
-
'
|
|
23
|
-
'
|
|
24
|
-
'
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
21
|
+
'Sign in to the ChatGPT web app as a workspace admin or owner.',
|
|
22
|
+
'Enable Developer mode for custom MCP apps (Business: Workspace settings → Apps, or Settings → Apps → Advanced settings; Enterprise/Edu: grant Developer mode under Permissions & roles → Connected data first).',
|
|
23
|
+
'Create a custom MCP app with the Remote MCP URL from INSTALL.md (channel-chatgpt.json is a deployment reference, not a plugin archive).',
|
|
24
|
+
'Select OAuth (authorization-code + PKCE S256). Do not configure a custom API key for a remote custom MCP app.',
|
|
25
|
+
'Scan tools, complete OAuth if prompted, review discovered Runtime Layer B tools, then save the app as a draft.',
|
|
26
|
+
formatCredentialPortalStep(
|
|
27
|
+
ctx,
|
|
28
|
+
`${ctx.credentialKey || 'channel'} credential references (not secrets to paste into ChatGPT)`
|
|
29
|
+
),
|
|
30
|
+
'Test representative workflows, then publish the app and configure workspace access and action controls.',
|
|
31
|
+
`After ChatGPT lists the app as active, re-run: ${ctx.resumeCommand}`,
|
|
32
|
+
'Optional: use --probe for AI Fabrix-side Runtime checks; ChatGPT Scan tools / OAuth / publication remain workspace-admin responsibilities.'
|
|
29
33
|
]
|
|
30
34
|
};
|
|
31
35
|
}
|
|
@@ -19,14 +19,13 @@ function buildMicrosoftCopilotApprovalGuide(ctx) {
|
|
|
19
19
|
portalUrl: 'https://admin.cloud.microsoft.com/',
|
|
20
20
|
steps: [
|
|
21
21
|
'Sign in to Microsoft 365 admin (https://admin.cloud.microsoft.com/) or Copilot Studio as a tenant admin.',
|
|
22
|
-
'
|
|
23
|
-
'
|
|
24
|
-
'
|
|
25
|
-
'
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
'Optional: add --probe when dataplane Copilot runtime routes are live on your environment.'
|
|
22
|
+
'Upload or sideload the generated appPackage.<env>.zip package artifact.',
|
|
23
|
+
'Review declarative agent + Remote MCP plugin details and approve tenant distribution.',
|
|
24
|
+
'Configure Entra/OAuth consent and user assignment for target groups.',
|
|
25
|
+
formatCredentialPortalStep(ctx, 'Copilot channel credential references (no secrets in package files)'),
|
|
26
|
+
'Verify Runtime MCP endpoint and Layer B tool visibility using INSTALL.md guidance.',
|
|
27
|
+
`After Copilot shows the package as available, re-run: ${ctx.resumeCommand}`,
|
|
28
|
+
'Optional: use --probe for AI Fabrix-side checks; tenant upload/approval remains Microsoft-admin responsibility.'
|
|
30
29
|
]
|
|
31
30
|
};
|
|
32
31
|
}
|
|
@@ -14,6 +14,7 @@ const path = require('path');
|
|
|
14
14
|
const Handlebars = require('handlebars');
|
|
15
15
|
const { getChannelTarget } = require('./channel-targets');
|
|
16
16
|
const { BUILDER_ROOT } = require('./channel-integration-template');
|
|
17
|
+
const { generateMicrosoftCopilotPackage } = require('./microsoft-copilot-package');
|
|
17
18
|
|
|
18
19
|
const SECRET_PATTERNS = [/xoxb-/i, /xoxp-/i, /client_secret/i, /signing_secret/i, /Bearer\s+/i];
|
|
19
20
|
const PLACEHOLDER_DOMAIN_RE = /\byour-domain\.com\b/i;
|
|
@@ -115,11 +116,22 @@ function renderInstallGuideIfPresent(templatesDir, runtimeValues, outputDir, dry
|
|
|
115
116
|
*/
|
|
116
117
|
function generateChannelArtifact(params) {
|
|
117
118
|
const templatesDir = getChannelTemplatesDir(params.channelTarget);
|
|
119
|
+
const outputPath = getArtifactOutputPath(params.projectRoot, params.channelTarget);
|
|
120
|
+
|
|
121
|
+
if (params.channelTarget === 'microsoft-copilot') {
|
|
122
|
+
return generateMicrosoftCopilotPackage({
|
|
123
|
+
runtimeValues: params.runtimeValues,
|
|
124
|
+
templatesDir,
|
|
125
|
+
outputPath,
|
|
126
|
+
dryRun: params.dryRun === true,
|
|
127
|
+
assertNoSecretsInArtifact,
|
|
128
|
+
renderTemplateFile
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
|
|
118
132
|
const manifestTemplate = path.join(templatesDir, 'manifest.template.json.hbs');
|
|
119
133
|
const content = renderTemplateFile(manifestTemplate, params.runtimeValues);
|
|
120
134
|
assertNoSecretsInArtifact(content);
|
|
121
|
-
|
|
122
|
-
const outputPath = getArtifactOutputPath(params.projectRoot, params.channelTarget);
|
|
123
135
|
const artifactHash = crypto.createHash('sha256').update(content, 'utf8').digest('hex');
|
|
124
136
|
const outputDir = path.dirname(outputPath);
|
|
125
137
|
|
|
@@ -48,8 +48,10 @@ const CREDENTIAL_SPECS = {
|
|
|
48
48
|
method: 'oauth2',
|
|
49
49
|
credentialConfig: {
|
|
50
50
|
type: 'oauth2',
|
|
51
|
-
grantType: '
|
|
51
|
+
grantType: 'authorization_code',
|
|
52
|
+
authorizationUrl: 'https://login.microsoftonline.com/common/oauth2/v2.0/authorize',
|
|
52
53
|
tokenUrl: 'https://login.microsoftonline.com/common/oauth2/v2.0/token',
|
|
54
|
+
scope: 'openid profile offline_access',
|
|
53
55
|
clientId: 'pending-channel-install',
|
|
54
56
|
clientSecret: 'pending-channel-install'
|
|
55
57
|
}
|