@bridge_gpt/mcp-server 0.2.38 → 0.2.41
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 +189 -14
- package/build/agent-capabilities/probe-context.js +2 -1
- package/build/agent-launchers/claude-executor-adapter.js +392 -0
- package/build/agent-launchers/executor-adapter-inspection.js +163 -0
- package/build/agent-launchers/executor-adapter-registry.js +90 -0
- package/build/agent-launchers/executor-adapter.js +136 -0
- package/build/agent-registry.js +28 -0
- package/build/agents.generated.js +1 -1
- package/build/claude-login.js +85 -0
- package/build/claude-user-config-doctor.js +59 -33
- package/build/commands.generated.js +12 -11
- package/build/conduct-epic/bridge-client.js +345 -0
- package/build/conduct-epic/checkpoint-store.js +479 -0
- package/build/conduct-epic/cli.js +1765 -0
- package/build/conduct-epic/lock.js +302 -0
- package/build/conduct-epic/pr-state.js +286 -0
- package/build/conduct-epic/spawn.js +101 -0
- package/build/conductor/bridge-api-client.js +37 -2
- package/build/conductor/doctor.js +11 -1
- package/build/conductor/install-doctor.js +184 -10
- package/build/conductor-bin.js +7 -7
- package/build/credential-store.js +10 -4
- package/build/credentials-cli.js +34 -19
- package/build/docs.generated.js +1 -1
- package/build/doctor.js +579 -88
- package/build/executor/agent-identity.js +32 -0
- package/build/executor/cli.js +50 -39
- package/build/executor/deps.js +15 -1
- package/build/executor/env.js +56 -45
- package/build/executor/index.js +9 -1
- package/build/executor/install-preflight.js +138 -0
- package/build/executor/job-errors.js +200 -0
- package/build/executor/job-runner.js +619 -268
- package/build/executor/observation.js +165 -0
- package/build/executor/permissions.js +163 -36
- package/build/executor/platform.js +54 -0
- package/build/executor/preflight.js +175 -67
- package/build/executor/process.js +39 -7
- package/build/executor/runner.js +19 -0
- package/build/executor/service-lifecycle.js +269 -0
- package/build/executor/service-unit.js +121 -12
- package/build/executor/stale-artifacts.js +70 -0
- package/build/executor/test-clock.js +188 -24
- package/build/executor/worker-command.js +22 -58
- package/build/executor/worker-log.js +82 -0
- package/build/executor/worktree-lock.js +264 -0
- package/build/index.js +527 -357
- package/build/install-bridge-conductor.js +376 -38
- package/build/install-bridge.js +414 -114
- package/build/install-doctor.js +13 -0
- package/build/install-reexec.js +5 -3
- package/build/mcp-install-state.js +130 -0
- package/build/mcp-profile.js +11 -2
- package/build/mcp-provisioning.js +15 -0
- package/build/merge-pull-request.js +562 -0
- package/build/phase-result-artifacts.js +450 -0
- package/build/pipeline-orchestrator.js +4 -0
- package/build/pipeline-utils.js +16 -0
- package/build/pipelines.generated.js +7 -7
- package/build/plane/preflight.js +18 -14
- package/build/plane/supervisor.js +8 -1
- package/build/project-root.js +34 -0
- package/build/readme.generated.js +1 -1
- package/build/run-unit-tests-launcher.js +36 -9
- package/build/setup-epic.js +57 -4
- package/build/sfcc/permissions.js +25 -6
- package/build/sfcc/reads-site-preference.js +6 -0
- package/build/sfcc/register.js +61 -23
- package/build/sfcc/registration-inventory.js +89 -0
- package/build/sfcc/setup-status.js +18 -34
- package/build/sfcc/tool-wrapper.js +294 -17
- package/build/sfcc/write-grants.js +33 -1
- package/build/sfcc/write-guard.js +41 -12
- package/build/sfcc/writes-custom-object-def.js +6 -2
- package/build/sfcc/writes-site-preference.js +6 -1
- package/build/sfcc/writes-system-object.js +11 -2
- package/build/sfcc/writes.js +13 -8
- package/build/start-tickets-prereqs.js +25 -15
- package/build/start-tickets.js +123 -21
- package/build/version.generated.js +1 -1
- package/build/worktree-core.js +9 -3
- package/docs/install/mcp-tool-integrations.md +54 -9
- package/docs/install/sfcc-integration.md +71 -24
- package/package.json +3 -3
- package/build/executor/worker-config-isolation.js +0 -287
package/build/install-doctor.js
CHANGED
|
@@ -21,6 +21,7 @@
|
|
|
21
21
|
*/
|
|
22
22
|
import path from "path";
|
|
23
23
|
import { resolveBapiCredentials } from "./credential-store.js";
|
|
24
|
+
import { detectClaudeLogin, formatClaudeLoginAdvisory } from "./claude-login.js";
|
|
24
25
|
/** Default production base URL (mirrors install-bridge's DEFAULT_BAPI_BASE_URL). */
|
|
25
26
|
const DEFAULT_BASE_URL = "https://bridgegpt-api.com";
|
|
26
27
|
/**
|
|
@@ -181,6 +182,18 @@ function readGithubConfiguredFlag(body) {
|
|
|
181
182
|
*/
|
|
182
183
|
export async function collectInstallStatusChecks(deps) {
|
|
183
184
|
const checks = [];
|
|
185
|
+
// Advisory-only Claude login marker (BAPI-791), independent of Bridge repo
|
|
186
|
+
// identity/credential resolution below — a worker authenticates through the
|
|
187
|
+
// executor host's own interactive `claude login`, not through anything this
|
|
188
|
+
// install-status section resolves. Bridge stores or diagnoses no Anthropic
|
|
189
|
+
// credential of its own, so this can only ever advise, never confirm.
|
|
190
|
+
const loginResult = await detectClaudeLogin({ homedir: deps.homedir, readFile: deps.readFile });
|
|
191
|
+
checks.push({
|
|
192
|
+
id: "claude-login",
|
|
193
|
+
label: "Claude login",
|
|
194
|
+
status: loginResult.detected ? "PASS" : "INFO",
|
|
195
|
+
detail: formatClaudeLoginAdvisory(loginResult),
|
|
196
|
+
});
|
|
184
197
|
const target = await resolveInstallDoctorTarget(deps);
|
|
185
198
|
if (!target.repoName) {
|
|
186
199
|
checks.push({
|
package/build/install-reexec.js
CHANGED
|
@@ -163,9 +163,11 @@ export async function runInstallBridgeWithLatestCli(argv, deps = {}) {
|
|
|
163
163
|
const errorLog = deps.errorLog ?? ((message) => console.error(message));
|
|
164
164
|
const localVersion = deps.localVersion ?? VERSION;
|
|
165
165
|
const { args: cleanedArgs, sentinelPresent } = stripInternalReexecSentinels(argv);
|
|
166
|
-
// Terminating condition,
|
|
167
|
-
// registry entirely.
|
|
168
|
-
|
|
166
|
+
// Terminating condition, the nested-conductor bypass, and `--dry-run`
|
|
167
|
+
// (BAPI-818, R2) all skip the registry entirely. A dry run's documented
|
|
168
|
+
// contract is "no network" — a version-freshness lookup is still a network
|
|
169
|
+
// call, so it cannot run first even though it is fail-open and unauthenticated.
|
|
170
|
+
if (sentinelPresent || cleanedArgs[0] === "conductor" || cleanedArgs.includes("--dry-run")) {
|
|
169
171
|
return runLocal(cleanedArgs);
|
|
170
172
|
}
|
|
171
173
|
// Default the comparison target to the local version: an unusable registry
|
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
* All filesystem access is through injected dependencies so the module is fully
|
|
16
16
|
* unit-testable with in-memory fakes.
|
|
17
17
|
*/
|
|
18
|
+
import { executorIdFromLaunchdFilename, executorIdFromSystemdFilename, } from "./executor/service-unit.js";
|
|
18
19
|
import { HOST_PLATFORM_ORDER, isHostPlatformId, } from "./mcp-host-targets.js";
|
|
19
20
|
/** Current on-disk schema version. */
|
|
20
21
|
export const MCP_INSTALL_STATE_VERSION = 1;
|
|
@@ -60,6 +61,74 @@ function normalizeProjectPaths(paths) {
|
|
|
60
61
|
return out;
|
|
61
62
|
}
|
|
62
63
|
// ---------------------------------------------------------------------------
|
|
64
|
+
// Executor service-unit locators (BAPI-779)
|
|
65
|
+
// ---------------------------------------------------------------------------
|
|
66
|
+
/** Home-relative prefix of the darwin LaunchAgents convention. */
|
|
67
|
+
const EXECUTOR_LAUNCHD_LOCATOR_PREFIX = "~/Library/LaunchAgents/";
|
|
68
|
+
/** Home-relative prefix of the linux systemd `--user` convention. */
|
|
69
|
+
const EXECUTOR_SYSTEMD_LOCATOR_PREFIX = "~/.config/systemd/user/";
|
|
70
|
+
/**
|
|
71
|
+
* Accept ONLY the two generated-unit conventions, and only in home-relative
|
|
72
|
+
* form.
|
|
73
|
+
*
|
|
74
|
+
* Everything else is rejected: an arbitrary absolute path (`/tmp/evil.service`),
|
|
75
|
+
* a system-domain unit (`/etc/systemd/system/x.service`), a traversal segment,
|
|
76
|
+
* a wrong extension, or a filename the generator's own parsers do not recognize
|
|
77
|
+
* as one of its units. Install state is a diagnostic record of what THIS tool
|
|
78
|
+
* generated — letting it name any path on the filesystem would turn a
|
|
79
|
+
* secret-free inventory into an attacker-influenced pointer list.
|
|
80
|
+
*/
|
|
81
|
+
function isGeneratedExecutorUnitLocator(value) {
|
|
82
|
+
if (typeof value !== "string" || value.length === 0)
|
|
83
|
+
return false;
|
|
84
|
+
if (value.includes("..") || value.includes("\\") || value.includes("\0"))
|
|
85
|
+
return false;
|
|
86
|
+
if (value.startsWith(EXECUTOR_LAUNCHD_LOCATOR_PREFIX)) {
|
|
87
|
+
const filename = value.slice(EXECUTOR_LAUNCHD_LOCATOR_PREFIX.length);
|
|
88
|
+
return filename.length > 0 && executorIdFromLaunchdFilename(filename) !== null;
|
|
89
|
+
}
|
|
90
|
+
if (value.startsWith(EXECUTOR_SYSTEMD_LOCATOR_PREFIX)) {
|
|
91
|
+
const filename = value.slice(EXECUTOR_SYSTEMD_LOCATOR_PREFIX.length);
|
|
92
|
+
return filename.length > 0 && executorIdFromSystemdFilename(filename) !== null;
|
|
93
|
+
}
|
|
94
|
+
return false;
|
|
95
|
+
}
|
|
96
|
+
/** Validate, dedupe, and stably order generated-unit locators. */
|
|
97
|
+
function normalizeExecutorServiceUnitPaths(paths) {
|
|
98
|
+
const seen = new Set();
|
|
99
|
+
const out = [];
|
|
100
|
+
for (const candidate of paths) {
|
|
101
|
+
if (typeof candidate !== "string")
|
|
102
|
+
continue;
|
|
103
|
+
if (!isGeneratedExecutorUnitLocator(candidate))
|
|
104
|
+
continue;
|
|
105
|
+
if (seen.has(candidate))
|
|
106
|
+
continue;
|
|
107
|
+
seen.add(candidate);
|
|
108
|
+
out.push(candidate);
|
|
109
|
+
}
|
|
110
|
+
out.sort();
|
|
111
|
+
return out;
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Convert an absolute generated-unit path into its canonical home-relative
|
|
115
|
+
* locator, or `null` when it is not one of this tool's generated units.
|
|
116
|
+
*
|
|
117
|
+
* Returning `null` rather than a best-effort string is deliberate: a caller that
|
|
118
|
+
* cannot produce a canonical locator must record nothing, not record a path the
|
|
119
|
+
* schema would have rejected anyway.
|
|
120
|
+
*/
|
|
121
|
+
export function canonicalExecutorServiceUnitLocator(unitPath, homeDir) {
|
|
122
|
+
if (typeof unitPath !== "string" || typeof homeDir !== "string" || homeDir.length === 0) {
|
|
123
|
+
return null;
|
|
124
|
+
}
|
|
125
|
+
const normalizedHome = homeDir.endsWith("/") ? homeDir.slice(0, -1) : homeDir;
|
|
126
|
+
if (!unitPath.startsWith(`${normalizedHome}/`))
|
|
127
|
+
return null;
|
|
128
|
+
const locator = `~/${unitPath.slice(normalizedHome.length + 1)}`;
|
|
129
|
+
return isGeneratedExecutorUnitLocator(locator) ? locator : null;
|
|
130
|
+
}
|
|
131
|
+
// ---------------------------------------------------------------------------
|
|
63
132
|
// Read
|
|
64
133
|
// ---------------------------------------------------------------------------
|
|
65
134
|
function isEnoent(err) {
|
|
@@ -116,12 +185,24 @@ export async function readMcpInstallState(cwd, deps) {
|
|
|
116
185
|
const projectConfigPaths = Array.isArray(rawPaths)
|
|
117
186
|
? rawPaths.filter((p) => typeof p === "string")
|
|
118
187
|
: [];
|
|
188
|
+
// An ABSENT field is the ordinary legacy-document case and must read as an
|
|
189
|
+
// empty roster, not as an invalid document — every state file written before
|
|
190
|
+
// BAPI-779 lacks it. A present-but-wrong-type field is a real schema
|
|
191
|
+
// violation and is reported as such.
|
|
192
|
+
const rawUnitPaths = doc.executorServiceUnitPaths;
|
|
193
|
+
if (rawUnitPaths !== undefined && !Array.isArray(rawUnitPaths)) {
|
|
194
|
+
return { status: "invalid", reason: "executorServiceUnitPaths is not an array" };
|
|
195
|
+
}
|
|
119
196
|
return {
|
|
120
197
|
status: "valid",
|
|
121
198
|
state: {
|
|
122
199
|
version: MCP_INSTALL_STATE_VERSION,
|
|
123
200
|
selectedPlatforms: normalizePlatforms(rawPlatforms),
|
|
124
201
|
projectConfigPaths: normalizeProjectPaths(projectConfigPaths),
|
|
202
|
+
// Entries that are not generated-unit locators are DROPPED rather than
|
|
203
|
+
// failing the read: they cannot be acted on, and losing the whole
|
|
204
|
+
// document over one bad entry would take the platform roster with it.
|
|
205
|
+
executorServiceUnitPaths: normalizeExecutorServiceUnitPaths(Array.isArray(rawUnitPaths) ? rawUnitPaths : []),
|
|
125
206
|
},
|
|
126
207
|
};
|
|
127
208
|
}
|
|
@@ -135,6 +216,10 @@ export function serializeMcpInstallState(state) {
|
|
|
135
216
|
version: state.version,
|
|
136
217
|
selectedPlatforms: state.selectedPlatforms,
|
|
137
218
|
projectConfigPaths: state.projectConfigPaths,
|
|
219
|
+
// Re-normalized at serialization time, not trusted from the input object:
|
|
220
|
+
// this function is the last gate before bytes hit disk, and a caller could
|
|
221
|
+
// hand it a hand-built state carrying an arbitrary path.
|
|
222
|
+
executorServiceUnitPaths: normalizeExecutorServiceUnitPaths(state.executorServiceUnitPaths ?? []),
|
|
138
223
|
};
|
|
139
224
|
return JSON.stringify(ordered, null, 2) + "\n";
|
|
140
225
|
}
|
|
@@ -146,10 +231,16 @@ export function serializeMcpInstallState(state) {
|
|
|
146
231
|
* secret-free error without reporting success.
|
|
147
232
|
*/
|
|
148
233
|
export async function writeMcpInstallState(cwd, input, deps) {
|
|
234
|
+
const existing = await readMcpInstallState(cwd, deps);
|
|
235
|
+
const priorUnitPaths = existing.status === "valid" ? existing.state.executorServiceUnitPaths : [];
|
|
149
236
|
const state = {
|
|
150
237
|
version: MCP_INSTALL_STATE_VERSION,
|
|
151
238
|
selectedPlatforms: normalizePlatforms(input.selectedPlatforms),
|
|
152
239
|
projectConfigPaths: normalizeProjectPaths(input.projectConfigPaths),
|
|
240
|
+
executorServiceUnitPaths: normalizeExecutorServiceUnitPaths([
|
|
241
|
+
...priorUnitPaths,
|
|
242
|
+
...(input.executorServiceUnitPaths ?? []),
|
|
243
|
+
]),
|
|
153
244
|
};
|
|
154
245
|
const finalPath = installStatePath(cwd);
|
|
155
246
|
return persistMcpInstallState(cwd, state, finalPath, deps);
|
|
@@ -198,10 +289,49 @@ export async function recordInstalledProjectArtifact(cwd, relPath, deps) {
|
|
|
198
289
|
const existing = await readMcpInstallState(cwd, deps);
|
|
199
290
|
const priorPlatforms = existing.status === "valid" ? existing.state.selectedPlatforms : [];
|
|
200
291
|
const priorPaths = existing.status === "valid" ? existing.state.projectConfigPaths : [];
|
|
292
|
+
const priorUnitPaths = existing.status === "valid" ? existing.state.executorServiceUnitPaths : [];
|
|
201
293
|
const state = {
|
|
202
294
|
version: MCP_INSTALL_STATE_VERSION,
|
|
203
295
|
selectedPlatforms: normalizePlatforms(priorPlatforms),
|
|
204
296
|
projectConfigPaths: normalizeProjectPaths([...priorPaths, relPath]),
|
|
297
|
+
// Preserved, not rebuilt: recording a workflow file must not drop the
|
|
298
|
+
// executor units a previous conductor run recorded (the same additive rule
|
|
299
|
+
// that motivated this helper for the platform roster).
|
|
300
|
+
executorServiceUnitPaths: normalizeExecutorServiceUnitPaths(priorUnitPaths),
|
|
301
|
+
};
|
|
302
|
+
return persistMcpInstallState(cwd, state, installStatePath(cwd), deps);
|
|
303
|
+
}
|
|
304
|
+
/**
|
|
305
|
+
* Record a generated executor service unit WITHOUT clobbering existing state.
|
|
306
|
+
*
|
|
307
|
+
* Mirrors {@link recordInstalledProjectArtifact}: read the current document,
|
|
308
|
+
* write back the union of every dimension. The caller supplies the ABSOLUTE
|
|
309
|
+
* unit path the generator returned plus the home directory it was rendered
|
|
310
|
+
* against; this helper canonicalizes them into the home-relative locator the
|
|
311
|
+
* schema accepts, and refuses anything that is not one of the two generated
|
|
312
|
+
* conventions.
|
|
313
|
+
*
|
|
314
|
+
* No secret can reach the file: the locator is a fixed-shape path under two
|
|
315
|
+
* known directories, and the serializer emits only schema-approved keys.
|
|
316
|
+
*/
|
|
317
|
+
export async function recordInstalledExecutorServiceUnit(cwd, unitPath, homeDir, deps) {
|
|
318
|
+
const locator = canonicalExecutorServiceUnitLocator(unitPath, homeDir);
|
|
319
|
+
if (locator === null) {
|
|
320
|
+
return {
|
|
321
|
+
ok: false,
|
|
322
|
+
error: "the unit path is not a generated executor service unit under the supported " +
|
|
323
|
+
"darwin LaunchAgents or linux systemd --user convention; nothing was recorded.",
|
|
324
|
+
};
|
|
325
|
+
}
|
|
326
|
+
const existing = await readMcpInstallState(cwd, deps);
|
|
327
|
+
const priorPlatforms = existing.status === "valid" ? existing.state.selectedPlatforms : [];
|
|
328
|
+
const priorPaths = existing.status === "valid" ? existing.state.projectConfigPaths : [];
|
|
329
|
+
const priorUnitPaths = existing.status === "valid" ? existing.state.executorServiceUnitPaths : [];
|
|
330
|
+
const state = {
|
|
331
|
+
version: MCP_INSTALL_STATE_VERSION,
|
|
332
|
+
selectedPlatforms: normalizePlatforms(priorPlatforms),
|
|
333
|
+
projectConfigPaths: normalizeProjectPaths(priorPaths),
|
|
334
|
+
executorServiceUnitPaths: normalizeExecutorServiceUnitPaths([...priorUnitPaths, locator]),
|
|
205
335
|
};
|
|
206
336
|
return persistMcpInstallState(cwd, state, installStatePath(cwd), deps);
|
|
207
337
|
}
|
package/build/mcp-profile.js
CHANGED
|
@@ -14,16 +14,19 @@ const VALID_GROUPS = new Set([
|
|
|
14
14
|
"conductor",
|
|
15
15
|
"pipeline-authoring",
|
|
16
16
|
"sfcc",
|
|
17
|
+
"sfcc-write",
|
|
17
18
|
]);
|
|
18
19
|
/**
|
|
19
20
|
* Resolve the active MCP groups from a raw env-var string.
|
|
20
21
|
*
|
|
21
22
|
* - `undefined` or blank → `{"core"}` (fail-safe to smallest surface).
|
|
22
|
-
* - `"full"` (any case) → all
|
|
23
|
+
* - `"full"` (any case) → all five groups, `sfcc-write` included.
|
|
23
24
|
* - Comma-separated list: each token is trimmed, lowercased, and checked
|
|
24
25
|
* against VALID_GROUPS. Unknown tokens are dropped. Known tokens are unioned
|
|
25
26
|
* with the implicit `"core"` baseline.
|
|
26
27
|
* - All-unknown tokens → `{"core"}`.
|
|
28
|
+
* - No group implies another beyond the `"core"` baseline — in particular
|
|
29
|
+
* `sfcc` and `sfcc-write` are independent in both directions (BAPI-817).
|
|
27
30
|
* - Raw input is never echoed in the output (security NFR).
|
|
28
31
|
*/
|
|
29
32
|
export function resolveProfiles(raw) {
|
|
@@ -35,7 +38,13 @@ export function resolveProfiles(raw) {
|
|
|
35
38
|
return baseline;
|
|
36
39
|
const tokens = trimmed.split(",").map((t) => t.trim().toLowerCase());
|
|
37
40
|
if (tokens.some((t) => t === "full")) {
|
|
38
|
-
return new Set([
|
|
41
|
+
return new Set([
|
|
42
|
+
"core",
|
|
43
|
+
"conductor",
|
|
44
|
+
"pipeline-authoring",
|
|
45
|
+
"sfcc",
|
|
46
|
+
"sfcc-write",
|
|
47
|
+
]);
|
|
39
48
|
}
|
|
40
49
|
for (const token of tokens) {
|
|
41
50
|
if (VALID_GROUPS.has(token)) {
|
|
@@ -307,6 +307,21 @@ function withWarnings(row, warnings) {
|
|
|
307
307
|
/**
|
|
308
308
|
* Provision MCP registrations for one created worktree row.
|
|
309
309
|
*
|
|
310
|
+
* FAIL-OPEN, and BAPI-790 makes that posture worth stating explicitly, because a
|
|
311
|
+
* fail-CLOSED requirement now sits directly downstream of it. This function
|
|
312
|
+
* ATTEMPTS the write and degrades to warnings; the executor then separately
|
|
313
|
+
* VERIFIES that a usable `.mcp.json` exists and registers `bridge-api`, and
|
|
314
|
+
* refuses to spawn when it does not (`verifyRequiredWorktreeMcpRegistration` in
|
|
315
|
+
* `executor/job-runner.ts`). The split is deliberate: a provisioning hiccup on a
|
|
316
|
+
* Tier-2 target must not kill a job, but a worker that loads MCP servers strictly
|
|
317
|
+
* from this file must not start if the file cannot serve that role. Keep the two
|
|
318
|
+
* postures distinct — folding the requirement in here would put it behind this
|
|
319
|
+
* function's fail-open catch and silently disarm it.
|
|
320
|
+
*
|
|
321
|
+
* The same file is also the source of the `--mcp-config` path and of the expected
|
|
322
|
+
* server-name set the per-spawn init-event assertion checks, so what this writes
|
|
323
|
+
* is exactly what a worker is later held to.
|
|
324
|
+
*
|
|
310
325
|
* - Non-`created` rows and rows without a path are returned unchanged.
|
|
311
326
|
* - A missing / malformed manifest yields a non-fatal warning and leaves the row
|
|
312
327
|
* status unchanged.
|