@bridge_gpt/mcp-server 0.2.65 → 0.2.67
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/README.md +269 -82
- package/build/agent-capabilities/probes.js +15 -5
- package/build/agent-launchers/claude-executor-adapter.js +55 -34
- package/build/agent-launchers/executor-adapter.js +34 -4
- package/build/agents.generated.js +1 -1
- package/build/bounded-wait.js +67 -10
- package/build/bridge-config.js +104 -0
- package/build/codex-bridge-profile.js +10 -1
- package/build/commands.generated.js +12 -12
- package/build/conductor/bridge-api-client.js +372 -10
- package/build/conductor/bring-up-facts.js +1 -1
- package/build/conductor/doctor.js +3 -3
- package/build/conductor/epic-child-continuation.js +61 -34
- package/build/conductor/epic-implementer-cli.js +34 -6
- package/build/conductor/errors.js +4 -3
- package/build/conductor/guided-repair.js +327 -0
- package/build/conductor/hook-runtime-qualification.js +1 -1
- package/build/conductor/merge-execution-contract.js +27 -8
- package/build/conductor/merge-execution-eligibility.js +3 -2
- package/build/conductor/pr-ci-producer.js +1 -1
- package/build/conductor/readiness-cli.js +46 -9
- package/build/conductor/readiness-sections.js +41 -6
- package/build/conductor/readiness.js +204 -30
- package/build/conductor/recovery-cli.js +703 -20
- package/build/conductor/recovery-operations.js +392 -3
- package/build/conductor/repair-contract.js +112 -3
- package/build/conductor/repository-workflow-remedy.js +125 -0
- package/build/conductor/tools.js +2 -2
- package/build/conductor-bundle-artifacts.js +8 -0
- package/build/credential-store.js +5 -4
- package/build/diagnostic-text.js +36 -0
- package/build/docs.generated.js +8 -3
- package/build/doctor.js +70 -10
- package/build/drive-epic-key.js +6 -5
- package/build/drive-epic.js +479 -131
- package/build/epic-coherence-input.js +2 -1
- package/build/epic-implementer-bin.js +30 -26
- package/build/epic-implementer-bundle-cli.js +2 -2
- package/build/epic-launch-diagnostic.js +31 -0
- package/build/epic-launch-profile.js +44 -14
- package/build/executor/cli.js +3 -2
- package/build/executor/completion-contract.js +48 -5
- package/build/executor/completion-supervision.js +9 -0
- package/build/executor/correction-dispatch.js +60 -0
- package/build/executor/env.js +25 -0
- package/build/executor/heartbeat.js +1 -1
- package/build/executor/http-client.js +35 -0
- package/build/executor/integration-job.js +6 -1
- package/build/executor/job-errors.js +38 -4
- package/build/executor/job-log-registry.js +340 -73
- package/build/executor/job-runner.js +485 -74
- package/build/executor/job-types.js +8 -13
- package/build/executor/observation.js +6 -1
- package/build/executor/preflight.js +136 -10
- package/build/executor/prompt-spec.js +113 -9
- package/build/executor/required-work-ledger.js +6 -1
- package/build/executor/resume-pre-spawn.js +2 -0
- package/build/executor/runner.js +31 -2
- package/build/executor/service-unit.js +7 -1
- package/build/executor/stale-artifacts.js +5 -4
- package/build/executor/types.js +1 -0
- package/build/executor/watch-cli.js +35 -8
- package/build/executor/worker-command.js +43 -43
- package/build/executor/worker-finalization.js +161 -43
- package/build/executor/worker-guard-hook.js +152 -12
- package/build/executor/worker-log.js +137 -70
- package/build/executor/worktree-gc.js +30 -6
- package/build/executor/worktree.js +82 -1
- package/build/index.js +482 -660
- package/build/init.js +107 -4
- package/build/install-bridge-conductor.js +8 -6
- package/build/install-bridge.js +287 -23
- package/build/install-doctor.js +172 -30
- package/build/install-reexec.js +69 -7
- package/build/macos-terminal-permission-guidance.js +16 -0
- package/build/model-thresholds-tool.js +185 -0
- package/build/phase-context-git-probe.js +68 -0
- package/build/phase-result-artifacts.js +173 -5
- package/build/pipelines.generated.js +19 -25
- package/build/plane/alembic-head.js +12 -3
- package/build/plane/base-worktree-guard.js +147 -0
- package/build/plane/cli.js +217 -17
- package/build/plane/code-snapshot.js +468 -0
- package/build/plane/defaults.js +48 -3
- package/build/plane/endpoint.js +41 -0
- package/build/plane/manifest.js +120 -1
- package/build/plane/member-roster.js +94 -19
- package/build/plane/preflight.js +30 -2
- package/build/plane/process-cwd.js +67 -0
- package/build/plane/restart.js +542 -0
- package/build/plane/shutdown.js +31 -0
- package/build/plane/status.js +145 -6
- package/build/plane/supervisor.js +588 -177
- package/build/plane/types.js +54 -2
- package/build/plane/worker-exit-code.js +2 -0
- package/build/printed-remedy-commands.js +37 -0
- package/build/project-root.js +99 -0
- package/build/readiness-check.js +1 -1
- package/build/readme.generated.js +1 -1
- package/build/review-generation.js +22 -0
- package/build/review-tickets.js +156 -13
- package/build/setup-epic.js +224 -127
- package/build/sfcc/config.js +9 -0
- package/build/sfcc/credentials.js +100 -38
- package/build/sfcc/hostname.js +67 -0
- package/build/sfcc/permissions.js +17 -13
- package/build/sfcc/register.js +1 -1
- package/build/sfcc/setup-status.js +2 -0
- package/build/sfcc/tool-wrapper.js +41 -43
- package/build/sfcc/tools-configured.js +96 -0
- package/build/sfcc/write-grants.js +17 -11
- package/build/sfcc/write-result.js +4 -3
- package/build/sfcc/writes-site-preference.js +20 -8
- package/build/start-tickets.js +332 -79
- package/build/tool-error-envelope.js +61 -0
- package/build/upgrade-cli.js +109 -24
- package/build/version.generated.js +3 -3
- package/build/visual-diff.js +22 -1
- package/build/windows-npm-entry.js +103 -0
- package/build/worker-guard-hook-bin.js +3 -5
- package/build/worker-guard-hook-main.js +47 -0
- package/build/worktree-core.js +136 -9
- package/docs/EPIC-IMPLEMENTER-INSTALL.md +849 -0
- package/docs/EPIC-IMPLEMENTER.md +662 -0
- package/docs/install/mcp-tool-integrations.md +25 -15
- package/docs/install/sfcc-integration.md +21 -8
- package/package.json +1 -1
- package/pipelines/implement-ticket.json +1 -1
- package/pipelines/plan-epic.json +0 -5
- package/pipelines/review-ticket.json +1 -1
- package/docs/CONDUCTOR.md +0 -605
- package/pipelines/idea-to-pr.json +0 -49
package/build/setup-epic.js
CHANGED
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* `setup-epic` — one-command bootstrap for an Epic
|
|
2
|
+
* `setup-epic` — one-command bootstrap for an Epic Implementer run.
|
|
3
3
|
*
|
|
4
4
|
* Creates the epic run, stores the plan DAG, and approves it. Before this
|
|
5
|
-
* existed the only path was hand-rolled HTTP with a hand-computed plan hash
|
|
6
|
-
*
|
|
7
|
-
*
|
|
5
|
+
* existed the only path was hand-rolled HTTP with a hand-computed plan hash —
|
|
6
|
+
* there was no tool to call.
|
|
7
|
+
*
|
|
8
|
+
* This is a separate run-creation workflow, not a `/plan-epic` pipeline step:
|
|
9
|
+
* `/plan-epic` authors the local sidecar and stops. An operator creates the child
|
|
10
|
+
* tickets, finalizes the plan, and then launches through the gated `drive-epic`
|
|
11
|
+
* entry point, which is what reaches this command.
|
|
8
12
|
*
|
|
9
13
|
* Ordering is load-bearing:
|
|
10
14
|
*
|
|
@@ -78,14 +82,16 @@
|
|
|
78
82
|
import { readFile as fsReadFile, stat as fsStat } from "node:fs/promises";
|
|
79
83
|
import os from "node:os";
|
|
80
84
|
import { createProductionEpicIntegrationGhRunner, ensureEpicIntegrationPullRequest, formatEpicIntegrationPullRequestOutcome, } from "./epic-integration-pr.js";
|
|
81
|
-
import { approveEpicPlan, buildConductorJiraUrl, conductorGetHeaders, createEpicRunWithDisposition, decodeCanonicalProvenance, fetchConductorReadiness, fetchEpicRunState, replaceEpicRunPolicy, resolveConductorBridgeApiAccess, resolveEpicLaunchBridgeApiAccess, storeEpicPlan, validateEpicPlan, ConductorBridgeApiError, } from "./conductor/bridge-api-client.js";
|
|
85
|
+
import { approveEpicPlan, buildConductorJiraUrl, conductorGetHeaders, createEpicRunWithDisposition, decodeCanonicalProvenance, fetchConductorReadiness, fetchEpicRunState, replaceEpicRunPolicy, resolveConductorBridgeApiAccess, resolveEpicLaunchBridgeApiAccess, safeDiagnosticMessage, storeEpicPlan, validateEpicPlan, ConductorBridgeApiError, } from "./conductor/bridge-api-client.js";
|
|
86
|
+
import { formatEpicLaunchChannelIdentity, getPinnedEpicLaunchPolicy, readEpicLaunchProfile, } from "./epic-launch-profile.js";
|
|
82
87
|
// BAPI-1133 (A2) — the single legacy-to-typed merge-execution-mode mapping and
|
|
83
88
|
// its total legacy resolver live in `merge-pull-request.ts` (no import cycle:
|
|
84
89
|
// that module never imports from `setup-epic.ts`). Reused here rather than
|
|
85
90
|
// reimplemented so the mapping stays single-defined.
|
|
86
91
|
import { mapLegacyMergeExecutionModeToPolicyMode, resolveMergeExecutionMode, } from "./merge-pull-request.js";
|
|
87
|
-
import { MERGE_EXECUTION_INELIGIBLE, MERGE_EXECUTION_MODE_GITHUB_ACTIONS, MERGE_WORKFLOW_REMEDY_COMMAND, } from "./conductor/merge-execution-contract.js";
|
|
92
|
+
import { MERGE_EXECUTION_INELIGIBLE, MERGE_EXECUTION_MODE_GITHUB_ACTIONS, MERGE_WORKFLOW_CODEOWNER_INSTRUCTION, MERGE_WORKFLOW_REMEDY_COMMAND, } from "./conductor/merge-execution-contract.js";
|
|
88
93
|
import { classifyMergeExecutionEligibility } from "./conductor/merge-execution-eligibility.js";
|
|
94
|
+
import { repositoryWorkflowRemedy } from "./conductor/repository-workflow-remedy.js";
|
|
89
95
|
import { resolveTrackedPaths } from "./git-reachability.js";
|
|
90
96
|
import { validateBranchName } from "./base-ref.js";
|
|
91
97
|
// Reused from the implement-epic client on purpose: adding a second wrapper under
|
|
@@ -99,6 +105,7 @@ import { validateBranchName } from "./base-ref.js";
|
|
|
99
105
|
import { getConfigFieldBaseBranch, getIndexScopeLifecycle, getParseStatus, } from "./epic-shared/bridge-client.js";
|
|
100
106
|
import { boundScopeLifecycleLabel, createExecFileRunCommand, ensureCommitResolvableLocally, performExactIndexScopeCut, pollIndexScopeLifecycle, readRemoteBranchHead, runGit, SCOPE_BOOTSTRAP_POLL_INTERVAL_MS, SCOPE_LIFECYCLE_LABELS, } from "./epic-shared/cut-protocol.js";
|
|
101
107
|
import { hashPlan } from "./conductor/plan.js";
|
|
108
|
+
import { PLACEHOLDER_NODE_KEY_RE } from "./conductor-bundle-artifacts.js";
|
|
102
109
|
import { BRING_UP_REFUSAL_MESSAGES, firstBringUpRefusal, } from "./conductor/bring-up-facts.js";
|
|
103
110
|
import { MCP_PACKAGE_NAME } from "./mcp-identity.js";
|
|
104
111
|
// BAPI-872: local plane-manifest binding, so `plane down` can later stop the
|
|
@@ -189,7 +196,7 @@ export function getSetupEpicUsage() {
|
|
|
189
196
|
return [
|
|
190
197
|
"Usage: mcp-server setup-epic --epic-key <KEY> --plan-file <path> [options]",
|
|
191
198
|
"",
|
|
192
|
-
"Bootstraps an Epic
|
|
199
|
+
"Bootstraps an Epic Implementer run: creates the run, stores the plan DAG,",
|
|
193
200
|
"and approves it. Idempotent — re-running reuses an existing live run.",
|
|
194
201
|
"",
|
|
195
202
|
"Branch strategy (default): a plan with TWO OR MORE nodes runs on a dedicated",
|
|
@@ -209,11 +216,12 @@ export function getSetupEpicUsage() {
|
|
|
209
216
|
"that selects no epic branch (--into-base, or a single-node plan) is unchanged",
|
|
210
217
|
"and composes no posture at all.",
|
|
211
218
|
"",
|
|
212
|
-
"Unattended runs have
|
|
213
|
-
"run is created: repository consent (ON by default; the repository owner can",
|
|
219
|
+
"Unattended runs have THREE repository prerequisites, refused by name before",
|
|
220
|
+
"any run is created: repository consent (ON by default; the repository owner can",
|
|
214
221
|
"switch 'Allow unattended Epic Implementer runs' off and on in the Bridge admin",
|
|
215
|
-
"panel's Setup page)
|
|
216
|
-
"
|
|
222
|
+
"panel's Setup page); an escalation channel, either a verified notify webhook or",
|
|
223
|
+
"the account's email address; and confirmed review and CI workflows on the",
|
|
224
|
+
"default branch. `drive-epic` readiness lists all three with their current state.",
|
|
217
225
|
"",
|
|
218
226
|
"Required:",
|
|
219
227
|
" --epic-key <KEY> Jira epic key (e.g. BAPI-405)",
|
|
@@ -278,7 +286,7 @@ export function getSetupEpicUsage() {
|
|
|
278
286
|
" 10799; remediate / ci_fix 3599; rebase / smoke 2699;",
|
|
279
287
|
" merge 1799); the server rejects a higher value with a",
|
|
280
288
|
" 422 naming the offending field. Full table:",
|
|
281
|
-
" docs/
|
|
289
|
+
" docs/epic-implementer/operator-runbook.md §4.",
|
|
282
290
|
" --replace-policy Authorize replacing a LIVE run's stored policy with the",
|
|
283
291
|
" policy file, as a complete replacement. Without it, a",
|
|
284
292
|
" divergent policy on a reused run is refused with a",
|
|
@@ -296,8 +304,8 @@ export function getSetupEpicUsage() {
|
|
|
296
304
|
" never a substitute for authoritative validation.",
|
|
297
305
|
"",
|
|
298
306
|
"After setup, the server-side reconciler picks the run up within ~30s.",
|
|
299
|
-
"To execute claimed jobs on this machine, run:",
|
|
300
|
-
` npx -y ${MCP_PACKAGE_NAME} executor --repo <name>`,
|
|
307
|
+
"To execute this run's claimed jobs on this machine, run:",
|
|
308
|
+
` npx -y ${MCP_PACKAGE_NAME} executor --repo <name> --epic-run-id <run-id>`,
|
|
301
309
|
].join("\n");
|
|
302
310
|
}
|
|
303
311
|
function takeValue(argv, i, flag) {
|
|
@@ -987,6 +995,20 @@ export function validateEpicPlanSidecar(parsed) {
|
|
|
987
995
|
`to get that scheduling protection (use [] when no files are predicted).`);
|
|
988
996
|
}
|
|
989
997
|
}
|
|
998
|
+
// BAPI-1366: refuse unfinalized `TBD-N` node keys before any graph, access, or
|
|
999
|
+
// network work. `/plan-epic` authors placeholders; finalization replaces them.
|
|
1000
|
+
// This runs here, not in the server lane, so an updated client refuses even
|
|
1001
|
+
// against an older server and in `--dry-run --local-only`.
|
|
1002
|
+
const placeholders = [...keys].filter((key) => PLACEHOLDER_NODE_KEY_RE.test(key)).sort();
|
|
1003
|
+
if (placeholders.length > 0) {
|
|
1004
|
+
return {
|
|
1005
|
+
ok: false,
|
|
1006
|
+
error: `Plan node keys ${placeholders.join(", ")} are placeholders, not tickets. ` +
|
|
1007
|
+
`Create the child tickets and run \`emit-epic-implementer-bundle finalize\` ` +
|
|
1008
|
+
`to replace every placeholder, then run this again (this invocation created ` +
|
|
1009
|
+
`no run, cut no branch, and incurred no charge).`,
|
|
1010
|
+
};
|
|
1011
|
+
}
|
|
990
1012
|
// Build one adjacency in a consistent predecessor -> successor direction, the
|
|
991
1013
|
// same way the server does. `depends_on` points at the predecessor; an edge's
|
|
992
1014
|
// `from` IS the predecessor.
|
|
@@ -1117,6 +1139,24 @@ function findCycle(keys, adjacency) {
|
|
|
1117
1139
|
}
|
|
1118
1140
|
return null;
|
|
1119
1141
|
}
|
|
1142
|
+
export function effectivePolicyFacts(policy, source) {
|
|
1143
|
+
const object = policy && typeof policy === "object" && !Array.isArray(policy)
|
|
1144
|
+
? policy : {};
|
|
1145
|
+
const numbers = (value) => {
|
|
1146
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
1147
|
+
return null;
|
|
1148
|
+
const entries = Object.entries(value).filter(([key, item]) => /^[a-z][a-z0-9_]{0,63}$/.test(key) && typeof item === "number" && Number.isFinite(item));
|
|
1149
|
+
return entries.length ? Object.fromEntries(entries) : null;
|
|
1150
|
+
};
|
|
1151
|
+
return {
|
|
1152
|
+
source,
|
|
1153
|
+
serial_execution: "serial",
|
|
1154
|
+
posture: object.posture === "unattended" ? "unattended" : "attended",
|
|
1155
|
+
caps: numbers(object.caps),
|
|
1156
|
+
job_timeouts: numbers(object.job_timeouts),
|
|
1157
|
+
escalation_channel: formatEpicLaunchChannelIdentity(source === "stored" ? "unavailable" : policyDeclaresNotifyWebhook(object) ? "configured webhook" : "account email"),
|
|
1158
|
+
};
|
|
1159
|
+
}
|
|
1120
1160
|
/** Propose a conductor epic feature branch name for an epic key (BAPI-655). */
|
|
1121
1161
|
export function proposeFeatureBranchName(epicKey) {
|
|
1122
1162
|
return `epic/${epicKey}`;
|
|
@@ -1274,12 +1314,13 @@ export function evaluateSetupEpicUnattendedPrerequisites(readiness, requestedPol
|
|
|
1274
1314
|
};
|
|
1275
1315
|
}
|
|
1276
1316
|
if (!unattended.repository_readiness_confirmed) {
|
|
1317
|
+
// BAPI-1359: the shared formatter names the specific missing item; the reason
|
|
1318
|
+
// code and the trailing sentence are unchanged.
|
|
1277
1319
|
return {
|
|
1278
1320
|
reason: "repository_readiness_unconfirmed",
|
|
1279
1321
|
message: "the unattended default authorizes automatic merge, which requires the review and " +
|
|
1280
|
-
"
|
|
1281
|
-
|
|
1282
|
-
"attended run. No run was created.",
|
|
1322
|
+
"epic-implementer-ci workflows to be CONFIRMED on the default branch, and they are not. " +
|
|
1323
|
+
`${repositoryWorkflowRemedy(readiness)} No run was created.`,
|
|
1283
1324
|
};
|
|
1284
1325
|
}
|
|
1285
1326
|
return null;
|
|
@@ -1352,8 +1393,10 @@ export function evaluateSetupEpicMergeExecutionPrerequisites(readiness, requeste
|
|
|
1352
1393
|
reason: MERGE_EXECUTION_INELIGIBLE,
|
|
1353
1394
|
message: "this run declares merge_execution_mode: github_actions, and the Bridge API " +
|
|
1354
1395
|
"conductor readiness report could not be read, so the repository's merge " +
|
|
1355
|
-
"capability could not be confirmed. Retry once the server is reachable
|
|
1356
|
-
|
|
1396
|
+
"capability could not be confirmed. Retry once the server is reachable. If " +
|
|
1397
|
+
"readiness then reports the merge workflow missing or out of date, resolve it " +
|
|
1398
|
+
`with \`${MERGE_WORKFLOW_REMEDY_COMMAND}\`. ${MERGE_WORKFLOW_CODEOWNER_INSTRUCTION} ` +
|
|
1399
|
+
NO_RUN_WAS_CREATED,
|
|
1357
1400
|
};
|
|
1358
1401
|
}
|
|
1359
1402
|
const verdict = classifyMergeExecutionEligibility(readiness);
|
|
@@ -1366,7 +1409,8 @@ export function evaluateSetupEpicMergeExecutionPrerequisites(readiness, requeste
|
|
|
1366
1409
|
reason: MERGE_EXECUTION_INELIGIBLE,
|
|
1367
1410
|
message: "this run declares merge_execution_mode: github_actions, and this repository's " +
|
|
1368
1411
|
`merge capability ${category}: ${verdict.detail}. Resolve it with ` +
|
|
1369
|
-
`\`${MERGE_WORKFLOW_REMEDY_COMMAND}\`, then re-run.
|
|
1412
|
+
`\`${MERGE_WORKFLOW_REMEDY_COMMAND}\`, then re-run. ` +
|
|
1413
|
+
`${MERGE_WORKFLOW_CODEOWNER_INSTRUCTION} ${NO_RUN_WAS_CREATED}`,
|
|
1370
1414
|
};
|
|
1371
1415
|
}
|
|
1372
1416
|
/**
|
|
@@ -1561,12 +1605,7 @@ composesDefaultPolicy = true) {
|
|
|
1561
1605
|
`(pass --feature-branch to force ${proposeFeatureBranchName(opts.epicKey)} anyway)`);
|
|
1562
1606
|
}
|
|
1563
1607
|
function errorDetail(err) {
|
|
1564
|
-
|
|
1565
|
-
const status = err.status !== undefined ? ` (HTTP ${err.status})` : "";
|
|
1566
|
-
const preview = err.bodyPreview ? `: ${err.bodyPreview}` : "";
|
|
1567
|
-
return `${err.message}${status}${preview}`;
|
|
1568
|
-
}
|
|
1569
|
-
return err instanceof Error ? err.message : String(err);
|
|
1608
|
+
return safeDiagnosticMessage(err, "operation failed");
|
|
1570
1609
|
}
|
|
1571
1610
|
/**
|
|
1572
1611
|
* Reconcile a policy file against a REUSED run's stored policy, applying it only
|
|
@@ -1588,11 +1627,13 @@ async function applyReusedRunPolicy(args) {
|
|
|
1588
1627
|
});
|
|
1589
1628
|
if (outcome.kind === "noop") {
|
|
1590
1629
|
args.result.policy_applied = "noop";
|
|
1630
|
+
args.result.effective_policy = effectivePolicyFacts(args.storedPolicy, "stored");
|
|
1591
1631
|
args.say("Policy: policy unchanged");
|
|
1592
1632
|
return true;
|
|
1593
1633
|
}
|
|
1594
1634
|
if (outcome.kind === "refused_divergent") {
|
|
1595
1635
|
args.result.policy_applied = "refused_divergent";
|
|
1636
|
+
args.result.effective_policy = effectivePolicyFacts(args.storedPolicy, "stored");
|
|
1596
1637
|
args.deps.errorLog(`Epic run ${args.epicRunId} is live and its stored policy differs from the ` +
|
|
1597
1638
|
`policy file. setup-epic will not silently change a running epic's policy.\n` +
|
|
1598
1639
|
`${outcome.diffLines.join("\n")}\n` +
|
|
@@ -1605,10 +1646,12 @@ async function applyReusedRunPolicy(args) {
|
|
|
1605
1646
|
}
|
|
1606
1647
|
catch (err) {
|
|
1607
1648
|
args.result.policy_applied = "refused_divergent";
|
|
1649
|
+
args.result.effective_policy = effectivePolicyFacts(args.storedPolicy, "stored");
|
|
1608
1650
|
args.deps.errorLog(`Failed to replace the run policy: ${errorDetail(err)}`);
|
|
1609
1651
|
return false;
|
|
1610
1652
|
}
|
|
1611
1653
|
args.result.policy_applied = "replaced";
|
|
1654
|
+
args.result.effective_policy = effectivePolicyFacts(args.requestedPolicy, "explicit");
|
|
1612
1655
|
args.say("Policy: policy applied: replaced");
|
|
1613
1656
|
for (const line of outcome.diffLines)
|
|
1614
1657
|
args.say(line);
|
|
@@ -1860,12 +1903,13 @@ function describeScopeTransition(state, status) {
|
|
|
1860
1903
|
* {@link runSetupEpicCli} in every observable way — same output, same refusals,
|
|
1861
1904
|
* same exit codes — and differs only in what it hands back.
|
|
1862
1905
|
*/
|
|
1863
|
-
export async function runSetupEpicWorkflow(argv, overrides = {}) {
|
|
1906
|
+
export async function runSetupEpicWorkflow(argv, overrides = {}, handoff) {
|
|
1864
1907
|
const sink = {};
|
|
1865
|
-
const exitCode = await executeSetupEpic(argv, overrides, sink);
|
|
1908
|
+
const exitCode = await executeSetupEpic(argv, overrides, sink, handoff);
|
|
1866
1909
|
return {
|
|
1867
1910
|
exitCode,
|
|
1868
1911
|
...(sink.epicRunId !== undefined ? { epicRunId: sink.epicRunId } : {}),
|
|
1912
|
+
...(sink.epicRunId !== undefined ? { runExists: true } : {}),
|
|
1869
1913
|
};
|
|
1870
1914
|
}
|
|
1871
1915
|
/**
|
|
@@ -1875,7 +1919,7 @@ export async function runSetupEpicWorkflow(argv, overrides = {}) {
|
|
|
1875
1919
|
export async function runSetupEpicCli(argv, overrides = {}) {
|
|
1876
1920
|
return (await runSetupEpicWorkflow(argv, overrides)).exitCode;
|
|
1877
1921
|
}
|
|
1878
|
-
async function executeSetupEpic(argv, overrides, runIdSink) {
|
|
1922
|
+
async function executeSetupEpic(argv, overrides, runIdSink, handoff) {
|
|
1879
1923
|
const deps = { ...createDefaultSetupEpicDeps(), ...overrides };
|
|
1880
1924
|
const parsed = parseSetupEpicArgs(argv);
|
|
1881
1925
|
if (parsed.status === "help") {
|
|
@@ -1891,6 +1935,14 @@ async function executeSetupEpic(argv, overrides, runIdSink) {
|
|
|
1891
1935
|
const opts = parsed.options;
|
|
1892
1936
|
// With --json, stdout carries the single result object and nothing else.
|
|
1893
1937
|
const say = opts.json ? deps.errorLog : deps.log;
|
|
1938
|
+
if (handoff && (handoff.access.baseUrl !== handoff.selection.baseUrl ||
|
|
1939
|
+
handoff.access.repoName !== handoff.selection.repoName ||
|
|
1940
|
+
(opts.baseUrl !== undefined && opts.baseUrl !== handoff.selection.baseUrl) ||
|
|
1941
|
+
(opts.repo !== undefined && opts.repo !== handoff.selection.repoName) ||
|
|
1942
|
+
(opts.launchProfile !== undefined && opts.launchProfile !== handoff.selection.profileName))) {
|
|
1943
|
+
deps.errorLog("setup-epic REFUSED (selection-conflict): delegated target or repository disagrees with the pinned launch selection.");
|
|
1944
|
+
return 1;
|
|
1945
|
+
}
|
|
1894
1946
|
// --- Read + validate the plan sidecar (no network yet) -------------------
|
|
1895
1947
|
let raw;
|
|
1896
1948
|
try {
|
|
@@ -1922,18 +1974,137 @@ async function executeSetupEpic(argv, overrides, runIdSink) {
|
|
|
1922
1974
|
return 1;
|
|
1923
1975
|
}
|
|
1924
1976
|
const localHash = hashPlan(plan);
|
|
1977
|
+
// Resolve the selected target and live-run identity before choosing a policy.
|
|
1978
|
+
// The offline local-only path keeps its no-network contract.
|
|
1979
|
+
let access;
|
|
1980
|
+
let selectedProfilePolicyPath = handoff?.profile?.policyRef?.path;
|
|
1981
|
+
if (opts.localOnly && !handoff) {
|
|
1982
|
+
const profileName = opts.launchProfile ?? deps.env.BAPI_EPIC_LAUNCH_PROFILE?.trim();
|
|
1983
|
+
if (profileName) {
|
|
1984
|
+
const profileRead = await readEpicLaunchProfile(profileName, {
|
|
1985
|
+
env: deps.env,
|
|
1986
|
+
homedir: deps.homedir,
|
|
1987
|
+
readFile: deps.readFile,
|
|
1988
|
+
host: os.hostname(),
|
|
1989
|
+
});
|
|
1990
|
+
if (!profileRead.ok) {
|
|
1991
|
+
deps.errorLog(profileRead.message);
|
|
1992
|
+
return 1;
|
|
1993
|
+
}
|
|
1994
|
+
selectedProfilePolicyPath = profileRead.profile.policyRef?.path;
|
|
1995
|
+
}
|
|
1996
|
+
}
|
|
1997
|
+
let existingRunId = null;
|
|
1998
|
+
let existingStatus = null;
|
|
1999
|
+
let existingBaseBranch = null;
|
|
2000
|
+
let existingReviewPolicy = null;
|
|
2001
|
+
// BAPI-754: the live run's SERVER-REDACTED stored policy, kept for the reuse
|
|
2002
|
+
// comparison. Redacted is the only form a client can ever see.
|
|
2003
|
+
let existingPolicyJson = null;
|
|
2004
|
+
if (!opts.localOnly) {
|
|
2005
|
+
// --- Resolve access ------------------------------------------------------
|
|
2006
|
+
// BAPI-1260: a caller that selected the epic-launch contract (drive-epic
|
|
2007
|
+
// forwards --launch-profile / --base-url) gets the target-first resolver and
|
|
2008
|
+
// the bound credential; the legacy resolver is kept byte-for-byte otherwise.
|
|
2009
|
+
const accessDeps = {
|
|
2010
|
+
env: deps.env,
|
|
2011
|
+
cwd: deps.cwd,
|
|
2012
|
+
homedir: deps.homedir,
|
|
2013
|
+
platform: deps.platform,
|
|
2014
|
+
readFile: deps.readFile,
|
|
2015
|
+
stat: deps.stat,
|
|
2016
|
+
repoName: opts.repo,
|
|
2017
|
+
};
|
|
2018
|
+
const accessResult = handoff
|
|
2019
|
+
? { ok: true, access: handoff.access, profile: handoff.profile }
|
|
2020
|
+
: opts.launchProfile !== undefined || opts.baseUrl !== undefined || Boolean(deps.env.BAPI_EPIC_LAUNCH_PROFILE?.trim())
|
|
2021
|
+
? await resolveEpicLaunchBridgeApiAccess({
|
|
2022
|
+
...(opts.launchProfile ? { profileName: opts.launchProfile } : {}),
|
|
2023
|
+
...(opts.baseUrl ? { baseUrl: opts.baseUrl } : {}),
|
|
2024
|
+
}, accessDeps)
|
|
2025
|
+
: await resolveConductorBridgeApiAccess(accessDeps);
|
|
2026
|
+
if (!accessResult.ok) {
|
|
2027
|
+
deps.errorLog(`Cannot reach the Bridge API: ${accessResult.error}`);
|
|
2028
|
+
return 1;
|
|
2029
|
+
}
|
|
2030
|
+
access = accessResult.access;
|
|
2031
|
+
say(`Epic: ${opts.epicKey}`);
|
|
2032
|
+
say(`Repo: ${access.repoName}`);
|
|
2033
|
+
say(`Plan: v${plan.plan_version}, ${plan.nodes.length} node(s), ${plan.edges.length} edge(s)`);
|
|
2034
|
+
say(`Local hash: ${localHash}`);
|
|
2035
|
+
for (const w of warnings)
|
|
2036
|
+
say(` [warn] ${w}`);
|
|
2037
|
+
selectedProfilePolicyPath = "profile" in accessResult
|
|
2038
|
+
? accessResult.profile?.policyRef?.path
|
|
2039
|
+
: undefined;
|
|
2040
|
+
// --- Step 0: pre-check ---------------------------------------------------
|
|
2041
|
+
// Never create on an ambiguous read. A wrong answer here mints a duplicate run.
|
|
2042
|
+
try {
|
|
2043
|
+
const state = await fetchEpicRunState(access, opts.epicKey, deps.fetch);
|
|
2044
|
+
existingRunId = state.epic_run?.epic_run_id ?? null;
|
|
2045
|
+
existingStatus = state.epic_run?.status ?? null;
|
|
2046
|
+
const existingPolicy = state.epic_run?.policy_json;
|
|
2047
|
+
existingPolicyJson = existingPolicy ?? null;
|
|
2048
|
+
const existingBase = existingPolicy && typeof existingPolicy === "object"
|
|
2049
|
+
? existingPolicy.base_branch
|
|
2050
|
+
: undefined;
|
|
2051
|
+
existingBaseBranch = typeof existingBase === "string" && existingBase.trim() !== ""
|
|
2052
|
+
? existingBase
|
|
2053
|
+
: null;
|
|
2054
|
+
// BAPI-679: read the existing run's review policy for the conflict guard
|
|
2055
|
+
// below. Absent/malformed reads as null — "the resolver's default applies" —
|
|
2056
|
+
// which is deliberately NOT the same as an explicitly selected source.
|
|
2057
|
+
const existingReview = existingPolicy && typeof existingPolicy === "object"
|
|
2058
|
+
? existingPolicy.review_policy
|
|
2059
|
+
: undefined;
|
|
2060
|
+
const existingReviewSource = existingReview && typeof existingReview === "object"
|
|
2061
|
+
? existingReview.source
|
|
2062
|
+
: undefined;
|
|
2063
|
+
existingReviewPolicy =
|
|
2064
|
+
typeof existingReviewSource === "string" && existingReviewSource.trim() !== ""
|
|
2065
|
+
? existingReviewSource
|
|
2066
|
+
: null;
|
|
2067
|
+
}
|
|
2068
|
+
catch (err) {
|
|
2069
|
+
if (err instanceof ConductorBridgeApiError && err.status === 404) {
|
|
2070
|
+
existingRunId = null; // No live run — the normal first-run path.
|
|
2071
|
+
}
|
|
2072
|
+
else if (err instanceof ConductorBridgeApiError && err.status === 409) {
|
|
2073
|
+
deps.errorLog(`Epic ${opts.epicKey} has MULTIPLE active runs — it is wedged, and every ` +
|
|
2074
|
+
`plan call will keep failing. Abandon the duplicate before retrying:\n` +
|
|
2075
|
+
` epic-implementer abandon-run --epic-run-id <duplicate_epic_run_id>\n` +
|
|
2076
|
+
`Detail: ${errorDetail(err)}`);
|
|
2077
|
+
return 1;
|
|
2078
|
+
}
|
|
2079
|
+
else {
|
|
2080
|
+
deps.errorLog(`Could not read the epic run state, so creating one would risk a duplicate ` +
|
|
2081
|
+
`(which wedges the epic permanently). Refusing to continue.\n` +
|
|
2082
|
+
`Detail: ${errorDetail(err)}`);
|
|
2083
|
+
return 1;
|
|
2084
|
+
}
|
|
2085
|
+
}
|
|
2086
|
+
}
|
|
1925
2087
|
// --- Read the policy file (still no network) ------------------------------
|
|
1926
2088
|
// BAPI-754: parsed here so a malformed file, or a contradiction between the
|
|
1927
2089
|
// file and the CLI flags, fails before credentials are resolved or any request
|
|
1928
2090
|
// is dispatched.
|
|
1929
2091
|
let filePolicy;
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
|
|
1934
|
-
|
|
2092
|
+
const selectedPolicyPath = opts.policyFile ?? (existingRunId === null ? selectedProfilePolicyPath : undefined);
|
|
2093
|
+
const selectedPolicySource = existingRunId !== null
|
|
2094
|
+
? "stored" : opts.policyFile !== undefined ? "explicit" : selectedProfilePolicyPath !== undefined ? "profile" : "default";
|
|
2095
|
+
if (selectedPolicyPath !== undefined) {
|
|
2096
|
+
const pinned = handoff ? getPinnedEpicLaunchPolicy(handoff.selection) : undefined;
|
|
2097
|
+
if (pinned !== undefined) {
|
|
2098
|
+
filePolicy = pinned;
|
|
2099
|
+
}
|
|
2100
|
+
else {
|
|
2101
|
+
const policyRead = await readSetupEpicPolicyFile(selectedPolicyPath, deps.readFile);
|
|
2102
|
+
if (!policyRead.ok) {
|
|
2103
|
+
deps.errorLog(policyRead.error);
|
|
2104
|
+
return 1;
|
|
2105
|
+
}
|
|
2106
|
+
filePolicy = policyRead.policy;
|
|
1935
2107
|
}
|
|
1936
|
-
filePolicy = policyRead.policy;
|
|
1937
2108
|
}
|
|
1938
2109
|
const policyDeclaresBranch = filePolicy !== undefined &&
|
|
1939
2110
|
["base_branch", "baseBranch"].some((key) => key in filePolicy && filePolicy[key] !== undefined && filePolicy[key] !== null);
|
|
@@ -1963,7 +2134,7 @@ async function executeSetupEpic(argv, overrides, runIdSink) {
|
|
|
1963
2134
|
const policyGate = preflightSetupEpicPolicy({
|
|
1964
2135
|
intoBase: opts.intoBase,
|
|
1965
2136
|
attended: opts.attended,
|
|
1966
|
-
policyFile:
|
|
2137
|
+
policyFile: selectedPolicyPath,
|
|
1967
2138
|
filePolicy,
|
|
1968
2139
|
policyDeclaresBranch,
|
|
1969
2140
|
featureBranch,
|
|
@@ -2001,42 +2172,14 @@ async function executeSetupEpic(argv, overrides, runIdSink) {
|
|
|
2001
2172
|
plan_version: plan.plan_version,
|
|
2002
2173
|
local_plan_hash: localHash,
|
|
2003
2174
|
server_validated: false,
|
|
2175
|
+
feature_branch: opts.intoBase ? null : resolveEffectiveFeatureBranch(featureBranch, policyGate.policy) ?? null,
|
|
2004
2176
|
policy_applied: "absent",
|
|
2177
|
+
effective_policy: effectivePolicyFacts(policyGate.policy, selectedPolicySource),
|
|
2005
2178
|
warnings,
|
|
2006
2179
|
}, null, 2));
|
|
2007
2180
|
}
|
|
2008
2181
|
return 0;
|
|
2009
2182
|
}
|
|
2010
|
-
// --- Resolve access ------------------------------------------------------
|
|
2011
|
-
// BAPI-1260: a caller that selected the epic-launch contract (drive-epic
|
|
2012
|
-
// forwards --launch-profile / --base-url) gets the target-first resolver and
|
|
2013
|
-
// the bound credential; the legacy resolver is kept byte-for-byte otherwise.
|
|
2014
|
-
const accessDeps = {
|
|
2015
|
-
env: deps.env,
|
|
2016
|
-
cwd: deps.cwd,
|
|
2017
|
-
homedir: deps.homedir,
|
|
2018
|
-
platform: deps.platform,
|
|
2019
|
-
readFile: deps.readFile,
|
|
2020
|
-
stat: deps.stat,
|
|
2021
|
-
repoName: opts.repo,
|
|
2022
|
-
};
|
|
2023
|
-
const accessResult = opts.launchProfile !== undefined || opts.baseUrl !== undefined
|
|
2024
|
-
? await resolveEpicLaunchBridgeApiAccess({
|
|
2025
|
-
...(opts.launchProfile ? { profileName: opts.launchProfile } : {}),
|
|
2026
|
-
...(opts.baseUrl ? { baseUrl: opts.baseUrl } : {}),
|
|
2027
|
-
}, accessDeps)
|
|
2028
|
-
: await resolveConductorBridgeApiAccess(accessDeps);
|
|
2029
|
-
if (!accessResult.ok) {
|
|
2030
|
-
deps.errorLog(`Cannot reach the Bridge API: ${accessResult.error}`);
|
|
2031
|
-
return 1;
|
|
2032
|
-
}
|
|
2033
|
-
const access = accessResult.access;
|
|
2034
|
-
say(`Epic: ${opts.epicKey}`);
|
|
2035
|
-
say(`Repo: ${access.repoName}`);
|
|
2036
|
-
say(`Plan: v${plan.plan_version}, ${plan.nodes.length} node(s), ${plan.edges.length} edge(s)`);
|
|
2037
|
-
say(`Local hash: ${localHash}`);
|
|
2038
|
-
for (const w of warnings)
|
|
2039
|
-
say(` [warn] ${w}`);
|
|
2040
2183
|
// --- Branch strategy (BAPI-1009) ----------------------------------------
|
|
2041
2184
|
// Already resolved above, before the network was touched. Reported HERE, ahead
|
|
2042
2185
|
// of any secondary policy detail, because it is the one decision that changes
|
|
@@ -2093,6 +2236,11 @@ async function executeSetupEpic(argv, overrides, runIdSink) {
|
|
|
2093
2236
|
reviewPolicy: opts.reviewPolicy,
|
|
2094
2237
|
mergeExecutionMode: declaredMergeExecutionMode,
|
|
2095
2238
|
});
|
|
2239
|
+
const selectedEffectivePolicyFacts = effectivePolicyFacts(existingRunId !== null ? existingPolicyJson : composedForCreate, selectedPolicySource);
|
|
2240
|
+
say(`Policy: effective ${selectedEffectivePolicyFacts.source}; serial execution; ` +
|
|
2241
|
+
`caps ${selectedEffectivePolicyFacts.caps ? JSON.stringify(selectedEffectivePolicyFacts.caps) : "unavailable"}; ` +
|
|
2242
|
+
`timeouts ${selectedEffectivePolicyFacts.job_timeouts ? JSON.stringify(selectedEffectivePolicyFacts.job_timeouts) : "unavailable"}; ` +
|
|
2243
|
+
`channel ${selectedEffectivePolicyFacts.escalation_channel}`);
|
|
2096
2244
|
// BAPI-1133 (A2). Reported only when THIS command stamped the key — i.e. it
|
|
2097
2245
|
// is present in the composed policy and was absent from whatever the
|
|
2098
2246
|
// operator supplied — as low-emphasis policy metadata, mirroring the
|
|
@@ -2128,59 +2276,6 @@ async function executeSetupEpic(argv, overrides, runIdSink) {
|
|
|
2128
2276
|
say(`Feature: ${effectiveFeatureBranch} (from the policy file; cut on origin with local ` +
|
|
2129
2277
|
`git at the canonical indexed commit; validated server-side at approval)`);
|
|
2130
2278
|
}
|
|
2131
|
-
// --- Step 0: pre-check ---------------------------------------------------
|
|
2132
|
-
// Never create on an ambiguous read. A wrong answer here mints a duplicate run.
|
|
2133
|
-
let existingRunId = null;
|
|
2134
|
-
let existingStatus = null;
|
|
2135
|
-
let existingBaseBranch = null;
|
|
2136
|
-
let existingReviewPolicy = null;
|
|
2137
|
-
// BAPI-754: the live run's SERVER-REDACTED stored policy, kept for the reuse
|
|
2138
|
-
// comparison. Redacted is the only form a client can ever see.
|
|
2139
|
-
let existingPolicyJson = null;
|
|
2140
|
-
try {
|
|
2141
|
-
const state = await fetchEpicRunState(access, opts.epicKey, deps.fetch);
|
|
2142
|
-
existingRunId = state.epic_run?.epic_run_id ?? null;
|
|
2143
|
-
existingStatus = state.epic_run?.status ?? null;
|
|
2144
|
-
const existingPolicy = state.epic_run?.policy_json;
|
|
2145
|
-
existingPolicyJson = existingPolicy ?? null;
|
|
2146
|
-
const existingBase = existingPolicy && typeof existingPolicy === "object"
|
|
2147
|
-
? existingPolicy.base_branch
|
|
2148
|
-
: undefined;
|
|
2149
|
-
existingBaseBranch = typeof existingBase === "string" && existingBase.trim() !== ""
|
|
2150
|
-
? existingBase
|
|
2151
|
-
: null;
|
|
2152
|
-
// BAPI-679: read the existing run's review policy for the conflict guard
|
|
2153
|
-
// below. Absent/malformed reads as null — "the resolver's default applies" —
|
|
2154
|
-
// which is deliberately NOT the same as an explicitly selected source.
|
|
2155
|
-
const existingReview = existingPolicy && typeof existingPolicy === "object"
|
|
2156
|
-
? existingPolicy.review_policy
|
|
2157
|
-
: undefined;
|
|
2158
|
-
const existingReviewSource = existingReview && typeof existingReview === "object"
|
|
2159
|
-
? existingReview.source
|
|
2160
|
-
: undefined;
|
|
2161
|
-
existingReviewPolicy =
|
|
2162
|
-
typeof existingReviewSource === "string" && existingReviewSource.trim() !== ""
|
|
2163
|
-
? existingReviewSource
|
|
2164
|
-
: null;
|
|
2165
|
-
}
|
|
2166
|
-
catch (err) {
|
|
2167
|
-
if (err instanceof ConductorBridgeApiError && err.status === 404) {
|
|
2168
|
-
existingRunId = null; // No live run — the normal first-run path.
|
|
2169
|
-
}
|
|
2170
|
-
else if (err instanceof ConductorBridgeApiError && err.status === 409) {
|
|
2171
|
-
deps.errorLog(`Epic ${opts.epicKey} has MULTIPLE active runs — it is wedged, and every ` +
|
|
2172
|
-
`plan call will keep failing. Abandon the duplicate before retrying:\n` +
|
|
2173
|
-
` epic-implementer abandon-run --epic-run-id <duplicate_epic_run_id>\n` +
|
|
2174
|
-
`Detail: ${errorDetail(err)}`);
|
|
2175
|
-
return 1;
|
|
2176
|
-
}
|
|
2177
|
-
else {
|
|
2178
|
-
deps.errorLog(`Could not read the epic run state, so creating one would risk a duplicate ` +
|
|
2179
|
-
`(which wedges the epic permanently). Refusing to continue.\n` +
|
|
2180
|
-
`Detail: ${errorDetail(err)}`);
|
|
2181
|
-
return 1;
|
|
2182
|
-
}
|
|
2183
|
-
}
|
|
2184
2279
|
// --- Feature-branch conflict guard against an existing live run ----------
|
|
2185
2280
|
// A selected branch that disagrees with the existing run's stored base branch
|
|
2186
2281
|
// must fail closed rather than patch or silently change that run. A matching
|
|
@@ -2508,6 +2603,7 @@ async function executeSetupEpic(argv, overrides, runIdSink) {
|
|
|
2508
2603
|
// "absent"; server_validated says whether the check was authoritative.
|
|
2509
2604
|
server_validated: validated !== null,
|
|
2510
2605
|
policy_applied: "absent",
|
|
2606
|
+
effective_policy: selectedEffectivePolicyFacts,
|
|
2511
2607
|
...(validated
|
|
2512
2608
|
? {
|
|
2513
2609
|
server_plan_hash: validated.planHash,
|
|
@@ -2579,6 +2675,7 @@ async function executeSetupEpic(argv, overrides, runIdSink) {
|
|
|
2579
2675
|
plan_approved: false,
|
|
2580
2676
|
server_validated: validated !== null,
|
|
2581
2677
|
policy_applied: "absent",
|
|
2678
|
+
effective_policy: selectedEffectivePolicyFacts,
|
|
2582
2679
|
warnings,
|
|
2583
2680
|
};
|
|
2584
2681
|
// BAPI-850: the EFFECTIVE feature branch (flag or policy file), so a
|
|
@@ -3128,14 +3225,14 @@ async function executeSetupEpic(argv, overrides, runIdSink) {
|
|
|
3128
3225
|
else {
|
|
3129
3226
|
say("The server-side reconciler will pick it up within ~30s.");
|
|
3130
3227
|
}
|
|
3131
|
-
|
|
3132
|
-
|
|
3133
|
-
|
|
3134
|
-
|
|
3135
|
-
|
|
3136
|
-
|
|
3137
|
-
|
|
3138
|
-
|
|
3228
|
+
if (handoff) {
|
|
3229
|
+
say("The launcher starts or reuses the executor appropriate for this run.");
|
|
3230
|
+
}
|
|
3231
|
+
else {
|
|
3232
|
+
say("To execute this run's claimed jobs on this machine, run:");
|
|
3233
|
+
say(` npx -y ${MCP_PACKAGE_NAME} executor --repo ${access.repoName} ` +
|
|
3234
|
+
`--epic-run-id ${result.epic_run_id}`);
|
|
3235
|
+
}
|
|
3139
3236
|
}
|
|
3140
3237
|
return exitCode;
|
|
3141
3238
|
}
|
package/build/sfcc/config.js
CHANGED
|
@@ -9,6 +9,15 @@
|
|
|
9
9
|
export const SFCC_VERSIONS = ["sfra", "pwakit", "sitegenesis", "storefrontnext", "hybrid"];
|
|
10
10
|
/** Pinned OCAPI Data API version used for all requests (configurable at call-time). */
|
|
11
11
|
export const DEFAULT_OCAPI_VERSION = "v25_6";
|
|
12
|
+
/**
|
|
13
|
+
* The `_v` value of the OCAPI Settings JSON an operator pastes in Business
|
|
14
|
+
* Manager. This is the settings-STRUCTURE version, not the OCAPI API version
|
|
15
|
+
* above; Business Manager rejects an API version here.
|
|
16
|
+
*
|
|
17
|
+
* UNVERIFIED: "23.2" is provisional until owner check OR-1 saves the emitted
|
|
18
|
+
* grant in Business Manager. If OR-1 shows a different value, change it here.
|
|
19
|
+
*/
|
|
20
|
+
export const OCAPI_SETTINGS_STRUCTURE_VERSION = "23.2";
|
|
12
21
|
/** Account Manager OAuth2 token endpoint host. */
|
|
13
22
|
export const AM_HOST = "account.demandware.com";
|
|
14
23
|
/** AM OAuth2 token endpoint URL. */
|