@bridge_gpt/mcp-server 0.2.34 → 0.2.37
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 +490 -366
- package/build/agent-capabilities/probe-context.js +8 -1
- package/build/agent-capabilities/probes.js +7 -1
- package/build/agents.generated.js +1 -1
- package/build/base-url.js +79 -0
- package/build/bridge-api-urls.js +9 -0
- package/build/chain-orchestrator.js +93 -15
- package/build/claude-review-workflow.js +264 -0
- package/build/claude-user-config-doctor.js +317 -0
- package/build/cli-release.js +53 -0
- package/build/commands.generated.js +6 -5
- package/build/conductor/bridge-api-client.js +393 -4
- package/build/conductor/deny-enforcement-preflight.js +1 -0
- package/build/conductor/done-gate.js +44 -5
- package/build/conductor/epic-reconcile.js +6 -0
- package/build/conductor/install-doctor.js +462 -0
- package/build/conductor-bin.js +3 -3
- package/build/conductor-bundle-artifacts.js +37 -15
- package/build/credential-store.js +205 -4
- package/build/direct-ticket-tools.js +70 -0
- package/build/doctor.js +473 -81
- package/build/executor/cli.js +83 -6
- package/build/executor/credentials.js +39 -11
- package/build/executor/deps.js +32 -1
- package/build/executor/env.js +71 -28
- package/build/executor/heartbeat.js +138 -17
- package/build/executor/http-client.js +49 -8
- package/build/executor/index.js +4 -0
- package/build/executor/job-errors.js +4 -0
- package/build/executor/job-runner.js +530 -20
- package/build/executor/observation.js +130 -0
- package/build/executor/permissions.js +114 -8
- package/build/executor/preflight.js +127 -8
- package/build/executor/prompt-spec.js +51 -0
- package/build/executor/runner.js +23 -2
- package/build/executor/service-unit.js +876 -0
- package/build/executor/test-clock.js +75 -3
- package/build/executor/types.js +4 -18
- package/build/executor/worker-command.js +73 -12
- package/build/executor/worker-config-isolation.js +287 -0
- package/build/executor/worker-finalization.js +68 -14
- package/build/executor/worktree.js +46 -4
- package/build/index.js +1039 -237
- package/build/init.js +503 -111
- package/build/install-bridge-conductor.js +491 -0
- package/build/install-bridge.js +1188 -247
- package/build/install-reexec.js +233 -0
- package/build/launcher-config-inspection.js +351 -0
- package/build/mcp-host-config.js +11 -1
- package/build/mcp-install-state.js +32 -0
- package/build/mcp-invoke.js +49 -6
- package/build/mcp-provisioning.js +52 -13
- package/build/mcp-registration-doctor.js +14 -5
- package/build/notifications.js +553 -0
- package/build/pipeline-orchestrator.js +146 -4
- package/build/pipeline-utils.js +3 -0
- package/build/pipelines.generated.js +32 -13
- package/build/plan-execution-ledger.js +550 -0
- package/build/plan-phase-routing.js +272 -0
- package/build/plane/alembic-head.js +110 -0
- package/build/plane/build-freshness.js +167 -0
- package/build/plane/cli.js +480 -0
- package/build/plane/defaults.js +266 -0
- package/build/plane/manifest.js +377 -0
- package/build/plane/member-logs.js +147 -0
- package/build/plane/member-roster.js +147 -0
- package/build/plane/preflight.js +289 -0
- package/build/plane/shutdown.js +195 -0
- package/build/plane/status.js +125 -0
- package/build/plane/supervisor.js +569 -0
- package/build/plane/test-fakes.js +156 -0
- package/build/plane/types.js +75 -0
- package/build/readme.generated.js +1 -1
- package/build/run-unit-tests-launcher.js +259 -0
- package/build/setup-epic.js +772 -28
- package/build/sfcc/log-gate.js +38 -11
- package/build/sfcc/log-query.js +55 -15
- package/build/sfcc/ocapi-shape.js +51 -14
- package/build/sfcc/output.js +41 -11
- package/build/sfcc/permissions.js +24 -2
- package/build/sfcc/read-projection.js +181 -0
- package/build/sfcc/read-result.js +158 -0
- package/build/sfcc/reads-custom-object-def.js +29 -18
- package/build/sfcc/reads-site-preference.js +75 -29
- package/build/sfcc/reads-system-object.js +40 -34
- package/build/sfcc/sfcc-result.js +106 -0
- package/build/sfcc/tool-wrapper.js +56 -13
- package/build/sfcc/write-grants.js +45 -22
- package/build/sfcc/write-guard.js +21 -13
- package/build/sfcc/write-result.js +61 -14
- package/build/sfcc/write-tool-common.js +126 -32
- package/build/sfcc/writes-system-object.js +11 -50
- package/build/start-tickets-prereqs.js +129 -0
- package/build/start-tickets.js +17 -13
- package/build/ticket-backend-metadata.js +59 -0
- package/build/ticket-key-utils.js +92 -0
- package/build/tool-error-envelope.js +71 -0
- package/build/tool-surface-gating.js +72 -0
- package/build/update-status.js +102 -0
- package/build/upgrade-advice.js +47 -0
- package/build/upgrade-cli.js +412 -110
- package/build/version.generated.js +1 -1
- package/build/worktree-core.js +73 -0
- package/docs/CONDUCTOR.md +132 -6
- package/docs/install/mcp-tool-integrations.md +29 -21
- package/package.json +9 -6
- package/pipelines/implement-ticket.json +19 -4
- package/build/conductor/supervisor-judgment-python.js +0 -141
- package/build/conductor/supervisor-judgment.js +0 -215
package/build/upgrade-cli.js
CHANGED
|
@@ -1,154 +1,456 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Bridge API MCP server one-command upgrade (BAPI-430).
|
|
2
|
+
* Bridge API MCP server one-command upgrade (BAPI-430, rebuilt for BAPI-728).
|
|
3
3
|
*
|
|
4
4
|
* Extracted from `index.ts` so the upgrade logic always runs from the latest
|
|
5
5
|
* published version (`@bridge_gpt/mcp-server@latest`) rather than the
|
|
6
6
|
* potentially-stale copy npx resolved at the call site.
|
|
7
7
|
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
8
|
+
* ---------------------------------------------------------------------------
|
|
9
|
+
* BAPI-728 — the defect this file was rebuilt to fix
|
|
10
|
+
* ---------------------------------------------------------------------------
|
|
11
|
+
* The old decision was `isNewerVersion(VERSION, latestVersion)`, where `VERSION`
|
|
12
|
+
* is the version of the CLI process npx just resolved. Invoked the documented
|
|
13
|
+
* way (`@latest`), `VERSION === latestVersion`, so that expression was ALWAYS
|
|
14
|
+
* false. Consequences:
|
|
15
|
+
*
|
|
16
|
+
* - `oldVersion` and `targetVersion` both collapsed to `VERSION`, so a run
|
|
17
|
+
* that genuinely repinned three configs from 0.2.16 to 0.2.36 announced
|
|
18
|
+
* itself as `Upgrade complete: 0.2.36 -> 0.2.36`.
|
|
19
|
+
* - The config pin was never an INPUT to anything. It moved only as a side
|
|
20
|
+
* effect of `runInit`, and nothing reported that it had.
|
|
21
|
+
* - A no-op and a twenty-release jump printed identical output.
|
|
22
|
+
*
|
|
23
|
+
* The fix separates two genuinely different questions that were conflated:
|
|
24
|
+
*
|
|
25
|
+
* 1. "Is the CLI I am running the newest CLI?" → `VERSION` vs the registry.
|
|
26
|
+
* This decides ONE thing: whether to re-exec from `@latest`.
|
|
27
|
+
* 2. "Is each project config pinned where it should be?" → the shared,
|
|
28
|
+
* read-only launcher inspection. This decides everything the user sees:
|
|
29
|
+
* per-config transitions, whether anything changed, and the exit status.
|
|
30
|
+
*
|
|
31
|
+
* Every launcher claim is verified by RE-READING the configs after the write.
|
|
32
|
+
* The command never reports a transition it did not confirm on disk.
|
|
16
33
|
*/
|
|
17
34
|
import { spawn } from "child_process";
|
|
18
|
-
import { stat } from "fs/promises";
|
|
35
|
+
import { readFile, stat } from "fs/promises";
|
|
19
36
|
import path from "path";
|
|
20
37
|
import { VERSION } from "./version.generated.js";
|
|
21
38
|
import { runInit } from "./init.js";
|
|
39
|
+
import { InitPreflightError } from "./init.js";
|
|
22
40
|
import { isNewerVersion } from "./update-check.js";
|
|
41
|
+
import { fetchLatestVersion } from "./cli-release.js";
|
|
42
|
+
import { CANONICAL_UPGRADE_COMMAND } from "./upgrade-advice.js";
|
|
43
|
+
import { describeLauncherReason, inspectLauncherConfigs, isApplicableStandardLauncher, isPrerequisiteFailure, LAUNCHER_CONFIG_TARGETS, } from "./launcher-config-inspection.js";
|
|
23
44
|
import { buildGenericAgentShellCommand, detectTerminal, getDefaultSpawnTerminalTabForPlatform, createDefaultStartTicketsDeps, } from "./start-tickets.js";
|
|
24
45
|
import { AGENT_REGISTRY } from "./agent-registry.js";
|
|
25
|
-
|
|
46
|
+
/** The sentinel this CLI passes to the child it re-execs from `@latest`. */
|
|
47
|
+
const INTERNAL_REEXEC_FLAG = "--internal-reexec";
|
|
48
|
+
// ---------------------------------------------------------------------------
|
|
49
|
+
// Default dependency construction
|
|
50
|
+
// ---------------------------------------------------------------------------
|
|
51
|
+
function defaultReexec(cwd, oldVersion) {
|
|
52
|
+
const npxCmd = process.platform === "win32" ? "npx.cmd" : "npx";
|
|
53
|
+
return new Promise((resolve) => {
|
|
54
|
+
const child = spawn(npxCmd, [
|
|
55
|
+
"-y",
|
|
56
|
+
"@bridge_gpt/mcp-server@latest",
|
|
57
|
+
"upgrade",
|
|
58
|
+
INTERNAL_REEXEC_FLAG,
|
|
59
|
+
"--old-version",
|
|
60
|
+
oldVersion,
|
|
61
|
+
], { stdio: "inherit", cwd });
|
|
62
|
+
child.on("close", (code) => resolve(code ?? 0));
|
|
63
|
+
child.on("error", (err) => {
|
|
64
|
+
console.error(`Bridge API upgrade failed: could not re-exec npx: ${err.message}`);
|
|
65
|
+
resolve(1);
|
|
66
|
+
});
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
function localInstallPath(cwd) {
|
|
70
|
+
return path.join(cwd, "node_modules", "@bridge_gpt", "mcp-server");
|
|
71
|
+
}
|
|
72
|
+
async function defaultHasCompetingLocalInstall(cwd) {
|
|
73
|
+
try {
|
|
74
|
+
await stat(localInstallPath(cwd));
|
|
75
|
+
return true;
|
|
76
|
+
}
|
|
77
|
+
catch {
|
|
78
|
+
return false;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
function defaultRemoveCompetingLocalInstall(cwd) {
|
|
82
|
+
const npmCmd = process.platform === "win32" ? "npm.cmd" : "npm";
|
|
83
|
+
return new Promise((resolve) => {
|
|
84
|
+
const child = spawn(npmCmd, ["uninstall", "@bridge_gpt/mcp-server"], {
|
|
85
|
+
stdio: "inherit",
|
|
86
|
+
cwd,
|
|
87
|
+
});
|
|
88
|
+
child.on("close", (code) => resolve(code === 0));
|
|
89
|
+
child.on("error", () => resolve(false));
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
async function defaultSpawnReconnect(cwd, command) {
|
|
26
93
|
try {
|
|
27
|
-
const
|
|
28
|
-
|
|
94
|
+
const terminal = detectTerminal(undefined, process.env);
|
|
95
|
+
const deps = createDefaultStartTicketsDeps();
|
|
96
|
+
const spawnTerminalTab = getDefaultSpawnTerminalTabForPlatform(process.platform);
|
|
97
|
+
const result = await spawnTerminalTab(deps, terminal, command, {
|
|
98
|
+
key: "reconnect",
|
|
99
|
+
worktreePath: cwd,
|
|
29
100
|
});
|
|
30
|
-
|
|
31
|
-
const data = (await res.json());
|
|
32
|
-
return data.version || null;
|
|
33
|
-
}
|
|
101
|
+
return result.ok;
|
|
34
102
|
}
|
|
35
103
|
catch {
|
|
36
|
-
return
|
|
104
|
+
return false;
|
|
37
105
|
}
|
|
38
|
-
return null;
|
|
39
106
|
}
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
107
|
+
/** Build the production dependency set. */
|
|
108
|
+
export function createDefaultUpgradeCliDeps(cwd) {
|
|
109
|
+
return {
|
|
110
|
+
cwd,
|
|
111
|
+
fetchLatestVersion,
|
|
112
|
+
readFile: (filePath) => readFile(filePath, "utf-8"),
|
|
113
|
+
hasCompetingLocalInstall: defaultHasCompetingLocalInstall,
|
|
114
|
+
removeCompetingLocalInstall: defaultRemoveCompetingLocalInstall,
|
|
115
|
+
runInit,
|
|
116
|
+
reexecFromLatest: defaultReexec,
|
|
117
|
+
spawnReconnect: defaultSpawnReconnect,
|
|
118
|
+
stdout: (m) => console.log(m),
|
|
119
|
+
stderr: (m) => console.error(m),
|
|
120
|
+
env: process.env,
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
// ---------------------------------------------------------------------------
|
|
124
|
+
// Target resolution
|
|
125
|
+
// ---------------------------------------------------------------------------
|
|
126
|
+
/**
|
|
127
|
+
* Decide the target version and how much we trust it.
|
|
128
|
+
*
|
|
129
|
+
* Note what is NOT here: the project's config pins. The target is "what the
|
|
130
|
+
* newest published version is"; the pins are what we compare AGAINST it. Mixing
|
|
131
|
+
* the two is precisely the bug this rewrite removes.
|
|
132
|
+
*/
|
|
133
|
+
export async function resolveUpgradeTarget(argv, deps) {
|
|
134
|
+
const fetched = await deps.fetchLatestVersion();
|
|
46
135
|
if (fetched) {
|
|
47
|
-
|
|
136
|
+
return { version: fetched, confidence: "registry-confirmed" };
|
|
48
137
|
}
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
138
|
+
if (argv.includes(INTERNAL_REEXEC_FLAG)) {
|
|
139
|
+
// We ARE the `@latest` child this CLI spawned, so `VERSION` is latest by
|
|
140
|
+
// construction — the parent resolved it through npx moments ago.
|
|
141
|
+
return { version: VERSION, confidence: "internal-reexec" };
|
|
142
|
+
}
|
|
143
|
+
// No reliable way exists to observe from inside the process whether npx
|
|
144
|
+
// resolved us via `@latest`, a bare spec, or a local node_modules copy — npm's
|
|
145
|
+
// exported environment does not carry the requested spec. So a canonical
|
|
146
|
+
// invocation must be asserted explicitly rather than guessed.
|
|
147
|
+
const asserted = deps.env?.BAPI_UPGRADE_ASSUME_LATEST;
|
|
148
|
+
if (typeof asserted === "string" && asserted.trim().toLowerCase() === "true") {
|
|
149
|
+
return { version: VERSION, confidence: "latest-invocation-confirmed" };
|
|
150
|
+
}
|
|
151
|
+
return { version: VERSION, confidence: "running-version-only" };
|
|
152
|
+
}
|
|
153
|
+
// ---------------------------------------------------------------------------
|
|
154
|
+
// Reporting helpers (pure)
|
|
155
|
+
// ---------------------------------------------------------------------------
|
|
156
|
+
/** Render the dry-run preview line for one inspected target. */
|
|
157
|
+
export function formatDryRunLine(found) {
|
|
158
|
+
const { relPath, targetVersion } = found;
|
|
159
|
+
switch (found.action) {
|
|
160
|
+
case "repin":
|
|
161
|
+
return ` ${relPath}: ${found.currentVersion} -> ${targetVersion}`;
|
|
162
|
+
case "already-current":
|
|
163
|
+
return ` ${relPath}: already ${targetVersion}`;
|
|
164
|
+
case "normalize-unpinned":
|
|
165
|
+
return ` ${relPath}: would normalize ${found.spec} -> @bridge_gpt/mcp-server@${targetVersion}`;
|
|
166
|
+
case "add":
|
|
167
|
+
return ` ${relPath}: would add @bridge_gpt/mcp-server@${targetVersion}`;
|
|
168
|
+
case "create":
|
|
169
|
+
return ` ${relPath}: would create with @bridge_gpt/mcp-server@${targetVersion}`;
|
|
170
|
+
case "skip-worktree-shim":
|
|
171
|
+
return ` ${relPath}: skipped — worktree mcp-invoke shim (no published-package pin applies)`;
|
|
172
|
+
case "skip-inactive":
|
|
173
|
+
return null; // Absent config for an inactive IDE — not worth a line.
|
|
174
|
+
default:
|
|
175
|
+
return null;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
/** Render the real-run result line for one verified config action. */
|
|
179
|
+
export function formatResultLine(action) {
|
|
180
|
+
const { relPath } = action;
|
|
181
|
+
switch (action.kind) {
|
|
182
|
+
case "repinned":
|
|
183
|
+
return ` ${relPath}: ${action.fromVersion} -> ${action.toVersion}`;
|
|
184
|
+
case "already-current":
|
|
185
|
+
return ` ${relPath}: already ${action.toVersion}`;
|
|
186
|
+
case "normalized":
|
|
187
|
+
return ` ${relPath}: normalized ${action.fromSpec} -> @bridge_gpt/mcp-server@${action.toVersion}`;
|
|
188
|
+
case "added":
|
|
189
|
+
return ` ${relPath}: added @bridge_gpt/mcp-server@${action.toVersion}`;
|
|
190
|
+
case "created":
|
|
191
|
+
return ` ${relPath}: created with @bridge_gpt/mcp-server@${action.toVersion}`;
|
|
192
|
+
case "skipped-worktree-shim":
|
|
193
|
+
return ` ${relPath}: skipped — worktree mcp-invoke shim preserved`;
|
|
194
|
+
case "skipped-ahead":
|
|
195
|
+
// Defensive: upgrade blocks on an ahead pin during preflight, so this is
|
|
196
|
+
// only reachable if a config changed underneath the run.
|
|
197
|
+
return ` ${relPath}: skipped — already pinned ahead at ${action.fromVersion}`;
|
|
198
|
+
case "skipped-inactive":
|
|
199
|
+
return null;
|
|
200
|
+
default:
|
|
201
|
+
return null;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* True when the run changed no launcher pin: every applicable standard launcher
|
|
206
|
+
* was already exactly at the target, and nothing was created, added, or
|
|
207
|
+
* normalized. This — and ONLY this — is what `Already up-to-date.` means.
|
|
208
|
+
*/
|
|
209
|
+
export function isEveryLauncherAlreadyCurrent(actions) {
|
|
210
|
+
const applicable = actions.filter((a) => a.kind !== "skipped-worktree-shim" &&
|
|
211
|
+
a.kind !== "skipped-inactive" &&
|
|
212
|
+
a.kind !== "skipped-ahead");
|
|
213
|
+
if (applicable.length === 0)
|
|
214
|
+
return false; // Nothing applicable ⇒ not "current".
|
|
215
|
+
return applicable.every((a) => a.kind === "already-current");
|
|
216
|
+
}
|
|
217
|
+
// ---------------------------------------------------------------------------
|
|
218
|
+
// The command
|
|
219
|
+
// ---------------------------------------------------------------------------
|
|
220
|
+
export async function runUpgradeCli(argv, injected) {
|
|
221
|
+
const deps = {
|
|
222
|
+
...createDefaultUpgradeCliDeps(injected?.cwd ?? process.cwd()),
|
|
223
|
+
...injected,
|
|
224
|
+
};
|
|
225
|
+
const { stdout, stderr } = deps;
|
|
226
|
+
const isDryRun = argv.includes("--dry-run");
|
|
227
|
+
const isInternalReexec = argv.includes(INTERNAL_REEXEC_FLAG);
|
|
228
|
+
// ---- Phase 1: CLI freshness — the ONLY thing `VERSION` decides ----
|
|
229
|
+
const target = await resolveUpgradeTarget(argv, deps);
|
|
230
|
+
if (target.confidence === "registry-confirmed" &&
|
|
231
|
+
isNewerVersion(VERSION, target.version) &&
|
|
232
|
+
!isInternalReexec &&
|
|
233
|
+
!isDryRun) {
|
|
234
|
+
stdout(`Update available: ${VERSION} -> ${target.version}. Re-executing from @latest...`);
|
|
235
|
+
return deps.reexecFromLatest(deps.cwd, VERSION);
|
|
236
|
+
}
|
|
237
|
+
if (target.confidence === "running-version-only") {
|
|
238
|
+
// Fail closed. Writing the running version into every project config when we
|
|
239
|
+
// could not confirm it IS the latest would replace an honest stale pin with a
|
|
240
|
+
// confidently wrong one.
|
|
241
|
+
stderr("Bridge API upgrade aborted: could not confirm the latest published version " +
|
|
242
|
+
"(the npm registry lookup failed) and this process was not started from @latest.");
|
|
243
|
+
stderr(` Nothing was changed. Re-run the canonical command: ${CANONICAL_UPGRADE_COMMAND}`);
|
|
244
|
+
return 1;
|
|
245
|
+
}
|
|
246
|
+
// ---- Phase 2: read-only launcher preflight, before ANY side effect ----
|
|
247
|
+
const isCreatable = deps.isCreatable ?? ((relPath) => relPath === ".mcp.json");
|
|
248
|
+
let inspections;
|
|
249
|
+
try {
|
|
250
|
+
inspections = await inspectLauncherConfigs({
|
|
251
|
+
cwd: deps.cwd,
|
|
252
|
+
targetVersion: target.version,
|
|
253
|
+
readFile: deps.readFile,
|
|
254
|
+
isCreatable,
|
|
255
|
+
targets: LAUNCHER_CONFIG_TARGETS,
|
|
256
|
+
});
|
|
257
|
+
}
|
|
258
|
+
catch (err) {
|
|
259
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
260
|
+
stderr(`Bridge API upgrade failed: could not inspect launcher configs: ${msg}`);
|
|
261
|
+
return 1;
|
|
262
|
+
}
|
|
263
|
+
const blocking = inspections.filter(isPrerequisiteFailure);
|
|
264
|
+
const ahead = inspections.filter((i) => i.action === "ahead");
|
|
265
|
+
if (blocking.length > 0 || ahead.length > 0) {
|
|
266
|
+
stderr("Bridge API upgrade aborted — these configs cannot be safely reconciled:");
|
|
267
|
+
for (const found of [...blocking, ...ahead]) {
|
|
268
|
+
stderr(` ${found.relPath}: ${describeLauncherReason(found.reason)}`);
|
|
72
269
|
}
|
|
270
|
+
stderr(" Nothing was changed. Resolve these by hand, then re-run the upgrade.");
|
|
271
|
+
return 1;
|
|
73
272
|
}
|
|
74
|
-
|
|
75
|
-
const oldVersion = oldVersionIdx !== -1 && oldVersionIdx + 1 < argv.length ? argv[oldVersionIdx + 1] : VERSION;
|
|
76
|
-
const targetVersion = isDryRun && isNewer ? latestVersion : VERSION;
|
|
273
|
+
// ---- Phase 3: dry run reports the REAL pre-mutation state and stops ----
|
|
77
274
|
if (isDryRun) {
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
275
|
+
stdout(`\n[Dry Run] Upgrade preview:`);
|
|
276
|
+
if (target.confidence === "registry-confirmed" &&
|
|
277
|
+
isNewerVersion(VERSION, target.version) &&
|
|
278
|
+
!isInternalReexec) {
|
|
279
|
+
stdout(`- Would re-exec from @latest to upgrade the CLI ${VERSION} -> ${target.version}`);
|
|
82
280
|
}
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
281
|
+
stdout(`- Would detect and remove competing local install in node_modules/@bridge_gpt/mcp-server if present.`);
|
|
282
|
+
stdout(`- Launcher pins (target @bridge_gpt/mcp-server@${target.version}):`);
|
|
283
|
+
const lines = inspections
|
|
284
|
+
.map(formatDryRunLine)
|
|
285
|
+
.filter((l) => l !== null);
|
|
286
|
+
if (lines.length === 0) {
|
|
287
|
+
stdout(" (no applicable launcher configs found)");
|
|
288
|
+
}
|
|
289
|
+
else {
|
|
290
|
+
for (const line of lines)
|
|
291
|
+
stdout(line);
|
|
292
|
+
}
|
|
293
|
+
if (isEveryLauncherAlreadyCurrent(toPlannedActions(inspections))) {
|
|
294
|
+
stdout(`- Would print: Already up-to-date.`);
|
|
91
295
|
}
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
console.log(`- Would print: ${oldVersion} -> ${targetVersion} and spawn a fresh reconnect session with command:\n ${spawnCommand}`);
|
|
296
|
+
stdout(`- Would refresh scaffolded artifacts (commands, agents, pipelines).`);
|
|
297
|
+
stdout(`- Would open a fresh reconnect session.`);
|
|
95
298
|
return 0;
|
|
96
299
|
}
|
|
97
|
-
//
|
|
98
|
-
|
|
99
|
-
|
|
300
|
+
// ---- Phase 4: competing local install — a reconciliation, not a transition ----
|
|
301
|
+
if (await deps.hasCompetingLocalInstall(deps.cwd)) {
|
|
302
|
+
stdout("Found a competing local installation in node_modules. Removing so npx resolves the pinned version...");
|
|
303
|
+
const removed = await deps.removeCompetingLocalInstall(deps.cwd);
|
|
304
|
+
if (!removed) {
|
|
305
|
+
// Leaving it in place means npx keeps preferring the stale local copy, so
|
|
306
|
+
// the repin below would be cosmetic. That is not a completed upgrade.
|
|
307
|
+
stderr("Bridge API upgrade failed: could not remove node_modules/@bridge_gpt/mcp-server.");
|
|
308
|
+
stderr(" Remove it manually (rm -rf node_modules/@bridge_gpt/mcp-server), then re-run the upgrade.");
|
|
309
|
+
return 1;
|
|
310
|
+
}
|
|
311
|
+
stdout(" Removed competing local install.");
|
|
312
|
+
}
|
|
313
|
+
// ---- Phase 5: reconcile ----
|
|
314
|
+
let initResult;
|
|
100
315
|
try {
|
|
101
|
-
|
|
102
|
-
await
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
stdio: "inherit",
|
|
108
|
-
cwd,
|
|
109
|
-
});
|
|
110
|
-
child.on("close", (code) => {
|
|
111
|
-
if (code !== 0)
|
|
112
|
-
console.warn("Warning: npm uninstall failed; you may need to remove node_modules/@bridge_gpt/mcp-server manually.");
|
|
113
|
-
resolve();
|
|
114
|
-
});
|
|
115
|
-
child.on("error", () => resolve());
|
|
316
|
+
stdout(`\nReconciling launcher pins to @bridge_gpt/mcp-server@${target.version}...\n`);
|
|
317
|
+
initResult = await deps.runInit(deps.cwd, {
|
|
318
|
+
targetVersion: target.version,
|
|
319
|
+
// Upgrade renders its own truthful per-config report below; the generic
|
|
320
|
+
// "updated entry and pinned version" summary would only contradict it.
|
|
321
|
+
suppressConfigSummary: true,
|
|
116
322
|
});
|
|
117
323
|
}
|
|
118
|
-
catch {
|
|
119
|
-
|
|
324
|
+
catch (err) {
|
|
325
|
+
if (err instanceof InitPreflightError) {
|
|
326
|
+
stderr("Bridge API upgrade aborted — these configs cannot be safely reconciled:");
|
|
327
|
+
for (const failure of err.failures) {
|
|
328
|
+
stderr(` ${failure.relPath}: ${failure.reason}`);
|
|
329
|
+
}
|
|
330
|
+
return 1;
|
|
331
|
+
}
|
|
332
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
333
|
+
stderr(`Bridge API upgrade failed: ${msg}`);
|
|
334
|
+
return 1;
|
|
120
335
|
}
|
|
336
|
+
// ---- Phase 6: verify every claim by re-reading the configs ----
|
|
337
|
+
let verified;
|
|
121
338
|
try {
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
339
|
+
verified = await inspectLauncherConfigs({
|
|
340
|
+
cwd: deps.cwd,
|
|
341
|
+
targetVersion: target.version,
|
|
342
|
+
readFile: deps.readFile,
|
|
343
|
+
isCreatable,
|
|
344
|
+
targets: LAUNCHER_CONFIG_TARGETS,
|
|
345
|
+
});
|
|
128
346
|
}
|
|
129
347
|
catch (err) {
|
|
130
348
|
const msg = err instanceof Error ? err.message : String(err);
|
|
131
|
-
|
|
349
|
+
stderr(`Bridge API upgrade failed: could not verify launcher configs after writing: ${msg}`);
|
|
132
350
|
return 1;
|
|
133
351
|
}
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
352
|
+
const verifiedByPath = new Map(verified.map((v) => [v.relPath, v]));
|
|
353
|
+
const verificationFailures = [];
|
|
354
|
+
for (const action of initResult.configActions) {
|
|
355
|
+
if (action.kind === "skipped-worktree-shim" ||
|
|
356
|
+
action.kind === "skipped-inactive" ||
|
|
357
|
+
action.kind === "skipped-ahead") {
|
|
358
|
+
// Assert the shim really was left alone rather than quietly rewritten.
|
|
359
|
+
const after = verifiedByPath.get(action.relPath);
|
|
360
|
+
if (action.kind === "skipped-worktree-shim" && after?.action !== "skip-worktree-shim") {
|
|
361
|
+
verificationFailures.push(`${action.relPath}: a worktree shim was expected to be preserved but no longer looks like one`);
|
|
362
|
+
}
|
|
363
|
+
continue;
|
|
364
|
+
}
|
|
365
|
+
const after = verifiedByPath.get(action.relPath);
|
|
366
|
+
if (!after) {
|
|
367
|
+
verificationFailures.push(`${action.relPath}: config disappeared after the write`);
|
|
368
|
+
continue;
|
|
369
|
+
}
|
|
370
|
+
if (after.action !== "already-current" || after.currentVersion !== target.version) {
|
|
371
|
+
verificationFailures.push(`${action.relPath}: expected @bridge_gpt/mcp-server@${target.version} after the write, ` +
|
|
372
|
+
`found ${after.spec ?? "no recognizable package token"}`);
|
|
148
373
|
}
|
|
149
374
|
}
|
|
150
|
-
|
|
151
|
-
|
|
375
|
+
// ---- Phase 7: report what actually happened ----
|
|
376
|
+
const resultLines = initResult.configActions
|
|
377
|
+
.map(formatResultLine)
|
|
378
|
+
.filter((l) => l !== null);
|
|
379
|
+
if (resultLines.length > 0) {
|
|
380
|
+
stdout("Launcher pins:");
|
|
381
|
+
for (const line of resultLines)
|
|
382
|
+
stdout(line);
|
|
383
|
+
}
|
|
384
|
+
stdout("Refreshed scaffolded artifacts (commands, agents, pipelines).");
|
|
385
|
+
if (verificationFailures.length > 0) {
|
|
386
|
+
stderr("\nBridge API upgrade did NOT converge:");
|
|
387
|
+
for (const failure of verificationFailures)
|
|
388
|
+
stderr(` ${failure}`);
|
|
389
|
+
stderr(" Re-run the upgrade, or repair the listed configs by hand.");
|
|
390
|
+
return 1;
|
|
391
|
+
}
|
|
392
|
+
if (!initResult.ok) {
|
|
393
|
+
// `runInit` already printed the path-specific readiness warning. Do not add a
|
|
394
|
+
// completion line on top of it — a config that authenticates as nobody is not
|
|
395
|
+
// a finished upgrade.
|
|
396
|
+
stderr("\nBridge API upgrade finished with an UNCONFIGURED MCP entry (see above).");
|
|
397
|
+
return 1;
|
|
398
|
+
}
|
|
399
|
+
const applicableStandard = verified.filter(isApplicableStandardLauncher);
|
|
400
|
+
if (isEveryLauncherAlreadyCurrent(initResult.configActions)) {
|
|
401
|
+
// The exact string `/upgrade-bridge` is contracted to relay verbatim.
|
|
402
|
+
stdout("\nAlready up-to-date.");
|
|
403
|
+
}
|
|
404
|
+
else if (applicableStandard.length === 0 && resultLines.length > 0) {
|
|
405
|
+
// Only worktree shims (or nothing applicable) — reporting "current" here
|
|
406
|
+
// would claim something about pins that were never evaluated.
|
|
407
|
+
stdout("\nNo standard launcher pin was changed.");
|
|
408
|
+
}
|
|
409
|
+
else {
|
|
410
|
+
stdout(`\nUpgrade complete: launcher pins are at ${target.version}.`);
|
|
411
|
+
stdout("Please reconnect your MCP now.");
|
|
412
|
+
}
|
|
413
|
+
// ---- Phase 8: reconnect — convenience only, never changes the verdict ----
|
|
414
|
+
const spawnCommand = buildGenericAgentShellCommand(AGENT_REGISTRY.claude, "Bridge API MCP server has been upgraded. Please reload/reconnect the MCP server in your environment.", deps.cwd, process.platform);
|
|
415
|
+
const spawned = await deps.spawnReconnect(deps.cwd, spawnCommand);
|
|
416
|
+
if (!spawned) {
|
|
417
|
+
stdout(`\nTo continue, manually run:\n${spawnCommand}\n`);
|
|
152
418
|
}
|
|
153
419
|
return 0;
|
|
154
420
|
}
|
|
421
|
+
/**
|
|
422
|
+
* Project pre-mutation inspections onto the action shape `isEveryLauncherAlreadyCurrent`
|
|
423
|
+
* consumes, so the dry run answers the currentness question exactly the way the
|
|
424
|
+
* real run will rather than with a second, drifting rule.
|
|
425
|
+
*/
|
|
426
|
+
function toPlannedActions(inspections) {
|
|
427
|
+
const actions = [];
|
|
428
|
+
for (const found of inspections) {
|
|
429
|
+
switch (found.action) {
|
|
430
|
+
case "repin":
|
|
431
|
+
actions.push({ relPath: found.relPath, kind: "repinned" });
|
|
432
|
+
break;
|
|
433
|
+
case "already-current":
|
|
434
|
+
actions.push({ relPath: found.relPath, kind: "already-current" });
|
|
435
|
+
break;
|
|
436
|
+
case "normalize-unpinned":
|
|
437
|
+
actions.push({ relPath: found.relPath, kind: "normalized" });
|
|
438
|
+
break;
|
|
439
|
+
case "add":
|
|
440
|
+
actions.push({ relPath: found.relPath, kind: "added" });
|
|
441
|
+
break;
|
|
442
|
+
case "create":
|
|
443
|
+
actions.push({ relPath: found.relPath, kind: "created" });
|
|
444
|
+
break;
|
|
445
|
+
case "skip-worktree-shim":
|
|
446
|
+
actions.push({ relPath: found.relPath, kind: "skipped-worktree-shim" });
|
|
447
|
+
break;
|
|
448
|
+
case "skip-inactive":
|
|
449
|
+
actions.push({ relPath: found.relPath, kind: "skipped-inactive" });
|
|
450
|
+
break;
|
|
451
|
+
default:
|
|
452
|
+
break;
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
return actions;
|
|
456
|
+
}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
// AUTO-GENERATED — do not edit manually. Regenerate with: npm run build
|
|
2
|
-
export const VERSION = "0.2.
|
|
2
|
+
export const VERSION = "0.2.37";
|